Implement linker-compatible library search.
authorBoris Kolpackov <boris@kolpackov.net>
Thu, 12 Nov 2009 16:42:36 +0000 (16:42 +0000)
committerBoris Kolpackov <boris@kolpackov.net>
Thu, 12 Nov 2009 16:42:36 +0000 (16:42 +0000)
ChangeLog
NEWS
doc/make.texi
implicit.c
make.h
remake.c
tests/ChangeLog
tests/scripts/features/vpath3 [new file with mode: 0644]
vpath.c

index 8ee33c437a0620de0dd1709a7fa7b7221b44e9d1..e8c89d13255fae95866fea1870b362ebb18f49ba 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2009-11-12  Boris Kolpackov  <boris@codesynthesis.com>
+
+       * vpath.c (vpath_search, selective_vpath_search): Add index arguments
+       which allows the caller to get the index of the matching directory.
+
+       * make.h (vpath_search): Update prototype.
+
+       * remake.c (library_search): Implement linker-compatible library
+       search. Use the new VPATH_SEARCH index functionality to keep track
+       of the directory index for each match. Select the match with the
+       lowest directory index.
+
+       * implicit.c (pattern_search): Pass NULL for the index arguments in
+       the VPATH_SEARCH call.
+
+       * doc/make.texi (Directory Search for Link Libraries): Describe the
+       new search behavior.
+
+       * NEWS: Add a note about the new behavior.
+
 2009-10-25  Paul Smith  <psmith@gnu.org>
 
        * AUTHORS, et.al.: Update copyright years.
diff --git a/NEWS b/NEWS
index 661f87bcf870d982265e013639df4fe23e11e42c..f452686a5dad4e9de96aa97f1d719b95072bcbe4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,15 @@ Version 3.81.90
   patterns are preferred. To detect this feature search for 'shortest-stem'
   in the .FEATURES special variable.
 
+* WARNING: Backward-incompatibility!
+  The library search behavior has changed to be compatible with the standard
+  linker behavior. Prior to this version for prerequisites specified using
+  the -lfoo syntax make fist searched for libfoo.so in the current directory,
+  vpath directories, and system directories. If that didn't yield a match,
+  make then searched for libfoo.a in these directories. Starting with this
+  version make searches first for libfoo.so and then for libfoo.a in each
+  of these directories in order.
+
 * New command line option: --eval=STRING causes STRING to be evaluated as
   makefile syntax (akin to using the $(eval ...) function).  The evaluation is
   performed after all default rules and variables are defined, but before any
index 7a3be3b03d920deb1700e76f80897ef8cbbfe1be..7187e1954e6585254ff4d034d15cf624f83ffa5b 100644 (file)
@@ -2425,17 +2425,15 @@ file, and the @emph{file name} of a library generally looks like
 @file{lib@var{name}.a}, not like @samp{-l@var{name}}.)@refill
 
 When a prerequisite's name has the form @samp{-l@var{name}}, @code{make}
-handles it specially by searching for the file @file{lib@var{name}.so} in
-the current directory, in directories specified by matching @code{vpath}
+handles it specially by searching for the file @file{lib@var{name}.so},
+and, if it is not found, for the file @file{lib@var{name}.a} in the current
+directory, in directories specified by matching @code{vpath}
 search paths and the @code{VPATH} search path, and then in the
 directories @file{/lib}, @file{/usr/lib}, and @file{@var{prefix}/lib}
 (normally @file{/usr/local/lib}, but MS-DOS/MS-Windows versions of
 @code{make} behave as if @var{prefix} is defined to be the root of the
 DJGPP installation tree).
 
-If that file is not found, then the file @file{lib@var{name}.a} is
-searched for, in the same directories as above.
-
 For example, if there is a @file{/usr/lib/libcurses.a} library on your
 system (and no @file{/usr/lib/libcurses.so} file), then
 
@@ -2457,8 +2455,7 @@ via the @code{.LIBPATTERNS} variable.  Each word in the value of this
 variable is a pattern string.  When a prerequisite like
 @samp{-l@var{name}} is seen, @code{make} will replace the percent in
 each pattern in the list with @var{name} and perform the above directory
