pkgsrc/pkgtools/cwrappers/files/bin/fixup-libtool.c

437 lines
10 KiB
C
Raw Normal View History

/* $NetBSD: fixup-libtool.c,v 1.7 2015/04/19 14:30:07 jperkin Exp $ */
/*-
* Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <nbcompat.h>
#include <nbcompat/stdio.h>
#include <sys/stat.h>
#include <nbcompat/err.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#define LIBPATH_HASH 256
struct unwrap_rule {
TAILQ_ENTRY(unwrap_rule) link;
char *src;
char *dst;
size_t src_len;
};
TAILQ_HEAD(unwrap_ruleset, unwrap_rule);
static struct unwrap_ruleset unwrap_rules =
TAILQ_HEAD_INITIALIZER(unwrap_rules);
void
register_unwrap(const char *rule)
{
const char *sep;
struct unwrap_rule *r;
sep = strchr(rule, ':');
if (sep == NULL)
goto failure;
if (strchr(sep + 1, ':') != NULL)
goto failure;
r = xmalloc(sizeof(*r));
r->src = xstrndup(rule, sep - rule);
if (sep[1])
r->dst = xstrdup(sep + 1);
else
r->dst = NULL;
r->src_len = sep - rule;
TAILQ_INSERT_TAIL(&unwrap_rules, r, link);
return;
failure:
errx(255, "Invalid transform rule: %s", rule);
}
struct processing_option {
FILE *output;
const char *lafile;
struct arglist hashtab[LIBPATH_HASH];
int first;
int in_lai;
int in_relink;
const char *last_opt;
size_t last_len;
};
static void
process_option(struct processing_option *opt, const char *line, size_t len,
int in_relink)
{
struct unwrap_rule *r;
struct argument *arg;
char *tmp;
const char *eol;
size_t i, wlen;
if (opt->in_lai && opt->last_opt && opt->last_len == len &&
strncmp(opt->last_opt, line, len) == 0)
return;
if (len >= 11 && strncmp(line, "-Wl,-rpath,", 11) == 0) {
if (in_relink)
goto print_option;
return;
}
if (len >= 15 && strncmp(line, "-Wl,-rpath-link,", 15) == 0) {
if (in_relink)
goto print_option;
return;
}
if (len > 2 && strncmp(line, "-D", 2) == 0)
return; /* No preprocessor options */
if (len > 2 && strncmp(line, "-I", 2) == 0)
return; /* No preprocessor options */
if (len >= 2 && strncmp(line, "-L", 2) == 0) {
if (opt->in_lai) {
TAILQ_FOREACH(r, &unwrap_rules, link) {
if (len - 2 < r->src_len)
continue;
if (strncmp(line + 2, r->src, r->src_len))
continue;
if (line[r->src_len + 2] != '/' &&
len != r->src_len + 2)
continue;
line += r->src_len + 2;
len -= r->src_len + 2;
tmp = xasprintf("-L%s%*.*s", r->dst,
(int)len, (int)len, line);
process_option(opt, tmp, strlen(tmp),
in_relink);
free(tmp);
return;
}
if (wrksrc) {
wlen = strlen(wrksrc);
if (wlen <= len - 2 &&
strncmp(line + 2, wrksrc, wlen) == 0 &&
(line[wlen + 2] == '/' || wlen + 2 == len))
return;
}
}
i = wrapper_hash2(line, len) & (LIBPATH_HASH - 1);
TAILQ_FOREACH(arg, opt->hashtab + i, link) {
if (strncmp(arg->val, line, len) == 0 &&
arg->val[len] == '\0')
break;
}
if (arg != NULL)
return;
arg = argument_new(xstrndup(line, len));
TAILQ_INSERT_TAIL(opt->hashtab + i, arg, link);
goto print_option;
}
opt->last_opt = line;
opt->last_len = len;
if (len < 3 || strncmp(line + len - 3, ".la", 3))
goto print_option;
for (eol = line + len - 3; eol > line; --eol) {
if (*eol == '/')
break;
}
if (opt->in_lai && *eol == '/') {
TAILQ_FOREACH(r, &unwrap_rules, link) {
if (eol < line + r->src_len)
continue;
if (strncmp(line, r->src, r->src_len))
continue;
if (line[r->src_len] != '/')
continue;
line += r->src_len;
len -= r->src_len;
if (strncmp(eol + 1, "lib", 3) == 0) {
tmp = xasprintf("-L%s%*.*s", r->dst,
(int)(eol - line), (int)(eol - line), line);
process_option(opt, tmp, strlen(tmp),
in_relink);
free(tmp);
eol += 4;
len = line + len - eol - 3;
fprintf(opt->output, " -l%*.*s", (int)len,
(int)len, eol);
} else {
tmp = xasprintf("%s%*.*s", r->dst,
(int)len, (int)len, line);
process_option(opt, tmp, strlen(tmp),
in_relink);
free(tmp);
}
return;
}
}
if (!opt->in_relink)
goto print_option;
if (opt->lafile && strncmp(opt->lafile, line, len) == 0 &&
len == strlen(opt->lafile))
goto print_option;
if (*line != '/' && line == eol) {
process_option(opt, "-L./.libs", 9, in_relink);
goto print_option;
}
if (*line != '/') {
tmp = xasprintf("-L%*.*s/.libs", (int)(eol - line),
(int)(eol - line), line);
process_option(opt, tmp, strlen(tmp), in_relink);
free(tmp);
goto print_option;
}
if (wrksrc == NULL)
goto print_option;
wlen = strlen(wrksrc);
if (wlen > len ||
(wlen != len && line[wlen] != '/') ||
strncmp(wrksrc, line, wlen))
goto print_option;
tmp = xasprintf("-L%*.*s/.libs", (int)(eol - line),
(int)(eol - line), line);
process_option(opt, tmp, strlen(tmp), in_relink);
free(tmp);
print_option:
if (opt->first)
opt->first = 0;
else
putc(' ', opt->output);
fwrite(line, len, 1, opt->output);
}
static void
process_variable(FILE *output, const char *lafile, const char *line,
int in_lai, int in_relink)
{
struct processing_option opt;
size_t len, len2;
struct argument *arg;
size_t i;
const char *command;
for (i = 0; i < LIBPATH_HASH; ++i)
TAILQ_INIT(opt.hashtab + i);
opt.output = output;
opt.lafile = lafile;
opt.in_lai = in_lai;
opt.in_relink = in_relink;
opt.last_opt = NULL;
opt.last_len = 0;
if (in_relink) {
command = strchr(line, ';');
if (command == NULL)
errx(255, "Unrecognizable relink format");
++command;
fwrite(line, command - line, 1, output);
fprintf(output, " %s", exec_path);
/* XXX document this logic */
line = command + 1;
len = strspn(line, " \t");
line += len;
len2 = len = strcspn(line, " \t");
command = line;
if (len == 0)
errx(255, "Unrecognizable relink format");
line += len;
len = strspn(line, " \t");
line += len;
if (len2 != 7 || strncmp(command, "libtool", 7)) {
len = strcspn(line, " \t");
if (len == 0)
errx(255, "Unrecognizable relink format");
line += len;
len = strspn(line, " \t");
line += len;
}
}
for (opt.first = !in_relink; *line ; line += len) {
len = strspn(line, " \t");
line += len;
len = strcspn(line, " \t");
if (len == 0)
break;
process_option(&opt, line, len, in_relink);
}
for (i = 0; i < LIBPATH_HASH; ++i) {
while ((arg = TAILQ_FIRST(opt.hashtab + i)) != NULL)
argument_unlink(opt.hashtab + i, &arg);
}
}
static void
fixup_libtool_la(const char *lafile, int in_lai)
{
static const char dep_lib[] = "dependency_libs='";
static const char relink_cmd[] = "relink_command=\"";
static const char relink_marker_cmd[] = "# buildlink modification\n";
struct stat st;
FILE *fp, *output;
char *line, *opt_start, *tmp_name;
const char *pass_lafile, *cur_option;
int in_relink, ignore_relink = 0;
char delimiter;
size_t len;
ssize_t cur;
fp = fopen(lafile, "r");
if (fp == NULL)
return;
if (fstat(fileno(fp), &st))
err(255, "fstat('%s') failed", lafile);
if ((st.st_mode & S_IFMT) != S_IFREG) {
fclose(fp);
return;
}
tmp_name = concat(lafile, ".tmp");
output = fopen(tmp_name, "w");
if (output == NULL)
err(255, "fopen('%s') failed", tmp_name);
line = NULL;
len = 0;
while ((cur = getline(&line, &len, fp)) > 0) {
if (strcmp(line, relink_marker_cmd) == 0) {
fwrite(line, 1, cur, output);
ignore_relink = 1;
continue;
}
if (strncmp(line, relink_cmd, sizeof(relink_cmd) - 1) == 0) {
if (ignore_relink) {
fwrite(line, 1, cur, output);
ignore_relink = 0;
continue;
}
cur_option = relink_cmd;
opt_start = line + sizeof(relink_cmd) - 1;
pass_lafile = lafile;
delimiter='\"';
in_relink = 1;
ignore_relink = 0;
} else if (strncmp(line, dep_lib, sizeof(dep_lib) - 1) == 0) {
cur_option = dep_lib;
opt_start = line + sizeof(dep_lib) - 1;
pass_lafile = NULL;
delimiter='\'';
in_relink = 0;
ignore_relink = 0;
} else {
fwrite(line, 1, cur, output);
ignore_relink = 0;
continue;
}
if (line[cur - 2] != delimiter || line[cur - 1] != '\n')
errx(255, "corrupted libtool archive '%s'", lafile);
if (debug) {
fputs("# buildlink modification, original:\n# ",
output);
fwrite(line, 1, cur, output);
} else {
fputs(relink_marker_cmd, output);
}
fputs(cur_option, output);
line[cur - 2] = '\0';
process_variable(output, pass_lafile, opt_start, in_lai,
in_relink);
fprintf(output, "%c\n", delimiter);
}
free(line);
if (ferror(fp) || fclose(fp) ||
ferror(output) || fclose(output) ||
rename(tmp_name, lafile)) {
unlink(tmp_name);
err(255, "output processing failed");
}
}
void
fixup_libtool(struct arglist *args)
{
struct argument *arg, *arg2;
const char *slash;
char *laifile;
size_t len;
TAILQ_FOREACH(arg, args, link) {
if (strcmp(arg->val, "-o"))
continue;
arg2 = TAILQ_NEXT(arg, link);
if (arg2 == NULL || arg2->val[0] == '-')
errx(255, "Missing argument for option -o");
arg = arg2;
len = strlen(arg->val);
if (len < 3)
continue;
if (strcmp(arg->val + len - 3, ".la") == 0)
break;
}
if (arg == NULL)
return;
fixup_libtool_la(arg->val, 0);
slash = strrchr(arg->val, '/');
if (slash == NULL)
laifile = xasprintf("./.libs/%si", arg->val);
else
laifile = xasprintf("%*.*s/.libs/%si",
(int)(slash - arg->val), (int)(slash - arg->val),
arg->val, slash + 1);
fixup_libtool_la(laifile, 1);
free(laifile);
}