/* 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;
}