From 3f117ba513d7f8ec8244103174f8635df7bd379b Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 20 Sep 2018 14:57:15 +0200 Subject: Use ninja library to generate build instead of raw string --- backend/BackendUtils.cpp | 45 +-- backend/BackendUtils.hpp | 4 +- backend/ninja/Ninja.cpp | 727 +++++++++++++++++++++++------------------------ backend/ninja/Ninja.hpp | 6 +- 4 files changed, 396 insertions(+), 386 deletions(-) (limited to 'backend') diff --git a/backend/BackendUtils.cpp b/backend/BackendUtils.cpp index a91fc95..6bb0ace 100644 --- a/backend/BackendUtils.cpp +++ b/backend/BackendUtils.cpp @@ -7,38 +7,49 @@ using namespace sibs; namespace backend { - bool isPathSubPathOf(const FileString &path, const FileString &subPathOf) + static bool isPathSubPathOf(const FileString &path, const FileString &subPathOf) { return _tinydir_strncmp(path.c_str(), subPathOf.c_str(), subPathOf.size()) == 0; } - - const _tinydir_char_t *sourceFileExtensions[] = + + const _tinydir_char_t *cFileExtensions[] = { TINYDIR_STRING("c"), TINYDIR_STRING("C"), - TINYDIR_STRING("cc"), + TINYDIR_STRING("cc") + }; + + const _tinydir_char_t *cppFileExtensions[] = + { TINYDIR_STRING("cp"), TINYDIR_STRING("cpp"), TINYDIR_STRING("cxx"), TINYDIR_STRING("c++") }; - bool BackendUtils::isSourceFile(tinydir_file *file) + + sibs::Language BackendUtils::getFileLanguage(tinydir_file *file) { if(!file->is_reg) - return false; + return sibs::Language::NONE; + + for(const _tinydir_char_t *sourceFileExtension : cFileExtensions) + { + if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) + return sibs::Language::C; + } - for(const _tinydir_char_t *sourceFileExtension : sourceFileExtensions) + for(const _tinydir_char_t *sourceFileExtension : cppFileExtensions) { if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) - return true; + return sibs::Language::CPP; } - return false; + return sibs::Language::NONE; } - void BackendUtils::collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const SibsConfig &sibsConfig) + void BackendUtils::collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const SibsConfig &sibsConfig, bool recursive) { - walkDir(projectPath, [ninjaProject, &sibsConfig](tinydir_file *file) + walkDir(projectPath, [ninjaProject, &sibsConfig, recursive](tinydir_file *file) { FileString pathNative = file->path; #if OS_FAMILY == OS_FAMILY_WINDOWS @@ -46,10 +57,11 @@ namespace backend #endif if(file->is_reg) { - if (isSourceFile(file)) + sibs::Language fileLanguage = getFileLanguage(file); + if (fileLanguage != sibs::Language::NONE) { string filePathUtf8 = toUtf8(pathNative.c_str() + sibsConfig.getProjectPath().size()); - ninjaProject->addSourceFile(filePathUtf8.c_str()); + ninjaProject->addSourceFile(fileLanguage, filePathUtf8.c_str()); } else { @@ -58,12 +70,13 @@ namespace backend } else { + // TODO: Verify what happens if test has test sub directory if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(pathNative.c_str(), sibsConfig.getTestPath())) { string filePathUtf8 = toUtf8(pathNative.c_str()); ninjaProject->addTestSourceDir(filePathUtf8.c_str()); } - else if(!directoryToIgnore(pathNative, sibsConfig.getIgnoreDirs())) + else if(recursive && !directoryToIgnore(pathNative, sibsConfig.getIgnoreDirs())) { FileString projectConfPath = file->path; #if OS_FAMILY == OS_FAMILY_WINDOWS @@ -82,11 +95,11 @@ namespace backend FileString subProjectBuildPath; readSibsConfig(file->path, projectConfPath, *subProjectConfig, subProjectBuildPath); - collectSourceFiles(file->path, subProject, *subProjectConfig); + collectSourceFiles(file->path, subProject, *subProjectConfig, true); ninjaProject->addSubProject(subProject, subProjectConfig, move(subProjectBuildPath)); } else - collectSourceFiles(file->path, ninjaProject, sibsConfig); + collectSourceFiles(file->path, ninjaProject, sibsConfig, true); } } }); diff --git a/backend/BackendUtils.hpp b/backend/BackendUtils.hpp index b2fe280..acef0ca 100644 --- a/backend/BackendUtils.hpp +++ b/backend/BackendUtils.hpp @@ -9,7 +9,7 @@ namespace backend class BackendUtils { public: - static bool isSourceFile(tinydir_file *file); - static void collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const sibs::SibsConfig &sibsConfig); + static sibs::Language getFileLanguage(tinydir_file *file); + static void collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const sibs::SibsConfig &sibsConfig, bool recursive = true); }; } diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index e6ca47a..6516cfb 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -5,6 +5,7 @@ #include "../../include/Exec.hpp" #include "../../include/PkgConfig.hpp" #include "../../include/GlobalLib.hpp" +#include using namespace std; using namespace sibs; @@ -17,7 +18,7 @@ using namespace sibs; namespace backend { - string join(const vector &list, const char *joinStr) + static string join(const vector &list, const char *joinStr) { if(list.empty()) return ""; string result; @@ -46,7 +47,7 @@ namespace backend return result; } - bool endsWith(const string &str, const string &endWithStr) + static bool endsWith(const string &str, const string &endWithStr) { if(endWithStr.size() > str.size()) return false; @@ -54,7 +55,7 @@ namespace backend return strncmp(&str[str.size() - endWithStr.size()], &endWithStr[0], endWithStr.size()) == 0; } - Ninja::LibraryType getNinjaLibraryType(PackageType packageType) + static Ninja::LibraryType getNinjaLibraryType(PackageType packageType) { switch(packageType) { @@ -71,7 +72,7 @@ namespace backend } } - string getIncludeOptionFlag(Compiler compiler, const string &filepath) + static string getIncludeOptionFlag(Compiler compiler, const string &filepath) { string result; switch (compiler) @@ -94,34 +95,26 @@ namespace backend return result; } - string getDefineFlag(Compiler compiler, const string &name, const string &value) + static string getDefineFlag(Compiler compiler, const string &name, const string &value) { - string result; switch (compiler) { case Compiler::GCC: { - result = "'-D"; - result += name; - result += "="; - result += value; - result += "'"; - break; + return "'-D" + name + "=" + value + "'"; } case Compiler::MSVC: { - result = "\"/D"; - result += name; - result += "="; - result += value; - result += "\""; - break; + return "\"/D" + name + "=" + value + "\""; } + default: + assert(false); + break; } - return result; + return ""; } - string getObjectFileNameFlag(Compiler compiler, const string &objectFileName) + static string getObjectFileNameFlag(Compiler compiler, const string &objectFileName) { string result; switch (compiler) @@ -142,7 +135,7 @@ namespace backend return result; } - string getLanguageVersionFlag(Compiler compiler, CVersion cVersion) + static vector getLanguageVersionFlag(Compiler compiler, CVersion cVersion) { switch (compiler) { @@ -150,23 +143,23 @@ namespace backend { switch(cVersion) { - case CVersion::C89: return "-std=c89 -pedantic"; - case CVersion::C99: return "-std=c99 -pedantic"; - case CVersion::C11: return "-std=c11 -pedantic"; + case CVersion::C89: return { ninja::NinjaArg("-std=c89"), ninja::NinjaArg("-pedantic") }; + case CVersion::C99: return { ninja::NinjaArg("-std=c99"), ninja::NinjaArg("-pedantic") }; + case CVersion::C11: return { ninja::NinjaArg("-std=c11"), ninja::NinjaArg("-pedantic") }; } break; } case Compiler::MSVC: { // TODO: Is it possible to specify c version in msvc? - return ""; + return {}; } } assert(false); - return ""; + return {}; } - string getLanguageVersionFlag(Compiler compiler, CPPVersion cppVersion) + static vector getLanguageVersionFlag(Compiler compiler, CPPVersion cppVersion) { switch (compiler) { @@ -174,9 +167,9 @@ namespace backend { switch(cppVersion) { - case CPPVersion::CPP11: return "-std=c++11 -pedantic"; - case CPPVersion::CPP14: return "-std=c++14 -pedantic"; - case CPPVersion::CPP17: return "-std=c++17 -pedantic"; + case CPPVersion::CPP11: return { ninja::NinjaArg("-std=c++11"), ninja::NinjaArg("-pedantic") }; + case CPPVersion::CPP14: return { ninja::NinjaArg("-std=c++14"), ninja::NinjaArg("-pedantic") }; + case CPPVersion::CPP17: return { ninja::NinjaArg("-std=c++17"), ninja::NinjaArg("-pedantic") }; } break; } @@ -185,18 +178,18 @@ namespace backend switch(cppVersion) { // Use /Za flag? - case CPPVersion::CPP11: return "/std=c++11"; - case CPPVersion::CPP14: return "/std=c++14"; - case CPPVersion::CPP17: return "/std=c++17"; + case CPPVersion::CPP11: return { ninja::NinjaArg("/std=c++11") }; + case CPPVersion::CPP14: return { ninja::NinjaArg("/std=c++14") }; + case CPPVersion::CPP17: return { ninja::NinjaArg("/std=c++17") }; } break; } } assert(false); - return ""; + return {}; } - const char* getObjectFileExtension(Compiler compiler) + static const char* getObjectFileExtension(Compiler compiler) { switch (compiler) { @@ -216,12 +209,12 @@ namespace backend customGlobalIncludeDirs = globalIncludeDirs; } - void Ninja::addSourceFile(const char *filepath) + void Ninja::addSourceFile(sibs::Language language, const char *filepath) { string filePathStr = filepath ? filepath : ""; if(filepath && !containsSourceFile(filePathStr)) { - sourceFiles.emplace_back(filePathStr); + sourceFiles.push_back({ language, filePathStr }); //printf("Adding source file: %s\n", filepath); } } @@ -247,16 +240,16 @@ namespace backend subProjects.emplace_back(NinjaSubProject{ subProject, config, move(buildPath) }); } - const std::vector& Ninja::getSourceFiles() const + const std::vector& Ninja::getSourceFiles() const { return sourceFiles; } bool Ninja::containsSourceFile(const string &filepath) const { - for(const string &sourceFile : sourceFiles) + for(const sibs::SourceFile &sourceFile : sourceFiles) { - if(sourceFile == filepath) + if(sourceFile.filepath == filepath) return true; } return false; @@ -342,12 +335,6 @@ namespace backend return GlobalLib::getLibs(globalLibDependencies, config, globalLibDir, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback); } - enum class SourceFileLanguage - { - C, - CPP - }; - Result Ninja::buildSubProjects(LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { for(auto &subProject : subProjects) @@ -367,6 +354,74 @@ namespace backend return Result::Ok(true); } + static vector getCompilerSanitizerFlags(const SibsConfig &config) + { + if(config.getCompiler() == Compiler::GCC && config.getSanitize()) + { + return { + ninja::NinjaArg("-fno-omit-frame-pointer"), + ninja::NinjaArg("-fsanitize=address"), + ninja::NinjaArg("-fsanitize=undefined") + }; + } + return {}; + } + + static vector getCompilerOptimizationFlags(const SibsConfig &config) + { + switch (config.getCompiler()) + { + case Compiler::GCC: + { + switch (config.getOptimizationLevel()) + { + case OPT_LEV_DEBUG: + { + return { + ninja::NinjaArg("-Og"), + ninja::NinjaArg("-g3"), + ninja::NinjaArg("-D_FORTIFY_SOURCE=2"), + ninja::NinjaArg("-D_GLIBCXX_ASSERTIONS"), + ninja::NinjaArg("-fasynchronous-unwind-tables") + }; + } + case OPT_LEV_RELEASE: + { + return { + ninja::NinjaArg("-O3"), + ninja::NinjaArg("-g0"), + ninja::NinjaArg("-DNDEBUG") + }; + } + } + break; + } + case Compiler::MSVC: + { + switch (config.getOptimizationLevel()) + { + case OPT_LEV_DEBUG: + { + return { + ninja::NinjaArg("/Od"), + ninja::NinjaArg("/MTd") + }; + } + case OPT_LEV_RELEASE: + { + return { + ninja::NinjaArg("/Ox"), + ninja::NinjaArg("/MT"), + ninja::NinjaArg("/DNDEBUG") + }; + } + } + break; + } + } + return {}; + } + Result Ninja::build(const SibsConfig &config, const _tinydir_char_t *savePath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { if (!sourceFiles.empty()) @@ -384,17 +439,15 @@ namespace backend FileString ninjaBuildFilePath = savePath; ninjaBuildFilePath += TINYDIR_STRING("/build.ninja"); - string result; - result.reserve(2048); - - result += "globalIncDir = "; + ninja::NinjaBuildFile ninjaBuildFile; + string globalIncDir; for(const auto &includeDir : config.getIncludeDirs()) { string includeDirRelative = "../../"; includeDirRelative += includeDir; - result += " "; - result += getIncludeOptionFlag(config.getCompiler(), includeDirRelative); + globalIncDir += " "; + globalIncDir += getIncludeOptionFlag(config.getCompiler(), includeDirRelative); } auto parentGlobalIncludeDirCallback = globalIncludeDirCallback; @@ -453,11 +506,10 @@ namespace backend }; } - string cflags; + vector cflags; auto cflagsCallbackFunc = [&cflags](const string &dependencyCflags) { - cflags += " "; - cflags += dependencyCflags; + cflags.push_back(ninja::NinjaArg::createRaw(dependencyCflags)); }; Result buildSubProjectResult = buildSubProjects(staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback); @@ -474,309 +526,206 @@ namespace backend allLinkerFlags += *it; } - result += dependencyExportIncludeDirs; - result += "\n\n"; + globalIncDir += dependencyExportIncludeDirs; + ninjaBuildFile.defineGlobalVariable("globalIncDir", globalIncDir); - string defines; + vector defines; for(const auto &definePair : config.getDefines()) { - defines += " "; - defines += getDefineFlag(config.getCompiler(), definePair.first, definePair.second); + defines.push_back(ninja::NinjaArg::createRaw(getDefineFlag(config.getCompiler(), definePair.first, definePair.second))); } -#if OS_TYPE == OS_TYPE_WINDOWS switch (SYSTEM_PLATFORM) { case PLATFORM_WIN64: - defines += " /DWIN64 /D_WIN64"; + defines.insert(defines.end(), { ninja::NinjaArg("/DWIN64"), ninja::NinjaArg("/D_WIN64") }); // fallthrough case PLATFORM_WIN32: - defines += " /DWIN32 /D_WIN32"; + defines.insert(defines.end(), { ninja::NinjaArg("/DWIN32"), ninja::NinjaArg("/D_WIN32") }); break; } - switch(libraryType) + if(SYSTEM_PLATFORM == PLATFORM_WIN32 || SYSTEM_PLATFORM == PLATFORM_WIN64) { - case LibraryType::EXECUTABLE: - defines += " _CONSOLE"; - break; - case LibraryType::STATIC: - defines += " _LIB"; - break; + switch(libraryType) + { + // TODO: Executable type does not guarantee the executable should be a console on windows. Find a way to define window type as well + case LibraryType::EXECUTABLE: + defines.push_back(ninja::NinjaArg(" _CONSOLE")); + break; + case LibraryType::STATIC: + defines.push_back(ninja::NinjaArg(" _LIB")); + break; + } } -#endif - string buildJob; - switch(libraryType) + // TODO: Verify ccache is installed + // -Werror + // TODO: Find equivalent -MMD -MP for other compilers than gcc. MMD is used to create "dependency files" -> if headers are modified then source files will be recompiled + // when compiling next time... + + vector compileCCommand; + vector compileCppCommand; + + switch(config.getCompiler()) { - case LibraryType::EXECUTABLE: + case Compiler::GCC: { - switch (config.getCompiler()) + vector baseCompileCArgs; + switch(libraryType) { - case Compiler::GCC: + case LibraryType::EXECUTABLE: { - result += "rule cpp_COMPILER\n"; - result += " depfile = $out.d\n"; - result += " command = ccache c++ $ARGS -c $in -o $out\n\n"; - - result += "rule BUILD_EXEC\n"; - result += " depfile = $out.d\n"; - result += " command = ccache c++ $ARGS -o $out $in $LINK_ARGS $aliasing\n\n"; - - result += "rule c_COMPILER\n"; - result += " depfile = $out.d\n"; - result += " command = ccache cc $ARGS -c $in -o $out\n\n"; + baseCompileCArgs.insert(baseCompileCArgs.end(), { + ninja::NinjaArg("ccache"), + ninja::NinjaArg("cc"), + ninja::NinjaArg("-c"), + ninja::NinjaArg("-fpie"), + ninja::NinjaArg("$in"), + ninja::NinjaArg("-o"), + ninja::NinjaArg("$out") + }); break; } - case Compiler::MSVC: + case LibraryType::STATIC: + case LibraryType::DYNAMIC: { - result += "rule cpp_COMPILER\n"; - result += " command = cl.exe $ARGS /c $in /Fo$out\n\n"; - - result += "rule BUILD_EXEC\n"; - result += " command = cl.exe $ARGS $in /Fe$out $LINK_ARGS\n\n"; - - result += "rule c_COMPILER\n"; - result += " command = cl.exe $ARGS /c $in /Fo$out\n\n"; + baseCompileCArgs.insert(baseCompileCArgs.end(), { + ninja::NinjaArg("ccache"), + ninja::NinjaArg("cc"), + ninja::NinjaArg("-c"), + ninja::NinjaArg("-fpic"), + ninja::NinjaArg("$in"), + ninja::NinjaArg("-o"), + ninja::NinjaArg("$out") + }); break; } } - buildJob = "BUILD_EXEC"; - break; - } - case LibraryType::STATIC: - { - switch (config.getCompiler()) + compileCCommand.insert(compileCCommand.end(), baseCompileCArgs.begin(), baseCompileCArgs.end()); + compileCCommand.insert(compileCCommand.end(), { + ninja::NinjaArg("-fdiagnostics-show-option"), + ninja::NinjaArg("-fdiagnostics-color=always"), + ninja::NinjaArg("-pipe"), + ninja::NinjaArg("-D_FILE_OFFSET_BITS=64"), + ninja::NinjaArg("-Winvalid-pch"), + ninja::NinjaArg("-fstack-protector"), + ninja::NinjaArg("-MMD"), + ninja::NinjaArg("-I" + config.getPackageName() + "@exe"), + ninja::NinjaArg("$globalIncDir") + }); + + compileCCommand.insert(compileCCommand.end(), cflags.begin(), cflags.end()); + compileCCommand.insert(compileCCommand.end(), defines.begin(), defines.end()); + + if(config.showWarnings) { - case Compiler::GCC: - { - result += "rule cpp_COMPILER\n"; - result += " depfile = $out.d\n"; - result += " command = ccache c++ $ARGS -c -fPIC $in -o $out\n\n"; - - result += "rule BUILD_STATIC\n"; - result += " command = ar rcs lib"; - result += config.getPackageName(); - result += ".a"; - result += " $in\n\n"; - - result += "rule c_COMPILER\n"; - result += " depfile = $out.d\n"; - result += " command = ccache cc $ARGS -c -fPIC $in -o $out\n\n"; - break; - } - case Compiler::MSVC: - { - result += "rule cpp_COMPILER\n"; - result += " command = cl.exe $ARGS /c $in /Fo$out\n\n"; - - result += "rule BUILD_STATIC\n"; - result += " command = lib.exe /OUT:$out $in\n\n"; - - result += "rule c_COMPILER\n"; - result += " command = cl.exe $ARGS /c $in /Fo$out\n\n"; - break; - } + compileCCommand.insert(compileCCommand.end(), { + ninja::NinjaArg("-Wall"), + ninja::NinjaArg("-Wextra"), + ninja::NinjaArg("-Werror=return-type") + }); } - - buildJob = "BUILD_STATIC"; - break; - } - case LibraryType::DYNAMIC: - { - switch (config.getCompiler()) + else { - case Compiler::GCC: - { - result += "rule cpp_COMPILER\n"; - result += " depfile = $out.d\n"; - result += " command = ccache c++ $ARGS -c -fPIC $in -o $out\n\n"; - - // --whole-archive - result += "rule BUILD_DYNAMIC\n"; - result += " depfile = $out.d\n"; - result += " command = ccache c++ $in -shared -o $out $LINK_ARGS $aliasing\n\n"; - - result += "rule c_COMPILER\n"; - result += " depfile = $out.d\n"; - result += " command = ccache cc $ARGS -c -fPIC $in -o $out\n\n"; - break; - } - case Compiler::MSVC: - { - result += "rule cpp_COMPILER\n"; - result += " command = cl.exe $ARGS /c $in /Fo$out\n\n"; - - //result += "rule BUILD_DYNAMIC\n"; - //result += " command = cl.exe /LD $in /Fe$out $LINK_ARGS\n\n"; - - result += "rule BUILD_DYNAMIC\n"; - result += " command = lib.exe /OUT:$out $in\n\n"; - - result += "rule c_COMPILER\n"; - result += " command = cl.exe $ARGS /c $in /Fo$out\n\n"; - break; - } + compileCCommand.push_back(ninja::NinjaArg("-w")); } - buildJob = "BUILD_DYNAMIC"; - break; - } - default: - assert(false); - return Result::Err("Unexpected error"); - } + vector optimizationFlags = getCompilerOptimizationFlags(config); + compileCCommand.insert(compileCCommand.end(), optimizationFlags.begin(), optimizationFlags.end()); - // TODO: Add equivalent functionality for msvc. Currently msvc always builds as debug build - string optimizationFlags; - switch (config.getOptimizationLevel()) - { - case OPT_LEV_DEBUG: - { - switch (config.getCompiler()) - { - case Compiler::GCC: - { - optimizationFlags = "'-Og'"; - break; - } - case Compiler::MSVC: - { - optimizationFlags = "/Od"; - break; - } - } + vector sanitizerFlags = getCompilerSanitizerFlags(config); + compileCCommand.insert(compileCCommand.end(), sanitizerFlags.begin(), sanitizerFlags.end()); + + compileCppCommand = compileCCommand; + compileCppCommand[1] = ninja::NinjaArg("c++"); + compileCppCommand.insert(compileCppCommand.end(), { + ninja::NinjaArg("-fexceptions"), + ninja::NinjaArg("-Wnon-virtual-dtor") + }); break; } - case OPT_LEV_RELEASE: + case Compiler::MSVC: { - switch (config.getCompiler()) + compileCCommand.insert(compileCCommand.end(), { + ninja::NinjaArg("cl.exe"), + ninja::NinjaArg("/c"), + ninja::NinjaArg("$in"), + ninja::NinjaArg("/Fo$out") + }); + + compileCCommand.insert(compileCCommand.end(), cflags.begin(), cflags.end()); + compileCCommand.insert(compileCCommand.end(), defines.begin(), defines.end()); + + if(config.showWarnings) + compileCCommand.push_back(ninja::NinjaArg("/Wall")); + else + compileCCommand.push_back(ninja::NinjaArg("/w")); + + switch (SYSTEM_PLATFORM) { - case Compiler::GCC: - { - optimizationFlags = "'-O3' '-DNDEBUG'"; + case PLATFORM_WIN32: + compileCCommand.push_back(ninja::NinjaArg("/MACHINE:X86")); break; - } - case Compiler::MSVC: - { - optimizationFlags = "/Ox /DNDEBUG"; + case PLATFORM_WIN64: + compileCCommand.push_back(ninja::NinjaArg("/MACHINE:X64")); break; - } } + + vector optimizationFlags = getCompilerOptimizationFlags(config); + compileCCommand.insert(compileCCommand.end(), optimizationFlags.begin(), optimizationFlags.end()); + + vector sanitizerFlags = getCompilerSanitizerFlags(config); + compileCCommand.insert(compileCCommand.end(), sanitizerFlags.begin(), sanitizerFlags.end()); + + compileCppCommand = compileCCommand; + compileCppCommand.push_back(ninja::NinjaArg("/EHs")); break; } } + vector cLanguageVersionFlags = getLanguageVersionFlag(config.getCompiler(), config.getCversion()); + compileCCommand.insert(compileCCommand.end(), cLanguageVersionFlags.begin(), cLanguageVersionFlags.end()); + + vector cppLanguageVersionFlags = getLanguageVersionFlag(config.getCompiler(), config.getCppVersion()); + compileCppCommand.insert(compileCppCommand.end(), cppLanguageVersionFlags.begin(), cppLanguageVersionFlags.end()); + + ninja::NinjaRule *compileCRule = ninjaBuildFile.createRule("compile_c", compileCCommand); + compileCRule->depFile = "$out.d"; + + ninja::NinjaRule *compileCppRule = ninjaBuildFile.createRule("compile_cpp", compileCppCommand); + compileCppRule->depFile = "$out.d"; + + bool usesCFiles = false; + bool usesCppFiles = false; + vector objectNames; objectNames.reserve(sourceFiles.size()); - for(const string &sourceFile : sourceFiles) + for(const sibs::SourceFile &sourceFile : sourceFiles) { - SourceFileLanguage sourceFileLanguage = SourceFileLanguage::CPP; - if(endsWith(sourceFile, ".c")) - sourceFileLanguage = SourceFileLanguage::C; - - const char *buildTarget; - switch(sourceFileLanguage) - { - case SourceFileLanguage::C: - buildTarget = "c_COMPILER"; - break; - case SourceFileLanguage::CPP: - buildTarget = "cpp_COMPILER"; - break; - } - - //string sourceFileEncoded = sourceFile; - //replace(sourceFileEncoded, '/', '@'); - string objectName = config.getPackageName() + "@exe/" + sourceFile; + string objectName = config.getPackageName() + "@exe/" + sourceFile.filepath; objectName += getObjectFileExtension(config.getCompiler()); - result += "build "; - result += objectName; - result += ": "; - result += buildTarget; - result += " ../../"; - result += sourceFile; - result += "\n"; - result += " ARGS = $globalIncDir "; - if(!defines.empty()) - result += defines; - - result += " "; - switch(sourceFileLanguage) - { - case SourceFileLanguage::C: - result += getLanguageVersionFlag(config.getCompiler(), config.getCversion()); - break; - case SourceFileLanguage::CPP: - result += getLanguageVersionFlag(config.getCompiler(), config.getCppVersion()); - break; - } - - switch (config.getCompiler()) + + switch(sourceFile.language) { - case Compiler::GCC: + case sibs::Language::C: { - // -Werror - // TODO: Find equivalent -MMD -MP for other compilers than gcc. MMD is used to create "dependency files" -> if headers are modified then source files will be recompiled - // when compiling next time... - result += " -fpie -MMD -MP '-I" + config.getPackageName() + "@exe' " + cflags + " '-I..'"; - if(config.showWarnings) - result += " -Wall -Wextra -Werror=return-type"; - else - result += " -w"; - result += " -fdiagnostics-show-option '-fdiagnostics-color=always' '-pipe' '-D_FILE_OFFSET_BITS=64' '-Winvalid-pch' -fstack-protector " + optimizationFlags; - if(sourceFileLanguage == SourceFileLanguage::CPP) - result += " -fexceptions -Wnon-virtual-dtor"; - - switch (config.getOptimizationLevel()) - { - case OPT_LEV_DEBUG: - result += " -g3 -D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS -fasynchronous-unwind-tables"; - break; - case OPT_LEV_RELEASE: - result += " -g0"; - break; - } - - if(config.getSanitize()) - { - result += " -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined"; - } + ninjaBuildFile.build(compileCRule, "../../" + sourceFile.filepath, objectName, {}); + usesCFiles = true; break; } - case Compiler::MSVC: + case sibs::Language::CPP: { - result += " "; - result += optimizationFlags; - result += " /EHs"; - if(config.showWarnings) - result += "/Wall "; - else - result += "/w "; - result += cflags; - switch (config.getOptimizationLevel()) - { - case OPT_LEV_DEBUG: - result += " /MTd"; - break; - case OPT_LEV_RELEASE: - result += " /MT"; - break; - } - switch (SYSTEM_PLATFORM) - { - case PLATFORM_WIN32: - result += " /MACHINE:X86"; - break; - case PLATFORM_WIN64: - result += " /MACHINE:X64"; - break; - } + ninjaBuildFile.build(compileCppRule, "../../" + sourceFile.filepath, objectName, {}); + usesCppFiles = true; break; } + default: + assert(false); + break; } - result += "\n\n"; + objectNames.emplace_back(objectName); } @@ -791,35 +740,62 @@ namespace backend { case LibraryType::EXECUTABLE: { - result += "build "; - result += config.getPackageName(); - result += ": " + buildJob + " "; - result += join(objectNames, " "); - result += "\n"; + vector buildExeArgs; switch (config.getCompiler()) { case Compiler::GCC: { - // TODO: Add flag to disable -ldl and -lm (dlopen, dlclose, floor, max, ...) - result += " LINK_ARGS = '-Wl,--no-undefined,--as-needed'"; + buildExeArgs.insert(buildExeArgs.end(), { + ninja::NinjaArg("ccache"), + ninja::NinjaArg(usesCppFiles ? "c++" : "cc"), + ninja::NinjaArg("-o"), + ninja::NinjaArg("$out"), + ninja::NinjaArg("$in"), + ninja::NinjaArg("-Wl,--no-undefined,--as-needed") + }); + if(config.getSanitize()) - result += " -lasan -lubsan"; - result += " -ldl -lm "; + { + buildExeArgs.insert(buildExeArgs.end(), { + ninja::NinjaArg("-lasan"), + ninja::NinjaArg("-lubsan") + }); + } + + // TODO: Add flag to disable -ldl and -lm (dlopen, dlclose, floor, max, ...) + buildExeArgs.insert(buildExeArgs.end(), { + ninja::NinjaArg("-ldl"), + ninja::NinjaArg("-lm") + }); break; } case Compiler::MSVC: { // TODO: Do not link all of these. Find a way to only link the ones that are needed - result += " LINK_ARGS = Ws2_32.lib Wldap32.lib Crypt32.lib Advapi32.lib Gdi32.lib User32.lib Userenv.lib OpenGL32.lib GlU32.lib Shell32.lib "; + buildExeArgs.insert(buildExeArgs.end(), { + ninja::NinjaArg("cl.exe"), + ninja::NinjaArg("$in"), + ninja::NinjaArg("/Fe$out"), + ninja::NinjaArg("Ws2_32.lib"), + ninja::NinjaArg("Wldap32.lib"), + ninja::NinjaArg("Crypt32.lib"), + ninja::NinjaArg("Advapi32.lib"), + ninja::NinjaArg("Gdi32.lib"), + ninja::NinjaArg("User32.lib"), + ninja::NinjaArg("Userenv.lib"), + ninja::NinjaArg("OpenGL32.lib"), + ninja::NinjaArg("GlU32.lib"), + ninja::NinjaArg("Shell32.lib") + }); break; } } if (!allLinkerFlags.empty()) - { - result += allLinkerFlags; - } - result += "\n\n"; + buildExeArgs.push_back(ninja::NinjaArg::createRaw(allLinkerFlags)); + + ninja::NinjaRule *buildExeRule = ninjaBuildFile.createRule("build_exec", buildExeArgs); + ninjaBuildFile.build(buildExeRule, join(objectNames, " "), config.getPackageName(), {}); projectGeneratedBinary += config.getPackageName(); #if OS_FAMILY == OS_FAMILY_WINDOWS @@ -829,30 +805,37 @@ namespace backend } case LibraryType::STATIC: { + vector buildStaticArgs; + string generatedFile; switch (config.getCompiler()) { case Compiler::GCC: { - result += "build lib"; - result += config.getPackageName(); - result += ".a: " + buildJob + " "; - result += join(objectNames, " "); - result += "\n\n"; - projectGeneratedBinary += "lib" + config.getPackageName() + ".a"; + generatedFile = "lib" + config.getPackageName() + ".a"; + buildStaticArgs.insert(buildStaticArgs.end(), { + ninja::NinjaArg::createRaw("ar"), + ninja::NinjaArg::createRaw("rcs"), + ninja::NinjaArg::createRaw("$out"), + ninja::NinjaArg::createRaw("$in") + }); break; } case Compiler::MSVC: { - result += "build "; - result += config.getPackageName(); - result += ".lib: " + buildJob + " "; - result += join(objectNames, " "); - result += "\n\n"; - projectGeneratedBinary += config.getPackageName() + ".lib"; + generatedFile = config.getPackageName() + ".lib"; + buildStaticArgs.insert(buildStaticArgs.end(), { + ninja::NinjaArg::createRaw("lib.exe"), + ninja::NinjaArg::createRaw("/OUT:$out"), + ninja::NinjaArg::createRaw("$in") + }); break; } } + + ninja::NinjaRule *buildStaticRule = ninjaBuildFile.createRule("build_static", buildStaticArgs); + ninjaBuildFile.build(buildStaticRule, join(objectNames, " "), generatedFile, {}); + projectGeneratedBinary += generatedFile; projectGeneratedBinary += "\""; if(parentProjStaticLinkerFlagCallbackFunc) parentProjStaticLinkerFlagCallbackFunc(projectGeneratedBinary); @@ -861,43 +844,68 @@ namespace backend } case LibraryType::DYNAMIC: { + vector buildDynamicArgs; + string generatedFile; switch (config.getCompiler()) { case Compiler::GCC: { - result += "build lib"; - result += config.getPackageName(); - result += ".so: " + buildJob + " "; - result += join(objectNames, " "); - result += "\n"; - result += " LINK_ARGS = '-Wl,--no-undefined,--as-needed'"; + generatedFile = "lib" + config.getPackageName() + ".so"; + + buildDynamicArgs.insert(buildDynamicArgs.end(), { + ninja::NinjaArg("ccache"), + ninja::NinjaArg(usesCppFiles ? "c++" : "cc"), + ninja::NinjaArg("$in"), + ninja::NinjaArg("-shared"), + ninja::NinjaArg("-o"), + ninja::NinjaArg("$out"), + ninja::NinjaArg("-Wl,--no-undefined,--as-needed") + }); + if(config.getSanitize()) - result += " -lasan -lubsan"; - result += " -ldl -lm "; - projectGeneratedBinary += "lib" + config.getPackageName() + ".so"; + { + buildDynamicArgs.insert(buildDynamicArgs.end(), { + ninja::NinjaArg("-lasan"), + ninja::NinjaArg("-lubsan") + }); + } + + // TODO: Add flag to disable -ldl and -lm (dlopen, dlclose, floor, max, ...) + buildDynamicArgs.insert(buildDynamicArgs.end(), { + ninja::NinjaArg("-ldl"), + ninja::NinjaArg("-lm") + }); break; } case Compiler::MSVC: { - result += "build "; - result += config.getPackageName(); - result += ".lib: " + buildJob + " "; - result += join(objectNames, " "); - result += "\n"; - // TODO: Do not link all of these. Find a way to only link the ones that are needed - result += " LINK_ARGS = Ws2_32.lib Wldap32.lib Crypt32.lib Advapi32.lib Gdi32.lib User32.lib Userenv.lib OpenGL32.lib GlU32.lib Shell32.lib "; - projectGeneratedBinary += config.getPackageName() + ".lib"; + generatedFile = config.getPackageName() + ".dll"; + buildDynamicArgs.insert(buildDynamicArgs.end(), { + ninja::NinjaArg::createRaw("lib.exe"), + ninja::NinjaArg::createRaw("/OUT:$out"), + ninja::NinjaArg::createRaw("$in"), + ninja::NinjaArg("Ws2_32.lib"), + ninja::NinjaArg("Wldap32.lib"), + ninja::NinjaArg("Crypt32.lib"), + ninja::NinjaArg("Advapi32.lib"), + ninja::NinjaArg("Gdi32.lib"), + ninja::NinjaArg("User32.lib"), + ninja::NinjaArg("Userenv.lib"), + ninja::NinjaArg("OpenGL32.lib"), + ninja::NinjaArg("GlU32.lib"), + ninja::NinjaArg("Shell32.lib") + }); break; } } if (!allLinkerFlags.empty()) - { - result += allLinkerFlags; - //result += " '-Wl,--no-whole-archive'"; - } - result += "\n\n"; + buildDynamicArgs.push_back(ninja::NinjaArg::createRaw(allLinkerFlags)); + + ninja::NinjaRule *buildDynamicRule = ninjaBuildFile.createRule("build_dynamic", buildDynamicArgs); + ninjaBuildFile.build(buildDynamicRule, join(objectNames, " "), generatedFile, {}); + projectGeneratedBinary += generatedFile; projectGeneratedBinary += "\""; if(parentProjDynamicLinkerFlagCallbackFunc) parentProjDynamicLinkerFlagCallbackFunc(projectGeneratedBinary); @@ -910,7 +918,7 @@ namespace backend } projectGeneratedBinaryFlags = allLinkerFlags + " " + projectGeneratedBinary; - + string result = ninjaBuildFile.generate(); Result fileOverwriteResult = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size())); if (fileOverwriteResult.isErr()) return fileOverwriteResult; @@ -983,19 +991,8 @@ namespace backend ninja.addGlobalIncludeDirs(parentExportIncludeDirs); if(!projectGeneratedBinaryFlags.empty()) ninja.addDependency(projectGeneratedBinaryFlags); - // TODO: Use same source file finder as in main.cpp - walkDirFilesRecursive(testSourceDirNative.c_str(), [&ninja, &sibsTestConfig](tinydir_file *file) - { - if (backend::BackendUtils::isSourceFile(file)) - { - string filePathUtf8 = toUtf8(file->path + sibsTestConfig.getProjectPath().size() + 1); - ninja.addSourceFile(filePathUtf8.c_str()); - } - else - { - //printf("Ignoring non-source file: %s\n", file->path + projectPath.size()); - } - }); + + backend::BackendUtils::collectSourceFiles(testSourceDirNative.c_str(), &ninja, sibsTestConfig, false); if(!ninja.getSourceFiles().empty()) { @@ -1014,7 +1011,7 @@ namespace backend if (!buildFileResult) return buildFileResult; - // Convenient to have project setup to tests as well + // Main projects test should also have compilation database, so we can use it inside IDE if(config.isMainProject()) { buildFileResult = buildCompilationDatabase(buildPath.c_str(), testSourceDirNative); diff --git a/backend/ninja/Ninja.hpp b/backend/ninja/Ninja.hpp index 68e51b1..8606708 100644 --- a/backend/ninja/Ninja.hpp +++ b/backend/ninja/Ninja.hpp @@ -42,11 +42,11 @@ namespace backend Ninja(); void addGlobalIncludeDirs(const std::string &globalIncludeDirs); - void addSourceFile(const char *filepath); + void addSourceFile(sibs::Language language, const char *filepath); void addTestSourceDir(const char *dir); void addDependency(const std::string &binaryFile); void addSubProject(Ninja *subProject, sibs::SibsConfig *config, sibs::FileString &&buildPath); - const std::vector& getSourceFiles() const; + const std::vector& getSourceFiles() const; sibs::Result build(const sibs::SibsConfig &config, const _tinydir_char_t *savePath, sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc = nullptr, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback = nullptr, sibs::GlobalIncludeDirCallbackFunc globalIncludeDirCallback = nullptr); private: sibs::Result buildSubProjects(sibs::LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, sibs::LinkerFlagCallbackFunc dynamicLinkerFlagCallback, sibs::GlobalIncludeDirCallbackFunc globalIncludeDirCallback); @@ -59,7 +59,7 @@ namespace backend sibs::Result buildCompilationDatabase(const _tinydir_char_t *buildFilePath, const sibs::FileString &savePath); private: std::string customGlobalIncludeDirs; - std::vector sourceFiles; + std::vector sourceFiles; std::vector testSourceDirs; std::vector binaryDependencies; std::vector subProjects; -- cgit v1.2.3