Subject: bug#10474: Building guile 2.x under mingw + msys

From: Eli Zaretskii <[email-masked]>
Date: Tue, 17 Jan 2012 20:14:58 +0200

> Date: Tue, 17 Jan 2012 10:09:21 +0200
> From: Eli Zaretskii <[email-masked]>
> CC: [email-masked], [email-masked]
> 
> The problem, AFAIK, is entirely different and quite mundane:
> canonicalize_file_name simply does not support Windows-style
> D:/foo/bar file names, nor does it support backslashes as separators
> in file names.
> 
> My evidence is that I added an fprintf to canonicalize-path where it
> calls canonicalize_file_name, and the file name passed to it was
> entirely reasonable, something like
> 
>    D:\path\to\guile-2.03/module/ice-9/boot-9.scm
> 
> (or some such, I'm writing this from my faulty memory).
> 
> I think I know how to fix canonicalize_file_name, and I will send a
> tentative patch later, perhaps even today, when I have it working and
> get past this abort.

The patch to fix canonicalize_file_name is below.  It gets me through
the "GEN guile-procedures.texi" step.

The next failure is this:

       GEN    guile-procedures.texi
     Backtrace:
     In unknown file:
	?: 3 [boot-closure #t #<catch-closure 2903300> ...]
	?: 2 [catch-closure]
	?: 1 [primitive-eval ((@ # %) (begin # # #))]
	?: 0 [primitive-load-path "d:\\usr\\eli\\utils\\guile-2.0.3\\libguile/d:/usr/eli/utils/guile-2.0.3/meta/guild"]

     ERROR: In procedure primitive-load-path:
     ERROR: In procedure canonicalize-path: No such file or directory

which again looks like failure to treat d:/foo/bar file names as
absolute.

Some random poking around reveals this in ice-9/boot-9.scm:

    (define (absolute-path? path)
      (string-prefix? "/" path))

which looks a likely culprit.  Changing it to recognize d:/foo/bar
absolute file names indeed solves this problem.  (I'm too embarrassed
to show the changes I did for that, as I know nothing about Scheme,
and my code is too ugly to go into upstream.  I don't even know how to
condition the change on the underlying platform being MS-Windows.)

The next problem is here:

       GUILEC ice-9/eval.go
     Backtrace:
     In unknown file:
	?: 5 [boot-closure #t #<catch-closure 231fd40> ...]
	?: 4 [catch-closure]
	?: 3 [primitive-eval ((@ # %) (begin # # #))]
	?: 2 [chmod #<input-output: ice-9/eval.go.9IiPxC 6> 438]
	?: 1 [boot-closure system-error "chmod" ...]
	?: 0 [delete-file "ice-9/eval.go.9IiPxC"]

     ERROR: In procedure delete-file:
     ERROR: In procedure delete-file: Permission denied

This happens because Guile calls fchmod, which is #define'd to -1 on
MinGW.

Solution: #define it to zero, since chmod is mostly a no-op on Windows
anyway.  This passes the eval.go step (and then fails while compiling
psyntax-pp.go; to be continued...).

As an aside, if this kind of operation is a frequent one, I'd suggest
to change the caller so that it never uses fchmod on MS-Windows,
because this function cannot be implemented there, except on the
latest versions (Windows 7 etc.).  Use chmod instead, even if that is
less efficient.

Btw, why doesn't the backtrace show file names, but says "unknown
file"?  Is this expected during the build, or is there another bug to
look for?  If the latter, any hints as to where to look for the
reason(s)?

Here's the patch for canonicalize-lgpl.c to make it Windows-friendly.
I will send it to the gnulib developers as well.

--- lib/canonicalize-lgpl.c~0	2011-10-22 16:19:34.000000000 +0200
+++ lib/canonicalize-lgpl.c	2012-01-17 13:10:52.608428500 +0200
@@ -51,6 +51,7 @@
 # define __realpath realpath
 # include "pathmax.h"
 # include "malloca.h"
+# include "dosname.h"
 # if HAVE_GETCWD
 #  if IN_RELOCWRAPPER
     /* When building the relocatable program wrapper, use the system's getcwd
@@ -101,6 +102,7 @@ __realpath (const char *name, char *reso
   const char *start, *end, *rpath_limit;
   long int path_max;
   int num_links = 0;
+  size_t prefix_len;
 
   if (name == NULL)
     {
@@ -143,7 +145,11 @@ __realpath (const char *name, char *reso
     rpath = resolved;
   rpath_limit = rpath + path_max;
 
-  if (name[0] != '/')
+  /* This is always zero for Posix hosts, but can be 2 for MS-Windows
+     and MS-DOS X:/foo/bar file names.  */
+  prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
+
+  if (!IS_ABSOLUTE_FILE_NAME (name))
     {
       if (!__getcwd (rpath, path_max))
         {
@@ -154,13 +160,19 @@ __realpath (const char *name, char *reso
     }
   else
     {
-      rpath[0] = '/';
-      dest = rpath + 1;
-      if (DOUBLE_SLASH_IS_DISTINCT_ROOT && name[1] == '/')
+      dest = rpath;
+      if (prefix_len)
+	{
+	  memcpy (rpath, name, prefix_len);
+	  dest += prefix_len;
+	}
+      *dest++ = '/';
+      if (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (name[1])
+	  && !prefix_len)
         *dest++ = '/';
     }
 
-  for (start = end = name; *start; start = end)
+  for (start = end = name + prefix_len; *start; start = end)
     {
 #ifdef _LIBC
       struct stat64 st;
@@ -170,11 +182,11 @@ __realpath (const char *name, char *reso
       int n;
 
       /* Skip sequence of multiple path-separators.  */
-      while (*start == '/')
+      while (ISSLASH (*start))
         ++start;
 
       /* Find end of path component.  */
-      for (end = start; *end && *end != '/'; ++end)
+      for (end = start; *end && !ISSLASH (*end); ++end)
         /* Nothing.  */;
 
       if (end - start == 0)
@@ -184,7 +196,7 @@ __realpath (const char *name, char *reso
       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
         {
           /* Back up to previous component, ignore if at root already.  */
-          if (dest > rpath + 1)
+          if (dest > rpath + prefix_len + 1)
             while ((--dest)[-1] != '/');
           if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
               && *dest == '/')
@@ -205,7 +217,7 @@ __realpath (const char *name, char *reso
               if (resolved)
                 {
                   __set_errno (ENAMETOOLONG);
-                  if (dest > rpath + 1)
+                  if (dest > rpath + prefix_len + 1)
                     dest--;
                   *dest = '\0';
                   goto error;
@@ -295,17 +307,25 @@ __realpath (const char *name, char *reso
               memmove (&extra_buf[n], end, len + 1);
               name = end = memcpy (extra_buf, buf, n);
 
-              if (buf[0] == '/')
+              if (IS_ABSOLUTE_FILE_NAME (buf))
                 {
-                  dest = rpath + 1;     /* It's an absolute symlink */
-                  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && buf[1] == '/')
+		  size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf);
+
+		  if (pfxlen)
+		    memcpy (rpath, buf, pfxlen);
+                  dest = rpath + pfxlen;     /* It's an absolute symlink.  */
+		  *dest++ = '/';
+                  if (DOUBLE_SLASH_IS_DISTINCT_ROOT && ISSLASH (buf[1])
+		      && pfxlen == 0)
                     *dest++ = '/';
+		  /* Install the new prefix to be in effect hereafter.  */
+		  prefix_len = pfxlen;
                 }
               else
                 {
                   /* Back up to previous component, ignore if at root
                      already: */
-                  if (dest > rpath + 1)
+                  if (dest > rpath + prefix_len + 1)
                     while ((--dest)[-1] != '/');
                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
                       && *dest == '/')
@@ -319,7 +339,7 @@ __realpath (const char *name, char *reso
             }
         }
     }
-  if (dest > rpath + 1 && dest[-1] == '/')
+  if (dest > rpath + prefix_len + 1 && dest[-1] == '/')
     --dest;
   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && *dest == '/')
     dest++;