aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--CMakeLists.txt1
-rw-r--r--README.md24
-rw-r--r--TODO3
-rw-r--r--backend/BackendUtils.cpp4
-rw-r--r--backend/ninja/Ninja.cpp104
-rw-r--r--include/Conf.hpp29
-rw-r--r--include/FileUtil.hpp1
-rw-r--r--include/Linker.hpp5
-rw-r--r--src/CmakeModule.cpp35
-rw-r--r--src/Conf.cpp48
-rw-r--r--src/FileUtil.cpp10
-rw-r--r--src/GlobalLib.cpp6
-rw-r--r--src/Linker.cpp53
-rw-r--r--src/main.cpp184
15 files changed, 451 insertions, 58 deletions
diff --git a/.gitmodules b/.gitmodules
index 03baf03..b830013 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "depends/libninja"]
path = depends/libninja
- url = git://git.dec05eba.com/libninja
+ url = https://repo.dec05eba.com/libninja
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 719e984..958db38 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,6 +20,7 @@ set(SOURCE_FILES
src/Platform.cpp
src/Version.cpp
src/VersionParser.cpp
+ src/Linker.cpp
depends/libninja/src/Ninja.cpp)
diff --git a/README.md b/README.md
index 17b4b0f..c2661c2 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ cd project-name
# Build the project in debug mode and run it
sibs run
# To compile with optimizations enabled, run:
-# sibs build --release
+sibs build --release
# and then the output binary is available in sibs-build/<platform-name>/release/project-name
```
@@ -67,7 +67,12 @@ On Arch Linux, sibs can alternatively be found on aur under the name `sibs-git`
## Windows
Use vcpkg to install the required dependencies and then generate visual studio (or another system) build files using CMakeLists.txt
# Usage
-After you have installed sibs, execute `sibs` without any arguments and you will get a list of commands and description for them. For debug builds, the created binary/library files will be located under `sibs-build/<platform>/debug`. For example on linux x86_64, the path for binaries would be: `sibs-build/linux_x86_64/debug`.
+After you have installed sibs, execute `sibs` without any arguments and you will get a list of commands and description for them. For debug builds, the created binary/library files will be located under `sibs-build/<platform>/debug`. For example on linux x86_64, the path for binaries would be: `sibs-build/linux_x86_64/debug`.\
+The current recommended way of using sibs is to add dependencies as git submodules and they will automatically be used. To use system dependencies use pkg-config.\
+You can list all your system packages with `pkg-config --list-all` (if the packages provide pkg-config files). To search for a package in this list you can use grep, like so: `pkg-config --list-all | grep 'pkg-name'` (replace pkg-name with the name of the package you want to search for).\
+To then show the version of the found package you can use `pkg-config --modversion pkg-name`.
+
+sibs will prefer mold linker in release mode if it's available and then the lld linker and then the gold linker. You can install mold/lld to improve compile times. Note that gdb debugging is a bit buggy when using mold/lld so use of mold/lld is only recommended in release mode.
# Quirks
Zig support has not been tested properly yet and currently always links to c library.
You can run zig tests with `sibs test --file filepath` or `sibs test --all-files`.
@@ -87,9 +92,9 @@ Users are required to manually install some libraries as they can't be included
This requirement might be removed later, if the gpu driver libraries required can somehow be detected and downloaded cross platform.
Libraries that are downloaded are available at: https://github.com/DEC05EBA/libraries
# Cross compilation
-Automatic cross compilation (`sibs build --platform <platform>`)currently only works from linux_x86_64 to win64 by using mingw-w64. You need to install `mingw-w64-gcc` and optionally `mingw-w64-pkg-config` if you want to use mingw-w64 system installed packages.
+Automatic cross compilation (`sibs build --platform <platform>`) currently only works from linux_x86_64 to win64 by using mingw-w64. You need to install `mingw-w64-gcc` and optionally `mingw-w64-pkg-config` if you want to use mingw-w64 system installed packages.
Cross compilation does currently not work if you have zig files as zig doesn't support libc when cross compiling at the moment.
-You can run `scripts/mingw_package.py` to automatically copy dynamic library dependencies of your executable to the same directory as the executable, so the library can be found when running the executable on windows; this also allows you to bundle your application and distribute it without external dependencies. To run `scripts/mingw_package.py` you need to install pefile python library `sudo pip install pefile`.
+You can run `scripts/mingw_package.py` to automatically copy dynamic library dependencies of your executable to the same directory as the executable, so the library can be found when running the executable on windows; this also allows you to bundle your application and distribute it without external dependencies. To run `scripts/mingw_package.py` you need to install pefile python library: `sudo pip install pefile`.
Manual cross compilation can be done by replacing c, c++ compilers and static library archiver (ar) using the environment variable CC, CXX and AR.
# IDE support
@@ -116,6 +121,7 @@ version = "c11"
[lang.cpp]
version = "c++14"
+enable_exceptions = "true"
[define]
BOOST_ASIO_SEPERATE_COMPILATION = "1"
@@ -194,8 +200,14 @@ The best way to do this is to create another git project for the dependency and
Using sub projects allows you to modify dependency and propagate changes to dependant project without pushing changes to remote git repository (faster development).
## lang.*
Optional. Allows you to change language specific configuration. \[lang.c] is for C and \[lang.cpp] is for C++.
-Version specifies the language version, for \[lang.c] the version can be ansi (alias for c89), c89, c99 or c11 - if not set, c11 will be used.
-For \[lang.cpp] the version can be c++11, c++14 or c++17 - if not set, c++14 will be used
+## lang.c
+### version
+Optional. The c standard version to use. Should be either ansi (alias for c89), c89, c99, c11 or c17. The default value is c11.
+## lang.cpp
+### version
+Optional. The c++ standard version to use. Should be either c++03, c++98, c++11, c++14, c++17 or c++20. The default value is c++14.
+### enable_exceptions
+Optional. This option should be either "true" or "false" and specifies if exceptions should be enabled for the project. The default value is "true".
## define
Optional. A list of definitions which are specified in name-value pairs where the name is the preprocessor to define (in c: #define name value)
## define.static
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..a34a75c
--- /dev/null
+++ b/TODO
@@ -0,0 +1,3 @@
+[dependencies] should automatically download dependency from vcpkg if available. This is useful for windows.
+Static libraries with dependencies should propagate their dependencies upwards to the first non-static library.
+If a dependency uses a c/c++ version newer than our project then give a compile error, or automatically upgrade our package version to the max version of the dependencies (and our project version). \ No newline at end of file
diff --git a/backend/BackendUtils.cpp b/backend/BackendUtils.cpp
index befd7ed..1109bf1 100644
--- a/backend/BackendUtils.cpp
+++ b/backend/BackendUtils.cpp
@@ -113,9 +113,13 @@ namespace backend
backend::Ninja *subProject = new backend::Ninja();
SibsConfig *subProjectConfig = new SibsConfig(sibsConfig.getCompiler(), file->path, sibsConfig.getOptimizationLevel(), false);
+ subProjectConfig->setSanitize(sibsConfig.getSanitize());
+ subProjectConfig->cpu = sibsConfig.cpu;
subProjectConfig->packaging = sibsConfig.packaging;
subProjectConfig->platform = sibsConfig.platform;
subProjectConfig->bundling = sibsConfig.bundling;
+ subProjectConfig->linker = sibsConfig.linker;
+ subProjectConfig->skipCompile = sibsConfig.skipCompile;
FileString subProjectBuildPath;
readSibsConfig(file->path, projectConfPath, *subProjectConfig, subProjectBuildPath);
diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp
index f4beeb8..802ffd8 100644
--- a/backend/ninja/Ninja.cpp
+++ b/backend/ninja/Ninja.cpp
@@ -122,6 +122,7 @@ namespace backend
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") };
+ case CVersion::C20: return { ninja::NinjaArg("-std=c20"), ninja::NinjaArg("-pedantic") };
}
break;
}
@@ -147,9 +148,12 @@ namespace backend
{
switch(cppVersion)
{
+ case CPPVersion::CPP03: return { ninja::NinjaArg("-std=c++03"), ninja::NinjaArg("-pedantic") };
+ case CPPVersion::CPP98: return { ninja::NinjaArg("-std=c++98"), ninja::NinjaArg("-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") };
+ case CPPVersion::CPP20: return { ninja::NinjaArg("-std=c++20"), ninja::NinjaArg("-pedantic") };
}
break;
}
@@ -158,9 +162,12 @@ namespace backend
switch(cppVersion)
{
// Use /Za flag?
+ case CPPVersion::CPP03: return { ninja::NinjaArg("/std:c++03") };
+ case CPPVersion::CPP98: return { ninja::NinjaArg("/std:c++98") };
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") };
+ case CPPVersion::CPP20: return { ninja::NinjaArg("/std:c++20") };
}
break;
}
@@ -372,6 +379,37 @@ namespace backend
return {};
}
+ static ninja::NinjaArg getCompilerCpuFlag(const SibsConfig &config)
+ {
+ // TODO: Also use -mcpu=native on non x86 systems
+ switch(config.getCompiler()) {
+ case Compiler::GCC:
+ case Compiler::MINGW_W64: {
+ switch(config.cpu) {
+ case sibs::CPU_INVALID:
+ case sibs::CPU_BASELINE:
+ return {};
+ case sibs::CPU_NATIVE:
+ return ninja::NinjaArg::createRaw("-march=native");
+ }
+ break;
+ }
+ case Compiler::MSVC: {
+ // msvc doesn't have any equivalent to gcc -march=native.
+ // TODO: Detect features at runtime here to manually add the cpu feature flags instead
+ switch(config.cpu) {
+ case sibs::CPU_INVALID:
+ case sibs::CPU_BASELINE:
+ return {};
+ case sibs::CPU_NATIVE:
+ return ninja::NinjaArg::createRaw("-march=native");
+ }
+ break;
+ }
+ }
+ return {};
+ }
+
static vector<ninja::NinjaArg> getCompilerOptimizationFlags(const SibsConfig &config)
{
switch (config.getCompiler())
@@ -802,8 +840,14 @@ namespace backend
// TODO: Allow configuring default linking flags. Maybe have `package.useThreads = false` to disable this flag
string allLinkerFlags;
- if(isSamePlatformFamily(config.platform, PLATFORM_LINUX))
- allLinkerFlags = "-pthread";
+
+ if(config.linker == "mold")
+ allLinkerFlags += " -B/usr/lib/mold/";
+ else if(!config.linker.empty())
+ allLinkerFlags += " -fuse-ld=" + config.linker;
+
+ if(isSamePlatformFamily(config.platform, PLATFORM_POSIX))
+ allLinkerFlags += " -pthread";
// TODO: Somehow check loading order, because it has to be correct to work.. Or does it for dynamic libraries?
// Anyways it's required for static libraries
@@ -1000,13 +1044,21 @@ namespace backend
vector<ninja::NinjaArg> optimizationFlags = getCompilerOptimizationFlags(config);
compileCCommand.insert(compileCCommand.end(), optimizationFlags.begin(), optimizationFlags.end());
+ for(auto &optFlag : optimizationFlags) {
+ allLinkerFlags += " " + optFlag.arg;
+ }
+
+ ninja::NinjaArg cpuFlag = getCompilerCpuFlag(config);
+ if(cpuFlag.type != ninja::NinjaArg::Type::NONE)
+ compileCCommand.push_back(std::move(cpuFlag));
+
ninja::NinjaArg sanitizerFlag = getCompilerSanitizerFlag(config);
if(sanitizerFlag.type != ninja::NinjaArg::Type::NONE)
compileCCommand.push_back(std::move(sanitizerFlag));
compileCppCommand = compileCCommand;
compileCppCommand.insert(compileCppCommand.end(), {
- ninja::NinjaArg("-fexceptions"),
+ ninja::NinjaArg(config.enableExceptions ? "-fexceptions" : "-fno-exceptions"),
ninja::NinjaArg("-Wnon-virtual-dtor")
});
@@ -1054,6 +1106,14 @@ namespace backend
vector<ninja::NinjaArg> optimizationFlags = getCompilerOptimizationFlags(config);
compileCCommand.insert(compileCCommand.end(), optimizationFlags.begin(), optimizationFlags.end());
+ for(auto &optFlag : optimizationFlags) {
+ allLinkerFlags += " " + optFlag.arg;
+ }
+
+ ninja::NinjaArg cpuFlag = getCompilerCpuFlag(config);
+ if(cpuFlag.type != ninja::NinjaArg::Type::NONE)
+ compileCCommand.push_back(std::move(cpuFlag));
+
ninja::NinjaArg sanitizerFlag = getCompilerSanitizerFlag(config);
if(sanitizerFlag.type != ninja::NinjaArg::Type::NONE)
compileCCommand.push_back(std::move(sanitizerFlag));
@@ -1304,6 +1364,7 @@ namespace backend
case LibraryType::EXECUTABLE:
{
vector<ninja::NinjaArg> buildExeArgs;
+ vector<ninja::NinjaArg> lastBuildExeArgs;
string executableName = config.getPackageName();
if(isSamePlatformFamily(config.platform, PLATFORM_WIN))
executableName += ".exe";
@@ -1374,7 +1435,7 @@ namespace backend
#else
if(!isSamePlatformFamily(config.platform, PLATFORM_WIN))
{
- buildExeArgs.insert(buildExeArgs.end(), {
+ lastBuildExeArgs.insert(lastBuildExeArgs.end(), {
ninja::NinjaArg::createRaw("-ldl"),
ninja::NinjaArg::createRaw("-lm")
});
@@ -1417,6 +1478,8 @@ namespace backend
if (!allLinkerFlags.empty())
buildExeArgs.push_back(ninja::NinjaArg::createRaw(allLinkerFlags));
+ buildExeArgs.insert(buildExeArgs.end(), lastBuildExeArgs.begin(), lastBuildExeArgs.end());
+
buildExeArgs.push_back(ninja::NinjaArg::createRaw(packagingFlags));
if(config.getCompiler() == Compiler::MINGW_W64)
{
@@ -1526,6 +1589,7 @@ namespace backend
case LibraryType::DYNAMIC:
{
vector<ninja::NinjaArg> buildDynamicArgs;
+ vector<ninja::NinjaArg> lastBuildDynamicArgs;
string generatedFile;
switch (config.getCompiler())
{
@@ -1606,7 +1670,7 @@ namespace backend
#else
if(!isSamePlatformFamily(config.platform, PLATFORM_WIN))
{
- buildDynamicArgs.insert(buildDynamicArgs.end(), {
+ lastBuildDynamicArgs.insert(lastBuildDynamicArgs.end(), {
ninja::NinjaArg::createRaw("-ldl"),
ninja::NinjaArg::createRaw("-lm")
});
@@ -1648,6 +1712,8 @@ namespace backend
if (!allLinkerFlags.empty())
buildDynamicArgs.push_back(ninja::NinjaArg::createRaw(allLinkerFlags));
+ buildDynamicArgs.insert(buildDynamicArgs.end(), lastBuildDynamicArgs.begin(), lastBuildDynamicArgs.end());
+
buildDynamicArgs.push_back(ninja::NinjaArg::createRaw(packagingFlags));
if(config.getCompiler() == Compiler::MINGW_W64)
{
@@ -1690,13 +1756,15 @@ namespace backend
if (fileOverwriteResult.isErr())
return fileOverwriteResult;
- Result<bool> buildResult = compile(savePath);
- if (!buildResult)
- return buildResult;
+ if(!config.skipCompile) {
+ Result<bool> buildResult = compile(savePath);
+ if (!buildResult)
+ return buildResult;
+ }
if((config.isMainProject() && !config.shouldBuildTests()) || config.isTest())
{
- buildResult = buildCompilationDatabase(savePath, config.getProjectPath());
+ Result<bool> buildResult = buildCompilationDatabase(savePath, config.getProjectPath());
if(!buildResult)
return buildResult;
}
@@ -1735,8 +1803,13 @@ namespace backend
SibsTestConfig sibsTestConfig(config.getCompiler(), testSourceDirNative.data, config.getOptimizationLevel());
sibsTestConfig.platform = config.platform;
sibsTestConfig.setSanitize(config.getSanitize());
- sibsTestConfig.zigTestFiles = move(config.zigTestFiles);
+ sibsTestConfig.cpu = config.cpu;
+ sibsTestConfig.zigTestFiles = config.zigTestFiles;
sibsTestConfig.zigTestAllFiles = config.zigTestAllFiles;
+ sibsTestConfig.cVersion = config.cVersion;
+ sibsTestConfig.cppVersion = config.cppVersion;
+ sibsTestConfig.linker = config.linker;
+ sibsTestConfig.skipCompile = config.skipCompile;
if(projectConfFileType == FileType::REGULAR)
{
Result<bool> result = Config::readFromFile(projectConfFilePath.data.c_str(), sibsTestConfig);
@@ -1818,9 +1891,16 @@ namespace backend
if (!buildFileResult)
return buildFileResult;
- if(!zigTest)
+ if(!zigTest && !config.testsBuildOnly)
{
- Result<ExecResult> runTestResult = exec({ Path(buildPath).join(toFileString(sibsTestConfig.getPackageName())).data }, true);
+ FileString executableName = toFileString(sibsTestConfig.getPackageName());
+ if(isSamePlatformFamily(sibsTestConfig.platform, PLATFORM_WIN))
+ executableName += TINYDIR_STRING(".exe");
+
+ std::vector<FileString> args = { Path(buildPath).join(executableName).data };
+ args.insert(args.end(), config.testRunArgs.begin(), config.testRunArgs.end());
+
+ Result<ExecResult> runTestResult = exec(args, true);
if(!runTestResult)
return Result<bool>::Err(runTestResult);
diff --git a/include/Conf.hpp b/include/Conf.hpp
index a93b44f..13fadcf 100644
--- a/include/Conf.hpp
+++ b/include/Conf.hpp
@@ -16,6 +16,13 @@
namespace sibs
{
+ enum CPU
+ {
+ CPU_INVALID,
+ CPU_BASELINE,
+ CPU_NATIVE
+ };
+
class ConfigValue
{
public:
@@ -116,14 +123,18 @@ namespace sibs
{
C89, // aka ansi
C99,
- C11
+ C11,
+ C20
};
enum class CPPVersion
{
+ CPP03,
+ CPP98,
CPP11,
CPP14,
- CPP17
+ CPP17,
+ CPP20
};
enum class Language
@@ -227,6 +238,7 @@ namespace sibs
sanitize(Sanitize::NONE),
showWarnings(false),
errorOnWarning(false),
+ enableExceptions(true),
zigTestAllFiles(false),
packaging(false),
bundling(false),
@@ -420,18 +432,27 @@ namespace sibs
std::vector<FileString> zigTestFiles;
bool showWarnings;
bool errorOnWarning;
+ bool enableExceptions;
bool zigTestAllFiles;
bool packaging;
bool bundling;
std::string versionStr;
PackageVersion version;
Platform platform;
+ CPU cpu = CPU_NATIVE;
bool use_lto;
bool include_debug_symbols_in_release;
std::vector<std::string> includeDirs;
std::vector<std::string> exposeIncludeDirs;
std::vector<std::string> ignoreDirs;
std::vector<std::string> libs;
+
+ CVersion cVersion;
+ CPPVersion cppVersion;
+ std::string linker;
+ bool testsBuildOnly = false;
+ bool skipCompile = false;
+ std::vector<FileString> testRunArgs;
protected:
virtual void processObject(StringView name) override;
virtual void processField(StringView name, const ConfigValue &value) override;
@@ -440,9 +461,9 @@ namespace sibs
virtual void finished() override;
void failInvalidFieldUnderObject(const StringView &fieldName) const;
void validatePackageTypeDefined() const;
- private:
void parseCLang(const StringView &fieldName, const ConfigValue &fieldValue);
void parseCppLang(const StringView &fieldName, const ConfigValue &fieldValue);
+ private:
void parsePlatformConfig(const StringView &fieldName, const ConfigValue &fieldValue);
std::string parsePlatformConfigStatic(const StringView &fieldName, const ConfigValue &fieldValue);
void parsePlatformConfigStaticDebug(const StringView &fieldName, const ConfigValue &fieldValue);
@@ -468,8 +489,6 @@ namespace sibs
std::vector<FileString> cmakeArgsGlobal;
std::vector<FileString> cmakeArgsStatic;
std::vector<FileString> cmakeArgsDynamic;
- CVersion cVersion;
- CPPVersion cppVersion;
bool useCmake;
bool buildTests;
bool finishedProcessing;
diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp
index 5bd4d33..2b4f3c5 100644
--- a/include/FileUtil.hpp
+++ b/include/FileUtil.hpp
@@ -81,6 +81,7 @@ namespace sibs
};
FileType getFileType(const _tinydir_char_t *path);
+ bool fileExists(const _tinydir_char_t *path);
void walkDir(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
void walkDirFiles(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
bool walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc);
diff --git a/include/Linker.hpp b/include/Linker.hpp
index acf31e4..59e39b3 100644
--- a/include/Linker.hpp
+++ b/include/Linker.hpp
@@ -2,12 +2,17 @@
#define SIBS_LINKER_HPP
#include <functional>
+#include <string>
namespace sibs
{
using LinkerFlagCallbackFunc = std::function<void(const std::string&)>;
using GlobalIncludeDirCallbackFunc = std::function<void(const std::string&)>;
using CflagsCallbackFunc = std::function<void(const std::string&)>;
+
+ bool is_gold_linker_installed();
+ bool is_lld_linker_installed();
+ bool is_mold_linker_installed();
}
#endif //SIBS_LINKER_HPP
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, {});
}