-searches using that library filename.  If no library is found, the next
-word in the list will be used.
+searches using each library filename.
 
 The default value for @code{.LIBPATTERNS} is @samp{lib%.so lib%.a},
 which provides the default behavior described above.
index 492599fda405e78d3b89b4b3d64bb4eac67d4ed4..6e67b718eb9b7fab15fb0ba62cf0db1018a36eb3 100644 (file)
@@ -721,7 +721,7 @@ pattern_search (struct file *file, int archive,
                      "lib/foo.c", and VPATH=src, searches for
                      "src/lib/foo.c".  */
                   {
-                    const char *vname = vpath_search (d->name, 0);
+                    const char *vname = vpath_search (d->name, 0, NULL, NULL);
                     if (vname)
                       {
                         DBS (DB_IMPLICIT,
diff --git a/make.h b/make.h
index c4b0c3255821b67b29f81774c9b3bb64c40eea05..2841c7fe6c5dc7a4b9403ac677a85a1195508230 100644 (file)
--- a/make.h
+++ b/make.h
@@ -431,7 +431,8 @@ void install_default_implicit_rules (void);
 
 void build_vpath_lists (void);
 void construct_vpath_list (char *pattern, char *dirpath);
-const char *vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr);
+const char *vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr,
+                          unsigned int* vpath_index, unsigned int* path_index);
 int gpath_search (const char *file, unsigned int len);
 
 void construct_include_path (const char **arg_dirs);
index 0855ace8586ea2e27a423c4d1edd859c54f24371..8bfd23bb5e443cd3b9e19842245d93577224b453 100644 (file)
--- a/remake.c
+++ b/remake.c
@@ -1280,7 +1280,7 @@ f_mtime (struct file *file, int search)
       if (mtime == NONEXISTENT_MTIME && search && !file->ignore_vpath)
        {
          /* If name_mtime failed, search VPATH.  */
-         const char *name = vpath_search (file->name, &mtime);
+         const char *name = vpath_search (file->name, &mtime, NULL, NULL);
          if (name
              /* Last resort, is it a library (-lxxx)?  */
              || (file->name[0] == '-' && file->name[1] == 'l'
@@ -1533,6 +1533,10 @@ library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr)
   unsigned int len;
   unsigned int liblen;
 
+  /* Information about the earliest (in the vpath sequence) match.  */
+  unsigned int best_vpath, best_path;
+  unsigned int std_dirs = 0;
+
   char **dp;
 
   libpatterns = xstrdup (variable_expand ("$(.LIBPATTERNS)"));
@@ -1541,7 +1545,9 @@ library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr)
   lib += 2;
   liblen = strlen (lib);
 
-  /* Loop through all the patterns in .LIBPATTERNS, and search on each one.  */
+  /* Loop through all the patterns in .LIBPATTERNS, and search on each one.
+     To implement the linker-compatible behavior we have to search through
+     all entries in .LIBPATTERNS and choose the "earliest" one.  */
   p2 = libpatterns;
   while ((p = find_next_token (&p2, &len)) != 0)
     {
@@ -1577,51 +1583,80 @@ library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr)
          if (mtime_ptr != 0)
            *mtime_ptr = mtime;
          file = strcache_add (libbuf);
-          goto fini;
+          /* This by definition will have the best index, so stop now.  */
+          break;
        }
 
       /* Now try VPATH search on that.  */
 
       {
-        file = vpath_search (libbuf, mtime_ptr);
-        if (file)
-          goto fini;
+        unsigned int vpath_index, path_index;
+        const char* f = vpath_search (libbuf, mtime_ptr ? &mtime : NULL,
+                                      &vpath_index, &path_index);
+        if (f)
+          {
+            /* If we have a better match, record it.  */
+            if (file == 0 ||
+                vpath_index < best_vpath ||
+                (vpath_index == best_vpath && path_index < best_path))
+              {
+                file = f;
+                best_vpath = vpath_index;
+                best_path = path_index;
+
+                if (mtime_ptr != 0)
+                  *mtime_ptr = mtime;
+              }
+          }
       }
 
       /* Now try the standard set of directories.  */
 
       if (!buflen)
-       {
-         for (dp = dirs; *dp != 0; ++dp)
-           {
-             int l = strlen (*dp);
-             if (l > libdir_maxlen)
-               libdir_maxlen = l;
-           }
-         buflen = strlen (libbuf);
-         buf = xmalloc(libdir_maxlen + buflen + 2);
-       }
+        {
+          for (dp = dirs; *dp != 0; ++dp)
+            {
+              int l = strlen (*dp);
+              if (l > libdir_maxlen)
+                libdir_maxlen = l;
+              std_dirs++;
+            }
+          buflen = strlen (libbuf);
+          buf = xmalloc(libdir_maxlen + buflen + 2);
+        }
       else if (buflen < strlen (libbuf))
-       {
-         buflen = strlen (libbuf);
-         buf = xrealloc (buf, libdir_maxlen + buflen + 2);
-       }
+        {
+          buflen = strlen (libbuf);
+          buf = xrealloc (buf, libdir_maxlen + buflen + 2);
+        }
+
+      {
+        /* Use the last std_dirs index for standard directories. This
+           was it will always be greater than the VPATH index.  */
+        unsigned int vpath_index = ~((unsigned int)0) - std_dirs;
+
+        for (dp = dirs; *dp != 0; ++dp)
+         {
+            sprintf (buf, "%s/%s", *dp, libbuf);
+            mtime = name_mtime (buf);
+            if (mtime != NONEXISTENT_MTIME)
+             {
+                if (file == 0 || vpath_index < best_vpath)
+                  {
+                    file = strcache_add (buf);
+                    best_vpath = vpath_index;
+
+                    if (mtime_ptr != 0)
+                      *mtime_ptr = mtime;
+                  }
+              }
+
+            vpath_index++;
+          }
+      }
 
-      for (dp = dirs; *dp != 0; ++dp)
-       {
-         sprintf (buf, "%s/%s", *dp, libbuf);
-         mtime = name_mtime (buf);
-         if (mtime != NONEXISTENT_MTIME)
-           {
-             if (mtime_ptr != 0)
-               *mtime_ptr = mtime;
-             file = strcache_add (buf);
-              goto fini;
-           }
-       }
     }
 
- fini:
   free (libpatterns);
   return file;
 }
