/*********************************************************************** * OSXCross * * Copyright (C) 2013, 2014 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. * ***********************************************************************/ /* * Important: * - Avoid the use of C++11 headers * - Avoid std:: for C functions * * Any other C++11 features can be used as long they are supported * by Clang 3.2. * * Debug messages can be enabled by setting 'OCDEBUG' (ENV) to 1. * * TODO: * - handle MACOSX_DEPLOYMENT_TARGET (env) * */ #include "compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #endif #ifdef __APPLE__ #include #include #include #include #include #endif #ifdef __FreeBSD__ #include #include #include #include #endif #ifdef _WIN32 #include #include #endif #include "oscompat.h" #undef check #undef major #undef minor #undef patch namespace { // // Misc helper tools // typedef std::vector string_vector; 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__) 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(_WIN32) size_t l = GetModuleFileName(nullptr, buf, (DWORD)len); #else ssize_t l = readlink("/proc/self/exe", buf, len); #endif if (l <= 0) return nullptr; buf[len - 1] = '\0'; p = strrchr(buf, '/'); if (*p) *p = '\0'; return buf; } __attribute__((unused)) std::string &fixPathDiv(std::string &path) { #ifdef _WIN32 for (auto &c : path) { if (c == '/') { c = '\\'; } } #else // let's assume the compiler is smart enough // to optimize this function call away #endif return path; } __attribute__((unused)) 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(-1)) content.reserve(static_cast(f.tellg())); content.assign(std::istreambuf_iterator(f), std::istreambuf_iterator()); return &content; } 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(-1)) { PROCESSENTRY32 *ppe = nullptr; zerope(); if (Process32First(h, &pe)) { do { std::cout << pe.szExeFile << " " << pe.th32ProcessID << std::endl; 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 { file.str(std::string()); 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; } 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); } 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 = nullptr) { 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 *files, listfilescallback cmp = nullptr) { #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 #warning TODO (void)dir; (void)files; (void)cmp; return false; #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); } std::string &realPath(const char *file, std::string &result, realpathcmp cmp = nullptr) { char *PATH = getenv("PATH"); const char *p = PATH; std::string sfile; struct stat st; assert(PATH); do { if (*p == ':') { ++p; } while (*p && *p != ':') { sfile += *p++; } sfile += "/"; sfile += file; if (!stat(sfile.c_str(), &st) && (!cmp || cmp(sfile.c_str(), st))) { break; } sfile.clear(); } while (*p); #ifndef _WIN32 if (!sfile.empty()) { char buf[PATH_MAX + 1]; ssize_t len; if ((len = readlink(sfile.c_str(), buf, PATH_MAX)) != -1) { result.assign(buf, len); } } #endif result.swap(sfile); return result; } std::string &getPathOfCommand(const char *command, std::string &result) { realPath(command, result, isExecutable); const size_t len = strlen(command) + 1; if (result.size() < len) { result.clear(); return result; } result.resize(result.size() - len); return result; } typedef unsigned long long time_type; 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((tp.tv_sec * 1000000000LL) + tp.tv_nsec); #endif struct timeval tv; if (gettimeofday(&tv, nullptr) == 0) return static_cast((tv.tv_sec * 1000000000LL) + (tv.tv_usec * 1000)); abort(); } class benchmark { public: benchmark() { s = getTime(); } time_type getDiff() { return getTime() - s; } void halt() { h = getTime(); } void resume() { s += getTime() - h; } ~benchmark() { time_type diff = getTime() - s; std::cerr << "took: " << diff / 1000000.0 << " ms" << std::endl; } private: time_type getTime() { return getNanoSeconds(); } time_type h; time_type s; }; // // OSVersion struct to ease OS Version comparison // struct OSVersion { constexpr OSVersion(int major, int minor, int patch = 0) : major(major), minor(minor), patch(patch) {} constexpr OSVersion() : major(), minor(), patch() {} constexpr int Num() const { return major * 10000 + minor * 100 + patch; }; constexpr bool operator>(const OSVersion &OSNum) const { return Num() > OSNum.Num(); } constexpr bool operator>=(const OSVersion &OSNum) const { return Num() >= OSNum.Num(); } constexpr bool operator<(const OSVersion &OSNum) const { return Num() < OSNum.Num(); } constexpr bool operator<=(const OSVersion &OSNum) const { return Num() <= OSNum.Num(); } constexpr bool operator!=(const OSVersion &OSNum) const { return Num() != OSNum.Num(); } bool operator!=(const char *val) const { size_t c = 0; const char *p = val; while (*p) { if (*p++ == '.') ++c; } switch (c) { case 1: return shortStr() != val; case 2: return Str() != val; default: return true; } } std::string Str() const { std::stringstream tmp; tmp << major << "." << minor << "." << patch; return tmp.str(); } std::string shortStr() const { std::stringstream tmp; tmp << major << "." << minor; return tmp.str(); } int major; int minor; int patch; }; static_assert(OSVersion(10, 6) != OSVersion(10, 5), ""); 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); if (!*p) return OSNum; while (*p && *p++ != '.') ; if (!*p) return OSNum; OSNum.patch = atoi(p); return OSNum; } typedef OSVersion GCCVersion; #define parseGCCVersion parseOSVersion typedef OSVersion ClangVersion; #define parseClangVersion parseOSVersion // // Default values for the Target struct // constexpr const char *getDefaultVendor() { return "apple"; } constexpr const char *getDefaultTarget() { return OSXCROSS_TARGET; } constexpr const char *getDefaultCompiler() { return "clang"; } constexpr const char *getDefaultCXXCompiler() { return "clang++"; } constexpr const char *getLinkerVersion() { return OSXCROSS_LINKER_VERSION; } constexpr const char *getLibLTOPath() { #ifdef OSXCROSS_LIBLTO_PATH return OSXCROSS_LIBLTO_PATH; #else return nullptr; #endif } const char *getDefaultCStandard() { return getenv("OSXCROSS_C_STANDARD"); } const char *getDefaultCXXStandard() { return getenv("OSXCROSS_CXX_STANDARD"); } #ifdef OSXCROSS_OSX_VERSION_MIN OSVersion getDefaultMinTarget() { if (!strcmp(OSXCROSS_OSX_VERSION_MIN, "default")) return OSVersion(); return parseOSVersion(OSXCROSS_OSX_VERSION_MIN); } #else constexpr OSVersion getDefaultMinTarget() { return OSVersion(); } #endif // // Machine // enum Machine { x86_64, i386, unknown }; constexpr const char *MachineStrs[] = { "x86_64", "i386", "unknown" }; constexpr const char *MachineStrs2[] = { "x86_64", "i686", "unknown" }; constexpr const char *getMachineString(Machine machine) { return MachineStrs[machine]; } constexpr const char *getMachineString2(Machine machine) { return MachineStrs2[machine]; } Machine parseMachine(const char *machine) { if (!strcmp(machine, "i386") || !strcmp(machine, "i486") || !strcmp(machine, "i586") || !strcmp(machine, "i686")) { return Machine::i386; } else if (!strcmp(machine, "x86_64")) { return Machine::x86_64; } return Machine::unknown; } // // Stdlib // enum StdLib { unset, libcxx, libstdcxx }; constexpr const char *StdLibStrs[] = { "default", "libc++", "libstdc++" }; constexpr const char *getStdLibString(StdLib stdlib) { return StdLibStrs[stdlib]; } // // Target struct // struct Target { Target() : vendor(getDefaultVendor()), target(getDefaultTarget()), stdlib(StdLib::unset), usegcclibs(), compiler(getDefaultCompiler()), lang(), langstd(), sourcefile(), outputname() { if (!getExecutablePath(execpath, sizeof(execpath))) abort(); } OSVersion getSDKOSNum() const { if (target.size() < 7) return OSVersion(); int n = atoi(target.c_str() + 6); return OSVersion(10, 4 + (n - 8)); } bool getSDKPath(std::string &path) const { OSVersion SDKVer = getSDKOSNum(); path = execpath; path += "/../SDK/MacOSX"; path += SDKVer.shortStr(); if (SDKVer <= OSVersion(10, 4)) { path += "u"; } path += ".sdk"; return dirExists(path); } void addMachine(const Machine machine) { auto &v = targetmachine; for (size_t i = 0; i < v.size(); ++i) { if (v[i] == machine) { v.erase(v.begin() + i); addMachine(machine); return; } } v.push_back(machine); } bool hasLibCXX() const { return getSDKOSNum() >= OSVersion(10, 7); } bool libCXXIsDefaultCXXLib() const { return stdlib != libstdcxx && hasLibCXX() && OSNum >= OSVersion(10, 9); } bool isLibCXX() const { return stdlib == StdLib::libcxx || libCXXIsDefaultCXXLib(); } bool isLibSTDCXX() const { return stdlib == StdLib::libstdcxx; } bool haveSourceFile() { return sourcefile != nullptr; } bool haveOutputName() { return outputname != nullptr; } bool isC(bool r = false) { if (!r && isCXX()) return false; if (langGiven() && lang[0] != 'o' && (!strcmp(lang, "c") || !strcmp(lang, "c-header"))) return true; if (haveSourceFile()) { const char *ext = strrchr(sourcefile, '.'); if (ext && !strcmp(ext, ".c")) return true; } return compiler.find("++") == std::string::npos && !isObjC(true); } bool isCXX() { bool CXXCompiler = compiler.find("++") != std::string::npos; if (!langGiven() && CXXCompiler && !isObjC(true)) return true; if (langGiven() && !strncmp(lang, "c++", 3)) return true; constexpr const char *CXXFileExts[] = { ".C", ".cc", ".cpp", ".CPP", ".c++", ".cp", ".cxx" }; if (haveSourceFile()) { const char *ext = strrchr(sourcefile, '.'); if (ext) { for (auto &cxxfe : CXXFileExts) { if (!strcmp(ext, cxxfe)) return true; } } } return CXXCompiler && !isC(true) && !isObjC(true); } bool isObjC(bool r = false) { if (!r && isCXX()) return false; if (langGiven() && lang[0] == 'o') return true; if (haveSourceFile()) { const char *ext = strrchr(sourcefile, '.'); if (ext && (!strcmp(ext, ".m") || !strcmp(ext, ".mm"))) return true; } return false; } bool isGCH() { if (haveOutputName()) { const char *ext = strrchr(outputname, '.'); if (!ext) return false; return !strcmp(ext, ".gch"); } return false; } bool isClang() const { return !compiler.compare(0, 4, "clang", 4); } bool isGCC() const { return !compiler.compare(0, 3, "gcc") || !compiler.compare(0, 3, "g++"); } bool isKnownCompiler() const { return isClang() || isGCC(); } bool langGiven() const { return lang != nullptr; } bool langStdGiven() const { return langstd != nullptr; } const char *getLangName() { if (isC()) return "C"; else if (isCXX()) return "C++"; else if (isObjC()) return "Obj-C"; else return "unknown"; } bool isCXX11orNewer() const { if (!langStdGiven()) return false; constexpr const char *STD[] = { "c++0x", "gnu++0x", "c++11", "gnu++11", "c++1y", "gnu++1y", "c++14", "gnu++14", "c++1z", "gnu++1z" }; for (auto std : STD) { if (!strcmp(langstd, std)) { return true; } } return false; } const std::string &getTriple() const { return triple; } const std::string getFullCompilerName() const { std::string compiler; if (isGCC()) { compiler = execpath; compiler += "/"; compiler += getTriple(); compiler += "-"; } if (isGCC()) { compiler += "base-"; } compiler += this->compiler; return compiler; } bool findClangIntrinsicHeaders(std::string &path) const { std::string clangbin; static std::stringstream dir; assert(isClang()); getPathOfCommand(compiler.c_str(), clangbin); if (clangbin.empty()) return false; static ClangVersion clangversion; static std::string pathtmp; dir.str(std::string()); clangversion = ClangVersion(); pathtmp.clear(); auto check = []()->bool { listFiles(dir.str().c_str(), nullptr, [](const char *file) { if (file[0] != '.' && isDirectory(file, dir.str().c_str())) { ClangVersion cv = parseClangVersion(file); if (cv != ClangVersion()) { static std::stringstream tmp; tmp.str(std::string()); tmp << dir.str() << "/" << file << "/include"; if (dirExists(tmp.str())) { if (cv > clangversion) { clangversion = cv; pathtmp = tmp.str(); } } } return true; } return true; }); return clangversion != ClangVersion(); }; dir << clangbin << "/../lib/clang"; if (!check()) { dir.str(std::string()); dir << clangbin << "/../include/clang"; if (!check()) { return false; } } path.swap(pathtmp); return clangversion != ClangVersion(); } bool Setup() { if (!isKnownCompiler()) { std::cerr << "warning: unknown compiler '" << compiler << "'" << std::endl; } std::string SDKPath; if (!getSDKPath(SDKPath)) { std::cerr << "cannot find Mac OS X SDK (expected in: " << SDKPath << ")" << std::endl; return false; } if (targetmachine.empty()) { targetmachine.push_back(machine); } if (!langStdGiven()) { if (isC()) { langstd = getDefaultCStandard(); } else if (isCXX()) { langstd = getDefaultCXXStandard(); } } triple = getMachineString(machine); triple += "-"; triple += vendor; triple += "-"; triple += target; otriple = getMachineString(Machine::x86_64); otriple += "-"; otriple += vendor; otriple += "-"; otriple += target; if (!OSNum.Num()) { if (stdlib != StdLib::libcxx) { OSNum = getDefaultMinTarget(); } else { OSNum = OSVersion(10, 7); // Hack } } else { if (OSNum > getSDKOSNum()) { std::cerr << "targeted OS X Version must be <= " << getSDKOSNum().Str() << " (SDK)" << std::endl; return false; } else if (OSNum < OSVersion(10, 4)) { std::cerr << "targeted OS X Version must be >= 10.4" << std::endl; return false; } } if (stdlib == StdLib::unset) { if (libCXXIsDefaultCXXLib()) stdlib = StdLib::libcxx; else stdlib = StdLib::libstdcxx; } else if (stdlib == StdLib::libcxx) { if (!hasLibCXX()) { std::cerr << "you need a newer SDK (10.7 at least) if you want to use libc++" << std::endl; return false; } if (OSNum.Num() && OSNum < OSVersion(10, 7)) { std::cerr << "you must target OS X 10.7 or newer if you want to use libc++" << std::endl; return false; } } std::string CXXHeaderPath = SDKPath; string_vector AdditionalCXXHeaderPaths; auto addCXXPath = [&](const std::string &path) { std::string tmp; tmp = CXXHeaderPath; tmp += "/"; tmp += path; AdditionalCXXHeaderPaths.push_back(tmp); }; auto addAbsoluteCXXPath = [&](const std::string &path) { AdditionalCXXHeaderPaths.push_back(path); }; (void)addAbsoluteCXXPath; GCCVersion gccversion; switch (stdlib) { case StdLib::libcxx: { CXXHeaderPath += "/usr/include/c++/v1"; if (!dirExists(CXXHeaderPath)) { std::cerr << "cannot find " << getStdLibString(stdlib) << " headers" << std::endl; return false; } break; } case StdLib::libstdcxx: { if (isGCC() && /*isCXX11orNewer()*/ true) break; if (usegcclibs) { #ifndef _WIN32 // Use libs from './build_gcc' installation CXXHeaderPath += "/../../"; CXXHeaderPath += otriple; CXXHeaderPath += "/include/c++"; static std::vector v; v.clear(); listFiles(CXXHeaderPath.c_str(), nullptr, [](const char *path) { if (path[0] != '.') v.push_back(parseGCCVersion(path)); return false; }); if (v.empty()) { std::cerr << "'-oc-use-gcc-libs' requires gcc to be installed " "(./build_gcc.sh)" << std::endl; return false; } std::sort(v.begin(), v.end()); gccversion = v[v.size() - 1]; CXXHeaderPath += "/"; CXXHeaderPath += gccversion.Str(); addCXXPath(otriple); #else std::cerr << "'-oc-use-gcc-libs' not implemented" << std::endl; return false; #endif } else { // Use SDK libs std::string tmp; if (getSDKOSNum() <= OSVersion(10, 5)) { CXXHeaderPath += "/usr/include/c++/4.0.0"; } else { CXXHeaderPath += "/usr/include/c++/4.2.1"; } tmp = getMachineString2(machine); tmp += "-apple-"; tmp += target; addCXXPath(tmp); } if (!dirExists(CXXHeaderPath)) { std::cerr << "cannot find " << getStdLibString(stdlib) << " headers" << std::endl; return false; } break; } case StdLib::unset: abort(); } fargs.push_back(getFullCompilerName()); if (isClang()) { std::string tmp; fargs.push_back("-target"); fargs.push_back(getTriple()); tmp = "-mlinker-version="; tmp += getLinkerVersion(); fargs.push_back(tmp); tmp.clear(); if (!findClangIntrinsicHeaders(tmp)) { std::cerr << "cannot find clang intrinsic headers, please report this " "issue to the OSXCross project" << std::endl; } else { fargs.push_back("-isystem"); fargs.push_back(tmp); } tmp.clear(); fargs.push_back("-isysroot"); fargs.push_back(SDKPath); if (isCXX()) { tmp = "-stdlib="; tmp += getStdLibString(stdlib); fargs.push_back(tmp); if (stdlib == StdLib::libcxx || (stdlib == StdLib::libstdcxx && usegcclibs)) { fargs.push_back("-nostdinc++"); // TODO: -Qunused-arguments ? } if (stdlib == StdLib::libstdcxx && usegcclibs) { // Use libs from './build_gcc' installation if (targetmachine.size() > 1) { std::cerr << "'-oc-use-gcc-libs' does not support multiple arch flags" << std::endl; return false; } fargs.push_back("-nodefaultlibs"); std::stringstream GCCLibSTDCXXPath; std::stringstream GCCLibPath; std::stringstream tmp; GCCLibSTDCXXPath << SDKPath << "/../../" << otriple << "/lib"; GCCLibPath << SDKPath << "/../../lib/gcc/" << otriple << "/" << gccversion.Str(); if (targetmachine[0] == Machine::i386) { GCCLibSTDCXXPath << "/" << getMachineString(Machine::i386); GCCLibPath << "/" << getMachineString(Machine::i386); } fargs.push_back("-Qunused-arguments"); tmp << GCCLibSTDCXXPath.str() << "/libstdc++.a"; fargs.push_back(tmp.str()); tmp.str(std::string()); tmp << GCCLibSTDCXXPath.str() << "/libsupc++.a"; fargs.push_back(tmp.str()); tmp.str(std::string()); tmp << GCCLibPath.str() << "/libgcc.a"; fargs.push_back(tmp.str()); tmp.str(std::string()); tmp << GCCLibPath.str() << "/libgcc_eh.a"; fargs.push_back(tmp.str()); fargs.push_back("-lc"); } } } else if (isGCC()) { if (isLibCXX()) { if (!langStdGiven()) langstd = "c++0x"; else if (!isCXX11orNewer()) { std::cerr << "warning: libc++ requires -std=c++11 (or later) with gcc" << std::endl; } } /* TODO: libgcc */ if (isCXX() && (/*!isCXX11orNewer() ||*/ isLibCXX())) { fargs.push_back("-nostdinc++"); fargs.push_back("-nodefaultlibs"); if (haveSourceFile() && !isGCH()) { std::string tmp; tmp = "-L"; tmp += SDKPath; tmp += "/usr/lib"; fargs.push_back(tmp); fargs.push_back("-lc"); if (isLibCXX()) { fargs.push_back("-lc++"); fargs.push_back("-lc++abi"); } else if (isLibSTDCXX()) { // Hack: Use SDKs libstdc++ as long // >= -std=c++11 is not given. fargs.push_back("-lstdc++"); } fargs.push_back(OSNum <= OSVersion(10, 4) ? "-lgcc_s.10.4" : "-lgcc_s.10.5"); } } else if (!isLibCXX() /*&& isCXX11orNewer()*/ && !isGCH()) { fargs.push_back("-static-libgcc"); fargs.push_back("-static-libstdc++"); } } auto addCXXHeaderPath = [&](const std::string &path) { fargs.push_back(isClang() ? "-cxx-isystem" : "-isystem"); fargs.push_back(path); }; addCXXHeaderPath(CXXHeaderPath); for (auto &path : AdditionalCXXHeaderPaths) addCXXHeaderPath(path); if (langGiven()) { fargs.push_back("-x"); fargs.push_back(lang); } if (langStdGiven()) { std::string tmp; tmp = "-std="; tmp += langstd; fargs.push_back(tmp); } if (OSNum.Num()) { std::string tmp; tmp = "-mmacosx-version-min="; tmp += OSNum.Str(); fargs.push_back(tmp); } for (auto machine : targetmachine) { switch (machine) { case Machine::i386: case Machine::x86_64: if (isGCC()) { if (targetmachine.size() > 1) { std::cerr << "gcc does not support multiple arch flags" << std::endl; return false; } fargs.push_back(machine == Machine::i386 ? "-m32" : "-m64"); } else { fargs.push_back("-arch"); fargs.push_back(getMachineString(machine)); } break; default: std::cerr << "unknown machine type" << std::endl; return false; } } if (haveOutputName()) { fargs.push_back("-o"); fargs.push_back(outputname); } return true; } const char *vendor; Machine machine; std::vector targetmachine; std::string target; OSVersion OSNum; StdLib stdlib; bool usegcclibs; std::string compiler; std::string triple; std::string otriple; const char *lang; const char *langstd; string_vector fargs; string_vector args; const char *sourcefile; const char *outputname; char execpath[PATH_MAX + 1]; }; // // Program 'sw_vers' // __attribute__((noreturn)) void prog_sw_vers(int argc, char **argv) { auto genFakeBuildVer = [](std::string & build)->std::string & { std::stringstream tmp; #if __has_builtin(__builtin_readcyclecounter) srand(static_cast(__builtin_readcyclecounter())); #else srand(static_cast(getNanoSeconds())); #endif for (int i = 0; i < 5; ++i) { tmp << std::hex << (rand() % 16 + 1); } build = tmp.str(); build.resize(5); return build; }; auto getProductVer = []()->OSVersion { char *p = getenv("OSXCROSS_SW_VERS_OSX_VERSION"); if (!p) p = getenv("MACOSX_DEPLOYMENT_TARGET"); if (p) return parseOSVersion(p); return getDefaultMinTarget(); }; if (argc == 2) { std::stringstream str; if (!strcmp(argv[1], "-productName")) { str << "Mac OS X"; } else if (!strcmp(argv[1], "-productVersion")) { str << getProductVer().shortStr(); } else if (!strcmp(argv[1], "-buildVersion")) { std::string build; str << genFakeBuildVer(build); } else { exit(EXIT_FAILURE); } std::cout << str.str() << std::endl; } else if (argc == 1) { std::string build; std::cout << "ProductName: Mac OS X" << std::endl; std::cout << "ProductVersion: " << getProductVer().shortStr() << std::endl; std::cout << "BuildVersion: " << genFakeBuildVer(build) << std::endl; } exit(EXIT_SUCCESS); } // // Program 'osxcross-env' // __attribute__((noreturn)) void prog_osxcross_conf(const Target &target) { std::string sdkpath; if (!target.getSDKPath(sdkpath)) { std::cerr << "cannot find Mac OS X SDK!" << std::endl; exit(EXIT_FAILURE); } // TODO: echo "export OSXCROSS_VERSION=..." std::cout << "export OSXCROSS_OSX_VERSION_MIN=" << getDefaultMinTarget().shortStr() << std::endl; std::cout << "export OSXCROSS_TARGET=" << getDefaultTarget() << std::endl; std::cout << "export OSXCROSS_SDK_VERSION=" << target.getSDKOSNum().shortStr() << std::endl; std::cout << "export OSXCROSS_SDK=" << sdkpath << std::endl; std::cout << "export OSXCROSS_TARBALL_DIR=" << target.execpath << "/../../tarballs" << std::endl; std::cout << "export OSXCROSS_PATCH_DIR=" << target.execpath << "/../../patches" << std::endl; std::cout << "export OSXCROSS_TARGET_DIR=" << target.execpath << "/.." << std::endl; std::cout << "export OSXCROSS_BUILD_DIR=" << target.execpath << "/../../build" << std::endl; std::cout << "export OSXCROSS_CCTOOLS_PATH=" << target.execpath << std::endl; std::cout << "export OSXCROSS_LINKER_VERSION=" << getLinkerVersion() << std::endl; exit(EXIT_SUCCESS); } // // Program 'osxcross-env' // __attribute__((noreturn)) void prog_osxcross_env(int argc, char **argv) { char epath[PATH_MAX + 1]; char *oldpath = getenv("PATH"); char *oldlibpath = getenv("LD_LIBRARY_PATH"); constexpr const char *ltopath = getLibLTOPath(); assert(oldpath); if (!getExecutablePath(epath, sizeof(epath))) { exit(EXIT_FAILURE); } // TODO: escape? auto check = [](const char * p, const char * desc)->const char * { if (!p) return nullptr; const char *pp = p; for (; *p; ++p) { auto badChar = [&](const char *p) { std::cerr << desc << " should not contain '" << *p << "'" << std::endl; const char *start = p - std::min(static_cast(p - pp), static_cast(30)); size_t len = std::min(strlen(start), static_cast(60)); std::cerr << std::string(start, len) << std::endl; while (start++ != p) { std::cerr << " "; } std::cerr << "^" << std::endl; exit(EXIT_FAILURE); }; switch (*p) { case '"': case '\'': case '$': case ' ': case ';': badChar(p); } } return pp; }; if (argc <= 1) { const std::string &pname = getParentProcessName(); if (pname == "csh" || pname == "tcsh") { std::cerr << std::endl << "you are invoking this program from a C shell, " << std::endl << "please use " << std::endl << std::endl << "setenv PATH `" << epath << "/osxcross-env -v=PATH`" << std::endl << "setenv LD_LIBRARY_PATH `" << epath << "/osxcross-env -v=LD_LIBRARY_PATH`" << std::endl << std::endl << "instead." << std::endl << std::endl; } } auto hasPath = [](const char * ov, const char * v, const char * vs)->bool { // ov = old value // v = value // vs = value suffix if (!ov || !v) return false; bool hasPathSeparator = false; for (auto p = ov; *p; ++p) { if (*p == ':') { hasPathSeparator = true; break; } } static std::string tmp; auto check = [&](int t)->bool { tmp.clear(); if (t == 0) tmp = ':'; tmp += v; if (vs) { tmp += vs; } if (t == 1) { tmp += ':'; } return strstr(ov, tmp.c_str()) != nullptr; }; return ((hasPathSeparator && (check(0) || check(1))) || check(-1)); }; check(oldpath, "PATH"); check(oldlibpath, "LD_LIBRARY_PATH"); check(ltopath, "LIB LTO PATH"); std::stringstream path; std::stringstream librarypath; std::map vars; path << oldpath; if (!hasPath(oldpath, epath, nullptr)) { path << ":" << epath; } if (oldlibpath) { librarypath << oldlibpath; } if (!hasPath(oldlibpath, epath, "/../lib")) { librarypath << ":" << epath << "/../lib"; } if (ltopath && !hasPath(oldlibpath, ltopath, nullptr)) { librarypath << ":" << ltopath; } vars["PATH"] = path.str(); vars["LD_LIBRARY_PATH"] = librarypath.str(); auto printVariable = [&](const std::string &var) { auto it = vars.find(var); if (it == vars.end()) { std::cerr << "unknown variable '" << var << "'" << std::endl; exit(EXIT_FAILURE); } std::cout << it->second << std::endl; }; if (argc <= 1) { std::cout << std::endl; for (auto &v : vars) { std::cout << "export " << v.first << "="; printVariable(v.first); std::cout << std::endl; } } else { if (strncmp(argv[1], "-v=", 3)) { exit(EXIT_FAILURE); } const char *var = argv[1] + 3; printVariable(var); } exit(EXIT_SUCCESS); } // // Program 'dsymutil' // __attribute__((noreturn)) void prog_dsymutil(int argc, char **argv) { (void)argc; (void)argv; exit(EXIT_SUCCESS); } // // detectTarget(): // - detect target and setup invocation command // // This function also handles/implements helper programs // like 'sw_vers', 'osxcross-env' and 'osxcross-conf' // bool detectTarget(int argc, char **argv, Target &target) { const char *cmd = argv[0]; const char *p = strrchr(cmd, '/'); size_t i = 0; if (p) cmd = &p[1]; target.args.reserve(static_cast(argc)); auto warnExtension = [](const char *extension) { std::cerr << "warning: '" << extension << "' is an OSXCross extension" << std::endl; }; auto parseArgs = [&]() { auto getVal = [&](char * arg, const char * flag, int & i)->const char * { const char *val = arg + strlen(flag); if (!*val) { val = argv[++i]; if (i >= argc) { std::cerr << "missing argument for '" << val << "'" << std::endl; return nullptr; } } return val; }; for (int i = 1; i < argc; ++i) { char *arg = argv[i]; if (!strncmp(arg, "-mmacosx-version-min=", 21)) { const char *val = arg + 21; target.OSNum = parseOSVersion(val); if (target.OSNum != val) { std::cerr << "warning: '-mmacosx-version-min=' (" << target.OSNum.Str() << " != " << val << ")" << std::endl; } } else if (!strncmp(arg, "-stdlib=", 8)) { const char *val = arg + 8; if (!strcmp(val, "libc++")) { target.stdlib = StdLib::libcxx; } else if (!strcmp(val, "libstdc++")) { target.stdlib = StdLib::libstdcxx; } if (target.isGCC()) { warnExtension("-stdlib="); } } else if (!strncmp(arg, "-std=", 5)) { const char *val = arg + 5; target.langstd = val; } else if (!strcmp(arg, "-oc-use-gcc-libs")) { if (target.isGCC()) { std::cerr << "warning: '" << arg << "' has no effect" << std::endl; continue; } target.usegcclibs = true; } else if (!strncmp(arg, "-o", 2)) { target.outputname = getVal(arg, "-o", i); } else if (!strncmp(arg, "-x", 2)) { target.lang = getVal(arg, "-x", i); } else if (!strcmp(arg, "-m32")) { target.addMachine(Machine::i386); } else if (!strcmp(arg, "-m64")) { target.addMachine(Machine::x86_64); } else if (!strncmp(arg, "-arch", 5)) { const char *val = getVal(arg, "-arch", i); if (!val) return; Machine machine = parseMachine(val); if (machine == Machine::unknown) { std::cerr << "warning '-arch': unknown machine type '" << val << "'" << std::endl; } const char *name = getMachineString(machine); if (strcmp(val, name)) { std::cerr << "warning '-arch': " << val << " != " << name << std::endl; } target.addMachine(machine); } else { if (arg[0] != '-') { // Detect source file const char *prevarg = ""; if (i > 1) { prevarg = argv[i - 1]; if (prevarg[0] == '-' && strlen(prevarg) > 2) { prevarg = ""; } } if (prevarg[0] != '-' || !strcmp(prevarg, "-c")) { const char *ext = strrchr(arg, '.'); if (!ext || (strcmp(ext, ".o") && strcmp(ext, ".a"))) target.sourcefile = arg; } } target.args.push_back(arg); } } }; auto checkCXXLib = [&]() { if (target.compiler.rfind("-libc++") == (target.compiler.size() - 7)) { if (target.stdlib != StdLib::unset && target.stdlib != StdLib::libcxx) { std::cerr << "warning: '-stdlib=" << getStdLibString(target.stdlib) << "' will be ignored" << std::endl; } target.compiler.resize(target.compiler.size() - 7); target.stdlib = StdLib::libcxx; } }; if (!strncmp(cmd, "osxcross-conf", 13)) { prog_osxcross_conf(target); } else if (!strncmp(cmd, "osxcross-env", 12)) { prog_osxcross_env(argc, argv); } else if (!strncmp(cmd, "sw_vers", 7)) { prog_sw_vers(argc, argv); } else if (!strncmp(cmd, "dsymutil", 8)) { prog_dsymutil(argc, argv); } for (auto M : MachineStrs) { const size_t len = strlen(M); ++i; if (!strncmp(cmd, M, len)) { target.machine = static_cast(i - 1); 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 = &p[1]; if (target.compiler == "wrapper") { exit(EXIT_SUCCESS); } else if (target.compiler == "sw_vers") { prog_sw_vers(argc, argv); } else if (target.compiler == "dsymutil") { prog_dsymutil(argc, argv); } else if (target.compiler == "cc") { target.compiler = getDefaultCompiler(); } else if (target.compiler == "c++") { target.compiler = getDefaultCXXCompiler(); } if (target.target != getDefaultTarget()) { std::cerr << "warning: target mismatch (" << target.target << " != " << getDefaultTarget() << ")" << std::endl; } parseArgs(); checkCXXLib(); return target.Setup(); } } if (!strncmp(cmd, "o32", 3)) { target.machine = Machine::i386; } else if (!strncmp(cmd, "o64", 3)) { target.machine = Machine::x86_64; } else { return false; } if (cmd[3]) target.compiler = &cmd[4]; parseArgs(); checkCXXLib(); return target.Setup(); } } // unnamed namespace // // Main routine // int main(int argc, char **argv) { char bbuf[sizeof(benchmark)]; auto b = new (bbuf) benchmark; Target target; bool debug = false; if (!detectTarget(argc, argv, target)) { std::cerr << "cannot detect target" << std::endl; return 1; } if (char *p = getenv("OCDEBUG")) { debug = (p[0] == '1'); } if (debug) { b->halt(); std::cerr << "detected target triple: " << target.getTriple() << std::endl; std::cerr << "detected compiler: " << target.compiler << std::endl; std::cerr << "detected stdlib: " << getStdLibString(target.stdlib) << std::endl; // std::cerr << "detected source file: " // << (target.sourcefile ? target.sourcefile : "") << std::endl; std::cerr << "detected language: " << target.getLangName() << std::endl; b->resume(); } auto cargs = new char *[target.fargs.size() + target.args.size() + 1]; size_t i = 0; for (auto &arg : target.fargs) { cargs[i++] = const_cast(arg.c_str()); } for (auto &arg : target.args) { cargs[i++] = const_cast(arg.c_str()); } cargs[i] = nullptr; auto printCommand = [&]() { std::string in; std::string out; for (int i = 0; i < argc; ++i) { in += argv[i]; in += " "; } for (auto &arg : target.fargs) { out += arg; out += " "; } for (auto &arg : target.args) { out += arg; out += " "; } std::cerr << "command (in): " << in << std::endl; std::cerr << "command (out): " << out << std::endl; }; concatEnvVariable("COMPILER_PATH", target.execpath); if (debug) { time_type diff = b->getDiff(); printCommand(); std::cerr << "time spent in wrapper: " << diff / 1000000.0 << " ms" << std::endl; } if (execvp(cargs[0], cargs)) { std::cerr << "invoking compiler failed" << std::endl; if (!debug) printCommand(); return 1; } __builtin_unreachable(); }