#include "toolchain.h" #include #include #include #include #include "sys.h" #include "globals.h" namespace toolchain { static const char *getPkgConfig() { static const char *pkgConfig = nullptr; if (pkgConfig != nullptr) { return pkgConfig; } pkgConfig = getenv("PKG_CONFIG"); if (pkgConfig != nullptr) { return pkgConfig; } return (pkgConfig = "pkg-config"); } static const char *getCCompiler() { static const char *cCompiler = nullptr; if (cCompiler != nullptr) { return cCompiler; } cCompiler = getenv("CC"); if (cCompiler != nullptr) { return cCompiler; } return (cCompiler = "cc"); } static const char *getCXXCompiler() { static const char *cxxCompiler = nullptr; if (cxxCompiler != nullptr) { return cxxCompiler; } cxxCompiler = getenv("CXX"); if (cxxCompiler != nullptr) { return cxxCompiler; } return (cxxCompiler = "g++"); } static const char *getCompilerFor(FileType type) { switch (type) { case FileType::C: return getCCompiler(); case FileType::CXX: return getCXXCompiler(); } abort(); } static bool isWhitespace(char ch) { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; } static void parseWhitespaceSeparated( const std::string &input, std::vector &output, size_t pos = 0) { size_t i = pos; while (true) { // Skip leading whitespace char ch = input[i]; while (isWhitespace(ch) || (ch == '\\' && isWhitespace(input[i + 1]))) { ch = input[i += 1]; } // Read non-whitespace std::string str; while (!isWhitespace(ch) && ch != '\0') { if (ch == '\\') { str += input[i + 1]; ch = input[i += 2]; } else { str += input[i]; ch = input[i += 1]; } } output.push_back(std::move(str)); if (ch == '\0') { break; } } } std::string objectFilePath(const std::string &path, const std::string &outDir) { return outDir + '/' + path + ".o"; } std::string targetFilePath( TargetType type, const std::string &path, const std::string &outDir) { std::string base = outDir + '/' + path; switch (type) { case TargetType::BINARY: return base; case TargetType::SHARED_LIBRARY: return base + ".so"; case TargetType::STATIC_LIBRARY: return base + ".a"; } abort(); } void getFlags(const BXVariables &vars, FileType type, std::vector &flags) { auto stdver = vars.find("std"); if (stdver != vars.end() && stdver->second.size() > 0) { flags.push_back("-std=" + stdver->second[0]); } auto pkgs = vars.find("pkgs"); if (pkgs != vars.end()) { std::vector argv; argv.push_back(getPkgConfig()); argv.push_back("--cflags"); for (auto &pkg: pkgs->second) { argv.push_back(pkg); } // Execute $(PKG_CONFIG) --cflags $(PKGS) std::string output; sys::execute(argv, &output, global::verbose >= 2); parseWhitespaceSeparated(output, flags); } auto warnings = vars.find("warnings"); if (warnings != vars.end()) { for (auto &w: warnings->second) { flags.push_back("-W" + w); } } auto includes = vars.find("includes"); if (includes != vars.end()) { for (auto &i: includes->second) { flags.push_back("-I" + i); } } auto sanitizers = vars.find("sanitizers"); if (sanitizers != vars.end()) { for (auto &s: sanitizers->second) { flags.push_back("-fsanitize=" + s); } } auto cflags = vars.find( type == FileType::C ? "cflags" : type == FileType::CXX ? "cxxflags" : nullptr); if (cflags != vars.end()) { for (auto &f: cflags->second) { flags.push_back(f); } } } void getLDLibs(const BXVariables &vars, std::vector &flags) { auto pkgs = vars.find("pkgs"); if (pkgs != vars.end()) { std::vector argv; argv.push_back(getPkgConfig()); argv.push_back("--libs"); for (auto &pkg: pkgs->second) { argv.push_back(pkg); } // Execute $(PKG_CONFIG) --cflags $(PKGS) std::string output; sys::execute(argv, &output, global::verbose >= 2); parseWhitespaceSeparated(output, flags); } auto ldlibs = vars.find("ldlibs"); if (ldlibs != vars.end()) { for (auto &l: ldlibs->second) { flags.push_back("-l" + l); } } } void getLDFlags(const BXVariables &vars, std::vector &flags) { auto sanitize = vars.find("sanitizers"); if (sanitize != vars.end()) { for (auto &s: sanitize->second) { flags.push_back("-fsanitizers=" + s); } } auto ldflags = vars.find("ldflags"); if (ldflags != vars.end()) { for (auto &f: ldflags->second) { flags.push_back(f); } } } std::vector getDependencies( const std::vector &flags, FileType type, const std::string &path) { std::vector argv; // $(compiler) argv.push_back(getCompilerFor(type)); // $(flags) for (auto &flag: flags) { argv.push_back(flag); } // -MM $< argv.push_back("-MM"); argv.push_back(path); // Execute $(compiler) $(flags) -MM $< std::string output; sys::execute(argv, &output, global::verbose >= 2); std::vector deps; size_t idx = output.find(':'); if (idx != std::string::npos) { parseWhitespaceSeparated(output, deps, idx + 1); } return deps; } std::vector getCompileCommand( const std::vector &flags, FileType type, const std::string &path, const std::string &outDir) { std::string objectPath = objectFilePath(path, outDir); std::vector argv; // $(compiler) argv.push_back(getCompilerFor(type)); // $(cflags) for (auto &flag: flags) { argv.push_back(flag.c_str()); } // -o $@ -c $< argv.push_back("-o"); argv.push_back(std::move(objectPath)); argv.push_back("-c"); argv.push_back(std::move(path)); return argv; } std::vector getLinkCommand( const std::vector &ldFlags, const std::vector &ldLibs, FileType type, TargetType targetType, const std::vector &objs, const std::string &path, const std::string &outDir) { // TODO: Use ar to create STATIC_LIBRARY, // use GCC with -shared to make SHARED_LIBRARY std::vector argv; // $(compiler) argv.push_back(getCompilerFor(type)); // $(ldflags) for (auto &flag: ldFlags) { argv.push_back(flag); } // -o $@ argv.push_back("-o"); argv.push_back(targetFilePath(targetType, path, outDir)); // $(objs) for (auto &obj: objs) { argv.push_back(obj); } // $(ldlibs) for (auto &flag: ldLibs) { argv.push_back(flag); } return argv; } }