/* which_lib.c Copyright (C) 1997, 2001, 2002 Free Software Foundation, Inc. Author: Nicola Pero Date: January 2002 Based on the original which_lib.c by Ovidiu Predescu, Author: Ovidiu Predescu 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/Libraries/ix86/linux-gnu/gnu-gnu-gnu -L/usr/GNUstep/System/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.1.1.0 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. 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 first 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, the tool looks for any available shared library with that name (regardless of wheter it's debug/profile/nothing), but only if shared=yes. 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 #include #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_SYS_STAT_H # include #endif #include #if HAVE_DIRENT_H # include #else /* FIXME - is the following needed ? */ # define dirent direct # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif /* determine filesystem max path length */ # include /* for PATH_MAX */ #ifdef _POSIX_VERSION # include #else # if HAVE_SYS_PARAM_H # include /* 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 #endif #define PATH_SEP "/" /* 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'; } } } /* Search for a library with library_name and extension ext. library_name must not contain the suffix, so library_name should be something like 'gnustep-base'. If suffix == NULL, any library with that name, regardless of the suffix (valid suffixes are: "", _d, _p, _s, _ds, _dp, _ps, _dps) will be accepted. If suffix != NULL, the suffix must be that one, otherwise it's not considered a match. ext is the wanted extension (normally, either .so or .a). Must not be NULL. Return 0 if it doesn't find anything mathching. Return 1 if a library with the appropriate extension/type matches in 'path' and print to stdout the name of the library. */ int search_for_library_with_type (const char *library_name, int library_name_len, char **library_paths, int paths_no, char *suffix, char *ext) { /* Iterate over the library_paths, looking for the library. */ int i, found = 0; if (show_all) { fprintf (stderr, "Scanning all paths for '%s', suffix '%s', " "extension '%s'\n ", library_name, suffix ? suffix : "(null)", ext); } for (i = 0; i < paths_no; i++) { DIR* dir; struct dirent* dirbuf; if (show_all) { fprintf (stderr, " Path: %s\n", library_paths[i]); } dir = opendir (library_paths[i]); 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 the required suffix, or one of the allowed suffixes if we have no required suffix. The allowed suffixes are: "", _d, _p, _s, _dp, _ds, _ps, _dps. */ { int suffix_len = filelen - (3 /* 'lib' */ + library_name_len /* library_name */ + extlen /* .so/.a */); if (suffix != NULL) { if (suffix_len == strlen (suffix)) { if (suffix_len != 0 && strncmp (dirbuf->d_name + 3 + library_name_len, suffix, suffix_len)) { continue; } } else { continue; } } else { switch (suffix_len) { case 0: { /* nothing - it's Ok. */ found = 1; 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; } found = 1; 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')) { found = 1; 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; } found = 1; break; } default: { continue; } } } /* If we're here, it's because it was found! */ { char filename[PATH_MAX + 1]; if (show_all) { fprintf (stderr, " Found!\n"); } /* Copy the name of the library without the "lib" prefix and the extension. */ strncpy (filename, dirbuf->d_name + 3, filelen - extlen - 3); filename[filelen - extlen - 3] = 0; printf (" -l%s", filename); found = 1; break; } } } } closedir (dir); if (found) { return 1; } } return 0; } /* Search for the library everywhere, and returns the library name. */ void output_library_name (const char *library_name, char** library_paths, int paths_no, int debug, int profile, int shared) { int library_name_len = strlen (library_name); /* We first perform the search of a matching library in all dirs. */ int i; char libname_suffix[5]; /* Setup the exact prefix of the library 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, "\n== Searching for library %s: ==\n", library_name); } if (show_all) { fprintf (stderr, "Scanning all paths for an exact match - " "libname_suffix='%s'\n", libname_suffix); } for (i = 0; i < paths_no; i++) { struct stat statbuf; char full_filename[PATH_MAX + 1]; if (show_all) { fprintf (stderr, " Path:%s\n", library_paths[i]); } if (stat (library_paths[i], &statbuf) < 0) /* Error occured or dir doesn't exist */ { if (show_all) { fprintf (stderr, " Could not read directory\n"); } continue; } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) /* Not a directory */ { if (show_all) { fprintf (stderr, " Directory is not a directory.\n"); } continue; } strcpy (full_filename, library_paths[i]); strcat (full_filename, PATH_SEP); strcat (full_filename, "lib"); strcat (full_filename, library_name); strcat (full_filename, libname_suffix); strcat (full_filename, shared ? shared_libext : libext); if (show_all) { fprintf (stderr, " Try %s\n", full_filename); } if (stat (full_filename, &statbuf) < 0 && errno != ENOENT) /* Error */ { if (show_all) { fprintf (stderr, " Could not read file\n"); } continue; } if ((statbuf.st_mode & S_IFMT) == S_IFREG) /* Found it! */ { if (show_all) { fprintf (stderr, " Found!\n"); } printf (" -l%s", library_name); if (*libname_suffix) { printf ("%s", libname_suffix); } 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. */ { char *extension = shared ? shared_libext : libext; /* _dp case: try _d, then _p */ if (debug && profile) { if (search_for_library_with_type (library_name, library_name_len, library_paths, paths_no, shared ? "_d" : "_ds", extension)) { return; } if (search_for_library_with_type (library_name, library_name_len, library_paths, paths_no, shared ? "_p" : "_ps", extension)) { return; } } /* _d or _p: try nothing. */ if ((debug && !profile) || (!debug && profile)) { if (search_for_library_with_type (library_name, library_name_len, library_paths, paths_no, shared ? "" : "_s", extension)) { return; } } /* nothing: try _d. */ if (!debug && !profile) { if (search_for_library_with_type (library_name, library_name_len, 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 (search_for_library_with_type (library_name, library_name_len, library_paths, paths_no, NULL, shared_libext)) { return; } } /* Last hope - return a static library with name 'library_name'. Any static library is all right. */ if (search_for_library_with_type (library_name, library_name_len, library_paths, paths_no, NULL, 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 name\n"); } printf (" -l%s", library_name); return; } int main (int argc, char** argv) { /* Type of libraries we prefer. */ int debug = 0; int profile = 0; int shared = 1; /* Array of strings that are the library paths passed on the command line. */ int paths_no = 0; char** library_paths = NULL; /* The same library paths, but converted to a format that Windows functions (if on Windows) understand, so that you should pass those to Windows functions accessing the filesystem. If you're not on Windows, there are the same as the library_paths. */ char **normalized_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); } { int i; for (i = 1; i < argc; i++) { /* Switch basing on the first letter of each argument, to reduce the number of comparisons needed. */ 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') { 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] = malloc (strlen (argv[i]) - 1); strcpy (library_paths[paths_no], argv[i] + 2); stripstr (library_paths[paths_no]); 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++; } } if (show_all) { int i; fprintf (stderr, "== Input: ==\n"); fprintf (stderr, "shared = %d\n", shared); fprintf (stderr, "debug = %d\n", debug); fprintf (stderr, "profile = %d\n", profile); 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]); } } /* Output -L flags, and normalize them for our own use. */ { int i; normalized_library_paths = malloc (sizeof (char *) * paths_no); for (i = 0; i < paths_no; i++) { printf (" -L%s", library_paths[i]); #ifdef __MINGW32__ if (library_paths[i][0] == '/' && library_paths[i][1] == '/') { /* Convert //server/ to a format Windows functions understand */ char *s; normalized_library_paths[i] = malloc ((PATH_MAX + 1) * sizeof (char)); strcpy (normalized_library_paths[i], &(library_paths[i][2])); s = strchr (path_rep, '/'); if (s) { *s = ':'; strcpy ((s + 1), &(library_paths[i][(int)(s - path_rep + 2)])); } } else #endif normalized_library_paths[i] = library_paths[i]; } } /* Now output the libraries. */ { int i; 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], normalized_library_paths, paths_no, debug, profile, shared); } } /* Output the other flags */ { int i; for (i = 0; i < other_flags_no; i++) { printf (" %s", other_flags[i]); } } puts (""); return 0; }