index f446dcbf2a4f5d10ed994c618dee9b05696d0737..eaacaece92fdf5f12187fa9cdbd7d6e2b66a21c4 100644 (file)
@@ -1,3 +1,8 @@
+2009-11-12  Boris Kolpackov  <boris@codesynthesis.com>
+
+       * scripts/features/vpath3: Test for the new library search
+       behavior.
+
 2009-10-06  Boris Kolpackov  <boris@codesynthesis.com>
 
        * scripts/features/se_explicit: Enable the test for now fixed
diff --git a/tests/scripts/features/vpath3 b/tests/scripts/features/vpath3
new file mode 100644 (file)
index 0000000..978c5ee
--- /dev/null
@@ -0,0 +1,50 @@
+#                                                                     -*-perl-*-
+
+$description = "Test the interaction of the -lfoo feature and vpath";
+$details = "";
+
+open(MAKEFILE,"> $makefile");
+
+# The Contents of the MAKEFILE ...
+
+print MAKEFILE "vpath %.a a1\n";
+print MAKEFILE "vpath %.so b1\n";
+print MAKEFILE "vpath % a2 b2\n";
+print MAKEFILE "vpath % b3\n";
+print MAKEFILE "all: -l1 -l2 -l3; \@echo \$^\n";
+
+# END of Contents of MAKEFILE
+
+close(MAKEFILE);
+
+mkdir("a1", 0777);
+mkdir("b1", 0777);
+mkdir("a2", 0777);
+mkdir("b2", 0777);
+mkdir("b3", 0777);
+
+@files_to_touch = ("a1${pathsep}lib1.a",
+                  "b1${pathsep}lib1.so",
+                  "a2${pathsep}lib2.a",
+                  "b2${pathsep}lib2.so",
+                  "lib3.a",
+                  "b3${pathsep}lib3.so");
+
+&touch(@files_to_touch);
+
+&run_make_with_options($makefile,"",&get_logfile);
+
+# Create the answer to what should be produced by this Makefile
+$answer = "a1${pathsep}lib1.a a2${pathsep}lib2.a lib3.a\n";
+
+if (&compare_output($answer,&get_logfile(1)))
+{
+  unlink @files_to_touch;
+  rmdir("a1");
+  rmdir("b1");
+  rmdir("a2");
+  rmdir("b2");
+  rmdir("b3");
+}
+
+1;
diff --git a/vpath.c b/vpath.c
index 9685468d4821e97e9f4d1d5376c38baddff18363..e9cd691ca80def82801ec1a78b03b2ea6b0f2a14 100644 (file)
--- a/vpath.c
+++ b/vpath.c
@@ -320,11 +320,12 @@ gpath_search (const char *file, unsigned int len)
 /* Search the given VPATH list for a directory where the name pointed to by
    FILE exists.  If it is found, we return a cached name of the existing file
    and set *MTIME_PTR (if MTIME_PTR is not NULL) to its modtime (or zero if no
-   stat call was done).  Otherwise we return NULL.  */
+   stat call was done). Also set the matching directory index in PATH_INDEX
+   if it is not NULL. Otherwise we return NULL.  */
 
 static const char *
 selective_vpath_search (struct vpath *path, const char *file,
-                        FILE_TIMESTAMP *mtime_ptr)
+                        FILE_TIMESTAMP *mtime_ptr, unsigned int* path_index)
 {
   int not_target;
   char *name;
@@ -504,6 +505,9 @@ selective_vpath_search (struct vpath *path, const char *file,
 
           /* Store the name we found and return it.  */
 
+          if (path_index)
+            *path_index = i;
+
           return strcache_add_len (name, (p + 1 - name) + flen);
        }
     }
