#include "LanguageSettings.h" #include #include #include #include #include #include #include #include #include #include "env.h" #include "file.h" namespace fs = std::filesystem; using namespace std::string_literals; LanguageSettings::LanguageSettings(): // C++ CXX{env_value("CXX")}, CXXFLAGS{env_value("CXXFLAGS")}, LDFLAGS{env_value("LDFLAGS")}, LDLIBS{env_value("LDLIBS")}, LIBS{env_value("LIBS")}, // C // CFLAGS AR{env_value("AR")} { std::string _default_compiler(find_executable({"g++", "clang++"})); if (_default_compiler.empty()) { throw std::runtime_error("No default C++ compiler found."); } // set defaults if (CXX.empty()) { CXX = _default_compiler; } if (CXXFLAGS.empty()) { CXXFLAGS = "-std=c++17 -Wall -O2"; } if (AR.empty()) { AR = "ar"; } } std::string LanguageSettings::getCompileCommand(const std::filesystem::path& target, const std::filesystem::path& source, const std::filesystem::path& build, const std::vector& includepaths) const { std::string includes{std::accumulate(includepaths.begin(), includepaths.end(), std::string{}, [](const std::string& sum, const fs::path& p){ return sum + " -I" + p.string();})}; std::string CXXFLAGS_add{is_dynamic_lib(build) ? " -fvisibility=hidden -fPIC"s : ""s}; // compile: $(CXX) $(CXXFLAGS) -c $< -o $@ return fmt::format("{}{}{}{} -c {} -o {}", CXX, CXXFLAGS.empty() ? ""s : (" "s + CXXFLAGS), CXXFLAGS_add, includes, source.string(), target.string()); } std::string LanguageSettings::getLinkCommand(const std::filesystem::path& target, const std::vector &inputs, const std::vector& link_libs) const { std::string input_string{std::accumulate(inputs.begin(), inputs.end(), std::string{}, [](const std::string& sum, const fs::path& p){ if (is_dynamic_lib(p) || is_dynamic_lib_link(p)) { return sum; } return sum + " " + p.string(); })}; if (is_static_lib(target)) { // link: $(AR) rcs libxyz.a x.o y.o z.o return fmt::format("{} rcs {}{}", AR, target.string(), input_string); } else if (is_executable_target(target) || is_dynamic_lib(target)) { // dynamic link: -shared -Wl,-soname,libXXX.so.N -o libXXX.so.N.M.O std::string LDFLAGS_add{is_dynamic_lib(target) ? fmt::format(" -shared -Wl,-soname,{}", soname_shorter(target.string()).string()) : ""s}; std::string link_libs_string; for (const auto& i: link_libs) { if (is_static_lib(i.string())) { input_string += " " + i.string(); } else if (is_dynamic_lib(i.string()) || is_dynamic_lib_link(i.string())) { input_string += " -L" + lib_path(i).string() + " -l" + dynamic_lib_name(i); } else { // link external dynamic lib, e.g. "fmt" link_libs_string += " -l" + i.string(); } } // link: $(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ return fmt::format("{}{}{}{}{}{}{} -o {}", CXX, LDFLAGS.empty() ? ""s : (" "s + LDFLAGS), LDFLAGS_add, input_string, LDLIBS.empty() ? ""s : (" "s + LDLIBS), LIBS.empty() ? ""s : (" "s + LIBS), link_libs_string, target.string()); } else if (is_dynamic_lib_link(target)) { // special case for dynamic libs: add links if (inputs.size() != 1) { throw std::runtime_error("For symlink "s + target.string() + ", can link to exactly one file, found "s + std::to_string(inputs.size())); } return fmt::format("ln -sf {} {}", inputs[0].filename().string(), target.string()); } else { throw std::runtime_error("No implementation for target type: "s + target.string()); } } std::string LanguageSettings::getDepCommand(const std::filesystem::path& target, const std::filesystem::path &source) const { // g++ -MM -MF -c return fmt::format("{} -MM -MF {} -c {}", CXX, target.string(), source.string()); } // TODO: // variables: // CC // CFLAGS // ARFLAGS // AS // ASFLAGS