From 31899d0a48108515d13508b660fb3bb82b869430 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 30 Oct 2018 13:35:36 +0100 Subject: Add support for emscripten, fix compdb for tests --- README.md | 5 +- backend/ninja/Ninja.cpp | 125 ++++++++++++++++++++++++++++++++++-------------- include/Exec.hpp | 1 + include/env.hpp | 5 ++ src/Exec.cpp | 4 ++ 5 files changed, 103 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 111d2dd..b80260b 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,16 @@ 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 -Cross compilation currently only works from linux64 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 `)currently only works from linux64 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`. + +Manual cross compilation can be done by replacing c, c++ compilers and linker (ar) using the environment variable CC, CXX and AR. # IDE support Sibs generates a compile_commands.json in the project root directory when executing `sibs build` and tools that support clang completion can be used, such as YouCompleteMe or cquery. To generate compile_commands.json that also finds header files (non-relative) you need to have compdb installed and available in your PATH environment variable: https://github.com/Sarcasm/compdb There are several editors that support YouCompleteMe, including Vim, Emacs and Visual Studio Code. Visual studio code now also supports clang completion with C/C++ extension by Microsoft. I recommend using Visual Studio Code along with cquery (https://github.com/cquery-project/cquery/wiki), which gives you very good IDE support for your C/C++ projects: ![Image of cquery extension in Visual Studio Code](preview.png) +If you are using Visual Studio Code then you should add .vscode/ to .gitignore or Visual Studio Code will lag a lot (because cquery adds a lot of files in .vscode directory). # Tests If your project contains a sub directory called "tests" then that directory will be used a test project. The test directory may contain a project.conf file which can contain \[dependencies] block for specifying test only dependencies. The test automatically includes the parent project as a dependency. # Project configuration template diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index 6aad314..71e9c08 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -390,10 +390,9 @@ namespace backend static vector getCompilerSanitizerFlags(const SibsConfig &config) { - if(config.getCompiler() == Compiler::GCC && config.getSanitize()) + if(config.getCompiler() == Compiler::GCC || config.getCompiler() == Compiler::MINGW_W64) { return { - ninja::NinjaArg::createRaw("-fno-omit-frame-pointer"), ninja::NinjaArg::createRaw("-fsanitize=address"), ninja::NinjaArg::createRaw("-fsanitize=undefined") }; @@ -417,7 +416,8 @@ namespace backend ninja::NinjaArg::createRaw("-D_FORTIFY_SOURCE=2"), ninja::NinjaArg::createRaw("-D_GLIBCXX_ASSERTIONS"), ninja::NinjaArg::createRaw("-fasynchronous-unwind-tables"), - ninja::NinjaArg::createRaw("-D_DEBUG") + ninja::NinjaArg::createRaw("-D_DEBUG"), + ninja::NinjaArg::createRaw("-fno-omit-frame-pointer"), }; } case OPT_LEV_RELEASE: @@ -767,6 +767,10 @@ namespace backend static string getCompilerLinker(Compiler compiler) { + char *ar = std::getenv("AR"); + if(ar) + return ar; + string result; switch(compiler) { @@ -783,17 +787,54 @@ namespace backend return result; } + enum class RuntimeCompilerType + { + NONE, + OTHER, + CLANG, + EMSCRIPTEN + }; + Result Ninja::build(const SibsConfig &config, const _tinydir_char_t *savePath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { - bool isCCompilerClang = false; - Result cCompilerVersion = exec(TINYDIR_STRING("cc --version")); - if(cCompilerVersion && cCompilerVersion.unwrap().exitCode == 0 && cCompilerVersion.unwrap().execStdout.find("clang") != string::npos) - isCCompilerClang = true; + string cCompilerName = getCompilerCExecutable(config.getCompiler()); + string cppCompilerName = getCompilerCppExecutable(config.getCompiler()); + string compilerLinker = getCompilerLinker(config.getCompiler()); + + RuntimeCompilerType cCompilerType = RuntimeCompilerType::NONE; + Result cCompilerVersion = exec(toFileString(cCompilerName) + TINYDIR_STRING(" --version")); + if(cCompilerVersion && cCompilerVersion.unwrap().exitCode == 0) + { + if(cCompilerVersion.unwrap().execStdout.find("Emscripten") != string::npos) + cCompilerType = RuntimeCompilerType::EMSCRIPTEN; + else if(cCompilerVersion.unwrap().execStdout.find("clang") != string::npos) + cCompilerType = RuntimeCompilerType::CLANG; + else + cCompilerType = RuntimeCompilerType::OTHER; + } - bool isCppCompilerClang = false; - Result cppCompilerVersion = exec(TINYDIR_STRING("c++ --version")); - if(cppCompilerVersion && cppCompilerVersion.unwrap().exitCode == 0 && cppCompilerVersion.unwrap().execStdout.find("clang") != string::npos) - isCppCompilerClang = true; + RuntimeCompilerType cppCompilerType = RuntimeCompilerType::NONE; + Result cppCompilerVersion = exec(toFileString(cppCompilerName) + TINYDIR_STRING(" --version")); + if(cppCompilerVersion && cppCompilerVersion.unwrap().exitCode == 0) + { + if(cppCompilerVersion.unwrap().execStdout.find("Emscripten") != string::npos) + cppCompilerType = RuntimeCompilerType::EMSCRIPTEN; + else if(cppCompilerVersion.unwrap().execStdout.find("clang") != string::npos) + cppCompilerType = RuntimeCompilerType::CLANG; + else + cppCompilerType = RuntimeCompilerType::OTHER; + } + + if(cCompilerType != RuntimeCompilerType::NONE && cppCompilerType != RuntimeCompilerType::NONE && cCompilerType != cppCompilerType) + return Result::Err("The c and c++ compiler has to be of the same type"); + + RuntimeCompilerType compilerType = cCompilerType; + if(compilerType == RuntimeCompilerType::NONE) + compilerType = cppCompilerType; + + bool sanitize = config.getSanitize(); + if(compilerType == RuntimeCompilerType::EMSCRIPTEN) + sanitize = false; Result createBuildDirResult = createDirectoryRecursive(savePath); if (!createBuildDirResult) @@ -818,16 +859,16 @@ namespace backend if(config.packaging && !config.isMainProject()) libraryType = LibraryType::STATIC; + // TODO: Emscripten dynamic library support is not good at the moment, remove this once emscripten has been improved + if(libraryType == LibraryType::DYNAMIC && compilerType == RuntimeCompilerType::EMSCRIPTEN) + libraryType = LibraryType::STATIC; + string savePathUtf8 = toUtf8(savePath); string projectPathUtf8 = toUtf8(config.getProjectPath()); FileString ninjaBuildFilePath = savePath; ninjaBuildFilePath += TINYDIR_STRING("/build.ninja"); - string cCompilerName = getCompilerCExecutable(config.getCompiler()); - string cppCompilerName = getCompilerCppExecutable(config.getCompiler()); - string compilerLinker = getCompilerLinker(config.getCompiler()); - ninja::NinjaBuildFile ninjaBuildFile; string globalIncDir; @@ -1053,8 +1094,11 @@ namespace backend vector optimizationFlags = getCompilerOptimizationFlags(config); compileCCommand.insert(compileCCommand.end(), optimizationFlags.begin(), optimizationFlags.end()); - vector sanitizerFlags = getCompilerSanitizerFlags(config); - compileCCommand.insert(compileCCommand.end(), sanitizerFlags.begin(), sanitizerFlags.end()); + if(sanitize) + { + vector sanitizerFlags = getCompilerSanitizerFlags(config); + compileCCommand.insert(compileCCommand.end(), sanitizerFlags.begin(), sanitizerFlags.end()); + } compileCppCommand = compileCCommand; compileCppCommand[0] = ninja::NinjaArg::createRaw(cppCompilerName); @@ -1101,8 +1145,11 @@ namespace backend vector optimizationFlags = getCompilerOptimizationFlags(config); compileCCommand.insert(compileCCommand.end(), optimizationFlags.begin(), optimizationFlags.end()); - vector sanitizerFlags = getCompilerSanitizerFlags(config); - compileCCommand.insert(compileCCommand.end(), sanitizerFlags.begin(), sanitizerFlags.end()); + if(sanitize) + { + vector sanitizerFlags = getCompilerSanitizerFlags(config); + compileCCommand.insert(compileCCommand.end(), sanitizerFlags.begin(), sanitizerFlags.end()); + } compileCppCommand = compileCCommand; compileCppCommand.push_back(ninja::NinjaArg("/EHs")); @@ -1288,7 +1335,7 @@ namespace backend case Compiler::MINGW_W64: case Compiler::GCC: { - packagingFlags = "-static -static-libgcc -static-libstdc++"; + packagingFlags = "-static-libgcc -static-libstdc++ -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic"; break; } case Compiler::MSVC: @@ -1319,10 +1366,11 @@ namespace backend string noUndefinedFlag; if(!onlyZigFiles) { + bool isCompilerClangLike = (compilerType == RuntimeCompilerType::CLANG) || (compilerType == RuntimeCompilerType::EMSCRIPTEN); if(usesCppFiles) - noUndefinedFlag = isCppCompilerClang ? "-Wl,-undefined,error" : "-Wl,--no-undefined,--as-needed"; + noUndefinedFlag = isCompilerClangLike ? "-Wl,-undefined,error" : "-Wl,--no-undefined,--as-needed"; else - noUndefinedFlag = isCCompilerClang ? "-Wl,-undefined,error" : "-Wl,--no-undefined,--as-needed"; + noUndefinedFlag = isCompilerClangLike ? "-Wl,-undefined,error" : "-Wl,--no-undefined,--as-needed"; } ninja::NinjaVariable zigObjectArgs("object_args"); @@ -1371,10 +1419,15 @@ namespace backend ninja::NinjaArg::createRaw(noUndefinedFlag) }); + if(compilerType == RuntimeCompilerType::EMSCRIPTEN) + { + buildExeArgs.push_back(ninja::NinjaArg::createRaw("-s MAIN_MODULE=1")); + } + if(!rpath.empty()) buildExeArgs.push_back(ninja::NinjaArg("-Wl,-rpath," + rpath)); - if(config.getSanitize()) + if(sanitize) { buildExeArgs.insert(buildExeArgs.end(), { ninja::NinjaArg::createRaw("-lasan"), @@ -1451,7 +1504,7 @@ namespace backend ninja::NinjaArg::createRaw("-luserenv"), ninja::NinjaArg::createRaw("-lopengl32"), ninja::NinjaArg::createRaw("-lglu32"), - ninja::NinjaArg::createRaw("-Wl,-Bstatic -lwinpthread"), + ninja::NinjaArg::createRaw("-Wl,-Bstatic -lwinpthread -Wl,-Bdynamic"), ninja::NinjaArg::createRaw("-lshell32") }); } @@ -1551,7 +1604,10 @@ namespace backend case Compiler::MINGW_W64: case Compiler::GCC: { - generatedFile = "lib" + config.getPackageName() + "." + CONFIG_DYNAMIC_LIB_FILE_EXTENSION; + const char *fileExtension = CONFIG_DYNAMIC_LIB_FILE_EXTENSION; + if(compilerType == RuntimeCompilerType::EMSCRIPTEN) + fileExtension = "wasm"; + generatedFile = "lib" + config.getPackageName() + "." + fileExtension; break; } case Compiler::MSVC: @@ -1594,7 +1650,12 @@ namespace backend ninja::NinjaArg::createRaw(noUndefinedFlag) }); - if(config.getSanitize()) + if(compilerType == RuntimeCompilerType::EMSCRIPTEN) + { + buildDynamicArgs.push_back(ninja::NinjaArg::createRaw("-s SIDE_MODULE=1")); + } + + if(sanitize) { buildDynamicArgs.insert(buildDynamicArgs.end(), { ninja::NinjaArg::createRaw("-lasan"), @@ -1665,7 +1726,7 @@ namespace backend ninja::NinjaArg::createRaw("-luserenv"), ninja::NinjaArg::createRaw("-lopengl32"), ninja::NinjaArg::createRaw("-lglu32"), - ninja::NinjaArg::createRaw("-Wl,-Bstatic -lwinpthread"), + ninja::NinjaArg::createRaw("-Wl,-Bstatic -lwinpthread -Wl,-Bdynamic"), ninja::NinjaArg::createRaw("-lshell32") }); } @@ -1697,7 +1758,7 @@ namespace backend if (!buildResult) return buildResult; - if(config.isMainProject()) + if((config.isMainProject() && !config.shouldBuildTests()) || config.isTest()) { buildResult = buildCompilationDatabase(savePath, config.getProjectPath()); if(!buildResult) @@ -1820,14 +1881,6 @@ namespace backend Result buildFileResult = ninja.build(sibsTestConfig, buildPath.c_str()); if (!buildFileResult) return buildFileResult; - - // Main projects test should also have compilation database, so we can use it inside IDE - if(config.isMainProject()) - { - buildFileResult = buildCompilationDatabase(buildPath.c_str(), testSourceDirNative); - if(!buildFileResult) - return buildFileResult; - } if(!zigTest) { diff --git a/include/Exec.hpp b/include/Exec.hpp index 9996073..93ce307 100644 --- a/include/Exec.hpp +++ b/include/Exec.hpp @@ -14,6 +14,7 @@ namespace sibs }; Result exec(const _tinydir_char_t *cmd, bool print = false); + Result exec(const FileString &cmd, bool print = false); } #endif //SIBS_EXEC_HPP diff --git a/include/env.hpp b/include/env.hpp index 72b7828..b25cb69 100644 --- a/include/env.hpp +++ b/include/env.hpp @@ -59,6 +59,11 @@ #define OS_TYPE OS_TYPE_LINUX #endif +#ifdef __EMSCRIPTEN__ + #define OS_FAMILY OS_FAMILY_POSIX + #define OS_TYPE OS_TYPE_LINUX +#endif + #if defined(__GNUC__) #if defined(__x86_64__) || defined(__pc64__) #define SIBS_ENV_64BIT diff --git a/src/Exec.cpp b/src/Exec.cpp index cfeb19d..cc82897 100644 --- a/src/Exec.cpp +++ b/src/Exec.cpp @@ -139,4 +139,8 @@ namespace sibs return Result::Err(errMsg); } #endif + Result exec(const FileString &cmd, bool print) + { + return exec(cmd.c_str(), print); + } } -- cgit v1.2.3