@@ -515,10 +519,12 @@ selective_vpath_search (struct vpath *path, const char *file,
 /* Search the VPATH list whose pattern matches FILE for a directory where FILE
    exists.  If it is found, return the cached name of an existing file, and
    set *MTIME_PTR (if MTIME_PTR is not NULL) to its modtime (or zero if no
-   stat call was done).  Otherwise we return 0.  */
+   stat call was done). Also set the matching directory index in VPATH_INDEX
+   and PATH_INDEX if they are not NULL.  Otherwise we return 0.  */
 
 const char *
-vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr)
+vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr,
+              unsigned int* vpath_index, unsigned int* path_index)
 {
   struct vpath *v;
 
@@ -532,23 +538,40 @@ vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr)
       || (vpaths == 0 && general_vpath == 0))
     return 0;
 
+  if (vpath_index)
+    {
+      *vpath_index = 0;
+      *path_index = 0;
+    }
+
   for (v = vpaths; v != 0; v = v->next)
-    if (pattern_matches (v->pattern, v->percent, file))
-      {
-        const char *p = selective_vpath_search (v, file, mtime_ptr);
-        if (p)
-          return p;
-      }
+    {
+      if (pattern_matches (v->pattern, v->percent, file))
+        {
+          const char *p = selective_vpath_search (
+            v, file, mtime_ptr, path_index);
+          if (p)
+            return p;
+        }
+
+      if (vpath_index)
+        ++*vpath_index;
+    }
+
 
   if (general_vpath != 0)
     {
-      const char *p = selective_vpath_search (general_vpath, file, mtime_ptr);
+      const char *p = selective_vpath_search (
+        general_vpath, file, mtime_ptr, path_index);
       if (p)
         return p;
     }
 
   return 0;
 }
+
+
+
 \f
 /* Print the data base of VPATH search paths.  */