Implement linker-compatible library search.

This commit is contained in:
Boris Kolpackov 2009-11-12 16:42:36 +00:00
parent 5f188b39a4
commit fe43fa9de3
9 changed files with 193 additions and 53 deletions

View File

@ -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> 2009-10-25 Paul Smith <psmith@gnu.org>
* AUTHORS, et.al.: Update copyright years. * AUTHORS, et.al.: Update copyright years.

9
NEWS
View File

@ -37,6 +37,15 @@ Version 3.81.90
patterns are preferred. To detect this feature search for 'shortest-stem' patterns are preferred. To detect this feature search for 'shortest-stem'
in the .FEATURES special variable. 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 * New command line option: --eval=STRING causes STRING to be evaluated as
makefile syntax (akin to using the $(eval ...) function). The evaluation is makefile syntax (akin to using the $(eval ...) function). The evaluation is
performed after all default rules and variables are defined, but before any performed after all default rules and variables are defined, but before any

View 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 @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} 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 handles it specially by searching for the file @file{lib@var{name}.so},
the current directory, in directories specified by matching @code{vpath} 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 search paths and the @code{VPATH} search path, and then in the
directories @file{/lib}, @file{/usr/lib}, and @file{@var{prefix}/lib} directories @file{/lib}, @file{/usr/lib}, and @file{@var{prefix}/lib}
(normally @file{/usr/local/lib}, but MS-DOS/MS-Windows versions of (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 @code{make} behave as if @var{prefix} is defined to be the root of the
DJGPP installation tree). 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 For example, if there is a @file{/usr/lib/libcurses.a} library on your
system (and no @file{/usr/lib/libcurses.so} file), then 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 variable is a pattern string. When a prerequisite like
@samp{-l@var{name}} is seen, @code{make} will replace the percent in @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 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 searches using each library filename.
word in the list will be used.
The default value for @code{.LIBPATTERNS} is @samp{lib%.so lib%.a}, The default value for @code{.LIBPATTERNS} is @samp{lib%.so lib%.a},
which provides the default behavior described above. which provides the default behavior described above.

View File

@ -721,7 +721,7 @@ pattern_search (struct file *file, int archive,
"lib/foo.c", and VPATH=src, searches for "lib/foo.c", and VPATH=src, searches for
"src/lib/foo.c". */ "src/lib/foo.c". */
{ {
const char *vname = vpath_search (d->name, 0); const char *vname = vpath_search (d->name, 0, NULL, NULL);
if (vname) if (vname)
{ {
DBS (DB_IMPLICIT, DBS (DB_IMPLICIT,

3
make.h
View File

@ -431,7 +431,8 @@ void install_default_implicit_rules (void);
void build_vpath_lists (void); void build_vpath_lists (void);
void construct_vpath_list (char *pattern, char *dirpath); 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); int gpath_search (const char *file, unsigned int len);
void construct_include_path (const char **arg_dirs); void construct_include_path (const char **arg_dirs);

101
remake.c
View File

@ -1280,7 +1280,7 @@ f_mtime (struct file *file, int search)
if (mtime == NONEXISTENT_MTIME && search && !file->ignore_vpath) if (mtime == NONEXISTENT_MTIME && search && !file->ignore_vpath)
{ {
/* If name_mtime failed, search 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 if (name
/* Last resort, is it a library (-lxxx)? */ /* Last resort, is it a library (-lxxx)? */
|| (file->name[0] == '-' && file->name[1] == 'l' || (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 len;
unsigned int liblen; 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; char **dp;
libpatterns = xstrdup (variable_expand ("$(.LIBPATTERNS)")); libpatterns = xstrdup (variable_expand ("$(.LIBPATTERNS)"));
@ -1541,7 +1545,9 @@ library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr)
lib += 2; lib += 2;
liblen = strlen (lib); 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; p2 = libpatterns;
while ((p = find_next_token (&p2, &len)) != 0) 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) if (mtime_ptr != 0)
*mtime_ptr = mtime; *mtime_ptr = mtime;
file = strcache_add (libbuf); file = strcache_add (libbuf);
goto fini; /* This by definition will have the best index, so stop now. */
break;
} }
/* Now try VPATH search on that. */ /* Now try VPATH search on that. */
{ {
file = vpath_search (libbuf, mtime_ptr); unsigned int vpath_index, path_index;
if (file) const char* f = vpath_search (libbuf, mtime_ptr ? &mtime : NULL,
goto fini; &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. */ /* Now try the standard set of directories. */
if (!buflen) if (!buflen)
{ {
for (dp = dirs; *dp != 0; ++dp) for (dp = dirs; *dp != 0; ++dp)
{ {
int l = strlen (*dp); int l = strlen (*dp);
if (l > libdir_maxlen) if (l > libdir_maxlen)
libdir_maxlen = l; libdir_maxlen = l;
} std_dirs++;
buflen = strlen (libbuf); }
buf = xmalloc(libdir_maxlen + buflen + 2); buflen = strlen (libbuf);
} buf = xmalloc(libdir_maxlen + buflen + 2);
}
else if (buflen < strlen (libbuf)) else if (buflen < strlen (libbuf))
{ {
buflen = strlen (libbuf); buflen = strlen (libbuf);
buf = xrealloc (buf, libdir_maxlen + buflen + 2); 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); free (libpatterns);
return file; return file;
} }

View 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> 2009-10-06 Boris Kolpackov <boris@codesynthesis.com>
* scripts/features/se_explicit: Enable the test for now fixed * scripts/features/se_explicit: Enable the test for now fixed

View File

@ -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;

45
vpath.c
View File

@ -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 /* 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 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 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 * static const char *
selective_vpath_search (struct vpath *path, const char *file, selective_vpath_search (struct vpath *path, const char *file,
FILE_TIMESTAMP *mtime_ptr) FILE_TIMESTAMP *mtime_ptr, unsigned int* path_index)
{ {
int not_target; int not_target;
char *name; char *name;
@ -504,6 +505,9 @@ selective_vpath_search (struct vpath *path, const char *file,
/* Store the name we found and return it. */ /* Store the name we found and return it. */
if (path_index)
*path_index = i;
return strcache_add_len (name, (p + 1 - name) + flen); 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 /* 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 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 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 * 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; struct vpath *v;
@ -532,23 +538,40 @@ vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr)
|| (vpaths == 0 && general_vpath == 0)) || (vpaths == 0 && general_vpath == 0))
return 0; return 0;
if (vpath_index)
{
*vpath_index = 0;
*path_index = 0;
}
for (v = vpaths; v != 0; v = v->next) for (v = vpaths; v != 0; v = v->next)
if (pattern_matches (v->pattern, v->percent, file)) {
{ if (pattern_matches (v->pattern, v->percent, file))
const char *p = selective_vpath_search (v, file, mtime_ptr); {
if (p) const char *p = selective_vpath_search (
return p; v, file, mtime_ptr, path_index);
} if (p)
return p;
}
if (vpath_index)
++*vpath_index;
}
if (general_vpath != 0) 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) if (p)
return p; return p;
} }
return 0; return 0;
} }
/* Print the data base of VPATH search paths. */ /* Print the data base of VPATH search paths. */