tools-make/which_lib.c
Adam Fedor a830aae513 Filesystem change
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/tools/make/trunk@16565 72102866-910b-0410-8b05-ffd578937521
2003-04-28 02:29:49 +00:00

895 lines
21 KiB
C

/*
which_lib.c
Copyright (C) 1997, 2001, 2002 Free Software Foundation, Inc.
Author: Nicola Pero <nicola@brainstorm.co.uk>
Date: January 2002
Based on the original which_lib.c by Ovidiu Predescu,
Author: Ovidiu Predescu <ovidiu@net-community.com>
Date: October 1997
This file is part of the GNUstep Makefile Package.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
You should have received a copy of the GNU General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*
Command line arguments are:
* a list of library search paths in the GCC notation, as in
-L/usr/lib/opt/hack/
-L/usr/GNUstep/Local/Library/Libraries/ix86/linux-gnu/gnu-gnu-gnu
-L/usr/GNUstep/System/Library/Libraries/ix86/linux-gnu/gnu-gnu-gnu
(order is important, paths are searched in the order they are listed);
* a list of libraries in the GCC notation, as in
-lgnustep-base -lgnustep-gui -lobjc
* flags specifying whether a debug, profile, static/shared library is
to be preferred, as in
debug=no profile=yes shared=yes
The tool outputs the same list of library search paths and the list
of libraries it received in input, with an important modification:
each library name is modified to match the available version of the
library (by appending nothing for a normal library, _d for a debug
version of the library, _p for a profile one, _s for a static one,
and the various combinations, _dp, _ds, _ps, _dps) -- giving
preference to libraries matching the specified debug, profile,
shared library flags. For example, if a debug=yes profile=no
shared=yes is specified, and libgnustep-base_d.so is in the library
search path, which_lib will replace -lgnustep-base with
-lgnustep-base_d in the output.
Here is exactly how the search is performed:
The tool first searches into the list of directories for a library
exactly matching the name and the type required. If found, it's
used.
If none is found, the library looks for an approximate match, as
detailed in the following list. Each search in the following list
is performed on the list of directories, and uses the shared flags
as specified.
If (debug=yes and profile=yes), the tool looks for a debug=yes
profile=no, then a debug=no profile=yes.
If (debug=yes and profile=no), the tool looks for a debug=no
profile=no.
If (debug=no and profile=yes), the tool looks for a debug=no
profile=no.
If (debug=no and profile=no), the tool looks for a debug=yes
profile=no.
If none is still found and shared=yes, the tool looks for any
available shared library with that name (regardless of wheter it's
debug/profile/nothing).
If none is still found, the tool looks for any available static
library with that name (regardless of any debug/profile/shared
flag).
If still not found, the tool outputs the unmodified library name (as
in -lgnustep-base) ... perhaps the library is somewhere else in the
linker path ... otherwise that will normally result in a linker
error later on.
*/
#include "config.h"
#include <stdio.h>
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#endif
#if HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <fcntl.h>
#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
/* determine filesystem max path length */
# include <limits.h> /* for PATH_MAX */
#ifdef _POSIX_VERSION
# include <utime.h>
#else
# if HAVE_SYS_PARAM_H
# include <sys/param.h> /* for MAXPATHLEN */
# endif
#endif
#ifndef PATH_MAX
# ifdef _POSIX_VERSION
# define PATH_MAX _POSIX_PATH_MAX
# else
# ifdef MAXPATHLEN
# define PATH_MAX MAXPATHLEN
# else
# define PATH_MAX 1024
# endif
# endif
#endif
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
/* Extension used for shared and non-shared libraries. */
char* libext = ".a";
char* shared_libext = ".so";
/* If set to 1, all code will print out information about what it
does. */
int show_all = 0;
/* Strips off carriage returns, newlines and spaces at the end of the
string. (this removes some \r\n issues on Windows) */
static void stripstr (unsigned char *s)
{
unsigned len;
if (s == NULL)
{
return;
}
len = strlen (s);
while (len > 0)
{
len--;
if (s[len] < 33)
{
s[len] = '\0';
}
}
}
/* Normalize the directory, and checks that it exists on disk and is a
directory. Return the normalized path, or NULL if the path does
not exist on disk, or is not a valid directory. */
static char *normalize_and_check_dir (char *path)
{
int length = strlen (path);
char *normalized_path = NULL;
struct stat statbuf;
#ifdef __MINGW32__
if (path[0] == '/' && path[1] == '/')
{
/* Convert //server/ to a format Windows functions understand. */
char *s;
/* Convert //server/path/to/ --> server/path/to/ */
normalized_path = malloc (length * sizeof (char));
strcpy (normalized_path, &(path[2]));
/* Convert server/path/to/ --> server:/path/to/ */
s = strchr (normalized_path, '/');
if (s)
{
/* The index of the '/' after 'server' in the original path. */
int index = 2 + (s - normalized_path);
*s = ':';
strcpy (s + 1, &(path[index]));
}
}
else
#endif
{
normalized_path = malloc ((length + 1) * sizeof (char));
strcpy (normalized_path, path);
}
/* Now check that the path exists and is a directory. */
if (stat (normalized_path, &statbuf) < 0)
/* Error occured or dir doesn't exist */
{
if (show_all)
{
fprintf (stderr, "Library path '%s' doesn't exist - ignored\n",
normalized_path);
}
free (normalized_path);
return NULL;
}
else if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
/* Not a directory */
{
if (show_all)
{
fprintf (stderr, "Library path '%s' isn't a directory - ignored\n",
normalized_path);
}
free (normalized_path);
return NULL;
}
stripstr (normalized_path);
return normalized_path;
}
/* Search for a library with library_name, suffix suffix and
extension ext.
library_name must not contain the suffix, so library_name should
be something like 'gnustep-base'.
suffix is the wanted suffix (valid suffixes are "", _d, _p, _s,
_ds, _dp, _ps, _dps). Must not be NULL.
ext is the wanted extension (normally, either .so or .a). Must
not be NULL.
Return 0 if it doesn't find anything matching.
Return 1 if a library with the appropriate suffix/extension/type
matches in 'path' and print to stdout the name of the library. */
static int search_for_lib_with_suffix_and_ext (const char *library_name,
char **library_paths,
int paths_no,
char *suffix,
char *ext)
{
/* Iterate over the library_paths, looking for the library. */
int i;
for (i = 0; i < paths_no; i++)
{
char full_filename[PATH_MAX + 1];
struct stat statbuf;
strcpy (full_filename, library_paths[i]);
strcat (full_filename, "/lib");
strcat (full_filename, library_name);
strcat (full_filename, suffix);
strcat (full_filename, ext);
if (show_all)
{
fprintf (stderr, " %s\n", full_filename);
}
if (stat (full_filename, &statbuf) < 0)
/* Error - likely that file doesn't exist. */
{
continue;
}
if ((statbuf.st_mode & S_IFMT) == S_IFREG)
/* Found it! */
{
if (show_all)
{
fprintf (stderr, " Found!\n");
}
printf (" -l%s", library_name);
if (*suffix)
{
printf ("%s", suffix);
}
return 1;
}
}
return 0;
}
/* Search for a library with library_name, extension ext and any
valid suffix.
The same comments as for 'search_for_lib_with_suffix_and_ext' apply,
except that any valid suffix is accepted (valid suffixes are: "",
_d, _p, _s, _ds, _dp, _ps, _dps).
*/
static int search_for_lib_with_ext (const char *library_name,
int library_name_len,
char **library_paths,
int paths_no,
char *ext)
{
/* Iterate over the library_paths, looking for the library. */
int i;
for (i = 0; i < paths_no; i++)
{
DIR* dir;
struct dirent* dirbuf;
int found = 0;
if (show_all)
{
fprintf (stderr, " %s/lib%s??%s\n", library_paths[i],
library_name, ext);
}
dir = opendir (library_paths[i]);
if (dir == NULL)
{
/* For some reasons, we can't read that path. Perhaps
someone removed the directory while we were running :-) */
continue;
}
while ((dirbuf = readdir (dir)))
{
/* Skip if it doesn't begin with 'lib'. This implicitly
skips "." and ".." in case they are returned. */
if (dirbuf->d_name[0] != 'l')
{
continue;
}
if (dirbuf->d_name[1] != 'i')
{
continue;
}
if (dirbuf->d_name[2] != 'b')
{
continue;
}
/* Skip if it does not match the library name. */
if (strncmp (dirbuf->d_name + 3, library_name, library_name_len))
{
continue;
}
else
{
int filelen, extlen;
filelen = strlen (dirbuf->d_name);
extlen = strlen (ext);
if (filelen - extlen <= 0)
{
/* Quite worrying this case. */
continue;
}
if (show_all)
{
fprintf (stderr, " Considering %s\n", dirbuf->d_name);
}
/* First check if the extension matches */
if (strcmp (dirbuf->d_name + filelen - extlen, ext))
{
/* No luck, skip this file */
continue;
}
/* The extension matches. Check the last remaining bit
- that the remaining string we have not checked is
one of the allowed suffixes. The allowed suffixes
are: "", _d, _p, _s, _dp, _ds, _ps, _dps. */
{
char f_suffix[5];
int j;
int suffix_len = filelen - (3 /* 'lib' */
+ library_name_len
/* library_name */
+ extlen /* .so/.a */);
switch (suffix_len)
{
/* In the following cases, 'break' means found,
'continue' means not found. */
case 0:
{
/* nothing - it's Ok. */
break;
}
case 1:
{
continue;
}
case 2:
{
/* Must be one of _d, _p, _s */
char c;
if (dirbuf->d_name[3 + library_name_len] != '_')
{
continue;
}
c = dirbuf->d_name[3 + library_name_len + 1];
if (c != 'd' || c != 'p' || c != 's')
{
continue;
}
break;
}
case 3:
{
/* Must be one of _dp, _ds, _ps */
char c, d;
if (dirbuf->d_name[3 + library_name_len] != '_')
{
continue;
}
c = dirbuf->d_name[3 + library_name_len + 1];
d = dirbuf->d_name[3 + library_name_len + 2];
if ((c == 'd' && (d == 'p' || d == 's'))
|| (c == 'p' && d == 's'))
{
break;
}
continue;
}
case 4:
{
if (dirbuf->d_name[3 + library_name_len] != '_')
{
continue;
}
if (dirbuf->d_name[3 + library_name_len] != 'd')
{
continue;
}
if (dirbuf->d_name[3 + library_name_len] != 'p')
{
continue;
}
if (dirbuf->d_name[3 + library_name_len] != 's')
{
continue;
}
break;
}
default:
{
continue;
}
}
/* If we're here, it's because it was found! */
if (show_all)
{
fprintf (stderr, " Found!\n");
}
for (j = 0; j < suffix_len; j++)
{
f_suffix[j] = dirbuf->d_name[library_name_len + 3 + j];
}
f_suffix[j] = '\0';
printf (" -l%s%s", library_name, f_suffix);
found = 1;
break;
}
}
}
closedir (dir);
if (found)
{
return 1;
}
}
return 0;
}
/* Search for the library everywhere, and returns the library name. */
static void output_library_name (const char *library_name,
char** library_paths, int paths_no,
int debug, int profile, int shared,
char *libname_suffix)
{
char *extension = shared ? shared_libext : libext;
int library_name_len = strlen (library_name);
if (show_all)
{
fprintf (stderr, "\n>>Library %s:\n", library_name);
}
/* We first perform the search of a matching library in all dirs. */
if (show_all)
{
fprintf (stderr, "Scanning all paths for an exact match\n");
}
if (search_for_lib_with_suffix_and_ext (library_name,
library_paths, paths_no,
libname_suffix,
extension))
{
return;
}
/* The library was not found. Try various approximations first,
slightly messing the original debug/profile requests, but still
honouring the shared=yes|no requirement. */
if (show_all)
{
fprintf (stderr, "Scanning all paths for an approximate match\n");
}
/* _dp case: try _d, then _p */
if (debug && profile)
{
if (search_for_lib_with_suffix_and_ext (library_name,
library_paths, paths_no,
shared ? "_d" : "_ds",
extension))
{
return;
}
if (search_for_lib_with_suffix_and_ext (library_name,
library_paths, paths_no,
shared ? "_p" : "_ps",
extension))
{
return;
}
}
/* _d or _p: try nothing. */
if ((debug && !profile) || (!debug && profile))
{
if (search_for_lib_with_suffix_and_ext (library_name,
library_paths, paths_no,
shared ? "" : "_s",
extension))
{
return;
}
}
/* nothing: try _d. */
if (!debug && !profile)
{
if (search_for_lib_with_suffix_and_ext (library_name,
library_paths, paths_no,
shared ? "_d" : "_ds",
extension))
{
return;
}
}
/* The library was still not found. Try to get whatever library we
have there. */
/* If a shared library is needed try to find a shared one first.
Any shared library is all right. */
if (shared)
{
if (show_all)
{
fprintf (stderr,
"Scanning all paths for any shared lib with that name\n");
}
if (search_for_lib_with_ext (library_name, library_name_len,
library_paths, paths_no, shared_libext))
{
return;
}
}
/* Last hope - return a static library with name 'library_name'.
Any static library is all right. */
if (show_all)
{
fprintf (stderr,
"Scanning all paths for any static lib with that name\n");
}
if (search_for_lib_with_ext (library_name, library_name_len,
library_paths, paths_no, libext))
{
return;
}
/* We couldn't locate the library. Output the library name we were
given, without any modification. Possibly it's somewhere else on
the linker path, otherwise (more likely) a linker error will
occur. Nothing we can do about it. */
if (show_all)
{
fprintf (stderr, "Library not found, using unmodified library name\n");
}
printf (" -l%s", library_name);
return;
}
int main (int argc, char** argv)
{
int i;
/* Type of libraries we prefer. */
int debug = 0;
int profile = 0;
int shared = 1;
/* Suffix of the libraries we prefer - something like "" or "_d" or
"_dp" or "_ps" */
char libname_suffix[5];
/* Array of strings that are the library paths passed on the command
line. If we are on Windows, we convert library paths to a format
that Windows functions understand before we save the paths in
library_paths, so that you could pass them to Windows functions
accessing the filesystem. We also check that the paths actually
exist on disk, and are directories, before putting them in the
array. */
int paths_no = 0;
char** library_paths = NULL;
/* The list of libraries */
int libraries_no = 0;
char** all_libraries = NULL;
/* Other flags which are printed to the output as they are. */
int other_flags_no = 0;
char** other_flags = NULL;
#ifdef __WIN32__
setmode(1, O_BINARY);
setmode(2, O_BINARY);
#endif
if (argc == 1)
{
printf ("usage: %s [-Lpath ...] -llibrary shared=yes|no debug=yes|no "
"profile=yes|no libext=string shared_libext=string "
"[show_all=yes]\n", argv[0]);
exit (1);
}
for (i = 1; i < argc; i++)
{
/* First switch basing on the first letter of each argument,
then compare. */
switch (argv[i][0])
{
case '-':
{
if (argv[i][1] == 'l')
{
if (all_libraries)
{
all_libraries = realloc (all_libraries,
(libraries_no + 1)
* sizeof (char*));
}
else
{
all_libraries = malloc ((libraries_no + 1)
* sizeof (char*));
}
all_libraries[libraries_no] = malloc (strlen (argv[i]) - 1);
strcpy (all_libraries[libraries_no], argv[i] + 2);
stripstr (all_libraries[libraries_no]);
libraries_no++;
continue;
}
else if (argv[i][1] == 'L')
{
char *lib_path = normalize_and_check_dir (argv[i] + 2);
/* Always print out the library search path flag,
regardless. */
printf (" %s", argv[i]);
if (lib_path != NULL)
{
if (library_paths)
{
library_paths = realloc (library_paths,
(paths_no + 1)
* sizeof (char*));
}
else
{
library_paths = malloc ((paths_no + 1)
* sizeof(char*));
}
library_paths[paths_no] = lib_path;
paths_no++;
}
continue;
}
break;
}
case 'd':
{
if (!strncmp (argv[i], "debug=", 6))
{
debug = !strncmp (argv[i] + 6, "yes", 3);
continue;
}
break;
}
case 'l':
{
if (!strncmp (argv[i], "libext=", 7))
{
libext = malloc (strlen (argv[i] + 7) + 1);
strcpy (libext, argv[i] + 7);
continue;
}
break;
}
case 'p':
{
if (!strncmp (argv[i], "profile=", 8))
{
profile = !strncmp (argv[i] + 8, "yes", 3);
continue;
}
break;
}
case 's':
{
if (!strncmp (argv[i], "shared=", 7))
{
shared = !strncmp (argv[i] + 7, "yes", 3);
continue;
}
else if (!strncmp (argv[i], "shared_libext=", 14))
{
shared_libext = malloc (strlen (argv[i] + 14) + 1);
strcpy (shared_libext, argv[i] + 14);
continue;
}
else if (!strncmp (argv[i], "show_all=", 9))
{
show_all = !strncmp (argv[i] + 9, "yes", 3);
continue;
}
break;
}
default:
break;
}
/* The flag is something different; keep it in the `other_flags' */
if (other_flags)
{
other_flags = realloc (other_flags,
(other_flags_no + 1) * sizeof (char*));
}
else
{
other_flags = malloc ((other_flags_no + 1) * sizeof (char*));
}
other_flags[other_flags_no] = malloc (strlen (argv[i]) + 1);
strcpy (other_flags[other_flags_no], argv[i]);
other_flags_no++;
}
/* Determine the exact libname_suffix of the libraries we are
looking for. */
libname_suffix[0] = '_';
libname_suffix[1] = '\0';
libname_suffix[2] = '\0';
libname_suffix[3] = '\0';
libname_suffix[4] = '\0';
i = 1;
if (debug)
{
libname_suffix[i] = 'd';
i++;
}
if (profile)
{
libname_suffix[i] = 'p';
i++;
}
if (!shared)
{
libname_suffix[i] = 's';
i++;
}
if (i == 1)
{
libname_suffix[0] = '\0';
}
if (show_all)
{
fprintf (stderr, ">>Input:\n");
fprintf (stderr, "shared = %d\n", shared);
fprintf (stderr, "debug = %d\n", debug);
fprintf (stderr, "profile = %d\n", profile);
fprintf (stderr, "libname_suffix = %s\n", libname_suffix);
fprintf (stderr, "libext = %s\n", libext);
fprintf (stderr, "shared_libext = %s\n", shared_libext);
fprintf (stderr, "library names:\n");
for (i = 0; i < libraries_no; i++)
{
fprintf (stderr, " %s\n", all_libraries[i]);
}
fprintf (stderr, "library paths:\n");
for (i = 0; i < paths_no; i++)
{
fprintf (stderr, " %s\n", library_paths[i]);
}
fprintf (stderr, "other flags:\n");
for (i = 0; i < other_flags_no; i++)
{
fprintf (stderr, " %s\n", other_flags[i]);
}
}
/* Now output the libraries. */
for (i = 0; i < libraries_no; i++)
{
/* Search for the library, and print (using -l%s) the library
name to standard output. */
output_library_name (all_libraries[i], library_paths,
paths_no, debug, profile, shared, libname_suffix);
}
/* Output the other flags */
for (i = 0; i < other_flags_no; i++)
{
printf (" %s", other_flags[i]);
}
printf (" ");
return 0;
}