// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "task_cc.h" #include #include #include #include #include "ctor.h" #include "execute.h" #include "util.h" #include "tools.h" TaskCC::TaskCC(const ctor::build_configuration& config_, const ctor::settings& settings_, const std::string& sourceDir_, const ctor::source& source) : Task(config_, settings_, sourceDir_) , sourceFile(sourceDir_) , config(config_) , settings(settings_) , sourceDir(sourceDir_) , _source(source) { sourceFile /= source.file; std::filesystem::path base = sourceFile.parent_path(); base /= cleanUp(config.target); base += "-"; base += sourceFile.stem(); target_type = ctor::target_type::object; output_system = config.system; source_language = source.language; if(source_language == ctor::language::automatic) { source_language = languageFromExtension(sourceFile); } switch(source_language) { case ctor::language::c: base += "_c"; break; case ctor::language::cpp: base += "_cc"; break; case ctor::language::assembler: base += "_asm"; break; case ctor::language::automatic: assert(0 && "This should never happen"); break; } if(source.output.empty()) { _targetFile = base; } else { _targetFile = source.output; } auto toolchain = getToolChain(config.system); _targetFile = extension(toolchain, target_type, config.system, _targetFile); depsFile = targetFile().parent_path() / targetFile().stem(); depsFile += ".d"; flagsFile = targetFile().parent_path() / targetFile().stem(); flagsFile += ".flags"; std::filesystem::create_directories(targetFile().parent_path()); } int TaskCC::registerDepTasksInner(const std::vector>& tasks) { for(const auto& task : tasks) { if(*task == _source.file) { if(std::find(dependsTasks.begin(), dependsTasks.end(), task) == dependsTasks.end()) { dependsTasks.push_back(task); } } } return 0; } std::string TaskCC::name() const { return {}; } 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) { switch(sourceLanguage()) { case ctor::language::c: std::cout << "CC "; break; case ctor::language::cpp: std::cout << "CXX "; break; case ctor::language::automatic: case ctor::language::assembler: // Only c/c++ handled by this task type. break; } std::cout << sourceFile.lexically_normal().string() << " => " << targetFile().lexically_normal().string() << std::endl; } const auto& cfg = ctor::get_configuration(); return execute(settings, compiler(), args, cfg.env); } 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(); } std::filesystem::path TaskCC::targetFile() const { return std::filesystem::path(settings.builddir) / _targetFile; } 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 { std::vector flags; auto toolchain = getToolChain(config.system); switch(sourceLanguage()) { case ctor::language::c: for(const auto& flag : config.flags.cflags) { append(flags, to_strings(toolchain, flag)); } append(flags, to_strings(toolchain, {ctor::toolchain::msvc, ctor::c_opt::custom, "/nologo"})); return flags; case ctor::language::cpp: for(const auto& flag : config.flags.cxxflags) { append(flags, to_strings(toolchain, flag)); } append(flags, to_strings(toolchain, {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/nologo"})); return flags; 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 toolchain = getToolChain(config.system); auto compiler_flags = flags(); std::vector args; switch(sourceLanguage()) { case ctor::language::c: { append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree, depsFile.string())); if(std::filesystem::path(config.target).extension() == ".so") { // Add -fPIC arg to all contained object files append(args, c_option(toolchain, ctor::c_opt::position_independent_code)); } append(args, c_option(toolchain, ctor::c_opt::no_link)); args.push_back(sourceFile.string()); append(args, c_option(toolchain, ctor::c_opt::output, targetFile().string())); // Relative include paths has to be altered to be relative to sourceDir for(const auto& flag : compiler_flags) { auto option = c_option(flag, toolchain); switch(option.opt) { case ctor::c_opt::include_path: { std::filesystem::path path(option.arg); if(path.is_relative()) { path = (sourceDir / path).lexically_normal(); append(args, c_option(toolchain, ctor::c_opt::include_path, path.string())); } } break; default: break; } args.push_back(flag); } } break; case ctor::language::cpp: { append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree, depsFile.string())); if(std::filesystem::path(config.target).extension() == ".so") { // Add -fPIC arg to all contained object files append(args, cxx_option(toolchain, ctor::cxx_opt::position_independent_code)); } append(args, cxx_option(toolchain, ctor::cxx_opt::no_link)); args.push_back(sourceFile.string()); append(args, cxx_option(toolchain, ctor::cxx_opt::output, targetFile().string())); // Relative include paths has to be altered to be relative to sourceDir for(const auto& flag : compiler_flags) { auto option = cxx_option(flag, toolchain); switch(option.opt) { case ctor::cxx_opt::include_path: { std::filesystem::path path(option.arg); if(path.is_relative()) { path = (sourceDir / path).lexically_normal(); append(args, cxx_option(toolchain, ctor::cxx_opt::include_path, path.string())); } } break; default: break; } args.push_back(flag); } } break; default: break; } return args; }