diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CmakeModule.cpp | 35 | ||||
-rw-r--r-- | src/Conf.cpp | 48 | ||||
-rw-r--r-- | src/FileUtil.cpp | 10 | ||||
-rw-r--r-- | src/GlobalLib.cpp | 6 | ||||
-rw-r--r-- | src/Linker.cpp | 53 | ||||
-rw-r--r-- | src/main.cpp | 184 |
6 files changed, 302 insertions, 34 deletions
diff --git a/src/CmakeModule.cpp b/src/CmakeModule.cpp index a706e64..62aaf34 100644 --- a/src/CmakeModule.cpp +++ b/src/CmakeModule.cpp @@ -194,11 +194,25 @@ namespace sibs _putenv("CXXFLAGS=-fPIC"); #endif #endif + std::vector<FileString> cmd = { cmakePath }; FileString cflags = TINYDIR_STRING("-fPIC"); FileString cxxflags; + // TODO: msvc + if(config.getCompiler() == Compiler::GCC || config.getCompiler() == Compiler::MINGW_W64) + { + switch(config.cpu) { + case sibs::CPU_INVALID: + case sibs::CPU_BASELINE: + break; + case sibs::CPU_NATIVE: + cflags += TINYDIR_STRING(" -march=native"); + break; + } + } + if(config.getCompiler() == Compiler::GCC || config.getCompiler() == Compiler::MINGW_W64) { cflags += TINYDIR_STRING(" -fno-omit-frame-pointer"); @@ -238,6 +252,11 @@ namespace sibs } } + if(config.linker == "mold") + cflags += TINYDIR_STRING(" -B/usr/lib/mold/"); + else if(!config.linker.empty()) + cflags += TINYDIR_STRING(" -fuse-ld=") + toFileString(config.linker); + cxxflags = cflags; cmd.push_back(TINYDIR_STRING("-DCMAKE_C_FLAGS=") + cflags); cmd.push_back(TINYDIR_STRING("-DCMAKE_CXX_FLAGS=") + cxxflags); @@ -299,14 +318,16 @@ namespace sibs return Result<bool>::Err(execResult); //nprintf("Compiling cmake generated ninja file: %s\n", buildPath.c_str()); - execResult = exec({ TINYDIR_STRING("ninja"), TINYDIR_STRING("-C"), buildPath }, true); - if(execResult.isOk()) - { - if(execResult.unwrap().exitCode != 0) - return Result<bool>::Err(execResult.unwrap().execStdout); + if(!config.skipCompile) { + execResult = exec({ TINYDIR_STRING("ninja"), TINYDIR_STRING("-C"), buildPath }, true); + if(execResult.isOk()) + { + if(execResult.unwrap().exitCode != 0) + return Result<bool>::Err(execResult.unwrap().execStdout); + } + else + return Result<bool>::Err(execResult); } - else - return Result<bool>::Err(execResult); if(config.getPackageType() != PackageType::EXECUTABLE) { diff --git a/src/Conf.cpp b/src/Conf.cpp index 543c8d9..964b9db 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -1125,9 +1125,13 @@ namespace sibs { cVersion = CVersion::C11; } + else if(cVersionStr.equals("c20")) + { + cVersion = CVersion::C20; + } else { - string errMsg = "Expected lang.c.version to be ansi, c89, c99 or c11, was "; + string errMsg = "Expected lang.c.version to be ansi, c89, c99, c11, c20, was "; errMsg += string(cVersionStr.data, cVersionStr.size); throw ParserException(errMsg); } @@ -1147,7 +1151,15 @@ namespace sibs if(fieldValue.isSingle()) { const StringView &cppVersionStr = fieldValue.asSingle(); - if(cppVersionStr.equals("c++11")) + if(cppVersionStr.equals("c++03")) + { + cppVersion = CPPVersion::CPP03; + } + else if(cppVersionStr.equals("c++98")) + { + cppVersion = CPPVersion::CPP98; + } + else if(cppVersionStr.equals("c++11")) { cppVersion = CPPVersion::CPP11; } @@ -1159,9 +1171,13 @@ namespace sibs { cppVersion = CPPVersion::CPP17; } + else if(cppVersionStr.equals("c++20")) + { + cppVersion = CPPVersion::CPP20; + } else { - string errMsg = "Expected lang.cpp.version to be c++11, c++14 or c++17, was "; + string errMsg = "Expected lang.cpp.version to be c++03, c++98, c++11, c++14, c++17 or c++20, was "; errMsg += string(cppVersionStr.data, cppVersionStr.size); throw ParserException(errMsg); } @@ -1169,6 +1185,24 @@ namespace sibs else throw ParserException("Expected lang.cpp.version to be a single value, was a list"); } + else if(fieldName.equals("enable_exceptions")) + { + if (fieldValue.isSingle()) + { + StringView value_str = fieldValue.asSingle(); + bool value_bool = false; + if(value_str.equals("true")) + value_bool = true; + else if(value_str.equals("false")) + value_bool = false; + else + throw ParserException("Expected " + string(currentObject.data, currentObject.size) + ".enable_exceptions to be either true or false"); + + enableExceptions = value_bool; + } + else + throw ParserException("Expected " + string(currentObject.data, currentObject.size) + ".enable_exceptions to be a single value, was a list"); + } else failInvalidFieldUnderObject(fieldName); } @@ -1399,6 +1433,14 @@ namespace sibs { parseDependencies(name, value); } + else if(currentObject.equals("lang.c")) + { + parseCLang(name, value); + } + else if(currentObject.equals("lang.cpp")) + { + parseCppLang(name, value); + } else { string errMsg = "project.conf: Expected category to be 'dependencies', was: '"; diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index e1142ec..02acfa4 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -141,6 +141,11 @@ namespace sibs else return FileType::FILE_NOT_FOUND; } + + bool fileExists(const _tinydir_char_t *path) { + struct stat64 fileStat; + return stat64(path, &fileStat) == 0; + } Result<u64> getFileLastModifiedTime(const _tinydir_char_t *path) { @@ -163,6 +168,11 @@ namespace sibs else return FileType::FILE_NOT_FOUND; } + + bool fileExists(const _tinydir_char_t *path) { + struct _stat64i32 fileStat; + return _wstat(path, &fileStat) == 0; + } Result<u64> getFileLastModifiedTime(const _tinydir_char_t *path) { diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index b390571..94145c1 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -156,6 +156,12 @@ namespace sibs sibsConfig.platform = parentConfig.platform; sibsConfig.packaging = parentConfig.packaging; sibsConfig.bundling = parentConfig.bundling; + sibsConfig.setSanitize(parentConfig.getSanitize()); + sibsConfig.cpu = parentConfig.cpu; + sibsConfig.linker = parentConfig.linker; + sibsConfig.use_lto = parentConfig.use_lto; + sibsConfig.skipCompile = parentConfig.skipCompile; + sibsConfig.include_debug_symbols_in_release = parentConfig.include_debug_symbols_in_release; Result<bool> result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); if (result.isErr()) return result; diff --git a/src/Linker.cpp b/src/Linker.cpp new file mode 100644 index 0000000..d03e3a4 --- /dev/null +++ b/src/Linker.cpp @@ -0,0 +1,53 @@ +#include "../include/Linker.hpp" +#include "../include/FileUtil.hpp" + +static void split_string(const std::string &str, char delimiter, std::function<bool(const char*,size_t)> callback) { + size_t index = 0; + while(index < str.size()) { + size_t end_index = str.find(delimiter, index); + if(end_index == std::string::npos) + end_index = str.size(); + + if(!callback(&str[index], end_index - index)) + break; + + index = end_index + 1; + } +} + +namespace sibs +{ + static bool is_linker_installed(const char *linker_binary_name) { + const char *path = getenv("PATH"); + if(!path) + return false; + + bool linker_found = false; + split_string(path, ':', [&](const char *str, size_t size) { + std::string fullpath(str, size); + fullpath += "/"; + fullpath += linker_binary_name; + + if(getFileType(fullpath.c_str()) == FileType::REGULAR) { + linker_found = true; + return false; + } + + return true; + }); + + return linker_found; + } + + bool is_gold_linker_installed() { + return is_linker_installed("ld.gold"); + } + + bool is_lld_linker_installed() { + return is_linker_installed("ld.lld"); + } + + bool is_mold_linker_installed() { + return is_linker_installed("ld.mold") && fileExists("/usr/lib/mold/ld"); + } +}
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5c3ad8c..0e216b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -129,15 +129,22 @@ static void usage() static void usageBuild(bool run) { - printf("Usage: sibs %s [project_path] [--debug|--release] [--sanitize=(address|undefined|leak|thread|none)] [--platform <platform>]\n\n", run ? "run" : "build"); + printf("Usage: sibs %s [project_path] [--debug|--release] [--sanitize=(address|undefined|leak|thread|none)] [--linker=lld|gold|mold] [--platform=<platform>] [--cpu=cpu]\n\n", run ? "run" : "build"); printf("%s a sibs project\n\n", run ? "Build and run" : "Build"); printf("Options:\n"); printf(" project_path The directory containing a project.conf file - Optional (default: current directory)\n"); printf(" --debug|--release Optimization level to build project and dependencies with (if not a system package) - Optional (default: --debug)\n"); - printf(" --sanitize Add runtime address/undefined behavior sanitization. Program can be up to 3 times slower and use 10 times as much RAM. Ignored if compiler doesn't support sanitization - Optional (default: none)\n"); - printf(" --platform The platform to build for - Optional (default: the running platform)\n"); + printf(" --sanitize=option Add runtime address/undefined behavior sanitization. Program can be up to 3 times slower and use 10 times as much RAM. Ignored if compiler doesn't support sanitization - Optional (default: none)\n"); + printf(" --platform=platform The platform to build for. Run \"sibs platforms\" to see a list of supported platforms - Optional (default: the running platform)\n"); printf(" --lto Use link-time optimization. May increase compile times - Optional (default: not used)\n"); printf(" --debug-symbols Include debug symbols in release mode - Optional (default: not used)\n"); + printf(" --linker=linker The linker to use. \"lld\", \"gold\" or \"mold\". \"gold\" - Optional (the compile automatically chooses best option)\n"); + printf(" --cpu=cpu The cpu to target. Should either be \"baseline\" or \"native\". \"baseline\" means to compile the binary to support a wide range of cpu's in the same architecture while \"native\" means to compile the binary specifically for your cpu, for better performance - Optional (default: native)\n"); + if(run) { + printf(" --args <args...> A list of arguments to run the program with\n"); + } else { + printf(" --skip-compile Skip compilation. This can be used to generate a compile_commands.json file without compiling. Note that the compile_commands.json can miss files that are generated when this option is used\n"); + } printf("Examples:\n"); printf(" sibs %s\n", run ? "run" : "build"); if(run) @@ -167,13 +174,20 @@ static void usageNew() static void usageTest() { - printf("Usage: sibs test [project_path] [--sanitize=(address|undefined|leak|thread|none)] [--file <filepath>...|--all-files]\n\n"); + printf("Usage: sibs test [project_path] [--debug|--release] [--sanitize=(address|undefined|leak|thread|none)] [--linker=lld|gold|mold] [--platform=<platform>] [--cpu=cpu] [--file <filepath>...|--all-files]\n\n"); printf("Build and run tests for a sibs project\n\n"); printf("Options:\n"); - printf(" project_path The directory containing a project.conf file - Optional (default: current directory)\n"); - printf(" --sanitize Add runtime address/undefined behavior sanitization. Program can be up to 3 times slower and use 10 times as much RAM. Ignored if compiler doesn't support sanitization - Optional (default: address)\n"); - printf(" --file Specify file to test, path to test file should be defined after this. Can be defined multiple times to test multiple files - Optional (default: not used), Only applicable for Zig\n"); - printf(" --all-files Test all files - Optional (default: not used), Only applicable for Zig\n"); + printf(" project_path The directory containing a project.conf file - Optional (default: current directory)\n"); + printf(" --debug|--release Optimization level to build project and dependencies with (if not a system package) - Optional (default: --debug)\n"); + printf(" --build|-b Build tests but don't run them\n"); + printf(" --sanitize=option Add runtime address/undefined behavior sanitization. Program can be up to 3 times slower and use 10 times as much RAM. Ignored if compiler doesn't support sanitization - Optional (default: none)\n"); + printf(" --file Specify file to test, path to test file should be defined after this. Can be defined multiple times to test multiple files - Optional (default: not used), Only applicable for Zig\n"); + printf(" --all-files Test all files - Optional (default: not used), Only applicable for Zig\n"); + printf(" --platform=platform The platform to build for. Run \"sibs platforms\" to see a list of supported platforms - Optional (default: the running platform)\n"); + printf(" --linker=linker The linker to use. \"lld\", \"gold\" or \"mold\". \"gold\" - Optional (the compile automatically chooses best option)\n"); + printf(" --cpu=cpu The cpu to target. Should either be \"baseline\" or \"native\". \"baseline\" means to compile the binary to support a wide range of cpu's in the same architecture while \"native\" means to compile the binary specifically for your cpu, for better performance - Optional (default: native)\n"); + printf(" --skip-compile Skip compilation. This can be used to generate a compile_commands.json file without compiling. Note that the compile_commands.json can miss files that are generated when this option is used. This option also skips running the tests\n"); + printf(" --args <args...> A list of arguments to run the program with\n"); printf("Examples:\n"); printf(" sibs test\n"); printf(" sibs test dirA/dirB\n"); @@ -368,6 +382,12 @@ static int buildProject(const FileString &projectPath, const FileString &project { FileString buildPath; readSibsConfig(projectPath, projectConfFilePath, sibsConfig, buildPath); + + if(run && sibsConfig.getPackageType() != PackageType::EXECUTABLE) { + ferr << "Error: sibs run can only be used with executable projects" << endl; + exit(7); + } + // Test project has the main project as dependency, and therefore the main project can't be built as an executable if(sibsConfig.shouldBuildTests()) { @@ -486,14 +506,26 @@ static Sanitize sanitize_string_to_type(const _tinydir_char_t *str) { return SANITIZE_INVALID; } +static CPU getCpuByName(const std::string &cpuName) { + if(cpuName == "baseline") + return CPU_BASELINE; + else if(cpuName == "native") + return CPU_NATIVE; + else + return CPU_INVALID; +} + static int buildProject(int argc, const _tinydir_char_t **argv, bool run) { OptimizationLevel optimizationLevel = OPT_LEV_NONE; FileString projectPath; Sanitize sanitize = Sanitize::NONE; + std::string linker; FileString platformName; + std::string cpuName; bool use_lto = false; bool include_debug_symbols_in_release = false; + bool skipCompile = false; std::vector<FileString> run_args; for(int i = 0; i < argc; ++i) @@ -527,29 +559,46 @@ static int buildProject(int argc, const _tinydir_char_t **argv, bool run) } else if(_tinydir_strncmp(arg, TINYDIR_STRING("--sanitize="), 11) == 0) { + if(sanitize != Sanitize::NONE) { + ferr << "Error: Sanitize defined more than once" << endl; + usageBuild(run); + } + sanitize = sanitize_string_to_type(arg + 11); if(sanitize == SANITIZE_INVALID) { ferr << "Error: Invalid sanitize option " << (arg + 11) << ", expected address, undefined, leak, thread or none" << endl; usageBuild(run); } } - else if(_tinydir_strcmp(arg, TINYDIR_STRING("--platform")) == 0) + else if(_tinydir_strncmp(arg, TINYDIR_STRING("--linker="), 9) == 0) { - if(i == argc - 1) - { - ferr << "Error: Expected platform to target after --platform" << endl; + if(!linker.empty()) { + ferr << "Error: Linker defined more than once" << endl; usageBuild(run); } - - ++i; - arg = argv[i]; - + linker = toUtf8(arg + 9); + } + else if(!run && _tinydir_strcmp(arg, TINYDIR_STRING("--skip-compile")) == 0) + { + skipCompile = true; + } + else if(_tinydir_strncmp(arg, TINYDIR_STRING("--platform="), 11) == 0) + { if(!platformName.empty()) { - ferr << "Error: Platform defined twice. First as " << platformName << " then as " << arg << endl; + ferr << "Error: Platform defined more than once" << endl; + usageBuild(run); + } + platformName = arg + 11; + } + else if(_tinydir_strncmp(arg, TINYDIR_STRING("--cpu="), 6) == 0) + { + if(!cpuName.empty()) + { + ferr << "Error: CPU defined more than once" << endl; usageBuild(run); } - platformName = arg; + cpuName = toUtf8(arg + 6); } else if(run && _tinydir_strcmp(arg, TINYDIR_STRING("--args")) == 0) { @@ -578,6 +627,9 @@ static int buildProject(int argc, const _tinydir_char_t **argv, bool run) if(platformName.empty()) platformName = toFileString(asString(SYSTEM_PLATFORM)); + if(cpuName.empty()) + cpuName = "native"; + string platformUtf8 = toUtf8(platformName); Platform platform = getPlatformByName(StringView(platformUtf8.data(), platformUtf8.size())); if(platform == PLATFORM_INVALID) @@ -587,6 +639,14 @@ static int buildProject(int argc, const _tinydir_char_t **argv, bool run) usageBuild(run); } + CPU cpu = getCpuByName(cpuName); + if(cpu == CPU_INVALID) + { + ferr << "Invalid cpu " << cpuName << endl; + ferr << "Expected one of: " << "baseline, cpu" << endl; + usageBuild(run); + } + bool crossCompileLinux64ToWin64 = (SYSTEM_PLATFORM == PLATFORM_LINUX_X86_64 && platform == PLATFORM_WIN64); if(platform != SYSTEM_PLATFORM && !crossCompileLinux64ToWin64) { @@ -628,36 +688,100 @@ static int buildProject(int argc, const _tinydir_char_t **argv, bool run) Compiler compiler = Compiler::MSVC; #endif + if(linker.empty()) { + // Static object files compiled with gcc are not compatible with lld (llvm linker) + // and we dont know which compiler was used to compile to code so we disable automatic + // use of lld by default. The user can still force lld with --linker=lld, if they are + // sure that they used clang to compile the code. + // TODO: Detect if linker has change and recompile everything (that was compiled with a different linker). + if(!use_lto && optimizationLevel != OPT_LEV_DEBUG && !include_debug_symbols_in_release) { + if(is_mold_linker_installed()) + linker = "mold"; + else if(is_lld_linker_installed()) + linker = "lld"; + else if(is_gold_linker_installed()) + linker = "gold"; + } else if(is_gold_linker_installed()) { + linker = "gold"; + } + } + SibsConfig sibsConfig(compiler, projectPath, optimizationLevel, false); sibsConfig.showWarnings = true; sibsConfig.platform = platform; + sibsConfig.cpu = cpu; sibsConfig.setSanitize(sanitize); + sibsConfig.linker = std::move(linker); sibsConfig.use_lto = use_lto; + sibsConfig.skipCompile = skipCompile; sibsConfig.include_debug_symbols_in_release = include_debug_symbols_in_release; return buildProject(projectPath, projectConfFilePath, sibsConfig, run, run_args); } static int testProject(int argc, const _tinydir_char_t **argv) { - if(argc > 2) - usageTest(); - + OptimizationLevel optimizationLevel = OPT_LEV_NONE; FileString projectPath; vector<FileString> filesToTest; bool testAllFiles = false; - Sanitize sanitize = Sanitize::ADDRESS; + Sanitize sanitize = Sanitize::NONE; + bool sanitize_defined = false; + bool buildOnly = false; + bool skipCompile = false; + std::string linker; + std::vector<FileString> run_args; for(int i = 0; i < argc; ++i) { const _tinydir_char_t *arg = argv[i]; - if(_tinydir_strncmp(arg, TINYDIR_STRING("--sanitize="), 11) == 0) + if(_tinydir_strcmp(arg, TINYDIR_STRING("--debug")) == 0) + { + if(optimizationLevel != OPT_LEV_NONE) + { + ferr << "Error: Optimization level defined more than once. First defined as " << asString(optimizationLevel) << " then as debug" << endl; + usageTest(); + } + optimizationLevel = OPT_LEV_DEBUG; + } + else if(_tinydir_strcmp(arg, TINYDIR_STRING("--release")) == 0) + { + if(optimizationLevel != OPT_LEV_NONE) + { + ferr << "Error: Optimization level defined more than once. First defined as " << asString(optimizationLevel) << " then as release" << endl; + usageTest(); + } + optimizationLevel = OPT_LEV_RELEASE; + } + else if(_tinydir_strncmp(arg, TINYDIR_STRING("--sanitize="), 11) == 0) { + if(sanitize_defined) { + ferr << "Error: Sanitize defined more than once" << endl; + usageTest(); + } + + sanitize_defined = true; sanitize = sanitize_string_to_type(arg + 11); if(sanitize == SANITIZE_INVALID) { ferr << "Error: Invalid sanitize option " << (arg + 11) << ", expected address, undefined, leak, thread or none" << endl; usageTest(); } } + else if(_tinydir_strncmp(arg, TINYDIR_STRING("--linker="), 9) == 0) + { + if(!linker.empty()) { + ferr << "Error: Linker defined more than once" << endl; + usageTest(); + } + linker = toUtf8(arg + 9); + } + else if(_tinydir_strcmp(arg, TINYDIR_STRING("--build")) == 0 || _tinydir_strcmp(arg, TINYDIR_STRING("-b")) == 0) + { + buildOnly = true; + } + else if(_tinydir_strcmp(arg, TINYDIR_STRING("--skip-compile")) == 0) + { + skipCompile = true; + } else if(_tinydir_strcmp(arg, TINYDIR_STRING("--file")) == 0) { if(i == argc - 1) @@ -691,6 +815,11 @@ static int testProject(int argc, const _tinydir_char_t **argv) usageTest(); } } + else if(_tinydir_strcmp(arg, TINYDIR_STRING("--args")) == 0) + { + run_args.insert(run_args.end(), argv + i + 1, argv + argc); + break; + } else if(_tinydir_strncmp(arg, TINYDIR_STRING("--"), 2) == 0) { ferr << "Error: Invalid argument " << arg << endl; @@ -706,6 +835,9 @@ static int testProject(int argc, const _tinydir_char_t **argv) projectPath = arg; } } + + if(optimizationLevel == OPT_LEV_NONE) + optimizationLevel = OPT_LEV_DEBUG; // TODO: If projectPath is not defined and working directory does not contain project.conf, then search every parent directory until one is found if(projectPath.empty()) @@ -773,11 +905,15 @@ static int testProject(int argc, const _tinydir_char_t **argv) Compiler compiler = Compiler::MSVC; #endif - SibsConfig sibsConfig(compiler, projectPath, OPT_LEV_DEBUG, true); + SibsConfig sibsConfig(compiler, projectPath, optimizationLevel, true); sibsConfig.showWarnings = true; sibsConfig.setSanitize(sanitize); + sibsConfig.linker =std::move(linker); sibsConfig.zigTestFiles = move(filesToTest); sibsConfig.zigTestAllFiles = testAllFiles; + sibsConfig.testsBuildOnly = buildOnly; + sibsConfig.skipCompile = skipCompile; + sibsConfig.testRunArgs = std::move(run_args); return buildProject(projectPath, projectConfFilePath, sibsConfig, false, {}); } |