/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #if !defined(__MINGW32__) # include #endif #include #include #include #include #ifdef __EMX__ # define SHELL_CMD "sh" # define GEN_EXPORTS "emxexp" # define DEF2IMPLIB_CMD "emximp" # define SHARE_SW "-Zdll -Zmtd" # define USE_OMF 1 # define TRUNCATE_DLL_NAME # define DYNAMIC_LIB_EXT "dll" # define EXE_EX ".exe" /* OMF is the native format under OS/2 */ # if USE_OMF # define STATIC_LIB_EXT "lib" # define OBJECT_EXT "obj" # define LIBRARIAN "emxomfar" # define LIBRARIAN_OPTS "cr" # else /* but the alternative, a.out, can fork() which is sometimes necessary */ # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # endif #endif #if defined(__APPLE__) # define SHELL_CMD "/bin/sh" # define DYNAMIC_LIB_EXT "dylib" # define MODULE_LIB_EXT "bundle" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" /* man libtool(1) documents ranlib option of -c. */ # define RANLIB "ranlib" # define PIC_FLAG "-fPIC -fno-common" # define SHARED_OPTS "-dynamiclib" # define MODULE_OPTS "-bundle -dynamic" # define DYNAMIC_LINK_OPTS "-flat_namespace" # define DYNAMIC_LINK_UNDEFINED "-undefined suppress" # define dynamic_link_version_func darwin_dynamic_link_function # define DYNAMIC_INSTALL_NAME "-install_name" # define DYNAMIC_LINK_NO_INSTALL "-dylib_file" # define HAS_REALPATH /*-install_name /Users/jerenk/apache-2.0-cvs/lib/libapr.0.dylib -compatibility_version 1 -current_version 1.0 */ # define LD_LIBRARY_PATH "DYLD_LIBRARY_PATH" # define LD_LIBRARY_PATH_LOCAL "DYLD_FALLBACK_LIBRARY_PATH" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || (defined(__sun) && defined(__GNUC__)) # define SHELL_CMD "/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RANLIB "ranlib" # define PIC_FLAG "-fPIC" # define RPATH "-rpath" # define SHARED_OPTS "-shared" # define MODULE_OPTS "-shared" # define LINKER_FLAG_PREFIX "-Wl," #if !defined(__sun) # define DYNAMIC_LINK_OPTS LINKER_FLAG_PREFIX "-export-dynamic" #else # define DYNAMIC_LINK_OPTS "" #endif # define ADD_MINUS_L # define LD_RUN_PATH "LD_RUN_PATH" # define LD_LIBRARY_PATH "LD_LIBRARY_PATH" # define LD_LIBRARY_PATH_LOCAL LD_LIBRARY_PATH #endif #if defined(__sun) && !defined(__GNUC__) # define SHELL_CMD "/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RANLIB "ranlib" # define PIC_FLAG "-fPIC" # define RPATH "-R" # define SHARED_OPTS "-G" # define MODULE_OPTS "-G" # define DYNAMIC_LINK_OPTS "" # define LINKER_FLAG_NO_EQUALS # define ADD_MINUS_L # define HAS_REALPATH # define LD_RUN_PATH "LD_RUN_PATH" # define LD_LIBRARY_PATH "LD_LIBRARY_PATH" # define LD_LIBRARY_PATH_LOCAL LD_LIBRARY_PATH #endif #if defined(_OSD_POSIX) # define SHELL_CMD "/usr/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define SHARED_OPTS "-G" # define MODULE_OPTS "-G" # define LINKER_FLAG_PREFIX "-Wl," # define NEED_SNPRINTF #endif #if defined(sinix) && defined(mips) && defined(__SNI_TARG_UNIX) # define SHELL_CMD "/usr/bin/sh" # define DYNAMIC_LIB_EXT "so" # define MODULE_LIB_EXT "so" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RPATH "-Brpath" # define SHARED_OPTS "-G" # define MODULE_OPTS "-G" # define LINKER_FLAG_PREFIX "-Wl," # define DYNAMIC_LINK_OPTS LINKER_FLAG_PREFIX "-Blargedynsym" # define NEED_SNPRINTF # define LD_RUN_PATH "LD_RUN_PATH" # define LD_LIBRARY_PATH "LD_LIBRARY_PATH" # define LD_LIBRARY_PATH_LOCAL LD_LIBRARY_PATH #endif #if defined(__MINGW32__) # define SHELL_CMD "sh" # define DYNAMIC_LIB_EXT "dll" # define MODULE_LIB_EXT "dll" # define STATIC_LIB_EXT "a" # define OBJECT_EXT "o" # define LIBRARIAN "ar" # define LIBRARIAN_OPTS "cr" # define RANLIB "ranlib" # define LINKER_FLAG_PREFIX "-Wl," # define SHARED_OPTS "-shared" # define MODULE_OPTS "-shared" # define MKDIR_NO_UMASK # define EXE_EXT ".exe" #endif #ifndef CC #define CC "clang" #endif #ifndef CXX #define CXX "g++" #endif #ifndef LINK_C #define LINK_C "clang" #endif #ifndef LINK_CXX #define LINK_CXX "g++" #endif #ifndef LIBDIR #define LIBDIR "/usr/local/lib" #endif #define OBJDIR ".libs" #ifndef SHELL_CMD #error Unsupported platform: Please add defines for SHELL_CMD etc. for your platform. #endif #ifdef NEED_SNPRINTF #include #endif #ifdef __EMX__ #include #endif #ifndef PATH_MAX #define PATH_MAX 1024 #endif /* We want to say we are libtool 1.4 for shlibtool compatibility. */ #define VERSION "1.4" #define DEBUG(fmt, ...) if(cmd->options.debug) printf(fmt, ## __VA_ARGS__) #define NOTICE(fmt, ...) if(!cmd->options.silent) printf(fmt, ## __VA_ARGS__) #define ERROR(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) enum tool_mode { MODE_UNKNOWN, MODE_COMPILE, MODE_LINK, MODE_EXECUTE, MODE_INSTALL, }; enum output_type { OUT_GENERAL, OUT_OBJECT, OUT_PROGRAM, OUT_LIB, OUT_STATIC_LIB_ONLY, OUT_DYNAMIC_LIB_ONLY, OUT_MODULE, }; enum pic_mode { PIC_UNKNOWN, PIC_PREFER, PIC_AVOID, }; enum shared_mode { SHARE_UNSET, SHARE_STATIC, SHARE_SHARED, }; enum lib_type { TYPE_UKNOWN, TYPE_STATIC_LIB, TYPE_DYNAMIC_LIB, TYPE_MODULE_LIB, TYPE_OBJECT, }; typedef struct { char const **vals; int num; } count_chars; typedef struct { char const *normal; char const *install; } library_name; typedef struct { count_chars *normal; count_chars *install; count_chars *dependencies; } library_opts; typedef struct { int silent; int debug; enum shared_mode shared; int export_all; int dry_run; enum pic_mode pic_mode; int export_dynamic; int no_install; } options_t; typedef struct { enum tool_mode mode; enum output_type output; options_t options; char const *output_name; char const *fake_output_name; char const *basename; char const *install_path; char const *compiler; char const *program; count_chars *program_opts; count_chars *arglist; count_chars *tmp_dirs; count_chars *obj_files; count_chars *dep_rpaths; count_chars *rpaths; library_name static_name; library_name shared_name; library_name module_name; library_opts static_opts; library_opts shared_opts; char const *version_info; char const *undefined_flag; } command_t; #ifdef RPATH static void add_rpath(count_chars *cc, char const *path); #endif static void usage(int code) { printf("Usage: jlibtool [OPTIONS...] COMMANDS...\n"); printf("jlibtool is a replacement for GNU libtool with similar functionality.\n\n"); printf(" --config show all configuration variables\n"); printf(" --debug enable verbose shell tracing\n"); printf(" --dry-run display commands without modifying any files\n"); printf(" --help display this help message and exit\n"); printf(" --mode=MODE use operational mode MODE (you *must* set mode)\n"); printf(" --silent don't print informational messages\n"); printf(" --tag=TAG Ignored for libtool compatibility\n"); printf(" --version print version information\n"); printf(" --shared Build shared libraries when using --mode=link\n"); printf(" --export-all Try to export 'def' file on some platforms\n"); printf("\nMODE must be one of the following:\n\n"); printf(" compile compile a source file into a jlibtool object\n"); printf(" execute automatically set library path, then run a program\n"); printf(" install install libraries or executables\n"); printf(" link create a library or an executable\n"); printf("\nMODE-ARGS can be the following:\n\n"); printf(" -export-dynamic accepted and ignored\n"); printf(" -module create a module when linking\n"); printf(" -shared create a shared library when linking\n"); printf(" -prefer-pic prefer position-independent-code when compiling\n"); printf(" -prefer-non-pic prefer non position-independent-code when compiling\n"); printf(" -static create a static library when linking\n"); printf(" -no-install link libraries locally\n"); printf(" -rpath arg Set install path for shared libraries\n"); printf(" -l arg pass '-l arg' to the link stage\n"); printf(" -L arg pass '-L arg' to the link stage\n"); printf(" -R dir add 'dir' to runtime library search path.\n"); printf(" -Zexe accepted and ignored\n"); printf(" -avoid-version accepted and ignored\n"); exit(code); } #if defined(NEED_SNPRINTF) /* Write at most n characters to the buffer in str, return the * number of chars written or -1 if the buffer would have been * overflowed. * * This is portable to any POSIX-compliant system has /dev/null */ static FILE *f = NULL; static int vsnprintf(char *str, size_t n, char const *fmt, va_list ap) { int res; if (!f) { f = fopen("/dev/null","w"); } if (!f) { return -1; } setvbuf(f, str, _IOFBF, n); res = vfprintf(f, fmt, ap); if ((res > 0) && (res < n)) { res = vsprintf( str, fmt, ap ); } return res; } static int snprintf(char *str, size_t n, char const *fmt, ...) { va_list ap; int res; va_start(ap, fmt); res = vsnprintf(str, n, fmt, ap); va_end(ap); return res; } #endif static void *lt_malloc(size_t size) { void *out; out = malloc(size); if (!out) { ERROR("Failed allocating %zu bytes, OOM\n", size); exit(1); } return out; } static void lt_const_free(const void *ptr) { void *tmp; memcpy(&tmp, &ptr, sizeof(tmp)); free(tmp); } static void init_count_chars(count_chars *cc) { cc->vals = (char const**) lt_malloc(PATH_MAX*sizeof(char*)); cc->num = 0; } static count_chars *alloc_countchars(void) { count_chars *out; out = lt_malloc(sizeof(count_chars)); if (!out) { exit(1); } init_count_chars(out); return out; } static void clear_count_chars(count_chars *cc) { int i; for (i = 0; i < cc->num; i++) { cc->vals[i] = NULL; } cc->num = 0; } static void push_count_chars(count_chars *cc, char const *newval) { cc->vals[cc->num++] = newval; } static char const *pop_count_chars(count_chars *cc) { if (!cc->num) { return NULL; } return cc->vals[--cc->num]; } static void insert_count_chars(count_chars *cc, char const *newval, int position) { int i; for (i = cc->num; i > position; i--) { cc->vals[i] = cc->vals[i-1]; } cc->vals[position] = newval; cc->num++; } static void append_count_chars(count_chars *cc, count_chars *cctoadd) { int i; for (i = 0; i < cctoadd->num; i++) { if (cctoadd->vals[i]) { push_count_chars(cc, cctoadd->vals[i]); } } } static char const *flatten_count_chars(count_chars *cc, char delim) { int i, size; char *newval; size = 0; for (i = 0; i < cc->num; i++) { if (cc->vals[i]) { size += strlen(cc->vals[i]) + 1; if (delim) { size++; } } } newval = (char*)lt_malloc(size + 1); newval[0] = '\0'; for (i = 0; i < cc->num; i++) { if (cc->vals[i]) { strcat(newval, cc->vals[i]); if (delim) { size_t len = strlen(newval); newval[len] = delim; newval[len + 1] = '\0'; } } } return newval; } static char *shell_esc(char const *str) { int in_quote = 0; char *cmd; uint8_t *d; uint8_t const *s; cmd = (char *)lt_malloc(2 * strlen(str) + 3); d = (unsigned char *)cmd; s = (const unsigned char *)str; #ifdef __MINGW32__ *d++ = '\"'; #endif for (; *s; ++s) { if (*s == '"') { *d++ = '\\'; in_quote++; } else if (*s == '\\' || (*s == ' ' && (in_quote % 2))) { *d++ = '\\'; } *d++ = *s; } #ifdef __MINGW32__ *d++ = '\"'; #endif *d = '\0'; return cmd; } static int external_spawn(command_t *cmd, char const *file, char const **argv) { file = file; /* -Wunused */ if (!cmd->options.silent) { char const **argument = argv; NOTICE("Executing: "); while (*argument) { NOTICE("%s ", *argument); argument++; } puts(""); } if (cmd->options.dry_run) { return 0; } #if defined(__EMX__) || defined(__MINGW32__) return spawnvp(P_WAIT, argv[0], argv); #else { pid_t pid; pid = fork(); if (pid == 0) { return execvp(argv[0], (char**)argv); } else { int status; waitpid(pid, &status, 0); /* * Exited via exit(status) */ if (WIFEXITED(status)) { return WEXITSTATUS(status); } #ifdef WTERMSIG if (WIFSIGNALED(status)) { return WTERMSIG(status); } #endif /* * Some other failure. */ return 1; } } #endif } static int run_command(command_t *cmd, count_chars *cc) { int ret; char *command; char *tmp; char const *raw; char const *spawn_args[4]; count_chars tmpcc; init_count_chars(&tmpcc); if (cmd->program) { push_count_chars(&tmpcc, cmd->program); } append_count_chars(&tmpcc, cmd->program_opts); append_count_chars(&tmpcc, cc); raw = flatten_count_chars(&tmpcc, ' '); command = shell_esc(raw); memcpy(&tmp, &raw, sizeof(tmp)); free(tmp); spawn_args[0] = SHELL_CMD; spawn_args[1] = "-c"; spawn_args[2] = command; spawn_args[3] = NULL; ret = external_spawn(cmd, spawn_args[0], spawn_args); free(command); return ret; } /* * print configuration * shlibpath_var is used in configure. */ #define printc(_x,_y) if (!value || !strcmp(value, _x)) printf(_x "=\"%s\"\n", _y) static void print_config(char const *value) { #ifdef LD_RUN_PATH printc("runpath_var", LD_RUN_PATH); #endif #ifdef LD_LIBRARY_PATH printc("shlibpath_var", LD_LIBRARY_PATH); #endif #ifdef LD_LIBRARY_PATH_LOCAL printc("shlocallibpath_var", LD_LIBRARY_PATH_LOCAL); #endif #ifdef SHELL_CMD printc("SHELL", SHELL_CMD); #endif #ifdef OBJECT_EXT printc("objext", OBJECT_EXT); #endif #ifdef OBJDIR printc("objdir", OBJDIR); #endif #ifdef DYNAMIC_LIB_EXT /* add a '.' prefix because libtool does that. */ printc("shrext_cmds", "echo ." DYNAMIC_LIB_EXT); /* add a '.' prefix because libtool does that. */ printc("shrext", "." DYNAMIC_LIB_EXT); #endif #ifdef EXE_EXT printc("exeext", EXE_EXT); #endif #ifdef STATIC_LIB_EXT printc("libext", STATIC_LIB_EXT); #endif #ifdef LIBRARIAN printc("AR", LIBRARIAN); #endif #ifdef LIBRARIAN_OPTS printc("AR_FLAGS", LIBRARIAN_OPTS); #endif #ifdef LINKER_FLAG_PREFIX printc("wl", LINKER_FLAG_PREFIX); #endif #ifdef RANLIB printc("ranlib", RANLIB); #endif } /* * Add a directory to the runtime library search path. */ static void add_runtime_dir_lib(char const *arg, command_t *cmd) { #ifdef RPATH add_rpath(cmd->shared_opts.dependencies, arg); #else (void) arg; /* -Wunused */ (void) cmd; #endif } static int parse_long_opt(char const *arg, command_t *cmd) { char *equal_pos = strchr(arg, '='); char var[50]; char value[500]; if (equal_pos) { strncpy(var, arg, equal_pos - arg); var[equal_pos - arg] = 0; if (strlen(equal_pos + 1) >= sizeof(var)) { return 0; } strcpy(value, equal_pos + 1); } else { strncpy(var, arg, sizeof(var) - 1); var[sizeof(var) - 1] = '\0'; value[0] = '\0'; } if (strcmp(var, "silent") == 0) { cmd->options.silent = 1; } else if (strcmp(var, "quiet") == 0) { cmd->options.silent = 1; } else if (strcmp(var, "debug") == 0) { cmd->options.debug = 1; } else if (strcmp(var, "mode") == 0) { if (cmd->mode != MODE_UNKNOWN) { ERROR("Cannot set --mode twice\n"); exit(1); } if (strcmp(value, "compile") == 0) { cmd->mode = MODE_COMPILE; cmd->output = OUT_OBJECT; } else if (strcmp(value, "link") == 0) { cmd->mode = MODE_LINK; cmd->output = OUT_LIB; } else if (strcmp(value, "install") == 0) { cmd->mode = MODE_INSTALL; } else if (strcmp(value, "execute") == 0) { cmd->mode = MODE_EXECUTE; } else { ERROR("Unknown mode \"%s\"\n", value); exit(1); } } else if (strcmp(var, "shared") == 0) { if ((cmd->mode == MODE_LINK) && (cmd->output == OUT_GENERAL)) { cmd->output = OUT_DYNAMIC_LIB_ONLY; } cmd->options.shared = SHARE_SHARED; } else if (strcmp(var, "export-all") == 0) { cmd->options.export_all = 1; } else if (strcmp(var, "dry-run") == 0) { NOTICE("Dry-run mode on!\n"); cmd->options.dry_run = 1; } else if (strcmp(var, "version") == 0) { NOTICE("Version " VERSION "\n"); } else if (strcmp(var, "help") == 0) { usage(0); } else if (strcmp(var, "config") == 0) { print_config(value); exit(0); } else { return 0; } return 1; } /* Return 1 if we eat it. */ static int parse_short_opt(char const *arg, command_t *cmd) { if (strcmp(arg, "export-dynamic") == 0) { cmd->options.export_dynamic = 1; return 1; } if (strcmp(arg, "module") == 0) { cmd->output = OUT_MODULE; return 1; } if (strcmp(arg, "shared") == 0) { if (cmd->mode == MODE_LINK) { cmd->output = OUT_DYNAMIC_LIB_ONLY; } cmd->options.shared = SHARE_SHARED; return 1; } if (strcmp(arg, "Zexe") == 0) { return 1; } if (strcmp(arg, "avoid-version") == 0) { return 1; } if (strcmp(arg, "prefer-pic") == 0) { cmd->options.pic_mode = PIC_PREFER; return 1; } if (strcmp(arg, "prefer-non-pic") == 0) { cmd->options.pic_mode = PIC_AVOID; return 1; } if (strcmp(arg, "static") == 0) { if ((cmd->mode == MODE_LINK) && (cmd->output == OUT_LIB)) { cmd->output = OUT_STATIC_LIB_ONLY; } cmd->options.shared = SHARE_STATIC; return 1; } if (cmd->mode == MODE_LINK) { if (strcmp(arg, "no-install") == 0) { cmd->options.no_install = 1; return 1; } if (arg[0] == 'L' || arg[0] == 'l') { /* Hack... */ arg--; push_count_chars(cmd->shared_opts.dependencies, arg); return 1; } else if (arg[0] == 'R' && arg[1]) { /* -Rdir Add dir to runtime library search path. */ add_runtime_dir_lib(&arg[1], cmd); return 1; } } return 0; } #ifdef TRUNCATE_DLL_NAME static char *truncate_dll_name(char *path) { /* Cut DLL name down to 8 characters after removing any mod_ prefix */ char *tmppath = strdup(path); char *newname = strrchr(tmppath, '/') + 1; char *ext = strrchr(newname, '.'); int len; if (ext == NULL) { return tmppath; } len = ext - newname; if (strncmp(newname, "mod_", 4) == 0) { strcpy(newname, newname + 4); len -= 4; } if (len > 8) { strcpy(newname + 8, strchr(newname, '.')); } return tmppath; } #endif static long safe_strtol(char const *nptr, char const **endptr, int base) { long rv; errno = 0; rv = strtol(nptr, (char**)endptr, 10); if (errno == ERANGE) { return 0; } return rv; } static void safe_mkdir(command_t *cmd, char const *path) { int status; mode_t old_umask; old_umask = umask(0); umask(old_umask); #ifdef MKDIR_NO_UMASK status = mkdir(path); #else status = mkdir(path, ~old_umask); #endif if ((status < 0) && (errno != EEXIST)) { NOTICE("Warning: mkdir of %s failed\n", path); } } /** Returns a file's name without the path * * @param path to break apart. * @return pointer in path. */ static char const *file_name(char const *path) { char const *name; name = strrchr(path, '/'); if (!name) { name = strrchr(path, '\\'); /* eww windows? */ } if (!name) { name = path; } else { name++; } return name; } #ifdef GEN_EXPORTS /** Returns a file's name without path or extension * * @param path to check * @return pointer in path. */ static char const *file_name_stripped(char const *path) { char const *name; char const *ext; name = file_name(path); ext = strrchr(name, '.'); if (ext) { char *trimmed; trimmed = lt_malloc(ext - name + 1); strncpy(trimmed, name, ext - name); trimmed[ext-name] = 0; return trimmed; } return name; } #endif /* version_info is in the form of MAJOR:MINOR:PATCH */ static char const *darwin_dynamic_link_function(char const *version_info) { char *newarg; long major, minor, patch; major = 0; minor = 0; patch = 0; if (version_info) { major = safe_strtol(version_info, &version_info, 10); if (version_info) { if (version_info[0] == ':') { version_info++; } minor = safe_strtol(version_info, &version_info, 10); if (version_info) { if (version_info[0] == ':') { version_info++; } patch = safe_strtol(version_info, &version_info, 10); } } } /* Avoid -dylib_compatibility_version must be greater than zero errors. */ if (major == 0) { major = 1; } newarg = (char*)lt_malloc(100); snprintf(newarg, 99, "-compatibility_version %ld -current_version %ld.%ld", major, major, minor); return newarg; } /* * Add a '.libs/' to the buffer. The caller ensures that * The buffer is large enough to handle 6 extra characters. */ static void add_dotlibs(char *buffer) { char *name = strrchr(buffer, '/'); if (!name) { if (!buffer[0]) { strcpy(buffer, ".libs/"); return; } name = buffer; } else { name++; } memmove(name + 6, name, strlen(name)); memcpy(name, ".libs/", 6); } static char *gen_library_name(char const *name, enum lib_type genlib) { char *newarg, *newext; newarg = (char *)calloc(strlen(name) + 11, 1); if (genlib == TYPE_MODULE_LIB && strncmp(name, "lib", 3) == 0) { name += 3; } if (genlib == TYPE_MODULE_LIB) { strcpy(newarg, file_name(name)); } else { strcpy(newarg, name); } newext = strrchr(newarg, '.'); if (!newext) { ERROR("Library path does not have an extension\n"); free(newarg); return NULL; } newext++; switch (genlib) { case TYPE_STATIC_LIB: strcpy(newext, STATIC_LIB_EXT); break; case TYPE_DYNAMIC_LIB: strcpy(newext, DYNAMIC_LIB_EXT); break; case TYPE_MODULE_LIB: strcpy(newext, MODULE_LIB_EXT); break; default: break; } add_dotlibs(newarg); return newarg; } static char *gen_install_name(char const *name, enum lib_type genlib) { char *newname; int rv; struct stat sb; newname = gen_library_name(name, genlib); if (!newname) return NULL; /* Check if it exists. If not, return NULL. */ rv = stat(newname, &sb); if (rv) { free(newname); return NULL; } return newname; } static char const *check_object_exists(command_t *cmd, char const *arg, int arglen) { char *newarg, *ext; struct stat sb; newarg = (char *)lt_malloc(arglen + 10); memcpy(newarg, arg, arglen); newarg[arglen] = 0; ext = newarg + arglen; strcpy(ext, OBJECT_EXT); DEBUG("Checking (obj): %s\n", newarg); if (stat(newarg, &sb) == 0) { return newarg; } free(newarg); return NULL; } /* libdircheck values: * 0 - no .libs suffix * 1 - .libs suffix */ static char *check_library_exists(command_t *cmd, char const *arg, int pathlen, int libdircheck, enum lib_type *libtype) { char *newarg, *ext; int pass, rv, newpathlen; newarg = (char *)lt_malloc(strlen(arg) + 10); strcpy(newarg, arg); newarg[pathlen] = '\0'; newpathlen = pathlen; if (libdircheck) { add_dotlibs(newarg); newpathlen += sizeof(".libs/") - 1; } strcpy(newarg + newpathlen, arg + pathlen); ext = strrchr(newarg, '.'); if (!ext) { ERROR("Error: Library path does not have an extension\n"); free(newarg); return NULL; } ext++; pass = 0; do { struct stat sb; switch (pass) { case 0: if (cmd->options.pic_mode != PIC_AVOID && cmd->options.shared != SHARE_STATIC) { strcpy(ext, DYNAMIC_LIB_EXT); *libtype = TYPE_DYNAMIC_LIB; break; } pass = 1; /* Fall through */ case 1: strcpy(ext, STATIC_LIB_EXT); *libtype = TYPE_STATIC_LIB; break; case 2: strcpy(ext, MODULE_LIB_EXT); *libtype = TYPE_MODULE_LIB; break; case 3: strcpy(ext, OBJECT_EXT); *libtype = TYPE_OBJECT; break; default: *libtype = TYPE_UKNOWN; break; } DEBUG("Checking (lib): %s\n", newarg); rv = stat(newarg, &sb); } while (rv != 0 && ++pass < 4); if (rv == 0) { return newarg; } free(newarg); return NULL; } static char * load_install_path(char const *arg) { FILE *f; char *path; f = fopen(arg,"r"); if (f == NULL) { return NULL; } path = lt_malloc(PATH_MAX); fgets(path, PATH_MAX, f); fclose(f); if (path[strlen(path)-1] == '\n') { path[strlen(path)-1] = '\0'; } /* Check that we have an absolute path. * Otherwise the file could be a GNU libtool file. */ if (path[0] != '/') { free(path); return NULL; } return path; } static char *load_noinstall_path(char const *arg, int pathlen) { char *newarg, *expanded_path; int newpathlen; newarg = (char *)lt_malloc(strlen(arg) + 10); strcpy(newarg, arg); newarg[pathlen] = 0; newpathlen = pathlen; strcat(newarg, ".libs"); newpathlen += sizeof(".libs") - 1; newarg[newpathlen] = 0; #ifdef HAS_REALPATH expanded_path = lt_malloc(PATH_MAX); expanded_path = realpath(newarg, expanded_path); /* Uh, oh. There was an error. Fall back on our first guess. */ if (!expanded_path) { expanded_path = newarg; } #else /* We might get ../ or something goofy. Oh, well. */ expanded_path = newarg; #endif return expanded_path; } static void add_dynamic_link_opts(command_t *cmd, count_chars *args) { #ifdef DYNAMIC_LINK_OPTS if (cmd->options.pic_mode != PIC_AVOID) { DEBUG("Adding linker opt: %s\n", DYNAMIC_LINK_OPTS); push_count_chars(args, DYNAMIC_LINK_OPTS); if (cmd->undefined_flag) { push_count_chars(args, "-undefined"); #if defined(__APPLE__) /* -undefined dynamic_lookup is used by the bundled Python in * 10.4, but if we don't set MACOSX_DEPLOYMENT_TARGET to 10.3+, * we'll get a linker error if we pass this flag. */ if (strcasecmp(cmd->undefined_flag, "dynamic_lookup") == 0) { insert_count_chars(cmd->program_opts, "MACOSX_DEPLOYMENT_TARGET=10.3", 0); } #endif push_count_chars(args, cmd->undefined_flag); } else { #ifdef DYNAMIC_LINK_UNDEFINED DEBUG("Adding linker opt: %s\n", DYNAMIC_LINK_UNDEFINED); push_count_chars(args, DYNAMIC_LINK_UNDEFINED); #endif } } #endif } /* Read the final install location and add it to runtime library search path. */ #ifdef RPATH static void add_rpath(count_chars *cc, char const *path) { int size = 0; char *tmp; #ifdef LINKER_FLAG_PREFIX size = strlen(LINKER_FLAG_PREFIX); #endif size = size + strlen(path) + strlen(RPATH) + 2; tmp = lt_malloc(size); #ifdef LINKER_FLAG_PREFIX strcpy(tmp, LINKER_FLAG_PREFIX); strcat(tmp, RPATH); #else strcpy(tmp, RPATH); #endif #ifndef LINKER_FLAG_NO_EQUALS strcat(tmp, "="); #endif strcat(tmp, path); push_count_chars(cc, tmp); } static void add_rpath_file(count_chars *cc, char const *arg) { char const *path; path = load_install_path(arg); if (path) { add_rpath(cc, path); lt_const_free(path); } } static void add_rpath_noinstall(count_chars *cc, char const *arg, int pathlen) { char const *path; path = load_noinstall_path(arg, pathlen); if (path) { add_rpath(cc, path); lt_const_free(path); } } #endif #ifdef DYNAMIC_LINK_NO_INSTALL static void add_dylink_noinstall(count_chars *cc, char const *arg, int pathlen, int extlen) { char const *install_path, *current_path, *name; char *exp_argument; int i_p_len, c_p_len, name_len, dyext_len, cur_len; install_path = load_install_path(arg); current_path = load_noinstall_path(arg, pathlen); if (!install_path || !current_path) { return; } push_count_chars(cc, DYNAMIC_LINK_NO_INSTALL); i_p_len = strlen(install_path); c_p_len = strlen(current_path); name = arg+pathlen; name_len = extlen-pathlen; dyext_len = sizeof(DYNAMIC_LIB_EXT) - 1; /* No, we need to replace the extension. */ exp_argument = (char *)lt_malloc(i_p_len + c_p_len + (name_len*2) + (dyext_len*2) + 2); cur_len = 0; strcpy(exp_argument, install_path); cur_len += i_p_len; exp_argument[cur_len++] = '/'; strncpy(exp_argument+cur_len, name, extlen-pathlen); cur_len += name_len; strcpy(exp_argument+cur_len, DYNAMIC_LIB_EXT); cur_len += dyext_len; exp_argument[cur_len++] = ':'; strcpy(exp_argument+cur_len, current_path); cur_len += c_p_len; exp_argument[cur_len++] = '/'; strncpy(exp_argument+cur_len, name, extlen-pathlen); cur_len += name_len; strcpy(exp_argument+cur_len, DYNAMIC_LIB_EXT); cur_len += dyext_len; push_count_chars(cc, exp_argument); } #endif #ifdef ADD_MINUS_L /* use -L -llibname to allow to use installed libraries */ static void add_minus_l(count_chars *cc, char const *arg) { char *newarg; char *name = strrchr(arg, '/'); char *file = strrchr(arg, '.'); if ((name != NULL) && (file != NULL) && (strstr(name, "lib") == (name + 1))) { *name = '\0'; *file = '\0'; file = name; file = file+4; push_count_chars(cc, "-L"); push_count_chars(cc, arg); /* we need one argument like -lapr-1 */ newarg = lt_malloc(strlen(file) + 3); strcpy(newarg, "-l"); strcat(newarg, file); push_count_chars(cc, newarg); } else { push_count_chars(cc, arg); } } #endif #if 0 static void add_linker_flag_prefix(count_chars *cc, char const *arg) { #ifndef LINKER_FLAG_PREFIX push_count_chars(cc, arg); #else char *newarg; newarg = (char*)lt_malloc(strlen(arg) + sizeof(LINKER_FLAG_PREFIX) + 1); strcpy(newarg, LINKER_FLAG_PREFIX); strcat(newarg, arg); push_count_chars(cc, newarg); #endif } #endif static int explode_static_lib(command_t *cmd, char const *lib) { count_chars tmpdir_cc, libname_cc; char const *tmpdir, *libname; char savewd[PATH_MAX]; char const *name; DIR *dir; struct dirent *entry; char const *lib_args[4]; /* Bah! */ if (cmd->options.dry_run) { return 0; } name = file_name(lib); init_count_chars(&tmpdir_cc); push_count_chars(&tmpdir_cc, ".libs/"); push_count_chars(&tmpdir_cc, name); push_count_chars(&tmpdir_cc, ".exploded/"); tmpdir = flatten_count_chars(&tmpdir_cc, 0); NOTICE("Making: %s\n", tmpdir); safe_mkdir(cmd, tmpdir); push_count_chars(cmd->tmp_dirs, tmpdir); getcwd(savewd, sizeof(savewd)); if (chdir(tmpdir) != 0) { NOTICE("Warning: could not explode %s\n", lib); return 1; } if (lib[0] == '/') { libname = lib; } else { init_count_chars(&libname_cc); push_count_chars(&libname_cc, "../../"); push_count_chars(&libname_cc, lib); libname = flatten_count_chars(&libname_cc, 0); } lib_args[0] = LIBRARIAN; lib_args[1] = "x"; lib_args[2] = libname; lib_args[3] = NULL; external_spawn(cmd, LIBRARIAN, lib_args); chdir(savewd); dir = opendir(tmpdir); while ((entry = readdir(dir)) != NULL) { #if defined(__APPLE__) && defined(RANLIB) /* Apple inserts __.SYMDEF which isn't needed. * Leopard (10.5+) can also add '__.SYMDEF SORTED' which isn't * much fun either. Just skip them. */ if (strstr(entry->d_name, "__.SYMDEF") != NULL) { continue; } #endif if (entry->d_name[0] != '.') { push_count_chars(&tmpdir_cc, entry->d_name); name = flatten_count_chars(&tmpdir_cc, 0); DEBUG("Adding object: %s\n", name); push_count_chars(cmd->obj_files, name); pop_count_chars(&tmpdir_cc); } } closedir(dir); return 0; } static int parse_input_file_name(char const *arg, command_t *cmd) { char const *ext = strrchr(arg, '.'); char const *name; int pathlen; enum lib_type libtype; char const *newarg; /* Can't guess the extension */ if (!ext) { return 0; } ext++; name = file_name(arg); pathlen = name - arg; /* * Were linking and have an archived object or object file * push it onto the list of object files which'll get used * to create the input files list for the linker. * * We assume that these are outside of the project were building, * as there's no reason to create .a files as part of the build * process. */ if (!strcmp(ext, STATIC_LIB_EXT) && (cmd->mode == MODE_LINK)) { struct stat sb; if (!stat(arg, &sb)) { DEBUG("Adding object: %s\n", arg); push_count_chars(cmd->obj_files, arg); return 1; } } /* * More object files, if were linking they get set as input * files. */ if (!strcmp(ext, "lo") || !strcmp(ext, OBJECT_EXT)) { newarg = check_object_exists(cmd, arg, ext - arg); if (!newarg) { ERROR("Can not find suitable object file for %s\n", arg); exit(1); } if (cmd->mode == MODE_LINK) { DEBUG("Adding object: %s\n", newarg); push_count_chars(cmd->obj_files, newarg); } else { push_count_chars(cmd->arglist, newarg); } return 1; } if (!strcmp(ext, "la")) { switch (cmd->mode) { case MODE_LINK: /* Try the .libs dir first! */ newarg = check_library_exists(cmd, arg, pathlen, 1, &libtype); if (!newarg) { /* Try the normal dir next. */ newarg = check_library_exists(cmd, arg, pathlen, 0, &libtype); if (!newarg) { ERROR("Can not find suitable library for %s\n", arg); exit(1); } } /* It is not ok to just add the file: a library may added with: 1 - -L path library_name. (For *.so in Linux). 2 - library_name. */ #ifdef ADD_MINUS_L if (libtype == TYPE_DYNAMIC_LIB) { add_minus_l(cmd->shared_opts.dependencies, newarg); } else if (cmd->output == OUT_LIB && libtype == TYPE_STATIC_LIB) { explode_static_lib(cmd, newarg); } else { push_count_chars(cmd->shared_opts.dependencies, newarg); } #else if (cmd->output == OUT_LIB && libtype == TYPE_STATIC_LIB) { explode_static_lib(cmd, newarg); } else { push_count_chars(cmd->shared_opts.dependencies, newarg); } #endif if (libtype == TYPE_DYNAMIC_LIB) { if (cmd->options.no_install) { #ifdef RPATH add_rpath_noinstall(cmd->shared_opts.dependencies, arg, pathlen); #endif } else { #ifdef RPATH add_rpath_file(cmd->shared_opts.dependencies, arg); #endif } } break; case MODE_INSTALL: /* * If we've already recorded a library to * install, we're most likely getting the .la * file that we want to install as. * * The problem is that we need to add it as the * directory, not the .la file itself. * Otherwise, we'll do odd things. */ if (cmd->output == OUT_LIB) { char *tmp; tmp = strdup(arg); tmp[pathlen] = '\0'; push_count_chars(cmd->arglist, tmp); } else { cmd->output = OUT_LIB; cmd->output_name = arg; cmd->static_name.install = gen_install_name(arg, 0); cmd->shared_name.install = gen_install_name(arg, 1); cmd->module_name.install = gen_install_name(arg, 2); if (!cmd->static_name.install && !cmd->shared_name.install && !cmd->module_name.install) { ERROR("Files to install do not exist\n"); exit(1); } } break; default: break; } return 1; } if (!strcmp(ext, "c")) { /* If we don't already have an idea what our output name will be. */ if (!cmd->basename) { char *tmp = lt_malloc(strlen(arg) + 4); strcpy(tmp, arg); strcpy(strrchr(tmp, '.') + 1, "lo"); cmd->basename = tmp; cmd->fake_output_name = strrchr(cmd->basename, '/'); if (cmd->fake_output_name) { cmd->fake_output_name++; } else { cmd->fake_output_name = cmd->basename; } } } return 0; } static int parse_output_file_name(char const *arg, command_t *cmd) { char const *name; char const *ext; char *newarg = NULL; size_t pathlen; cmd->fake_output_name = arg; name = file_name(arg); ext = strrchr(name, '.'); #ifdef EXE_EXT if (!ext || strcmp(ext, EXE_EXT) == 0) { #else if (!ext) { #endif cmd->basename = arg; cmd->output = OUT_PROGRAM; #if defined(_OSD_POSIX) cmd->options.pic_mode = PIC_AVOID; #endif newarg = (char *)lt_malloc(strlen(arg) + 5); strcpy(newarg, arg); #ifdef EXE_EXT if (!ext) { strcat(newarg, EXE_EXT); } #endif cmd->output_name = newarg; return 1; } ext++; pathlen = name - arg; if (strcmp(ext, "la") == 0) { assert(cmd->mode == MODE_LINK); cmd->basename = arg; cmd->static_name.normal = gen_library_name(arg, TYPE_STATIC_LIB); cmd->shared_name.normal = gen_library_name(arg, TYPE_DYNAMIC_LIB); cmd->module_name.normal = gen_library_name(arg, TYPE_MODULE_LIB); cmd->static_name.install = gen_install_name(arg, TYPE_STATIC_LIB); cmd->shared_name.install = gen_install_name(arg, TYPE_DYNAMIC_LIB); cmd->module_name.install = gen_install_name(arg, TYPE_MODULE_LIB); if (!cmd->options.dry_run) { char *newname; char *newext; newname = lt_malloc(strlen(cmd->static_name.normal) + 1); strcpy(newname, cmd->static_name.normal); newext = strrchr(newname, '/'); if (!newext) { /* Check first to see if the dir already exists! */ safe_mkdir(cmd, ".libs"); } else { *newext = '\0'; safe_mkdir(cmd, newname); } free(newname); } #ifdef TRUNCATE_DLL_NAME if (shared) { arg = truncate_dll_name(arg); } #endif cmd->output_name = arg; return 1; } if (strcmp(ext, STATIC_LIB_EXT) == 0) { assert(cmd->mode == MODE_LINK); cmd->basename = arg; cmd->options.shared = SHARE_STATIC; cmd->output = OUT_STATIC_LIB_ONLY; cmd->static_name.normal = gen_library_name(arg, TYPE_STATIC_LIB); cmd->static_name.install = gen_install_name(arg, TYPE_STATIC_LIB); if (!cmd->options.dry_run) { char *newname; char *newext; newname = lt_malloc(strlen(cmd->static_name.normal) + 1); strcpy(newname, cmd->static_name.normal); newext = strrchr(newname, '/'); if (!newext) { /* Check first to see if the dir already exists! */ safe_mkdir(cmd, ".libs"); } else { *newext = '\0'; safe_mkdir(cmd, newname); } free(newname); } cmd->output_name = arg; return 1; } if (strcmp(ext, DYNAMIC_LIB_EXT) == 0) { assert(cmd->mode == MODE_LINK); cmd->basename = arg; cmd->options.shared = SHARE_SHARED; cmd->output = OUT_DYNAMIC_LIB_ONLY; cmd->shared_name.normal = gen_library_name(arg, TYPE_DYNAMIC_LIB); cmd->module_name.normal = gen_library_name(arg, TYPE_MODULE_LIB); cmd->shared_name.install = gen_install_name(arg, TYPE_DYNAMIC_LIB); cmd->module_name.install = gen_install_name(arg, TYPE_MODULE_LIB); if (!cmd->options.dry_run) { char *newname; char *newext; newname = lt_malloc(strlen(cmd->shared_name.normal) + 1); strcpy(newname, cmd->shared_name.normal); newext = strrchr(newname, '/'); if (!newext) { /* Check first to see if the dir already exists! */ safe_mkdir(cmd, ".libs"); } else { *newext = '\0'; safe_mkdir(cmd, newname); } free(newname); } cmd->output_name = arg; return 1; } if (strcmp(ext, "lo") == 0) { char *newext; cmd->basename = arg; cmd->output = OUT_OBJECT; newarg = (char *)lt_malloc(strlen(arg) + 2); strcpy(newarg, arg); newext = strrchr(newarg, '.') + 1; strcpy(newext, OBJECT_EXT); cmd->output_name = newarg; return 1; } if (strcmp(ext, DYNAMIC_LIB_EXT) == 0) { ERROR("Please build libraries with .la target, not ." DYNAMIC_LIB_EXT "\n"); exit(1); } if (strcmp(ext, STATIC_LIB_EXT) == 0) { ERROR("Please build libraries with .la target, not ." STATIC_LIB_EXT "\n"); exit(1); } return 0; } static char const *automode(char const *arg, command_t *cmd) { if (cmd->mode != MODE_UNKNOWN) return arg; if (!strcmp(arg, "CC") || !strcmp(arg, "CXX")) { DEBUG("Now in compile mode, guessed from: %s\n", arg); arg = CC; cmd->mode = MODE_COMPILE; } else if (!strcmp(arg, "LINK") || !strcmp(arg, "LINK.c") || !strcmp(arg, "LINK.cxx")) { DEBUG("Now in linker mode, guessed from: %s\n", arg); arg = LINK_C; cmd->mode = MODE_LINK; } return arg; } #ifdef GEN_EXPORTS static void generate_def_file(command_t *cmd) { char def_file[1024]; char implib_file[1024]; char *ext; FILE *hDef; char *export_args[1024]; int num_export_args = 0; char *cmd; int cmd_size = 0; int a; if (cmd->output_name) { strcpy(def_file, cmd->output_name); strcat(def_file, ".def"); hDef = fopen(def_file, "w"); if (hDef != NULL) { fprintf(hDef, "LIBRARY '%s' INITINSTANCE\n", file_name_stripped(cmd->output_name)); fprintf(hDef, "DATA NONSHARED\n"); fprintf(hDef, "EXPORTS\n"); fclose(hDef); for (a = 0; a < cmd->num_obj_files; a++) { cmd_size += strlen(cmd->obj_files[a]) + 1; } cmd_size += strlen(GEN_EXPORTS) + strlen(def_file) + 3; cmd = (char *)lt_malloc(cmd_size); strcpy(cmd, GEN_EXPORTS); for (a=0; a < cmd->num_obj_files; a++) { strcat(cmd, " "); strcat(cmd, cmd->obj_files[a] ); } strcat(cmd, ">>"); strcat(cmd, def_file); puts(cmd); export_args[num_export_args++] = SHELL_CMD; export_args[num_export_args++] = "-c"; export_args[num_export_args++] = cmd; export_args[num_export_args++] = NULL; external_spawn(cmd, export_args[0], (char const**)export_args); cmd->arglist[cmd->num_args++] = strdup(def_file); /* Now make an import library for the dll */ num_export_args = 0; export_args[num_export_args++] = DEF2IMPLIB_CMD; export_args[num_export_args++] = "-o"; strcpy(implib_file, ".libs/"); strcat(implib_file, cmd->basename); ext = strrchr(implib_file, '.'); if (ext) { *ext = '\0'; } strcat(implib_file, "."); strcat(implib_file, STATIC_LIB_EXT); export_args[num_export_args++] = implib_file; export_args[num_export_args++] = def_file; export_args[num_export_args++] = NULL; external_spawn(cmd, export_args[0], (char const**)export_args); } } } #endif #if 0 static char const* expand_path(char const *relpath) { char foo[PATH_MAX], *newpath; getcwd(foo, PATH_MAX-1); newpath = (char*)lt_malloc(strlen(foo)+strlen(relpath)+2); strcpy(newpath, foo); strcat(newpath, "/"); strcat(newpath, relpath); return newpath; } #endif static void link_fixup(command_t *cmd) { /* If we were passed an -rpath directive, we need to build * shared objects too. Otherwise, we should only create static * libraries. */ if (!cmd->install_path && (cmd->output == OUT_DYNAMIC_LIB_ONLY || cmd->output == OUT_MODULE || cmd->output == OUT_LIB)) { if (cmd->options.shared == SHARE_SHARED) { cmd->install_path = LIBDIR; } } if (cmd->output == OUT_DYNAMIC_LIB_ONLY || cmd->output == OUT_MODULE || cmd->output == OUT_LIB) { push_count_chars(cmd->shared_opts.normal, "-o"); if (cmd->output == OUT_MODULE) { push_count_chars(cmd->shared_opts.normal, cmd->module_name.normal); } else { push_count_chars(cmd->shared_opts.normal, cmd->shared_name.normal); #ifdef DYNAMIC_INSTALL_NAME push_count_chars(cmd->shared_opts.normal, DYNAMIC_INSTALL_NAME); if (!cmd->install_path) { ERROR("Installation mode requires -rpath\n"); exit(1); } { char *tmp = lt_malloc(PATH_MAX); strcpy(tmp, cmd->install_path); if (cmd->shared_name.install) { strcat(tmp, strrchr(cmd->shared_name.install, '/')); } else { strcat(tmp, strrchr(cmd->shared_name.normal, '/')); } push_count_chars(cmd->shared_opts.normal, tmp); } #endif } append_count_chars(cmd->shared_opts.normal, cmd->obj_files); append_count_chars(cmd->shared_opts.normal, cmd->shared_opts.dependencies); if (cmd->options.export_all) { #ifdef GEN_EXPORTS generate_def_file(cmd); #endif } } if (cmd->output == OUT_LIB || cmd->output == OUT_STATIC_LIB_ONLY) { push_count_chars(cmd->static_opts.normal, "-o"); push_count_chars(cmd->static_opts.normal, cmd->output_name); } if (cmd->output == OUT_PROGRAM) { if (cmd->output_name) { push_count_chars(cmd->arglist, "-o"); push_count_chars(cmd->arglist, cmd->output_name); append_count_chars(cmd->arglist, cmd->obj_files); append_count_chars(cmd->arglist, cmd->shared_opts.dependencies); add_dynamic_link_opts(cmd, cmd->arglist); } } } static void post_parse_fixup(command_t *cmd) { switch (cmd->mode) { case MODE_COMPILE: #ifdef PIC_FLAG if (cmd->options.pic_mode != PIC_AVOID) { push_count_chars(cmd->arglist, PIC_FLAG); } #endif if (cmd->output_name) { push_count_chars(cmd->arglist, "-o"); push_count_chars(cmd->arglist, cmd->output_name); } break; case MODE_LINK: link_fixup(cmd); break; case MODE_INSTALL: if (cmd->output == OUT_LIB) { link_fixup(cmd); } default: break; } #ifdef USE_OMF if (cmd->output == OUT_OBJECT || cmd->output == OUT_PROGRAM || cmd->output == OUT_LIB || cmd->output == OUT_DYNAMIC_LIB_ONLY) { push_count_chars(cmd->arglist, "-Zomf"); } #endif if (cmd->options.shared && (cmd->output == OUT_OBJECT || cmd->output == OUT_LIB || cmd->output == OUT_DYNAMIC_LIB_ONLY)) { #ifdef SHARE_SW push_count_chars(cmd->arglist, SHARE_SW); #endif } } static int run_mode(command_t *cmd) { int rv = 0; count_chars *cctemp; cctemp = (count_chars*)lt_malloc(sizeof(count_chars)); init_count_chars(cctemp); switch (cmd->mode) { case MODE_COMPILE: rv = run_command(cmd, cmd->arglist); if (rv) goto finish; break; case MODE_INSTALL: /* Well, we'll assume it's a file going to a directory... */ /* For brain-dead install-sh based scripts, we have to repeat * the command N-times. install-sh should die. */ if (!cmd->output_name) { rv = run_command(cmd, cmd->arglist); if (rv) goto finish; } if (cmd->output_name) { append_count_chars(cctemp, cmd->arglist); insert_count_chars(cctemp, cmd->output_name, cctemp->num - 1); rv = run_command(cmd, cctemp); if (rv) goto finish; clear_count_chars(cctemp); } if (cmd->static_name.install) { append_count_chars(cctemp, cmd->arglist); insert_count_chars(cctemp, cmd->static_name.install, cctemp->num - 1); rv = run_command(cmd, cctemp); if (rv) goto finish; #if defined(__APPLE__) && defined(RANLIB) /* From the Apple libtool(1) manpage on Tiger/10.4: * ---- * With the way libraries used to be created, errors were possible * if the library was modified with ar(1) and the table of * contents was not updated by rerunning ranlib(1). Thus the * link editor, ld, warns when the modification date of a library * is more recent than the creation date of its table of * contents. Unfortunately, this means that you get the warning * even if you only copy the library. * ---- * * This means that when we install the static archive, we need to * rerun ranlib afterwards. */ char const *lib_args[3], *static_lib_name; { char *tmp; size_t len1, len2; len1 = strlen(cmd->arglist->vals[cmd->arglist->num - 1]); static_lib_name = file_name(cmd->static_name.install); len2 = strlen(static_lib_name); tmp = lt_malloc(len1 + len2 + 2); snprintf(tmp, len1 + len2 + 2, "%s/%s", cmd->arglist->vals[cmd->arglist->num - 1], static_lib_name); lib_args[0] = RANLIB; lib_args[1] = tmp; lib_args[2] = NULL; external_spawn(cmd, RANLIB, lib_args); free(tmp); } #endif clear_count_chars(cctemp); } if (cmd->shared_name.install) { append_count_chars(cctemp, cmd->arglist); insert_count_chars(cctemp, cmd->shared_name.install, cctemp->num - 1); rv = run_command(cmd, cctemp); if (rv) goto finish; clear_count_chars(cctemp); } if (cmd->module_name.install) { append_count_chars(cctemp, cmd->arglist); insert_count_chars(cctemp, cmd->module_name.install, cctemp->num - 1); rv = run_command(cmd, cctemp); if (rv) goto finish; clear_count_chars(cctemp); } break; case MODE_LINK: if (cmd->output == OUT_STATIC_LIB_ONLY || cmd->output == OUT_LIB) { #ifdef RANLIB char const *lib_args[3]; #endif /* Removes compiler! */ cmd->program = LIBRARIAN; push_count_chars(cmd->program_opts, LIBRARIAN_OPTS); push_count_chars(cmd->program_opts, cmd->static_name.normal); rv = run_command(cmd, cmd->obj_files); if (rv) goto finish; #ifdef RANLIB lib_args[0] = RANLIB; lib_args[1] = cmd->static_name.normal; lib_args[2] = NULL; external_spawn(cmd, RANLIB, lib_args); #endif } if (cmd->output == OUT_DYNAMIC_LIB_ONLY || cmd->output == OUT_MODULE || cmd->output == OUT_LIB) { cmd->program = NULL; clear_count_chars(cmd->program_opts); append_count_chars(cmd->program_opts, cmd->arglist); if (cmd->output == OUT_MODULE) { #ifdef MODULE_OPTS push_count_chars(cmd->program_opts, MODULE_OPTS); #endif } else { #ifdef SHARED_OPTS push_count_chars(cmd->program_opts, SHARED_OPTS); #endif #ifdef dynamic_link_version_func push_count_chars(cmd->program_opts, dynamic_link_version_func(cmd->version_info)); #endif } add_dynamic_link_opts(cmd, cmd->program_opts); rv = run_command(cmd, cmd->shared_opts.normal); if (rv) goto finish; } if (cmd->output == OUT_PROGRAM) { rv = run_command(cmd, cmd->arglist); if (rv) goto finish; } break; case MODE_EXECUTE: { char *l, libpath[PATH_MAX]; if (!cmd->arglist->num) { ERROR("No command to execute.\n"); rv = 1; goto finish; } if (strlen(cmd->arglist->vals[0]) >= PATH_MAX) { ERROR("Libpath too long no buffer space\n"); rv = 1; goto finish; } strcpy(libpath, cmd->arglist->vals[0]); add_dotlibs(libpath); l = strrchr(libpath, '/'); if (!l) l = strrchr(libpath, '\\'); if (l) { *l = '\0'; l = libpath; } else { l = ".libs/"; } l = "./build/lib/.libs"; setenv(LD_LIBRARY_PATH_LOCAL, l, 1); #ifdef __APPLE__ setenv("DYLD_FALLBACK_LIBRARY_PATH", l, 1); #endif if (!getenv("FR_LIBRARY_PATH")) { setenv("FR_LIBRARY_PATH", "./build/lib/local/.libs", 1); } rv = run_command(cmd, cmd->arglist); if (rv) goto finish; } break; default: break; } finish: free(cctemp); return rv; } static void cleanup_tmp_dir(char const *dirname) { DIR *dir; struct dirent *entry; char fullname[1024]; dir = opendir(dirname); if (!dir) { return; } if ((strlen(dirname) + 1 + sizeof(entry->d_name)) >= sizeof(fullname)) { ERROR("Dirname too long, out of buffer space\n"); (void) closedir(dir); return; } while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] != '.') { strcpy(fullname, dirname); strcat(fullname, "/"); strcat(fullname, entry->d_name); (void) remove(fullname); } } rmdir(dirname); (void) closedir(dir); } static void cleanup_tmp_dirs(command_t *cmd) { int d; for (d = 0; d < cmd->tmp_dirs->num; d++) { cleanup_tmp_dir(cmd->tmp_dirs->vals[d]); } } static int ensure_fake_uptodate(command_t *cmd) { /* FIXME: could do the stat/touch here, but nah... */ char const *touch_args[3]; if (cmd->mode == MODE_INSTALL) { return 0; } if (!cmd->fake_output_name) { return 0; } touch_args[0] = "touch"; touch_args[1] = cmd->fake_output_name; touch_args[2] = NULL; return external_spawn(cmd, "touch", touch_args); } /* Store the install path in the *.la file */ static int add_for_runtime(command_t *cmd) { if (cmd->mode == MODE_INSTALL) { return 0; } if (cmd->output == OUT_DYNAMIC_LIB_ONLY || cmd->output == OUT_LIB) { FILE *f=fopen(cmd->fake_output_name,"w"); if (f == NULL) { return -1; } fprintf(f,"%s\n", cmd->install_path); fclose(f); return(0); } else { return(ensure_fake_uptodate(cmd)); } } static void parse_args(int argc, char *argv[], command_t *cmd) { int a; char const *arg, *base; int arg_used; /* * We now take a major step past libtool. * * IF there's no "--mode=...", AND we recognise * the binary as a "special" name, THEN replace it * with the correct one, and set the correct mode. * * For example if were called 'CC' then we know we should * probably be compiling stuff. */ base = file_name(argv[0]); arg = automode(base, cmd); if (arg != base) { push_count_chars(cmd->arglist, arg); assert(cmd->mode != MODE_UNKNOWN); } /* * We first pass over the command-line arguments looking for * "--mode", etc. If so, then use the libtool compatibility * method for building the software. Otherwise, auto-detect it * via "-o" and the extensions. */ base = NULL; if (cmd->mode == MODE_UNKNOWN) for (a = 1; a < argc; a++) { arg = argv[a]; if (strncmp(arg, "--mode=", 7) == 0) { base = NULL; break; } /* * Stop if we get another magic method */ if ((a == 1) && ((strncmp(arg, "LINK", 4) == 0) || (strcmp(arg, "CC") == 0) || (strcmp(arg, "CXX") == 0))) { base = NULL; break; } if (strncmp(arg, "-o", 2) == 0) { base = argv[++a]; } } /* * There were no magic args or an explicit --mode= but we did * find an output file, so guess what mode were meant to be in * from its extension. */ if (base) { arg = strrchr(base, '.'); if (!arg) { cmd->mode = MODE_LINK; push_count_chars(cmd->arglist, LINK_C); } #ifdef EXE_EXT else if (strcmp(arg, EXE_EXT) == 0) { cmd->mode = MODE_LINK; push_count_chars(cmd->arglist, LINK_C); } #endif else if (strcmp(arg + 1, DYNAMIC_LIB_EXT) == 0) { cmd->mode = MODE_LINK; push_count_chars(cmd->arglist, LINK_C); } else if (strcmp(arg + 1, STATIC_LIB_EXT) == 0) { cmd->mode = MODE_LINK; push_count_chars(cmd->arglist, LINK_C); } else if (strcmp(arg + 1, "la") == 0) { cmd->mode = MODE_LINK; push_count_chars(cmd->arglist, LINK_C); } else if ((strcmp(arg + 1, "lo") == 0) || (strcmp(arg + 1, "o") == 0)) { cmd->mode = MODE_COMPILE; push_count_chars(cmd->arglist, CC); } } for (a = 1; a < argc; a++) { arg = argv[a]; arg_used = 1; if (cmd->mode == MODE_EXECUTE) { push_count_chars(cmd->arglist, arg); continue; } if (arg[0] == '-') { /* * Double dashed (long) single dash (short) */ arg_used = (arg[1] == '-') ? parse_long_opt(arg + 2, cmd) : parse_short_opt(arg + 1, cmd); if (arg_used) continue; /* * Ignore all options after the '--execute' */ if (cmd->mode == MODE_EXECUTE) continue; /* * We haven't done anything with it yet, but * there are still some arg/value pairs. * * Try some of the more complicated short opts... */ if (a + 1 < argc) { /* * We found an output file! */ if ((arg[1] == 'o') && (arg[2] == '\0')) { arg = argv[++a]; arg_used = parse_output_file_name(arg, cmd); /* * -MT literal dependency */ } else if (!strcmp(arg + 1, "MT")) { DEBUG("Adding: %s\n", arg); push_count_chars(cmd->arglist, arg); arg = argv[++a]; NOTICE(" %s\n", arg); push_count_chars(cmd->arglist, arg); arg_used = 1; /* * Runtime library search path */ } else if (!strcmp(arg + 1, "rpath")) { /* Aha, we should try to link both! */ cmd->install_path = argv[++a]; arg_used = 1; } else if (!strcmp(arg + 1, "release")) { /* Store for later deciphering */ cmd->version_info = argv[++a]; arg_used = 1; } else if (!strcmp(arg + 1, "version-info")) { /* Store for later deciphering */ cmd->version_info = argv[++a]; arg_used = 1; } else if (!strcmp(arg + 1, "export-symbols-regex")) { /* Skip the argument. */ ++a; arg_used = 1; } else if (!strcmp(arg + 1, "undefined")) { cmd->undefined_flag = argv[++a]; arg_used = 1; /* * Add dir to runtime library search path. */ } else if ((arg[1] == 'R') && !arg[2]) { add_runtime_dir_lib(argv[++a], cmd); arg_used = 1; } } /* * Ok.. the argument doesn't begin with a dash * maybe it's an input file. * * Check its extension to see if it's a known input * file and verify it exists. */ } else { arg_used = parse_input_file_name(arg, cmd); } /* * If we still don't have a run mode, look for a magic * program name CC, LINK, or whatever. Then replace that * with the name of the real program we want to run. */ if (!arg_used) { if ((cmd->arglist->num == 0) && (cmd->mode == MODE_UNKNOWN)) { arg = automode(arg, cmd); } DEBUG("Adding: %s\n", arg); push_count_chars(cmd->arglist, arg); } } } int main(int argc, char *argv[]) { int rc; command_t cmd; memset(&cmd, 0, sizeof(cmd)); cmd.options.pic_mode = PIC_UNKNOWN; cmd.mode = MODE_UNKNOWN; cmd.output = OUT_GENERAL; /* * Initialise the various argument lists */ cmd.program_opts = alloc_countchars(); cmd.arglist = alloc_countchars(); cmd.tmp_dirs = alloc_countchars(); cmd.obj_files = alloc_countchars(); cmd.dep_rpaths = alloc_countchars(); cmd.rpaths = alloc_countchars(); cmd.static_opts.normal = alloc_countchars(); cmd.shared_opts.normal = alloc_countchars(); cmd.shared_opts.dependencies = alloc_countchars(); /* * Fill up the various argument lists */ parse_args(argc, argv, &cmd); post_parse_fixup(&cmd); /* * We couldn't figure out which mode to operate in */ if (cmd.mode == MODE_UNKNOWN) { usage(1); } rc = run_mode(&cmd); if (!rc) { add_for_runtime(&cmd); } cleanup_tmp_dirs(&cmd); return rc; }