mirror of
https://github.com/Relintai/osxcross.git
synced 2025-02-03 22:45:56 +01:00
* Support for generating fat object files with gcc and '-foc-use-gcc-libstdc++' has been removed. This feature was not 100% correctly implemented; using multiple source files did not work, i.e.: 'o32-g++ -m32 -m64 a.cpp b.cpp' would have failed; And I refuse to implement that, instead I am removing all source file handling from the wrapper with this commit for simplicity. This feature should be implemented in the gcc driver instead. This does NOT affect clang's fat object file support, which is implemented in clang's darwin driver. * '-oc-use-gcc-lib' has been renamed to '-foc-use-gcc-libstdc++'. * Added support for '-stdc++' and '-gstdc++' compiler "shortcuts" o32-clang++ --> uses libstdc++ for <= 10.8 and libc++ for >= 10.9 o32-clang++-libc++ --> uses the SDK's libc++ o32-clang++-stdc++ --> uses the SDK's libstdc++ o32-clang++-gstdc++ --> uses gcc's (build_gcc.sh) libstdc++ * Entirely rewrote the command line parser; the previous one wasn't very readable. * Minor Readme Updates * Added unit tests * Removed OSXCROSS_C_STANDARD / OSXCROSS_CXX_STANDARD support I am no longer parsing -std=, so this feature has to be dropped. Setting the language standard via an env variable isn't a good idea anyway. * Removed unneeded stuff Other Changes: * Version bump to 0.11
613 lines
13 KiB
C++
613 lines
13 KiB
C++
/***********************************************************************
|
|
* OSXCross Compiler Wrapper *
|
|
* Copyright (C) 2014, 2015 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. *
|
|
***********************************************************************/
|
|
|
|
#include "compat.h"
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <istream>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <climits>
|
|
#include <cassert>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <dirent.h>
|
|
#else
|
|
#include <windows.h>
|
|
#include <tlhelp32.h>
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
#include <mach-o/dyld.h>
|
|
#include <CoreServices/CoreServices.h>
|
|
#include <mach/mach.h>
|
|
#include <mach/mach_time.h>
|
|
#include <libproc.h>
|
|
#endif
|
|
|
|
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
|
#include <sys/sysctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/user.h>
|
|
#endif
|
|
|
|
#ifdef __FreeBSD__
|
|
#include <libutil.h>
|
|
#endif
|
|
|
|
#include "tools.h"
|
|
|
|
namespace tools {
|
|
|
|
//
|
|
// Terminal text colors
|
|
//
|
|
|
|
bool isTerminal() {
|
|
#ifndef _WIN32
|
|
static bool first = false;
|
|
static bool val;
|
|
|
|
if (!first) {
|
|
val = !!isatty(fileno(stderr));
|
|
first = true;
|
|
}
|
|
|
|
return val;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Executable path
|
|
//
|
|
|
|
char *getExecutablePath(char *buf, size_t len) {
|
|
char *p;
|
|
#ifdef __APPLE__
|
|
unsigned int l = len;
|
|
if (_NSGetExecutablePath(buf, &l) != 0)
|
|
return nullptr;
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
|
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
|
size_t l = len;
|
|
if (sysctl(mib, 4, buf, &l, nullptr, 0) != 0)
|
|
return nullptr;
|
|
#elif defined(__OpenBSD__)
|
|
int mib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV};
|
|
char **argv;
|
|
size_t l;
|
|
const char *comm;
|
|
int ok = 0;
|
|
if (sysctl(mib, 4, NULL, &l, NULL, 0) < 0)
|
|
abort();
|
|
argv = new char *[l];
|
|
if (sysctl(mib, 4, argv, &l, NULL, 0) < 0)
|
|
abort();
|
|
comm = argv[0];
|
|
if (*comm == '/' || *comm == '.') {
|
|
char *rpath;
|
|
if ((rpath = realpath(comm, NULL))) {
|
|
strlcpy(buf, rpath, len);
|
|
free(rpath);
|
|
ok = 1;
|
|
}
|
|
} else {
|
|
char *sp;
|
|
char *xpath = strdup(getenv("PATH"));
|
|
char *path = strtok_r(xpath, ":", &sp);
|
|
struct stat st;
|
|
if (!xpath)
|
|
abort();
|
|
while (path) {
|
|
snprintf(buf, len, "%s/%s", path, comm);
|
|
if (!stat(buf, &st) && (st.st_mode & S_IXUSR)) {
|
|
ok = 1;
|
|
break;
|
|
}
|
|
path = strtok_r(NULL, ":", &sp);
|
|
}
|
|
free(xpath);
|
|
}
|
|
if (ok)
|
|
l = strlen(buf);
|
|
else
|
|
l = 0;
|
|
delete[] argv;
|
|
#elif defined(_WIN32)
|
|
size_t l = GetModuleFileName(nullptr, buf, (DWORD)len);
|
|
#else
|
|
ssize_t l = readlink("/proc/self/exe", buf, len);
|
|
assert(l > 0 && "/proc not mounted?");
|
|
#endif
|
|
if (l <= 0)
|
|
return nullptr;
|
|
buf[len - 1] = '\0';
|
|
p = strrchr(buf, PATHDIV);
|
|
if (p)
|
|
*p = '\0';
|
|
return buf;
|
|
}
|
|
|
|
const std::string &getParentProcessName() {
|
|
static std::string name;
|
|
#ifdef _WIN32
|
|
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
PROCESSENTRY32 pe;
|
|
|
|
auto zerope = [&]() {
|
|
memset(&pe, 0, sizeof(pe));
|
|
pe.dwSize = sizeof(PROCESSENTRY32);
|
|
};
|
|
|
|
zerope();
|
|
|
|
auto pid = GetCurrentProcessId();
|
|
decltype(pid) ppid = -1;
|
|
|
|
if (Process32First(h, &pe)) {
|
|
do {
|
|
if (pe.th32ProcessID == pid) {
|
|
ppid = pe.th32ParentProcessID;
|
|
break;
|
|
}
|
|
} while (Process32Next(h, &pe));
|
|
}
|
|
|
|
if (ppid != static_cast<decltype(ppid)>(-1)) {
|
|
PROCESSENTRY32 *ppe = nullptr;
|
|
zerope();
|
|
|
|
if (Process32First(h, &pe)) {
|
|
do {
|
|
if (pe.th32ProcessID == ppid) {
|
|
ppe = &pe;
|
|
break;
|
|
}
|
|
} while (Process32Next(h, &pe));
|
|
}
|
|
|
|
if (ppe) {
|
|
char *p = strrchr(ppe->szExeFile, '\\');
|
|
if (p) {
|
|
name = p + 1;
|
|
} else {
|
|
name = ppe->szExeFile;
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(h);
|
|
|
|
if (!name.empty()) {
|
|
return name;
|
|
}
|
|
#else
|
|
auto getName = [](const char * path)->const char * {
|
|
if (const char *p = strrchr(path, '/')) {
|
|
return p + 1;
|
|
}
|
|
return path;
|
|
};
|
|
auto ppid = getppid();
|
|
#ifdef __APPLE__
|
|
char path[PROC_PIDPATHINFO_MAXSIZE];
|
|
if (proc_pidpath(ppid, path, sizeof(path))) {
|
|
name = getName(path);
|
|
return name;
|
|
}
|
|
#elif defined(__FreeBSD__)
|
|
struct kinfo_proc *proc = kinfo_getproc(ppid);
|
|
if (proc) {
|
|
name = getName(proc->ki_comm);
|
|
free(proc);
|
|
return name;
|
|
}
|
|
#else
|
|
std::stringstream file;
|
|
file << "/proc/" << ppid << "/comm";
|
|
if (getFileContent(file.str(), name)) {
|
|
if (!name.empty() && name.rbegin()[0] == '\n') {
|
|
name.resize(name.size() - 1);
|
|
}
|
|
return name;
|
|
} else {
|
|
clear(file);
|
|
file << "/proc/" << ppid << "/exe";
|
|
char buf[PATH_MAX + 1];
|
|
if (readlink(file.str().c_str(), buf, sizeof(buf)) > 0) {
|
|
buf[PATH_MAX] = '\0';
|
|
name = getName(buf);
|
|
return name;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
name = "unknown";
|
|
return name;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
std::string &fixPathDiv(std::string &path) {
|
|
for (auto &c : path) {
|
|
if (c == '/')
|
|
c = '\\';
|
|
}
|
|
return path;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Environment
|
|
//
|
|
|
|
void concatEnvVariable(const char *var, const std::string &val) {
|
|
std::string nval = val;
|
|
if (char *oldval = getenv(var)) {
|
|
nval += ":";
|
|
nval += oldval;
|
|
}
|
|
setenv(var, nval.c_str(), 1);
|
|
}
|
|
|
|
std::string &escapePath(const std::string &path, std::string &escapedpath) {
|
|
for (const char *p = path.c_str(); *p; ++p) {
|
|
switch (*p) {
|
|
case '"':
|
|
case '\'':
|
|
case '\\':
|
|
case '$':
|
|
case '(':
|
|
case ')':
|
|
case ' ':
|
|
case ';':
|
|
case ':':
|
|
escapedpath += '\\';
|
|
}
|
|
escapedpath += *p;
|
|
}
|
|
return escapedpath;
|
|
}
|
|
|
|
void splitPath(const char *path, std::vector<std::string> &result) {
|
|
char *sp;
|
|
char *xpath = strdup(path);
|
|
char *p = strtok_r(xpath, ":", &sp);
|
|
if (!xpath)
|
|
abort();
|
|
while (p) {
|
|
result.push_back(p);
|
|
p = strtok_r(NULL, ":", &sp);
|
|
}
|
|
free(xpath);
|
|
}
|
|
|
|
std::string joinPath(const std::vector<std::string> &path) {
|
|
std::string tmp;
|
|
std::string escaped;
|
|
for (size_t i = 0; i < path.size(); ++i) {
|
|
escaped.clear();
|
|
tmp += escapePath(path[i], escaped);
|
|
if (i != path.size() - 1)
|
|
tmp += ":";
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
bool hasPath(const std::vector<std::string> &path, const char *find) {
|
|
for (const std::string &p : path)
|
|
if (p == find)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Files and Directories
|
|
//
|
|
|
|
std::string *getFileContent(const std::string &file, std::string &content) {
|
|
std::ifstream f(file.c_str());
|
|
|
|
if (!f.is_open())
|
|
return nullptr;
|
|
|
|
f.seekg(0, std::ios::end);
|
|
auto len = f.tellg();
|
|
f.seekg(0, std::ios::beg);
|
|
|
|
if (len != static_cast<decltype(len)>(-1))
|
|
content.reserve(static_cast<size_t>(f.tellg()));
|
|
|
|
content.assign(std::istreambuf_iterator<char>(f),
|
|
std::istreambuf_iterator<char>());
|
|
|
|
return &content;
|
|
}
|
|
|
|
bool writeFileContent(const std::string &file, const std::string &content) {
|
|
std::ofstream f(file.c_str());
|
|
|
|
if (!f.is_open())
|
|
return false;
|
|
|
|
f << content;
|
|
return f.good();
|
|
}
|
|
|
|
bool fileExists(const std::string &file) {
|
|
struct stat st;
|
|
return !stat(file.c_str(), &st);
|
|
}
|
|
|
|
bool dirExists(const std::string &dir) {
|
|
struct stat st;
|
|
return !stat(dir.c_str(), &st) && S_ISDIR(st.st_mode);
|
|
}
|
|
|
|
typedef bool (*listfilescallback)(const char *file);
|
|
|
|
bool isDirectory(const char *file, const char *prefix) {
|
|
struct stat st;
|
|
if (prefix) {
|
|
std::string tmp = prefix;
|
|
tmp += "/";
|
|
tmp += file;
|
|
return !stat(tmp.c_str(), &st) && S_ISDIR(st.st_mode);
|
|
} else {
|
|
return !stat(file, &st) && S_ISDIR(st.st_mode);
|
|
}
|
|
}
|
|
|
|
bool listFiles(const char *dir, std::vector<std::string> *files,
|
|
listfilescallback cmp) {
|
|
#ifndef _WIN32
|
|
DIR *d = opendir(dir);
|
|
dirent *de;
|
|
|
|
if (!d)
|
|
return false;
|
|
|
|
while ((de = readdir(d))) {
|
|
if ((!cmp || cmp(de->d_name)) && files) {
|
|
files->push_back(de->d_name);
|
|
}
|
|
}
|
|
|
|
closedir(d);
|
|
return true;
|
|
#else
|
|
WIN32_FIND_DATA fdata;
|
|
HANDLE handle;
|
|
|
|
handle = FindFirstFile(dir, &fdata);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
|
|
do {
|
|
if ((!cmp || cmp(fdata.cFileName)) && files) {
|
|
files->push_back(fdata.cFileName);
|
|
}
|
|
} while (FindNextFile(handle, &fdata));
|
|
|
|
FindClose(handle);
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
typedef bool (*realpathcmp)(const char *file, const struct stat &st);
|
|
|
|
bool isExecutable(const char *f, const struct stat &) {
|
|
return !access(f, F_OK | X_OK);
|
|
}
|
|
|
|
bool ignoreCCACHE(const char *f, const struct stat &) {
|
|
const char *name = getFileName(f);
|
|
return name && strstr(name, "ccache") != name;
|
|
}
|
|
|
|
bool realPath(const char *file, std::string &result,
|
|
realpathcmp cmp1, realpathcmp cmp2) {
|
|
char *PATH = getenv("PATH");
|
|
const char *p = PATH ? PATH : "";
|
|
struct stat st;
|
|
|
|
result.clear();
|
|
|
|
do {
|
|
if (*p == ':')
|
|
++p;
|
|
|
|
while (*p && *p != ':')
|
|
result += *p++;
|
|
|
|
result += "/";
|
|
result += file;
|
|
|
|
if (!stat(result.c_str(), &st)) {
|
|
#ifndef _WIN32
|
|
char buf[PATH_MAX + 1];
|
|
|
|
if (realpath(result.c_str(), buf)) {
|
|
result.assign(buf);
|
|
} else {
|
|
ssize_t len;
|
|
char path[PATH_MAX];
|
|
size_t pathlen;
|
|
size_t n = 0;
|
|
|
|
pathlen = result.find_last_of(PATHDIV);
|
|
|
|
if (pathlen == std::string::npos)
|
|
pathlen = result.length();
|
|
else
|
|
++pathlen; // PATHDIV
|
|
|
|
memcpy(path, result.c_str(), pathlen); // not null terminated
|
|
|
|
while ((len = readlink(result.c_str(), buf, PATH_MAX)) != -1) {
|
|
if (buf[0] != PATHDIV) {
|
|
result.assign(path, pathlen);
|
|
result.append(buf, len);
|
|
} else {
|
|
result.assign(buf, len);
|
|
pathlen = strrchr(buf, PATHDIV) - buf + 1; // + 1: PATHDIV
|
|
memcpy(path, buf, pathlen);
|
|
}
|
|
if (++n >= 1000) {
|
|
err << result << ": too many levels of symbolic links"
|
|
<< err.endl();
|
|
result.clear();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((!cmp1 || cmp1(result.c_str(), st)) &&
|
|
(!cmp2 || cmp2(result.c_str(), st)))
|
|
break;
|
|
}
|
|
|
|
result.clear();
|
|
} while (*p);
|
|
|
|
return !result.empty();
|
|
}
|
|
|
|
bool getPathOfCommand(const char *command, std::string &result,
|
|
realpathcmp cmp) {
|
|
if (realPath(command, result, isExecutable, cmp))
|
|
stripFileName(result);
|
|
|
|
return !result.empty();
|
|
}
|
|
|
|
void stripFileName(std::string &path) {
|
|
size_t lastpathdiv = path.find_last_of(PATHDIV);
|
|
if (lastpathdiv != 0 && lastpathdiv != std::string::npos)
|
|
path.resize(lastpathdiv);
|
|
}
|
|
|
|
const char *getFileName(const char *file) {
|
|
const char *p = strrchr(file, PATHDIV);
|
|
|
|
if (!p)
|
|
p = file;
|
|
else
|
|
++p;
|
|
|
|
return p;
|
|
}
|
|
|
|
const char *getFileExtension(const char *file) {
|
|
const char *p = strrchr(file, '.');
|
|
|
|
if (!p)
|
|
p = "";
|
|
|
|
return p;
|
|
}
|
|
|
|
//
|
|
// Time
|
|
//
|
|
|
|
time_type getNanoSeconds() {
|
|
#ifdef __APPLE__
|
|
union {
|
|
AbsoluteTime at;
|
|
time_type ull;
|
|
} tmp;
|
|
tmp.ull = mach_absolute_time();
|
|
Nanoseconds ns = AbsoluteToNanoseconds(tmp.at);
|
|
tmp.ull = UnsignedWideToUInt64(ns);
|
|
return tmp.ull;
|
|
#elif defined(__linux__)
|
|
struct timespec tp;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
|
|
return static_cast<time_type>((tp.tv_sec * 1000000000LL) + tp.tv_nsec);
|
|
#endif
|
|
struct timeval tv;
|
|
if (gettimeofday(&tv, nullptr) == 0)
|
|
return static_cast<time_type>((tv.tv_sec * 1000000000LL) +
|
|
(tv.tv_usec * 1000));
|
|
abort();
|
|
}
|
|
|
|
//
|
|
// OSVersion
|
|
//
|
|
|
|
OSVersion parseOSVersion(const char *OSVer) {
|
|
const char *p = OSVer;
|
|
OSVersion OSNum;
|
|
|
|
OSNum.major = atoi(p);
|
|
|
|
while (*p && *p++ != '.')
|
|
;
|
|
if (!*p)
|
|
return OSNum;
|
|
|
|
OSNum.minor = atoi(p);
|
|
|
|
while (*p && *p++ != '.')
|
|
;
|
|
if (!*p)
|
|
return OSNum;
|
|
|
|
OSNum.patch = atoi(p);
|
|
return OSNum;
|
|
}
|
|
|
|
//
|
|
// OS Compat
|
|
//
|
|
|
|
#ifdef _WIN32
|
|
int setenv(const char *name, const char *value, int overwrite) {
|
|
std::string buf;
|
|
(void)overwrite; // TODO
|
|
|
|
buf = name;
|
|
buf += '=';
|
|
buf += value;
|
|
|
|
return putenv(buf.c_str());
|
|
}
|
|
|
|
int unsetenv(const char *name) { return setenv(name, "", 1); }
|
|
#endif
|
|
|
|
} // namespace tools
|