osxcross/wrapper/main.cpp
2022-06-12 16:41:56 +02:00

589 lines
15 KiB
C++

/***********************************************************************
* OSXCross Compiler Wrapper *
* Copyright (C) 2014-2016 by Thomas Poechtrager *
* t.poechtrager@gmail.com *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***********************************************************************/
/*
* Debug messages can be enabled by setting 'OCDEBUG' (ENV) to >= 1.
*/
#include "compat.h"
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include "tools.h"
#include "target.h"
#include "progs.h"
using namespace tools;
using namespace target;
int unittest = 0;
int debug = 0;
namespace {
void warnExtension(const char *extension) {
static bool noextwarnings = !!getenv("OSXCROSS_NO_EXTENSION_WARNINGS");
if (noextwarnings)
return;
warn << extension << " is an osxcross extension" << warn.endl();
warninfo << "you can silence this warning via "
<< "'OSXCROSS_NO_EXTENSION_WARNINGS=1' (env)" << warninfo.endl();
}
__attribute__((unused))
void warnDeprecated(const char *flag, const char *replacement = nullptr) {
if (replacement)
warn << flag << " is deprecated; "
<< "please use " << replacement << " instead"
<< warn.endl();
else
warn << flag << " is deprecated and will be "
<< "removed soon" << warn.endl();
}
//
// Command Line Options
//
namespace commandopts {
typedef bool (*optFun)(Target &target, const char *opt, const char *val,
char **);
bool versionmin(Target &target, const char *, const char *val, char **) {
target.OSNum = parseOSVersion(val);
if (target.OSNum != val)
warn << "'-mmacosx-version-min=' (" << target.OSNum.Str()
<< " != " << val << ")" << warn.endl();
return true;
}
bool arch(Target &target, const char *opt, const char *val, char **) {
Arch arch;
if (!strcmp(opt, "-arch")) {
arch = parseArch(val);
if (arch == Arch::unknown)
warn << "'-arch': unknown architecture '" << val << "'"
<< warn.endl();
const char *name = getArchName(arch);
if (strcmp(val, name))
warn << "'-arch': '" << val << "' != '" << name << "'" << warn.endl();
} else {
if (!strcmp(opt, "-m16") || !strcmp(opt, "-mx32")) {
err << "'" << opt << "' is not supported" << err.endl();
return false;
} else if (!strcmp(opt, "-m32")) {
arch = Arch::i386;
} else if (!strcmp(opt, "-m64")) {
arch = Arch::x86_64;
} else {
__builtin_unreachable();
}
}
if (target.isClang())
target.addArch(arch);
else
target.arch = arch;
return true;
}
bool stdlib(Target &target, const char *, const char *val, char **) {
if (target.isGCC())
warnExtension("'-stdlib='");
size_t i = 0;
for (auto stdlibname : StdLibNames) {
if (!strcmp(val, stdlibname)) {
target.stdlib = static_cast<StdLib>(i);
break;
}
++i;
}
if (i == (sizeof(StdLibNames) / sizeof(StdLibNames[0]))) {
err << "value of '-stdlib=' must be ";
for (size_t j = 0; j < i; ++j) {
err << "'" << StdLibNames[j] << "'";
if (j == i - 2)
err << " or ";
else if (j < i - 2)
err << ", ";
}
err << err.endl();
return false;
}
return true;
}
bool language(Target &target, const char *, const char *val, char **) {
target.language = val;
return true;
}
bool usegcclibstdcxx(Target &target, const char *, const char *, char **) {
target.stdlib = StdLib::libstdcxx;
target.usegcclibs = true;
return true;
}
bool compilerpath(Target &target, const char *, const char *path, char **) {
target.compilerpath = path;
return true;
}
bool intrinsicpath(Target &target, const char *, const char *path, char **) {
target.intrinsicpath = path;
return true;
}
bool runprog(Target &target, const char *, const char *progname, char **cargs) {
auto *prog = program::getprog(progname);
if (!prog)
exit(EXIT_FAILURE);
std::vector<char *> args;
args.push_back(const_cast<char *>(progname));
while (*cargs)
args.push_back(*cargs++);
args.push_back(nullptr);
(*prog)(args.size() - 1, args.data(), target);
}
bool liblto(Target &target, const char *opt, const char *, char **) {
target.wliblto = !strcmp(opt, "-Wliblto");
return true;
}
bool checkincludepath(Target &, const char *opt, const char *path, char **) {
#ifndef __APPLE__
constexpr const char *DangerousIncludePaths[] = { "/usr/include",
"/usr/local/include" };
static bool noinccheck = !!getenv("OSXCROSS_NO_INCLUDE_PATH_WARNINGS");
if (noinccheck)
return true;
char buf[PATH_MAX + 1];
const char *rpath = realpath(path, buf);
if (!rpath)
rpath = path;
for (const char *dpath : DangerousIncludePaths) {
if (!strncmp(rpath, dpath, strlen(dpath))) {
warn << "possibly dangerous include path specified: '" << opt << " "
<< path << "'";
if (strcmp(path, rpath))
warn << " (" << rpath << ")";
warn << warn.endl();
warninfo << "you can silence this warning via "
<< "'OSXCROSS_NO_INCLUDE_PATH_WARNINGS=1' (env)"
<< warninfo.endl();
}
}
#else
(void)opt;
(void)path;
#endif
return true;
}
constexpr struct Opt {
const char *name;
const size_t namelen;
const optFun fun;
const bool valrequired;
const bool pusharg;
const char *valseparator;
const size_t valseparatorlen;
constexpr Opt(const char *name, optFun fun, const bool valrequired = false,
const bool pusharg = false, const char *valseparator = nullptr)
: name(name), namelen(constexprStrLen(name)), fun(fun),
valrequired(valrequired), pusharg(pusharg),
valseparator(valseparator),
valseparatorlen(valseparator ? constexprStrLen(valseparator) : 0) {}
} opts[] = {
{"-mmacos-version-min", versionmin, true, false, "="},
{"-mmacosx-version-min", versionmin, true, false, "="},
{"-stdlib", stdlib, true, false, "="},
{"-arch", arch, true},
{"-m16", arch},
{"-m32", arch},
{"-mx32", arch},
{"-m64", arch},
{"-x", language, true, true},
{"-foc-use-gcc-libstdc++", usegcclibstdcxx},
{"-foc-run-prog", runprog, true, false, "="}, // for internal use only
{"-Wliblto", liblto, false, true},
{"-Wno-liblto", liblto, false, true},
{"-isystem", checkincludepath, true, true},
{"-icxx-isystem", checkincludepath, true, true},
{"-cxx-isystem", checkincludepath, true, true},
{"-I", checkincludepath, true, true},
// sets a custom path for the compiler
{"-foc-compiler-path", compilerpath, true, false, "="},
// specifies an additional directory to search when looking for clang's
// intrinsic paths
{"-foc-intrinsic-path", intrinsicpath, true, false, "="}
};
bool parse(int argc, char **argv, Target &target) {
target.args.reserve(argc);
if (char *p = getenv("MACOSX_DEPLOYMENT_TARGET")) {
target.OSNum = parseOSVersion(p);
unsetenv("MACOSX_DEPLOYMENT_TARGET");
}
for (int i = 1; i < argc; ++i) {
char *arg = argv[i];
if (*arg != '-') {
target.args.push_back(arg);
continue;
}
bool pusharg = true;
int j = i;
for (const Opt &opt : opts) {
if (strncmp(arg, opt.name, opt.namelen))
continue;
pusharg = opt.pusharg;
const char *val = nullptr;
if (opt.valrequired) {
val = arg + opt.namelen;
if (opt.valseparator &&
strncmp(val, opt.valseparator, opt.valseparatorlen)) {
err << "expected '" << opt.name << opt.valseparator << "<val>' "
<< "instead of '" << arg << "'" << err.endl();
return false;
} else {
val += opt.valseparatorlen;
}
if (!opt.valseparator && !*val && i < argc - 1)
val = argv[++i];
if (!*val) {
err << "missing argument for '" << opt.name << "'" << err.endl();
return false;
}
}
if (opt.fun && !opt.fun(target, opt.name, val, &argv[i + 1]))
return false;
break;
}
if (pusharg) {
for (; j <= i; ++j)
target.args.push_back(argv[j]);
}
}
return true;
}
} // namespace commandopts
void detectCXXLib(Target &target) {
if (target.compilername.size() <= 7)
return;
StdLib prevstdlib = target.stdlib;
if (endsWith(target.compilername, "-stdc++")) {
target.stdlib = StdLib::libstdcxx;
target.compilername.resize(target.compilername.size() - 7);
} else if (endsWith(target.compilername, "-gstdc++")) {
target.stdlib = StdLib::libstdcxx;
target.usegcclibs = true;
target.compilername.resize(target.compilername.size() - 8);
} else if (endsWith(target.compilername, "-libc++")) {
target.stdlib = StdLib::libcxx;
target.compilername.resize(target.compilername.size() - 7);
}
if (prevstdlib != StdLib::unset && prevstdlib != target.stdlib)
warn << "ignoring '-stdlib=" << getStdLibString(prevstdlib) << "'"
<< warn.endl();
}
//
// detectTarget():
// detect target and setup invocation command
//
bool detectTarget(int argc, char **argv, Target &target) {
const char *cmd = argv[0];
const char *p = strrchr(cmd, '/');
char archName[16];
size_t len;
if (p)
cmd = &p[1];
if (auto *prog = program::getprog(cmd))
(*prog)(argc, argv, target);
// -> x86_64 <- -apple-darwin13
p = strchr(cmd, '-');
len = (p ? p : cmd) - cmd;
if (len >= sizeof(archName))
return false;
memcpy(archName, cmd, len);
archName[len] = '\0';
target.arch = parseArch(archName);
if (target.arch != Arch::unknown) {
cmd += len;
if (*cmd++ != '-')
return false;
if (strncmp(cmd, "apple-", 6))
return false;
cmd += 6;
if (strncmp(cmd, "darwin", 6))
return false;
if (!(p = strchr(cmd, '-')))
return false;
target.target = std::string(cmd, p - cmd);
target.compiler = getCompilerIdentifier(&p[1]);
target.compilername = &p[1];
if (target.compilername == "cc") {
target.compiler = getDefaultCompilerIdentifier();
target.compilername = getDefaultCompilerName();
} else if (target.compilername == "c++") {
target.compiler = getDefaultCXXCompilerIdentifier();
target.compilername = getDefaultCXXCompilerName();
} else if (auto *prog = program::getprog(target.compilername)) {
(*prog)(argc, argv, target);
}
if (target.target != getDefaultTarget())
warn << "this wrapper was built for target "
<< "'" << getDefaultTarget() << "'" << warn.endl();
if (!commandopts::parse(argc, argv, target))
return false;
detectCXXLib(target);
return target.setup();
}
if (!strncmp(cmd, "o32", 3))
target.arch = Arch::i386;
else if (!strncmp(cmd, "o64h", 4))
target.arch = Arch::x86_64h;
else if (!strncmp(cmd, "o64", 3))
target.arch = Arch::x86_64;
else if (!strncmp(cmd, "oa64e", 5))
target.arch = Arch::arm64e;
else if (!strncmp(cmd, "oa64", 4))
target.arch = Arch::arm64;
else
return false;
if (const char *p = strchr(cmd, '-')) {
const char *compilername = &cmd[p - cmd + 1];
target.compiler = getCompilerIdentifier(compilername);
target.compilername = compilername;
}
if (!commandopts::parse(argc, argv, target))
return false;
detectCXXLib(target);
return target.setup();
}
} // unnamed namespace
//
// Main routine
//
int main(int argc, char **argv) {
char bbuf[sizeof(benchmark)];
auto b = new (bbuf) benchmark;
Target target;
char **cargs = nullptr;
int rc = -1;
if (char *p = getenv("OCDEBUG"))
debug = atoi(p);
if (char *p = getenv("OSXCROSS_UNIT_TEST")) {
unittest = atoi(p);
if ((p = getenv("OSXCROSS_PROG_NAME")))
argv[0] = p;
}
if (!detectTarget(argc, argv, target)) {
err << "while detecting target" << err.endl();
return 1;
}
if (debug) {
b->halt();
if (debug >= 2) {
dbg << "detected target triple: " << target.getTriple() << dbg.endl();
dbg << "detected compiler: " << target.compilername << dbg.endl();
dbg << "detected stdlib: " << getStdLibString(target.stdlib)
<< dbg.endl();
b->resume();
}
}
#ifdef __DragonFly__
// Escape DragonFlyBSD's weird PFS paths.
std::string escapedexecpath;
escapePath(target.execpath, escapedexecpath);
concatEnvVariable("COMPILER_PATH", escapedexecpath);
#else
concatEnvVariable("COMPILER_PATH", target.execpath);
#endif
auto printCommand = [&]() {
std::string in;
std::string out;
for (int i = 0; i < argc; ++i) {
in += argv[i];
in += " ";
}
out += target.compilerpath;
if (target.compilerpath != target.fargs[0]) {
out += " (";
out += target.fargs[0];
out += ") ";
} else {
out += " ";
}
for (size_t i = 1; i < target.fargs.size(); ++i) {
out += target.fargs[i];
out += " ";
}
for (auto &arg : target.args) {
out += arg;
out += " ";
}
if (!unittest)
dbg << "--> " << in << dbg.endl();
dbg << "<-- " << out << dbg.endl();
};
if (rc == -1) {
cargs = new char *[target.fargs.size() + target.args.size() + 1];
size_t i = 0;
for (auto &arg : target.fargs)
cargs[i++] = const_cast<char *>(arg.c_str());
for (auto &arg : target.args)
cargs[i++] = const_cast<char *>(arg.c_str());
cargs[i] = nullptr;
}
if (debug) {
time_type diff = b->getDiff();
if (rc == -1)
printCommand();
dbg << "=== time spent in wrapper: " << diff / 1000000.0 << " ms"
<< dbg.endl();
}
if (unittest == 2)
return 0;
if (rc == -1 && execvp(target.compilerpath.c_str(), cargs)) {
err << "invoking compiler failed" << err.endl();
if (!debug)
printCommand();
return 1;
}
return rc;
}