// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "task_cc.h" #include #include #include #include "libctor.h" #include "settings.h" #include "execute.h" #include "util.h" namespace { bool isClean(char c) { return c != '.' && c != '/'; } std::string cleanUp(const std::string& path) { std::string cleaned; for(const auto& c : path) { if(isClean(c)) { cleaned += c; } else { cleaned += '_'; } } return cleaned; } } TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, const std::string& sourceDir, const Source& source) : Task(config) , config(config) , settings(settings) , sourceDir(sourceDir) { sourceFile = sourceDir; sourceFile /= source.file; std::filesystem::path base = settings.builddir; base /= sourceFile.parent_path(); std::filesystem::create_directories(base); base /= cleanUp(config.target); base += "-"; base += sourceFile.stem(); target_type = TargetType::Object; source_language = source.language; if(source_language == Language::Auto) { source_language = languageFromExtension(sourceFile); } switch(source_language) { case Language::C: base += "_c"; break; case Language::Cpp: base += "_cc"; break; case Language::Asm: base += "_asm"; break; case Language::Auto: assert(0 && "This should never happen"); break; } targetFile = base; targetFile += ".o"; depsFile = base; depsFile += ".d"; flagsFile = base; flagsFile += ".flags"; } std::string TaskCC::name() const { return target(); } bool TaskCC::dirtyInner() { if(!std::filesystem::exists(sourceFile)) { //std::cout << "Missing source file: " << std::string(sourceFile) << "\n"; return true; } if(!std::filesystem::exists(targetFile)) { //std::cout << "Missing targetFile\n"; return true; } if(!std::filesystem::exists(depsFile)) { //std::cout << "Missing depsFile\n"; return true; } if(!std::filesystem::exists(flagsFile)) { //std::cout << "Missing flagsFile\n"; return true; } if(std::filesystem::last_write_time(sourceFile) > std::filesystem::last_write_time(depsFile)) { //std::cout << "The sourceFile newer than depsFile\n"; return true; } { auto lastFlags = readFile(flagsFile.string()); if(flagsString() != lastFlags) { //std::cout << "The compiler flags changed\n"; return true; } } auto depList = readDeps(depsFile.string()); for(const auto& dep : depList) { if(!std::filesystem::exists(dep) || std::filesystem::last_write_time(targetFile) < std::filesystem::last_write_time(dep)) { //std::cout << "The targetFile older than " << std::string(dep) << "\n"; return true; } } if(std::filesystem::last_write_time(sourceFile) > std::filesystem::last_write_time(targetFile)) { //std::cout << "The targetFile older than sourceFile\n"; return true; } return false; } int TaskCC::runInner() { if(!std::filesystem::exists(sourceFile)) { std::cout << "Missing source file: " << sourceFile.string() << "\n"; return 1; } auto args = getCompilerArgs(); { // Write flags to file. std::ofstream flagsStream(flagsFile.string()); flagsStream << flagsString(); } if(settings.verbose == 0) { std::cout << compiler() << " " << sourceFile.lexically_normal().string() << " => " << targetFile.lexically_normal().string() << "\n"; } return execute(compiler(), args, settings.verbose > 0); } int TaskCC::clean() { if(std::filesystem::exists(targetFile)) { std::cout << "Removing " << targetFile.string() << "\n"; std::filesystem::remove(targetFile); } if(std::filesystem::exists(depsFile)) { std::cout << "Removing " << depsFile.string() << "\n"; std::filesystem::remove(depsFile); } if(std::filesystem::exists(flagsFile)) { std::cout << "Removing " << flagsFile.string() << "\n"; std::filesystem::remove(flagsFile); } return 0; } std::vector TaskCC::depends() const { return {}; } std::string TaskCC::target() const { return targetFile.string(); } bool TaskCC::derived() const { return true; } std::string TaskCC::toJSON() const { std::string json; json += "\t{\n"; json += "\t\t\"directory\": \"" + sourceDir.string() + "\",\n"; json += "\t\t\"file\": \"" + sourceFile.lexically_normal().string() + "\",\n"; json += "\t\t\"output\": \"" + targetFile.string() + "\",\n"; json += "\t\t\"arguments\": [ \"" + compiler() + "\""; auto args = getCompilerArgs(); for(const auto& arg : args) { json += ", \"" + arg + "\""; } json += " ]\n"; json += "\t}"; return json; } std::string TaskCC::source() const { return sourceFile.string(); } std::vector TaskCC::flags() const { switch(sourceLanguage()) { case Language::C: return config.flags.cflags; case Language::Cpp: return config.flags.cxxflags; default: std::cerr << "Unknown CC target type\n"; exit(1); break; } } std::string TaskCC::flagsString() const { std::string flagsStr = compiler(); for(const auto& flag : flags()) { flagsStr += " " + flag; } return flagsStr; } std::vector TaskCC::getCompilerArgs() const { auto compiler_flags = flags(); std::vector args; args.push_back("-MMD"); if(std::filesystem::path(config.target).extension() == ".so") { // Add -fPIC arg to all contained object files args.push_back("-fPIC"); } args.push_back("-c"); args.push_back(sourceFile.string()); args.push_back("-o"); args.push_back(targetFile.string()); for(const auto& flag : compiler_flags) { // Is arg an added include path? if(flag.substr(0, 2) == "-I") { std::string include_path = flag.substr(2); include_path.erase(0, include_path.find_first_not_of(' ')); std::filesystem::path path(include_path); // Is it relative? if(path.is_relative()) { path = (sourceDir / path).lexically_normal(); std::string new_include_path = "-I" + path.string(); args.push_back(new_include_path); continue; } } args.push_back(flag); } return args; }