diff options
Diffstat (limited to 'scripts/jlibtool.c')
-rw-r--r-- | scripts/jlibtool.c | 2604 |
1 files changed, 2604 insertions, 0 deletions
diff --git a/scripts/jlibtool.c b/scripts/jlibtool.c new file mode 100644 index 0000000..4192ac3 --- /dev/null +++ b/scripts/jlibtool.c @@ -0,0 +1,2604 @@ +/* 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/stat.h> +#include <sys/types.h> + +#if !defined(__MINGW32__) +# include <sys/wait.h> +#endif + +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <assert.h> + +#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 <stdarg.h> +#endif + +#ifdef __EMX__ +#include <process.h> +#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 + 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; +} |