asuswrt-merlin.ng/release/src-rt-5.02axhnd.675x/hostTools/mlibtool/mlibtool.c
2022-05-26 12:19:01 -04:00

1482 lines
42 KiB
C

/*
* mlibtool: The libtool accelerator
*
* A mini version of libtool that does the right thing on simple, sane systems.
* On insane systems, mlibtool simply requires that true libtool be installed,
* and calls it transparently.
*
* Call as
* $ mlibtool libtool <libtool options>
* Or, for the truly committed,
* $ mlibtool false <libtool options>
*
* http://bitbucket.org/GregorR/mlibtool
* http://github.com/GregorR/mlibtool
*/
#define MLIBTOOL_VERSION "0.10"
/*
* Copyright (c) 2013 Gregor Richards
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _XOPEN_SOURCE 500
/* headers used in written .l* files */
#define SANE_HEADER "# SYSTEM_IS_SANE\n"
#define PACKAGE "libtool (mlibtool) " MLIBTOOL_VERSION
#define PACKAGE_HEADER "# Generated by " PACKAGE "\n"
/* our binary runner script */
#define BIN_SCRIPT_1 "#!/bin/sh\n" \
PACKAGE_HEADER \
"LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}"
#define BIN_SCRIPT_2 "\"\n" \
"export LD_LIBRARY_PATH\n" \
"exec \""
#define BIN_SCRIPT_3 "\" \"$@\"\n"
/* macro to fail with perror if a function fails */
#define ORX(into, func, bad, args) do { \
(into) = func args; \
if ((into) == (bad)) { \
perror("mlibtool: " #func); \
exit(1); \
} \
} while (0)
/* macro to fail to libtool if a function fails (must have struct Options *opt
* as a local variable) */
#define ORL(into, func, bad, args) do { \
(into) = func args; \
if ((into) == bad) { \
perror("mlibtool: " #func); \
execLibtool(opt); \
} \
} while (0)
/* make sure we're POSIX */
#if defined(unix) || defined(__unix) || defined(__unix__)
#include <unistd.h>
#endif
#ifndef _POSIX_VERSION
/* Not even POSIX. This system can't possibly be sane, so there's no point in
* trying to handle builds. */
int execv(const char *path, char *const argv[]);
int main(int argc, char **argv)
{
int i;
for (i = 1; i < argc && argv[i][0] == '-'; i++);
execv(argv[i], argv + i);
return 1;
}
#else
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
/* a simple buffer type for our persistent char ** commands */
struct Buffer {
char **buf;
size_t bufused, bufsz;
};
#define BUFFER_DEFAULT_SZ 8
#define INIT_BUFFER(ubuf) do { \
struct Buffer *buf_ = &(ubuf); \
ORX(buf_->buf, malloc, NULL, (BUFFER_DEFAULT_SZ * sizeof(char *))); \
buf_->bufused = 0; \
buf_->bufsz = BUFFER_DEFAULT_SZ; \
} while (0)
#define WRITE_BUFFER(ubuf, val) do { \
struct Buffer *buf_ = &(ubuf); \
while (buf_->bufused >= buf_->bufsz) { \
buf_->bufsz *= 2; \
ORL(buf_->buf, realloc, NULL, (buf_->buf, buf_->bufsz * sizeof(char *))); \
} \
buf_->buf[buf_->bufused++] = (val); \
} while (0)
#define FREE_BUFFER(ubuf) do { \
free((ubuf).buf); \
} while (0)
/* Generate a filename to cache sanity (allocates) */
static char *cachedSanityName(char *cc, char **argv)
{
int i;
char *repr = NULL;
char *dirC, *dir, *ret;
for (i = 0; argv[i]; i++) {
char *ext;
if (!strcmp(argv[i], "-o") && argv[i+1]) {
repr = argv[i+1];
break;
} else if ((ext = strrchr(argv[i], '.'))) {
if (ext[1] == 'l' && (ext[2] == 'a' || ext[2] == 'o')) {
repr = argv[i];
break;
}
}
}
if (repr == NULL) {
/* nothing to use! */
return NULL;
}
/* get its directory name */
ORX(dirC, strdup, NULL, (repr));
dir = dirname(dirC);
/* and make the cache name */
ORX(ret, malloc, NULL, (strlen(dir) + strlen(cc) + 13));
sprintf(ret, "%s/.libs", dir);
mkdir(ret, 0777);
sprintf(ret, "%s/.libs/sane.%s", dir, cc);
free(dirC);
return ret;
}
/* Cache the sanity of this system if possible */
static void systemCacheSanity(char *cc, char **argv, int sane)
{
FILE *f;
char *cacheName = cachedSanityName(cc, argv);
if (!cacheName) return;
if (access(cacheName, F_OK) == 0) {
free(cacheName);
return;
}
f = fopen(cacheName, "w");
if (f) {
fwrite(sane ? "1" : "0", 1, 1, f);
fclose(f);
}
free(cacheName);
}
/* Check the cache for system sanity */
static int systemCachedSanity(char *cc, char **argv)
{
FILE *f;
int cs = -1;
char *cacheName = cachedSanityName(cc, argv);
if (!cacheName) return -1;
f = fopen(cacheName, "r");
if (f) {
cs = fgetc(f);
if (cs == EOF) cs = -1;
else cs -= '0';
fclose(f);
}
free(cacheName);
return cs;
}
/* our modes */
enum Mode {
MODE_UNKNOWN = 0,
MODE_COMPILE,
MODE_LINK,
MODE_INSTALL
};
/* options necessary to handle modes */
struct Options {
int dryRun, quiet, retryIfFail;
int buildShared, buildStatic; /* also effects -fPIC in .o files */
int arglt; /* where the libtool command starts */
int argc;
char **argv, **cmd;
};
/* redirect to libtool */
static void execLibtool(struct Options *opt)
{
int arglt = opt->arglt;
char **argv = opt->argv;
if (!opt->quiet)
fprintf(stderr, "mlibtool: unsupported configuration, trying libtool (%s)\n", argv[arglt]);
execvp(argv[arglt], argv + arglt);
perror(argv[arglt]);
exit(1);
}
/* Generic function to spawn a child and wait for it, exiting if the child
* fails. */
static void spawn(struct Options *opt, char *const *cmd)
{
size_t i;
int fail = 0;
/* output the command */
if (!opt->quiet) {
fprintf(stderr, "mlibtool:");
for (i = 0; cmd[i]; i++)
fprintf(stderr, " %s", cmd[i]);
fprintf(stderr, "\n");
}
/* and run it */
if (!opt->dryRun) {
pid_t pid;
int tmpi;
ORL(pid, fork, -1, ());
if (pid == 0) {
execvp(cmd[0], cmd);
perror(cmd[0]);
exit(1);
}
if (waitpid(pid, &tmpi, 0) != pid) {
perror(cmd[0]);
fail = 1;
} else if (tmpi != 0)
fail = 1;
}
if (fail) {
if (opt->retryIfFail) {
execLibtool(opt);
} else {
exit(1);
}
}
}
/* Check for sanity by reading a .lo file. If cc is provided, fall back to that
* if no .lo files are found. */
static int checkLoSanity(struct Options *opt, char *cc)
{
int sane = 0, foundlo = 0;
size_t i;
/* look for a .lo file and check it for sanity */
for (i = 1; opt->cmd[i]; i++) {
char *arg = opt->cmd[i];
if (arg[0] != '-') {
char *ext = strrchr(arg, '.');
if (ext && (!strcmp(ext, ".lo") || !strcmp(ext, ".la"))) {
FILE *f;
foundlo = 1;
f = fopen(arg, "r");
if (f) {
char buf[sizeof(SANE_HEADER)];
fgets(buf, sizeof(SANE_HEADER), f);
if (!strcmp(buf, SANE_HEADER)) sane = 1;
fclose(f);
break;
}
}
}
}
if (!foundlo && cc)
return 1;
return sane;
}
static void usage(enum Mode mode);
/* mode functions */
static void ltcompile(struct Options *);
static void ltlink(struct Options *);
static void ltinstall(struct Options *);
int main(int argc, char **argv)
{
int argi;
/* options */
struct Options opt;
int insane = 0;
char *modeS = NULL;
enum Mode mode = MODE_UNKNOWN;
int sane = 0;
memset(&opt, 0, sizeof(opt));
/* mlibtool-specific options come first */
for (argi = 1; argi < argc && argv[argi][0] == '-'; argi++) {
char *arg = argv[argi];
if (!strcmp(arg, "--enable-static")) {
opt.buildStatic = 1;
} else if (!strcmp(arg, "--enable-shared")) {
opt.buildShared = 1;
} else if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
usage(MODE_UNKNOWN);
exit(0);
} else {
usage(MODE_UNKNOWN);
exit(1);
}
}
/* if neither static nor shared were specified, enabled both */
if (!opt.buildStatic && !opt.buildShared)
opt.buildStatic = opt.buildShared = 1;
/* next argument must be target libtool */
opt.arglt = argi;
for (; argi < argc && argv[argi][0] != '-'; argi++);
/* collect arguments up to --mode */
for (; argi < argc; argi++) {
char *arg = argv[argi];
if (!strcmp(arg, "-n") || !strcmp(arg, "--dry-run")) {
opt.dryRun = 1;
} else if (!strcmp(arg, "--quiet") ||
!strcmp(arg, "--silent")) {
opt.quiet = 1;
} else if (!strcmp(arg, "--no-quiet") ||
!strcmp(arg, "--no-silent")) {
opt.quiet = 0;
} else if (!strcmp(arg, "--version")) {
printf("%s\n", PACKAGE);
exit(0);
} else if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) {
usage(MODE_UNKNOWN);
exit(0);
} else if (!strncmp(arg, "--mode=", 7) && argi < argc - 1) {
modeS = arg + 7;
argi++;
break;
} else if (!strncmp(arg, "--tag=", 6) ||
!strcmp(arg, "-v") ||
!strcmp(arg, "--verbose") ||
!strcmp(arg, "--no-verbose")) {
/* ignored for compatibility */
} else {
insane = 1;
}
}
opt.argc = argc;
opt.argv = argv;
opt.cmd = argv + argi;
if (!modeS) {
usage(MODE_UNKNOWN);
exit(1);
}
/* check the mode */
if (!strcmp(modeS, "compile")) {
mode = MODE_COMPILE;
} else if (!strcmp(modeS, "link")) {
mode = MODE_LINK;
} else if (!strcmp(modeS, "install")) {
mode = MODE_INSTALL;
}
/* if they're asking for mode help, give it to them */
if (argi < argc) {
if (!strcmp(argv[argi], "--help") || !strcmp(argv[argi], "-h")) {
usage(mode);
exit(0);
}
}
/* next argument is the compiler, use that to check for sanity */
if (!insane) {
if (mode == MODE_COMPILE) {
sane = 1;
} else if (mode == MODE_LINK) {
sane = checkLoSanity(&opt, opt.cmd[0]);
} else if (mode == MODE_INSTALL) {
/* we can always do something here */
sane = 1;
}
}
if (!sane) {
/* just go to libtool */
execLibtool(&opt);
} else if (mode == MODE_COMPILE) {
ltcompile(&opt);
} else if (mode == MODE_LINK) {
ltlink(&opt);
} else if (mode == MODE_INSTALL) {
ltinstall(&opt);
} else {
execLibtool(&opt);
}
return 0;
}
static void usage(enum Mode mode)
{
printf("Use: mlibtool [mlibtool-options] <target-libtool> [options] --mode=<mode> <command>\n"
"\n"
"mlibtool options:\n"
"\t--enable-static: build non-PIC .o files and build .a files\n"
"\t--enable-shared: build PIC .o files and build .so files\n"
"\t(if neither is specified, both --enable-static and --enable-shard are assumed)\n"
"\n"
"Options:\n"
"\t-n|--dry-run: display commands without modifying any files\n"
"\t--mode=<mode>: use operational mode <mode>\n"
"\n");
printf("<mode> must be one of the following:\n"
"\tcompile: compile a source file into a libtool object\n"
"\tinstall: install libraries or executables\n"
"\tlink: create a library or an executable\n"
"\n");
if (mode != MODE_UNKNOWN)
printf("Recognized mode options:\n");
if (mode == MODE_COMPILE) {
printf("\t-o <name>: set the output file name to <name>\n"
"\t-prefer-pic|-shared: build only a PIC file\n"
"\t-prefer-non-pic|-static: build only a non-PIC file\n"
"\t-Wc,<flag>: pass flag directly to cc\n"
"\n");
} else if (mode == MODE_LINK) {
printf("\t-o <name>: set the output file name to <name>\n"
"\t-all-static: create a static binary/library\n"
"\t-avoid-version: avoid adding version info to library names\n"
"\t-export-dynamic: cc -rdynamic\n"
"\t-L<dir>: search both <dir> and <dir>/.libs\n"
"\t-module: build a module suitable for dlopen\n"
"\t-rpath <dir>: build a shared library to be installed to <dir>\n"
"\t (note: this flag is REQUIRED to build a shared\n"
"\t library, but does NOT set an RPATH in the\n"
"\t resultant library)\n");
printf("\t-version-info <current>:<rev>:<age>: set version info\n"
"\t-Wc,<flag>|-Xcompiler <flag>|-XCClinker <flag>: pass <flag>\n"
"\t to cc\n"
"\n");
printf("Mode options ignored for GNU libtool compatibility:\n"
"\t-bindir <dir>\n"
"\n");
printf("Unsupported mode options:\n"
"\t-dlopen, -dlpreopen, -export-symbols, -export-symbols-regex,\n"
"\t-objectlist, -precious-files-regex, -release, -shared,\n"
"\t-shrext, -static, -static-libtool-libs, -weak\n"
"\n");
} else if (mode == MODE_INSTALL) {
printf("\t(none)\n\n");
}
printf("mlibtool is a mini version of libtool for sensible systems. If you're\n"
"compiling for Linux or BSD with supported invocation commands,\n"
"<target-libtool> will never be called.\n"
"\n"
"Unrecognized invocations will be redirected to <target-libtool>.\n");
}
static void ltcompile(struct Options *opt)
{
struct Buffer outCmd;
size_t i;
char *ext;
FILE *f;
/* options */
char *outName = NULL;
char *inName = NULL;
size_t outNamePos = 0;
int preferPic = 0, preferNonPic = 0;
int buildPic = 0, buildNonPic = 0;
/* option derivatives */
char *outDirC = NULL,
*outDir = NULL,
*libsDir = NULL,
*outBaseC = NULL,
*outBase = NULL,
*picFile = NULL,
*nonPicFile = NULL;
/* allocate the output command */
INIT_BUFFER(outCmd);
/* and copy it in */
WRITE_BUFFER(outCmd, opt->cmd[0]);
for (i = 1; opt->cmd[i]; i++) {
char *arg = opt->cmd[i];
char *narg = opt->cmd[i+1];
if (arg[0] == '-') {
if (!strcmp(arg, "-o") && narg) {
/* output name */
WRITE_BUFFER(outCmd, arg);
ORL(outName, strdup, NULL, (narg));
outNamePos = outCmd.bufused;
WRITE_BUFFER(outCmd, narg);
i++;
} else if (!strcmp(arg, "-prefer-pic") ||
!strcmp(arg, "-shared")) {
preferPic = 1;
} else if (!strcmp(arg, "-prefer-non-pic") ||
!strcmp(arg, "-static")) {
preferNonPic = 1;
} else if (!strncmp(arg, "-Wc,", 4)) {
WRITE_BUFFER(outCmd, arg + 4);
} else if (!strcmp(arg, "-no-suppress")) {
/* ignored for compatibility */
} else {
WRITE_BUFFER(outCmd, arg);
}
} else {
inName = arg;
WRITE_BUFFER(outCmd, arg);
}
}
/* if we don't have an input name, fail */
if (!inName) {
fprintf(stderr, "error: --mode=compile with no input file\n");
exit(1);
}
/* if both preferPic and preferNonPic were specified, neither were specified */
if (preferPic && preferNonPic)
preferPic = preferNonPic = 0;
if (preferPic)
buildPic = 1;
else if (!preferNonPic)
buildPic = opt->buildShared;
if (preferNonPic)
buildNonPic = 1;
else if (!preferPic)
buildNonPic = opt->buildStatic;
/* if we don't have an output name, guess */
if (!outName) {
/* + 4: .lo\0 */
ORL(outName, malloc, NULL, (strlen(inName) + 4));
strcpy(outName, inName);
if ((ext = strrchr(outName, '.'))) {
strcpy(ext, ".lo");
} else {
strcat(outName, ".lo");
}
/* and add it to the command */
WRITE_BUFFER(outCmd, "-o");
outNamePos = outCmd.bufused;
WRITE_BUFFER(outCmd, outName);
} else {
/* make sure the output name includes .lo */
fprintf(stderr, "%s\n", outName);
if ((ext = strrchr(outName, '.'))) {
if (strcmp(ext, ".lo")) {
fprintf(stderr, "error: --mode=compile used to compile something other than a .lo file\n");
exit(1);
}
} else {
fprintf(stderr, "error: --mode=compile used to compile an executable\n");
exit(1);
}
}
/* get the directory names */
ORL(outDirC, strdup, NULL, (outName));
outDir = dirname(outDirC);
ORL(outBaseC, strdup, NULL, (outName));
outBase = basename(outBaseC);
ext = strrchr(outBase, '.');
if (ext) *ext = '\0';
/* make the .libs dir */
ORL(libsDir, malloc, NULL, (strlen(outDir) + 7));
sprintf(libsDir, "%s/.libs", outDir);
if (!opt->dryRun) mkdir(libsDir, 0777); /* ignore errors */
/* and generate the pic/non-pic names */
ORL(picFile, malloc, NULL, (strlen(libsDir) + strlen(outBase) + 4));
sprintf(picFile, "%s/%s.o", libsDir, outBase);
ORL(nonPicFile, malloc, NULL, (strlen(outDir) + strlen(outBase) + 4));
sprintf(nonPicFile, "%s/%s.o", outDir, outBase);
/* now do the actual building */
if (buildNonPic) {
outCmd.buf[outNamePos] = nonPicFile;
WRITE_BUFFER(outCmd, NULL);
spawn(opt, outCmd.buf);
outCmd.bufused--;
if (!buildPic && !opt->dryRun)
link(nonPicFile, picFile);
}
if (buildPic) {
WRITE_BUFFER(outCmd, "-fPIC");
WRITE_BUFFER(outCmd, "-DPIC");
outCmd.buf[outNamePos] = picFile;
WRITE_BUFFER(outCmd, NULL);
spawn(opt, outCmd.buf);
outCmd.bufused--;
if (!buildNonPic && !opt->dryRun)
link(picFile, nonPicFile);
}
/* and finally, write the .lo file */
f = fopen(outName, "w");
if (!f) {
perror(outName);
exit(1);
}
fprintf(f, SANE_HEADER
PACKAGE_HEADER
"pic_object='.libs/%s.o'\n"
"non_pic_object='%s.o'\n",
outBase, outBase);
fclose(f);
free(nonPicFile);
free(picFile);
free(libsDir);
free(outBaseC);
free(outDirC);
free(outName);
FREE_BUFFER(outCmd);
}
/* add a canonicalized library dir to the list */
static void addLibDir(struct Options *opt,
struct Buffer *libDirs,
struct Buffer *tofree,
char *dir)
{
char *libDir;
if ((libDir = realpath(dir, NULL))) {
WRITE_BUFFER(*libDirs, libDir);
WRITE_BUFFER(*tofree, libDir);
} else {
WRITE_BUFFER(*libDirs, dir);
}
}
/* the most complicated part of linking is linking in .la files */
static void linkLaFile(struct Options *opt,
int buildLib,
struct Buffer *outCmd,
struct Buffer *libDirs,
struct Buffer *dependencyLibs,
struct Buffer *tofree,
char *arg)
{
/* link to this library */
char *laDirC, *laDir, *laBaseC, *laBase, *aarg, *ext;
int wholeArchive = 0;
FILE *f;
/* OK, it's a .la file, figure out the .libs name */
ORL(laDirC, strdup, NULL, (arg));
laDir = dirname(laDirC);
ORL(laBaseC, strdup, NULL, (arg));
laBase = basename(laBaseC);
/* get the -l name */
ext = strrchr(laBase, '.');
if (ext) *ext = '\0';
/* add -L for the .libs path */
ORL(aarg, malloc, NULL, (strlen(laDir) + 9));
sprintf(aarg, "-L%s/.libs", laDir);
WRITE_BUFFER(*outCmd, aarg);
WRITE_BUFFER(*tofree, aarg);
addLibDir(opt, libDirs, tofree, aarg + 2);
/* if there's only a .a, libtool specifies we bring in the whole archive */
if (buildLib) {
ORL(aarg, malloc, NULL, (strlen(laDir) + strlen(laBase) + 11));
sprintf(aarg, "%s/.libs/%s.so", laDir, laBase);
if (access(aarg, F_OK) != 0)
wholeArchive = 1;
free(aarg);
}
if (wholeArchive) {
/* this is GNU-ld-specific, so retry if it doesn't work */
opt->retryIfFail = 1;
WRITE_BUFFER(*outCmd, "-Wl,--whole-archive");
} else {
/* if we're not linking in the whole archive, then this becomes a
* dependency */
if (dependencyLibs) {
char *realla;
if ((realla = realpath(arg, NULL))) {
WRITE_BUFFER(*dependencyLibs, realla);
WRITE_BUFFER(*tofree, realla);
} else {
WRITE_BUFFER(*dependencyLibs, arg);
}
}
}
/* and add -l<lib name> */
if (!strncmp(laBase, "lib", 3)) laBase += 3;
ORL(aarg, malloc, NULL, (strlen(laBase) + 3));
sprintf(aarg, "-l%s", laBase);
WRITE_BUFFER(*outCmd, aarg);
WRITE_BUFFER(*tofree, aarg);
if (wholeArchive) {
WRITE_BUFFER(*outCmd, "-Wl,--no-whole-archive");
}
free(laBaseC);
free(laDirC);
/* then add any dependencies */
f = fopen(arg, "r");
if (f) {
char *lbuf;
size_t lbufsz, lbufused;
lbufsz = 32;
ORL(lbuf, malloc, NULL, (lbufsz));
while (fgets(lbuf, lbufsz, f)) {
lbufused = strlen(lbuf);
/* read in the remainder of the line */
while (lbuf[lbufused-1] != '\n') {
lbufsz *= 2;
ORL(lbuf, realloc, NULL, (lbuf, lbufsz));
if (!fgets(lbuf + lbufused, lbufsz - lbufused, f)) break;
lbufused = strlen(lbuf);
}
/* is this a dependency_libs line? */
if (!strncmp(lbuf, "dependency_libs='", 17)) {
char *part, *saveptr;
char *dlibs = lbuf + 17;
char *end = strrchr(dlibs, '\'');
if (end) *end = '\0';
/* now go one by one through the libs */
part = strtok_r(dlibs, " ", &saveptr);
while (part) {
/* if this is a .la file, need to recurse */
char *ext = strrchr(part, '.');
if (ext && !strcmp(ext, ".la")) {
linkLaFile(opt, buildLib, outCmd, libDirs, NULL, tofree, part);
} else {
/* otherwise, just add it */
char *pdup;
ORL(pdup, strdup, NULL, (part));
WRITE_BUFFER(*outCmd, pdup);
WRITE_BUFFER(*tofree, pdup);
}
part = strtok_r(NULL, " ", &saveptr);
}
}
}
free(lbuf);
fclose(f);
}
}
static void ltlink(struct Options *opt)
{
struct Buffer outCmd, outAr, libDirs, dependencyLibs, tofree;
size_t i;
char *ext;
int tmpi;
/* options */
int major = 0,
minor = 0,
revision = 0,
module = 0,
avoidVersion = 0,
rpathSpecified = 0,
insane = 0;
char *outName = NULL,
*rpath = NULL;
size_t outNamePos = 0;
/* option derivatives */
int buildBinary = 0,
buildLib = 0,
buildSo = 0,
buildA = 0,
buildPicA = 0;
char *outDirC = NULL,
*outDir = NULL,
*libsDir = NULL,
*outBaseC = NULL,
*outBase = NULL,
*afile = NULL,
*soname = NULL,
*longname = NULL,
*linkname = NULL;
/* before we can even start, we have to figure out what we're building to
* know whether to build the command out of static .o or pic .o files */
for (i = 1; opt->cmd[i]; i++) {
if (opt->cmd[i+1]) {
if (!strcmp(opt->cmd[i], "-o"))
outName = opt->cmd[i+1];
else if (!strcmp(opt->cmd[i], "-rpath"))
rpathSpecified = 1;
}
}
if (outName) {
ext = strrchr(outName, '.');
if (ext && !strcmp(ext, ".la")) {
/* it's a libtool library */
buildLib = 1;
buildA = opt->buildStatic;
} else {
/* it's a binary */
buildBinary = 1;
}
}
/* should we build a .so? */
if (buildLib) {
if (rpathSpecified) {
buildSo = opt->buildShared;
} else {
buildA = 1;
buildPicA = 1;
}
}
/* allocate our buffers */
INIT_BUFFER(outCmd);
INIT_BUFFER(outAr);
INIT_BUFFER(libDirs);
INIT_BUFFER(dependencyLibs);
INIT_BUFFER(tofree);
WRITE_BUFFER(outCmd, opt->cmd[0]);
WRITE_BUFFER(outAr, "ar");
WRITE_BUFFER(outAr, "rc");
WRITE_BUFFER(outAr, "a.a"); /* to be replaced */
/* `pwd`/.libs is always in -L */
WRITE_BUFFER(outCmd, "-L.libs");
addLibDir(opt, &libDirs, &tofree, ".libs");
/* read in the command */
for (i = 1; opt->cmd[i]; i++) {
char *arg = opt->cmd[i];
char *narg = opt->cmd[i+1];
if (arg[0] == '-') {
if (!strcmp(arg, "-all-static")) {
WRITE_BUFFER(outCmd, "-static");
} else if (!strcmp(arg, "-avoid-version")) {
avoidVersion = 1;
} else if (!strcmp(arg, "-export-dynamic")) {
WRITE_BUFFER(outCmd, "-rdynamic");
} else if (!strncmp(arg, "-L", 2)) {
char *llibs;
/* need both the -L path specified and .../.libs */
WRITE_BUFFER(outCmd, arg);
WRITE_BUFFER(dependencyLibs, arg);
addLibDir(opt, &libDirs, &tofree, arg + 2);
ORL(llibs, malloc, NULL, (strlen(arg) + 7));
sprintf(llibs, "%s/.libs", arg);
WRITE_BUFFER(outCmd, llibs);
WRITE_BUFFER(dependencyLibs, llibs);
WRITE_BUFFER(tofree, llibs);
addLibDir(opt, &libDirs, &tofree, llibs + 2);
} else if (!strncmp(arg, "-l", 2)) {
WRITE_BUFFER(outCmd, arg);
WRITE_BUFFER(dependencyLibs, arg);
} else if (!strcmp(arg, "-module")) {
module = 1;
} else if (!strcmp(arg, "-o") && narg) {
WRITE_BUFFER(outCmd, arg);
outNamePos = outCmd.bufused;
WRITE_BUFFER(outCmd, narg);
i++;
} else if (!strcmp(arg, "-rpath") && narg) {
rpath = narg;
i++;
} else if (!strcmp(arg, "-version-info") && narg) {
/* current:revision:age instead of major.minor.revision */
int current = 0;
revision = 0;
/* if the format fails in any way, just use 0 */
sscanf(narg, "%d:%d:%d", &current, &revision, &minor);
if (minor > current) minor = current;
major = current - minor;
i++;
} else if (!strncmp(arg, "-Wc,", 4)) {
WRITE_BUFFER(outCmd, arg + 4);
} else if (narg &&
(!strcmp(arg, "-Xcompiler") ||
!strcmp(arg, "-XCClinker"))) {
WRITE_BUFFER(outCmd, narg);
i++;
} else if (!strcmp(arg, "-dlopen") ||
!strcmp(arg, "-dlpreopen") ||
!strcmp(arg, "-export-symbols") ||
!strcmp(arg, "-export-symbols-regex") ||
!strcmp(arg, "-objectlist") ||
!strcmp(arg, "-precious-files-regex") ||
!strcmp(arg, "-release") ||
!strcmp(arg, "-shared") ||
!strcmp(arg, "-shrext") ||
!strcmp(arg, "-static") ||
!strcmp(arg, "-static-libtool-libs") ||
!strcmp(arg, "-weak")) {
/* unsupported */
insane = 1;
} else if (!strcmp(arg, "-bindir") && narg) {
/* ignored for compatibility */
i++;
} else if (!strcmp(arg, "-no-fast-install") ||
!strcmp(arg, "-no-install") ||
!strcmp(arg, "-no-undefined")) {
/* ignored for compatibility */
} else {
WRITE_BUFFER(outCmd, arg);
}
} else {
ext = strrchr(arg, '.');
if (ext && !strcmp(ext, ".lo")) {
/* make separate versions for the .a and the .so */
char *loDirC, *loDir, *loBaseC, *loBase, *loPic, *loNonPic;
/* OK, it's a .lo file, figure out the .libs name */
ORL(loDirC, strdup, NULL, (arg));
loDir = dirname(loDirC);
ORL(loBaseC, strdup, NULL, (arg));
loBase = basename(loBaseC);
ext = strrchr(loBase, '.');
if (ext) *ext = '\0';
/* make the .libs/.o version */
ORL(loPic, malloc, NULL, (strlen(loDir) + strlen(loBase) + 10));
sprintf(loPic, "%s/.libs/%s.o", loDir, loBase);
ORL(loNonPic, malloc, NULL, (strlen(loDir) + strlen(loBase) + 4));
sprintf(loNonPic, "%s/%s.o", loDir, loBase);
/* which .o we choose depends on a complexicon of situations */
if (buildPicA)
WRITE_BUFFER(outAr, loPic);
else
WRITE_BUFFER(outAr, loNonPic);
if (buildBinary)
WRITE_BUFFER(outCmd, loNonPic);
else
WRITE_BUFFER(outCmd, loPic);
WRITE_BUFFER(tofree, loPic);
WRITE_BUFFER(tofree, loNonPic);
free(loBaseC);
free(loDirC);
} else if (ext && !strcmp(ext, ".la")) {
linkLaFile(opt, buildLib, &outCmd, &libDirs, &dependencyLibs, &tofree, arg);
} else {
WRITE_BUFFER(outAr, arg);
WRITE_BUFFER(outCmd, arg);
}
}
}
if (insane) {
/* just go to libtool */
execLibtool(opt);
}
/* make sure an output name was specified */
if (!outName) {
outName = "a.out";
buildBinary = 1;
WRITE_BUFFER(outCmd, "-o");
outNamePos = outCmd.bufused;
WRITE_BUFFER(outCmd, outName);
}
/* get the directory names */
ORL(outDirC, strdup, NULL, (outName));
outDir = dirname(outDirC);
ORL(outBaseC, strdup, NULL, (outName));
outBase = basename(outBaseC);
/* make the .libs dir */
ORL(libsDir, malloc, NULL, (strlen(outDir) + 7));
sprintf(libsDir, "%s/.libs", outDir);
if (!opt->dryRun) mkdir(libsDir, 0777); /* ignore errors */
/* building a binary involves making a wrapper */
if (buildBinary) {
char *realName;
ORL(realName, malloc, NULL, (strlen(outDir) + strlen(outBase) + 8));
sprintf(realName, "%s/.libs/%s", outDir, outBase);
/* do the actual build */
outCmd.buf[outNamePos] = realName;
WRITE_BUFFER(outCmd, NULL);
spawn(opt, outCmd.buf);
outCmd.bufused--;
/* then make the wrapper */
if (!opt->dryRun) {
char *absName;
FILE *f;
f = fopen(outName, "w");
if (!f) {
perror(outName);
execLibtool(opt);
}
fputs(BIN_SCRIPT_1, f);
/* write all the library paths */
for (i = 0; i < libDirs.bufused; i++) {
if (i != 0) fputs(":", f);
fputs(libDirs.buf[i], f);
}
fputs(BIN_SCRIPT_2, f);
/* then the program name */
if ((absName = realpath(realName, NULL))) {
fputs(absName, f);
free(absName);
} else {
fputs(realName, f);
}
if (fputs(BIN_SCRIPT_3, f) == EOF) {
perror(outName);
execLibtool(opt);
}
fclose(f);
/* now try to make it executable */
chmod(outName, 0755);
}
free(realName);
}
/* the rest all require a shortname */
ext = strrchr(outBase, '.');
if (ext) *ext = '\0';
/* building a .a library is mostly simple */
if (buildA) {
char *apath;
ORL(afile, malloc, NULL, (strlen(outBase) + 3));
sprintf(afile, "%s.a", outBase);
ORL(apath, malloc, NULL, (strlen(outDir) + strlen(afile) + 8));
sprintf(apath, "%s/.libs/%s", outDir, afile);
outAr.buf[2] = apath;
/* run ar */
WRITE_BUFFER(outAr, NULL);
spawn(opt, outAr.buf);
outAr.bufused--;
/* and make sure to ranlib too! */
outAr.buf[1] = "ranlib";
outAr.buf[3] = NULL;
spawn(opt, outAr.buf + 1);
free(apath);
}
/* and building a .so file is the most complicated */
if (buildSo) {
char *sopath = NULL,
*longpath = NULL,
*linkpath = NULL,
*sonameFlag = NULL;
if (!avoidVersion) {
/* we have three filenames:
* (1) the soname, .so.major
* (2) the long name, .so.major.minor.revision
* (3) the linker name, .software
*/
ORL(soname, malloc, NULL, (strlen(outBase) + 4*sizeof(int) + 5));
sprintf(soname, "%s.so.%d", outBase, major);
ORL(longname, malloc, NULL, (strlen(outBase) + 3*4*sizeof(int) + 7));
sprintf(longname, "%s.so.%d.%d.%d", outBase, major, minor, revision);
ORL(linkname, malloc, NULL, (strlen(outBase) + 4));
sprintf(linkname, "%s.so", outBase);
} else {
/* just one soname: .so */
ORL(soname, malloc, NULL, (strlen(outBase) + 4));
sprintf(soname, "%s.so", outBase);
}
/* and get full paths for them all */
#define FULLPATH(x) do { \
ORL(x ## path, malloc, NULL, (strlen(outDir) + strlen(x ## name) + 8)); \
sprintf(x ## path, "%s/.libs/%s", outDir, x ## name); \
} while (0)
FULLPATH(so);
if (!avoidVersion) {
FULLPATH(long);
FULLPATH(link);
}
#undef FULLPATH
/* unlink anything that already exists */
unlink(sopath);
if (longpath)
unlink(longpath);
if (linkpath)
unlink(linkpath);
/* set up the link command */
ORL(sonameFlag, malloc, NULL, (strlen(soname) + 8));
sprintf(sonameFlag, "-Wl,-h,%s", soname);
WRITE_BUFFER(outCmd, "-shared");
WRITE_BUFFER(outCmd, sonameFlag);
WRITE_BUFFER(tofree, sonameFlag);
outCmd.buf[outNamePos] = longpath ? longpath : sopath;
/* link */
WRITE_BUFFER(outCmd, NULL);
spawn(opt, outCmd.buf);
outCmd.bufused--;
if (!opt->dryRun && !avoidVersion) {
/* link in the shorter names */
if ((tmpi = symlink(longname, sopath)) < 0) {
perror(sopath);
exit(1);
}
if ((tmpi = symlink(longname, linkpath)) < 0) {
perror(linkpath);
exit(1);
}
}
free(sopath);
free(longpath);
free(linkpath);
}
/* finally, make the .la file */
if (buildLib) {
FILE *f = fopen(outName, "w");
if (!f) {
perror(outName);
execLibtool(opt);
}
fprintf(f, SANE_HEADER
PACKAGE_HEADER);
if (soname) {
/* we have a .so */
fprintf(f, "dlname='%s'\n", soname);
if (longname && linkname) {
/* and other names */
fprintf(f, "library_names='%s %s %s'\n",
longname, soname, linkname);
} else {
fprintf(f, "library_names='%s'\n", soname);
}
} else {
fprintf(f, "dlname=''\nlibrary_names=''\n");
}
fprintf(f, "old_library='%s'\n"
"inherited_linker_flags=''\n", afile ? afile : "");
fprintf(f, "dependency_libs='");
for (i = 0; i < dependencyLibs.bufused; i++)
fprintf(f, " %s", dependencyLibs.buf[i]);
fprintf(f, "'\n");
/* version info */
fprintf(f, "current=%d\n"
"age=%d\n"
"revision=%d\n",
(major + minor) /* current is weird */,
minor,
revision);
fprintf(f, "installed=no\n"
"shouldnotlink=%s\n"
"dlopen=''\n"
"dlpreopen=''\n"
"libdir='%s'\n",
(module ? "yes" : "no"),
(rpath ? rpath : ""));
fclose(f);
}
free(afile);
free(soname);
free(longname);
free(linkname);
free(libsDir);
free(outBaseC);
free(outDirC);
for (i = 0; i < tofree.bufused; i++) free(tofree.buf[i]);
FREE_BUFFER(tofree);
FREE_BUFFER(dependencyLibs);
FREE_BUFFER(libDirs);
FREE_BUFFER(outAr);
FREE_BUFFER(outCmd);
}
static void ltinstall(struct Options *opt)
{
size_t i, j;
char *dirC, *dir, *baseC, *base, *ext, *target;
struct Buffer installCmd, cpCmd, tofree;
int haveInst = 0, haveCp = 0;
INIT_BUFFER(installCmd);
INIT_BUFFER(cpCmd);
INIT_BUFFER(tofree);
/* copy in the install command as stands */
WRITE_BUFFER(installCmd, opt->cmd[0]);
for (i = 1; opt->cmd[i] && opt->cmd[i][0] == '-'; i++) {
WRITE_BUFFER(installCmd, opt->cmd[i]);
}
/* and make a cp command for things that install doesn't support */
WRITE_BUFFER(cpCmd, "cp");
WRITE_BUFFER(cpCmd, "-P");
/* if the command seems invalid, just run it */
if (!opt->cmd[i]) {
spawn(opt, opt->cmd);
return;
}
/* find the target */
for (j = i; opt->cmd[j]; j++);
j--;
target = opt->cmd[j];
opt->cmd[j] = NULL;
/* and go through all the other files */
for (; opt->cmd[i]; i++) {
char *laFile = NULL;
/* check if this is a .la file */
ext = strrchr(opt->cmd[i], '.');
if (ext && !strcmp(ext, ".la"))
laFile = opt->cmd[i];
/* get the directory info */
ORL(dirC, strdup, NULL, (opt->cmd[i]));
dir = dirname(dirC);
ORL(baseC, strdup, NULL, (opt->cmd[i]));
base = basename(baseC);
if (!laFile) {
char *libsF;
haveInst = 1;
/* check if there's a .libs version */
ORL(libsF, malloc, NULL, (strlen(dir) + strlen(base) + 8));
sprintf(libsF, "%s/.libs/%s", dir, base);
if (access(libsF, F_OK) == 0) {
/* use that one */
WRITE_BUFFER(installCmd, libsF);
WRITE_BUFFER(tofree, libsF);
} else {
/* use the provided argument */
WRITE_BUFFER(installCmd, opt->cmd[i]);
free(libsF);
}
} else {
/* install all the files specified in the .la */
FILE *f;
/* open the file */
f = fopen(laFile, "r");
if (f) {
char *lbuf;
size_t lbufsz, lbufused;
lbufsz = 32;
ORL(lbuf, malloc, NULL, (lbufsz));
while (fgets(lbuf, lbufsz, f)) {
lbufused = strlen(lbuf);
/* read in the remainder of the line */
while (lbuf[lbufused-1] != '\n') {
lbufsz *= 2;
ORL(lbuf, realloc, NULL, (lbuf, lbufsz));
if (!fgets(lbuf + lbufused, lbufsz - lbufused, f)) break;
lbufused = strlen(lbuf);
}
/* is this a library_names or old_library line? */
if (!strncmp(lbuf, "library_names='", 15) ||
!strncmp(lbuf, "old_library='", 13)) {
char *part, *saveptr;
char *dlibs = lbuf + 13 + (lbuf[0] == 'l' ? 2 : 0);
char *end = strrchr(dlibs, '\'');
if (end) *end = '\0';
if (!dlibs[0]) continue;
/* now go one by one through the libs */
part = strtok_r(dlibs, " ", &saveptr);
while (part) {
/* and install it */
char *fullName;
ORL(fullName, malloc, NULL, (strlen(dir) + strlen(part) + 8));
sprintf(fullName, "%s/.libs/%s", dir, part);
haveCp = 1;
WRITE_BUFFER(cpCmd, fullName);
WRITE_BUFFER(tofree, fullName);
part = strtok_r(NULL, " ", &saveptr);
}
}
}
free(lbuf);
fclose(f);
}
}
free(baseC);
free(dirC);
}
/* now run the commands */
if (haveInst) {
WRITE_BUFFER(installCmd, target);
WRITE_BUFFER(installCmd, NULL);
spawn(opt, installCmd.buf);
}
if (haveCp) {
WRITE_BUFFER(cpCmd, target);
WRITE_BUFFER(cpCmd, NULL);
spawn(opt, cpCmd.buf);
}
/* and free everything */
for (i = 0; i < tofree.bufused; i++) free(tofree.buf[i]);
FREE_BUFFER(tofree);
FREE_BUFFER(cpCmd);
FREE_BUFFER(installCmd);
}
#endif /* _POSIX_VERSION */