diff options
-rw-r--r-- | backend/BackendUtils.cpp | 51 | ||||
-rw-r--r-- | backend/BackendUtils.hpp | 6 | ||||
-rw-r--r-- | backend/ninja/Ninja.cpp | 223 | ||||
m--------- | depends/libninja | 0 | ||||
-rw-r--r-- | include/Conf.hpp | 36 | ||||
-rw-r--r-- | include/Exec.hpp | 4 | ||||
-rw-r--r-- | include/FileUtil.hpp | 24 | ||||
-rw-r--r-- | include/GlobalLib.hpp | 4 | ||||
-rw-r--r-- | include/PkgConfig.hpp | 9 | ||||
-rw-r--r-- | src/CmakeModule.cpp | 53 | ||||
-rw-r--r-- | src/Conf.cpp | 26 | ||||
-rw-r--r-- | src/Exec.cpp | 173 | ||||
-rw-r--r-- | src/GlobalLib.cpp | 89 | ||||
-rw-r--r-- | src/PkgConfig.cpp | 83 | ||||
-rw-r--r-- | src/main.cpp | 78 | ||||
-rw-r--r-- | tests/src/confTest/confTest.cpp | 24 |
16 files changed, 399 insertions, 484 deletions
diff --git a/backend/BackendUtils.cpp b/backend/BackendUtils.cpp index 158cd12..befd7ed 100644 --- a/backend/BackendUtils.cpp +++ b/backend/BackendUtils.cpp @@ -6,9 +6,9 @@ using namespace std; using namespace sibs; -static const char *cCompilerPath = nullptr; -static const char *cppCompilerPath = nullptr; -static const char *linkerPath = nullptr; +static std::vector<FileString> cCompilerPath; +static std::vector<FileString> cppCompilerPath; +static std::vector<FileString> linkerPath; namespace backend { @@ -108,7 +108,6 @@ namespace backend #endif projectConfPath += TINYDIR_STRING("project.conf"); - auto projectConfFileType = getFileType(projectConfPath.c_str()); if(!sibsConfig.isTest() && getFileType(projectConfPath.c_str()) == FileType::REGULAR) { backend::Ninja *subProject = new backend::Ninja(); @@ -131,82 +130,82 @@ namespace backend }); } - string BackendUtils::getCompilerCExecutable(Compiler compiler) + std::vector<sibs::FileString> BackendUtils::getCompilerCExecutable(Compiler compiler) { - if(cCompilerPath) + if(!cCompilerPath.empty()) return cCompilerPath; char *cc = std::getenv("CC"); if(cc) { - cCompilerPath = cc; + cCompilerPath = { toFileString(cc) }; return cCompilerPath; } switch(compiler) { case Compiler::GCC: - cCompilerPath = "ccache cc"; + cCompilerPath = { TINYDIR_STRING("ccache"), TINYDIR_STRING("cc") }; break; case Compiler::MINGW_W64: - cCompilerPath = "x86_64-w64-mingw32-cc"; + cCompilerPath = { TINYDIR_STRING("x86_64-w64-mingw32-cc") }; break; case Compiler::MSVC: - cCompilerPath = "cl.exe"; + cCompilerPath = { TINYDIR_STRING("cl.exe") }; break; } return cCompilerPath; } - string BackendUtils::getCompilerCppExecutable(Compiler compiler) + std::vector<sibs::FileString> BackendUtils::getCompilerCppExecutable(Compiler compiler) { - if(cppCompilerPath) + if(!cppCompilerPath.empty()) return cppCompilerPath; char *cxx = std::getenv("CXX"); if(cxx) { - cppCompilerPath = cxx; + cppCompilerPath = { toFileString(cxx) }; return cppCompilerPath; } switch(compiler) { case Compiler::GCC: - cppCompilerPath = "ccache c++"; + cppCompilerPath = { TINYDIR_STRING("ccache"), TINYDIR_STRING("c++") }; break; case Compiler::MINGW_W64: - cppCompilerPath = "x86_64-w64-mingw32-c++"; + cppCompilerPath = { TINYDIR_STRING("x86_64-w64-mingw32-c++") }; break; case Compiler::MSVC: - cppCompilerPath = "cl.exe"; + cppCompilerPath = { TINYDIR_STRING("cl.exe") }; break; } return cppCompilerPath; } - string BackendUtils::getCompilerLinker(Compiler compiler) + std::vector<sibs::FileString> BackendUtils::getCompilerLinker(Compiler compiler) { - if(linkerPath) + if(!linkerPath.empty()) return linkerPath; char *ar = std::getenv("AR"); if(ar) { - linkerPath = ar; + linkerPath = { toFileString(ar) }; return linkerPath; } switch(compiler) { case Compiler::GCC: - linkerPath = "ar"; + linkerPath = { TINYDIR_STRING("ar") }; break; case Compiler::MINGW_W64: - linkerPath = "x86_64-w64-mingw32-ar"; + linkerPath = { TINYDIR_STRING("x86_64-w64-mingw32-ar") }; break; case Compiler::MSVC: - linkerPath = "lib.exe"; + linkerPath = { TINYDIR_STRING("lib.exe") }; break; } return linkerPath; @@ -215,7 +214,9 @@ namespace backend RuntimeCompilerType BackendUtils::getCCompilerType(Compiler compiler) { RuntimeCompilerType cCompilerType = RuntimeCompilerType::NONE; - Result<ExecResult> cCompilerVersion = exec(toFileString(getCompilerCExecutable(compiler)) + TINYDIR_STRING(" --version")); + std::vector<FileString> args = getCompilerCExecutable(compiler); + args.push_back(TINYDIR_STRING("--version")); + Result<ExecResult> cCompilerVersion = exec(args); if(cCompilerVersion && cCompilerVersion.unwrap().exitCode == 0) { if(cCompilerVersion.unwrap().execStdout.find("Emscripten") != string::npos) @@ -231,7 +232,9 @@ namespace backend RuntimeCompilerType BackendUtils::getCppCompilerType(Compiler compiler) { RuntimeCompilerType cppCompilerType = RuntimeCompilerType::NONE; - Result<ExecResult> cppCompilerVersion = exec(toFileString(getCompilerCppExecutable(compiler)) + TINYDIR_STRING(" --version")); + std::vector<FileString> args = getCompilerCppExecutable(compiler); + args.push_back(TINYDIR_STRING("--version")); + Result<ExecResult> cppCompilerVersion = exec(args); if(cppCompilerVersion && cppCompilerVersion.unwrap().exitCode == 0) { if(cppCompilerVersion.unwrap().execStdout.find("Emscripten") != string::npos) diff --git a/backend/BackendUtils.hpp b/backend/BackendUtils.hpp index d53620c..9992e5c 100644 --- a/backend/BackendUtils.hpp +++ b/backend/BackendUtils.hpp @@ -21,9 +21,9 @@ namespace backend static sibs::Language getFileLanguage(const _tinydir_char_t *extension); static sibs::Language getFileLanguage(tinydir_file *file); static void collectSourceFiles(const _tinydir_char_t *projectPath, Ninja *ninjaProject, const sibs::SibsConfig &sibsConfig, bool recursive = true); - static std::string getCompilerCExecutable(sibs::Compiler compiler); - static std::string getCompilerCppExecutable(sibs::Compiler compiler); - static std::string getCompilerLinker(sibs::Compiler compiler); + static std::vector<sibs::FileString> getCompilerCExecutable(sibs::Compiler compiler); + static std::vector<sibs::FileString> getCompilerCppExecutable(sibs::Compiler compiler); + static std::vector<sibs::FileString> getCompilerLinker(sibs::Compiler compiler); static RuntimeCompilerType getCCompilerType(sibs::Compiler compiler); static RuntimeCompilerType getCppCompilerType(sibs::Compiler compiler); }; diff --git a/backend/ninja/Ninja.cpp b/backend/ninja/Ninja.cpp index ea3d96f..be78ba1 100644 --- a/backend/ninja/Ninja.cpp +++ b/backend/ninja/Ninja.cpp @@ -40,43 +40,6 @@ namespace backend return result; } - static string join(const vector<string> &list, const char *joinStr) - { - if(list.empty()) return ""; - string result; - long stringSize = 0; - long joinStrLen = strlen(joinStr); - int i = 0; - for(const string &str : list) - { - stringSize += str.size(); - if(!str.empty() && i > 0) - stringSize += joinStrLen; - ++i; - } - - result.reserve(stringSize); - - i = 0; - for(const string &str : list) - { - if(i > 0) - result += joinStr; - result += str; - ++i; - } - - return result; - } - - static bool endsWith(const string &str, const string &endWithStr) - { - if(endWithStr.size() > str.size()) - return false; - else - return strncmp(&str[str.size() - endWithStr.size()], &endWithStr[0], endWithStr.size()) == 0; - } - static Ninja::LibraryType getNinjaLibraryType(PackageType packageType) { switch(packageType) @@ -147,31 +110,6 @@ namespace backend return ""; } - static string getObjectFileNameFlag(Compiler compiler, const string &objectFileName) - { - string result; - switch (compiler) - { - case Compiler::MINGW_W64: - case Compiler::GCC: - { - result = "-o "; - result += objectFileName; - break; - } - case Compiler::MSVC: - { - result = "/Fo"; - result += objectFileName; - break; - } - default: - assert(false); - break; - } - return result; - } - static vector<ninja::NinjaArg> getLanguageVersionFlag(Compiler compiler, CVersion cVersion) { switch (compiler) @@ -320,22 +258,20 @@ namespace backend Result<bool> Ninja::getLinkerFlags(const SibsConfig &config, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback, CflagsCallbackFunc cflagsCallbackFunc) const { - const vector<PackageListDependency*> &packageListDependencies = config.getPackageListDependencies(); + const vector<PackageListDependency> &packageListDependencies = config.getPackageListDependencies(); Result<FileString> globalLibDirResult = getHomeDir(); if (!globalLibDirResult) return Result<bool>::Err(globalLibDirResult); - FileString globalLibDir = globalLibDirResult.unwrap(); - globalLibDir += TINYDIR_STRING("/.cache/sibs/lib/"); - globalLibDir += toFileString(asString(config.platform)); - Result<bool> createGlobalLibDirResult = createDirectoryRecursive(globalLibDir.c_str()); + Path globalLibDir = Path(globalLibDirResult.unwrap()).join(".cache/sibs/lib").join(toFileString(asString(config.platform))); + Result<bool> createGlobalLibDirResult = createDirectoryRecursive(globalLibDir.data.c_str()); if(createGlobalLibDirResult.isErr()) return createGlobalLibDirResult; // If pkg-config is not available then it will be ignored and we check if package instead exists as a global lib (on github etc) - vector<PackageListDependency*> globalLibDependencies; - vector<PackageListDependency*> pkgConfigDependencies; - for(PackageListDependency *dependency : packageListDependencies) + vector<PackageListDependency> globalLibDependencies; + vector<PackageListDependency> pkgConfigDependencies; + for(const PackageListDependency &dependency : packageListDependencies) { // PkgConfig libraries, even the static ones are most likely not built statically against libgcc/libc++, so we don't use them if(!config.packaging && PkgConfig::validatePkgConfigPackageVersionExists(dependency)) @@ -353,7 +289,7 @@ namespace backend { printf("%s, using global lib...\n", pkgConfigFlagsResult.getErrMsg().c_str()); globalLibDependencies.reserve(globalLibDependencies.size() + pkgConfigDependencies.size()); - for (PackageListDependency *pkgConfigDependency : pkgConfigDependencies) + for (const PackageListDependency &pkgConfigDependency : pkgConfigDependencies) { globalLibDependencies.push_back(pkgConfigDependency); } @@ -368,7 +304,7 @@ namespace backend cflagsCallbackFunc(pkgConfigFlag.cflags); } - return GlobalLib::getLibs(globalLibDependencies, config, globalLibDir, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback); + return GlobalLib::getLibs(globalLibDependencies, config, globalLibDir.data, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallback, globalIncludeDirCallback); } Result<bool> Ninja::buildSubProjects(LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) @@ -764,9 +700,9 @@ namespace backend Result<bool> Ninja::build(const SibsConfig &config, const _tinydir_char_t *savePath, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallback, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { - string cCompilerName = BackendUtils::getCompilerCExecutable(config.getCompiler()); - string cppCompilerName = BackendUtils::getCompilerCppExecutable(config.getCompiler()); - string compilerLinker = BackendUtils::getCompilerLinker(config.getCompiler()); + std::vector<FileString> cCompilerName = BackendUtils::getCompilerCExecutable(config.getCompiler()); + std::vector<FileString> cppCompilerName = BackendUtils::getCompilerCppExecutable(config.getCompiler()); + std::vector<FileString> compilerLinker = BackendUtils::getCompilerLinker(config.getCompiler()); RuntimeCompilerType cCompilerType = BackendUtils::getCCompilerType(config.getCompiler()); RuntimeCompilerType cppCompilerType = BackendUtils::getCppCompilerType(config.getCompiler()); @@ -786,19 +722,17 @@ namespace backend if (!createBuildDirResult) return createBuildDirResult; - FileString generatedHeadersDir = config.getProjectPath() + TINYDIR_STRING("/sibs-build/"); - generatedHeadersDir += toFileString(asString(config.platform)) + TINYDIR_STRING("/"); - generatedHeadersDir += TINYDIR_STRING("generated-headers"); - Result<bool> createGeneratedHeadersDirResult = createDirectoryRecursive(generatedHeadersDir.c_str()); + Path generatedHeadersDir = Path(config.getProjectPath()).join(TINYDIR_STRING("sibs-build")).join(toFileString(asString(config.platform))).join(TINYDIR_STRING("generated-headers")); + Result<bool> createGeneratedHeadersDirResult = createDirectoryRecursive(generatedHeadersDir.data.c_str()); if (!createGeneratedHeadersDirResult) return createGeneratedHeadersDirResult; - FileString generatedZigHeadersDir = generatedHeadersDir + TINYDIR_STRING("/zig"); - Result<bool> createGeneratedZigHeadersDirResult = createDirectory(generatedZigHeadersDir.c_str()); + Path generatedZigHeadersDir = Path(generatedHeadersDir).join(TINYDIR_STRING("zig")); + Result<bool> createGeneratedZigHeadersDirResult = createDirectory(generatedZigHeadersDir.data.c_str()); if (!createGeneratedZigHeadersDirResult) return createGeneratedZigHeadersDirResult; - string generatedZigHeaderDirUtf8 = toUtf8(generatedZigHeadersDir); + string generatedZigHeaderDirUtf8 = toUtf8(generatedZigHeadersDir.data); LibraryType libraryType = getNinjaLibraryType(config.getPackageType()); // TODO: Instead of statically linking everything, maybe we should build everything as they prefer to be built @@ -814,8 +748,7 @@ namespace backend string savePathUtf8 = toUtf8(savePath); string projectPathUtf8 = toUtf8(config.getProjectPath()); - FileString ninjaBuildFilePath = savePath; - ninjaBuildFilePath += TINYDIR_STRING("/build.ninja"); + Path ninjaBuildFilePath = Path(savePath).join(TINYDIR_STRING("build.ninja")); ninja::NinjaBuildFile ninjaBuildFile; @@ -929,7 +862,7 @@ namespace backend } globalIncDir += dependencyExportIncludeDirs; - globalIncDir += " " + getIncludeOptionFlag(config.getCompiler(), toUtf8(generatedHeadersDir)); + globalIncDir += " " + getIncludeOptionFlag(config.getCompiler(), toUtf8(generatedHeadersDir.data)); ninjaBuildFile.defineGlobalVariable("globalIncDir", globalIncDir); // TODO: Find a better way to convert includes, this could be slow... ninjaBuildFile.defineGlobalVariable("globalIncDirZig", convertCIncludeSyntaxToZigInclude(config.getCompiler(), globalIncDir)); @@ -992,7 +925,6 @@ namespace backend case LibraryType::EXECUTABLE: { baseCompileCArgs.insert(baseCompileCArgs.end(), { - ninja::NinjaArg::createRaw(cCompilerName), ninja::NinjaArg("-c"), ninja::NinjaArg::createRaw("-fPIE -fPIC"), ninja::NinjaArg("$in"), @@ -1005,7 +937,6 @@ namespace backend case LibraryType::DYNAMIC: { baseCompileCArgs.insert(baseCompileCArgs.end(), { - ninja::NinjaArg::createRaw(cCompilerName), ninja::NinjaArg("-c"), ninja::NinjaArg("-fPIC"), ninja::NinjaArg("$in"), @@ -1064,11 +995,18 @@ namespace backend compileCCommand.push_back(std::move(sanitizerFlag)); compileCppCommand = compileCCommand; - compileCppCommand[0] = ninja::NinjaArg::createRaw(cppCompilerName); compileCppCommand.insert(compileCppCommand.end(), { ninja::NinjaArg("-fexceptions"), ninja::NinjaArg("-Wnon-virtual-dtor") }); + + for(auto it = cCompilerName.rbegin(), end = cCompilerName.rend(); it != end; ++it) { + compileCCommand.insert(compileCCommand.begin(), ninja::NinjaArg(*it)); + } + + for(auto it = cppCompilerName.rbegin(), end = cppCompilerName.rend(); it != end; ++it) { + compileCppCommand.insert(compileCppCommand.begin(), ninja::NinjaArg(*it)); + } break; } case Compiler::MSVC: @@ -1388,9 +1326,17 @@ namespace backend case Compiler::MINGW_W64: case Compiler::GCC: { + if(usesCppFiles) { + for(const FileString &arg : cppCompilerName) { + buildExeArgs.push_back(ninja::NinjaArg(arg)); + } + } else { + for(const FileString &arg : cCompilerName) { + buildExeArgs.push_back(ninja::NinjaArg(arg)); + } + } string rpath = extractDynamicLibDirsFromLinkerFlags(dynamicLinkerFlags); buildExeArgs.insert(buildExeArgs.end(), { - ninja::NinjaArg::createRaw(usesCppFiles ? cppCompilerName : cCompilerName), ninja::NinjaArg::createRaw("-o"), ninja::NinjaArg::createRaw("$out"), ninja::NinjaArg::createRaw("$in"), @@ -1437,9 +1383,11 @@ namespace backend } case Compiler::MSVC: { + for(const FileString &arg : cppCompilerName) { + buildExeArgs.push_back(ninja::NinjaArg(arg)); + } // TODO: Do not link all of these. Find a way to only link the ones that are needed buildExeArgs.insert(buildExeArgs.end(), { - ninja::NinjaArg::createRaw(cppCompilerName), ninja::NinjaArg::createRaw("$in"), ninja::NinjaArg::createRaw("/Fe$out"), ninja::NinjaArg::createRaw("Ws2_32.lib"), @@ -1535,8 +1483,10 @@ namespace backend case Compiler::MINGW_W64: case Compiler::GCC: { + for(const FileString &arg : compilerLinker) { + buildStaticArgs.push_back(ninja::NinjaArg(arg)); + } buildStaticArgs.insert(buildStaticArgs.end(), { - ninja::NinjaArg::createRaw(compilerLinker), ninja::NinjaArg::createRaw("rcs"), ninja::NinjaArg::createRaw("$out"), ninja::NinjaArg::createRaw("$in") @@ -1545,8 +1495,10 @@ namespace backend } case Compiler::MSVC: { + for(const FileString &arg : compilerLinker) { + buildStaticArgs.push_back(ninja::NinjaArg(arg)); + } buildStaticArgs.insert(buildStaticArgs.end(), { - ninja::NinjaArg::createRaw(compilerLinker), ninja::NinjaArg::createRaw("/OUT:$out"), ninja::NinjaArg::createRaw("$in") }); @@ -1623,8 +1575,16 @@ namespace backend case Compiler::MINGW_W64: case Compiler::GCC: { + if(usesCppFiles) { + for(const FileString &arg : cppCompilerName) { + buildDynamicArgs.push_back(ninja::NinjaArg(arg)); + } + } else { + for(const FileString &arg : cCompilerName) { + buildDynamicArgs.push_back(ninja::NinjaArg(arg)); + } + } buildDynamicArgs.insert(buildDynamicArgs.end(), { - ninja::NinjaArg::createRaw(usesCppFiles ? cppCompilerName : cCompilerName), ninja::NinjaArg::createRaw("$in"), ninja::NinjaArg::createRaw("-shared"), ninja::NinjaArg("-Wl,-soname," + generatedFile), @@ -1665,8 +1625,10 @@ namespace backend } case Compiler::MSVC: { + for(const FileString &arg : compilerLinker) { + buildDynamicArgs.push_back(ninja::NinjaArg(arg)); + } buildDynamicArgs.insert(buildDynamicArgs.end(), { - ninja::NinjaArg::createRaw(compilerLinker), ninja::NinjaArg::createRaw("/OUT:$out"), ninja::NinjaArg::createRaw("$in"), ninja::NinjaArg::createRaw("Ws2_32.lib"), @@ -1735,7 +1697,7 @@ namespace backend if(!sourceFiles.empty()) { string result = ninjaBuildFile.generate(); - Result<bool> fileOverwriteResult = sibs::fileOverwrite(ninjaBuildFilePath.c_str(), sibs::StringView(result.data(), result.size())); + Result<bool> fileOverwriteResult = sibs::fileOverwrite(ninjaBuildFilePath.data.c_str(), sibs::StringView(result.data(), result.size())); if (fileOverwriteResult.isErr()) return fileOverwriteResult; @@ -1777,24 +1739,18 @@ namespace backend parentExportIncludeDirs += getIncludeOptionFlag(config.getCompiler(), parentExportIncludeDir); } -#if OS_FAMILY == OS_FAMILY_POSIX - FileString testSourceDirNative = testSourceDir; - FileString projectConfFilePath = testSourceDir; -#else - FileString testSourceDirNative = utf8To16(testSourceDir); - FileString projectConfFilePath = testSourceDirNative; -#endif - projectConfFilePath += TINYDIR_STRING("/project.conf"); + Path testSourceDirNative = toFileString(testSourceDir); + Path projectConfFilePath = Path(testSourceDirNative).join("project.conf"); - FileType projectConfFileType = getFileType(projectConfFilePath.c_str()); - SibsTestConfig sibsTestConfig(config.getCompiler(), testSourceDirNative, config.getOptimizationLevel()); + FileType projectConfFileType = getFileType(projectConfFilePath.data.c_str()); + SibsTestConfig sibsTestConfig(config.getCompiler(), testSourceDirNative.data, config.getOptimizationLevel()); sibsTestConfig.platform = config.platform; sibsTestConfig.setSanitize(config.getSanitize()); sibsTestConfig.zigTestFiles = move(config.zigTestFiles); sibsTestConfig.zigTestAllFiles = config.zigTestAllFiles; if(projectConfFileType == FileType::REGULAR) { - Result<bool> result = Config::readFromFile(projectConfFilePath.c_str(), sibsTestConfig); + Result<bool> result = Config::readFromFile(projectConfFilePath.data.c_str(), sibsTestConfig); if(!result) return result; } @@ -1826,7 +1782,7 @@ namespace backend bool zigTest = false; if(config.zigTestAllFiles) { - backend::BackendUtils::collectSourceFiles(testSourceDirNative.c_str(), &ninja, sibsTestConfig); + backend::BackendUtils::collectSourceFiles(testSourceDirNative.data.c_str(), &ninja, sibsTestConfig); // TODO: This can be optimized as well. No need to insert non-zig files if we are going to remove them. // Maybe pass a filter callback function to @collectSourceFiles. for(auto it = ninja.sourceFiles.begin(); it != ninja.sourceFiles.end(); ) @@ -1853,33 +1809,29 @@ namespace backend } else { - backend::BackendUtils::collectSourceFiles(testSourceDirNative.c_str(), &ninja, sibsTestConfig); + backend::BackendUtils::collectSourceFiles(testSourceDirNative.data.c_str(), &ninja, sibsTestConfig); } if(!ninja.getSourceFiles().empty()) { - FileString buildPath = testSourceDirNative; - buildPath += TINYDIR_STRING("/sibs-build/") + toFileString(asString(config.platform)); + Path buildPath = Path(testSourceDirNative).join(TINYDIR_STRING("sibs-build")).join(toFileString(asString(config.platform))); switch(sibsTestConfig.getOptimizationLevel()) { case OPT_LEV_DEBUG: - buildPath += TINYDIR_STRING("/debug"); + buildPath.join(TINYDIR_STRING("debug")); break; case OPT_LEV_RELEASE: - buildPath += TINYDIR_STRING("/release"); + buildPath.join(TINYDIR_STRING("release")); break; } - Result<bool> buildFileResult = ninja.build(sibsTestConfig, buildPath.c_str()); + Result<bool> buildFileResult = ninja.build(sibsTestConfig, buildPath.data.c_str()); if (!buildFileResult) return buildFileResult; if(!zigTest) { - FileString testExecutableName = buildPath; - testExecutableName += TINYDIR_STRING("/"); - testExecutableName += toFileString(sibsTestConfig.getPackageName()); - Result<ExecResult> runTestResult = exec(testExecutableName.c_str(), true); + Result<ExecResult> runTestResult = exec({ Path(buildPath).join(toFileString(sibsTestConfig.getPackageName())).data }, true); if(!runTestResult) return Result<bool>::Err(runTestResult); @@ -1893,26 +1845,7 @@ namespace backend Result<bool> Ninja::compile(const _tinydir_char_t *buildFilePath) { -#if OS_TYPE == OS_TYPE_LINUX - FileString command; - if(isatty(STDOUT_FILENO) == 1) - { - command = TINYDIR_STRING("script -eqc 'ninja -C \""); - command += buildFilePath; - command += TINYDIR_STRING("\"' /dev/null"); - } - else - { - command = TINYDIR_STRING("ninja -C \""); - command += buildFilePath; - command += TINYDIR_STRING("\""); - } -#else - FileString command = TINYDIR_STRING("ninja -C \""); - command += buildFilePath; - command += TINYDIR_STRING("\""); -#endif - Result<ExecResult> execResult = exec(command.c_str(), true); + Result<ExecResult> execResult = exec({ TINYDIR_STRING("ninja"), TINYDIR_STRING("-C"), buildFilePath }, true); if(execResult.isOk()) { if(execResult.unwrap().exitCode == 0) @@ -1929,15 +1862,17 @@ namespace backend Result<bool> Ninja::buildCompilationDatabase(const _tinydir_char_t *buildFilePath, const FileString &saveDir) { - FileString command = TINYDIR_STRING("ninja -C \""); - command += buildFilePath; - command += TINYDIR_STRING("\" -t compdb compile_c compile_cpp > \""); - command += saveDir; - command += TINYDIR_STRING("/compile_commands.json\""); - Result<ExecResult> execResult = exec(command.c_str(), false); + Result<ExecResult> execResult = exec({ + TINYDIR_STRING("ninja"), TINYDIR_STRING("-C"), buildFilePath, TINYDIR_STRING("-t"), + TINYDIR_STRING("compdb"), TINYDIR_STRING("compile_c"), TINYDIR_STRING("compile_cpp") + }, false); if(execResult) { - if(execResult.unwrap().exitCode != 0) + if(execResult.unwrap().exitCode == 0) + { + fileOverwrite(Path(saveDir).join("compile_commands.json").data.c_str(), { execResult.unwrap().execStdout.data(), execResult.unwrap().execStdout.size() }); + } + else { string errMsg = "Failed to build compilation database, reason: "; errMsg += execResult.unwrap().execStdout; diff --git a/depends/libninja b/depends/libninja -Subproject f3f053984698845828f169b1bcc09065bf9fe84 +Subproject 691adcce8c2b23af4d0ed855ac7003bda0c8a17 diff --git a/include/Conf.hpp b/include/Conf.hpp index 8098206..cfe0133 100644 --- a/include/Conf.hpp +++ b/include/Conf.hpp @@ -237,10 +237,10 @@ namespace sibs switch(optimizationLevel) { case OPT_LEV_DEBUG: - cmakeArgsGlobal = TINYDIR_STRING("-G Ninja \"-DCMAKE_BUILD_TYPE=Debug\""); + cmakeArgsGlobal = { TINYDIR_STRING("-G"), TINYDIR_STRING("Ninja"), TINYDIR_STRING("-DCMAKE_BUILD_TYPE=Debug") }; break; case OPT_LEV_RELEASE: - cmakeArgsGlobal = TINYDIR_STRING("-G Ninja \"-DCMAKE_BUILD_TYPE=Release\""); + cmakeArgsGlobal = { TINYDIR_STRING("-G"), TINYDIR_STRING("Ninja"), TINYDIR_STRING("-DCMAKE_BUILD_TYPE=Release") }; break; } } @@ -274,7 +274,7 @@ namespace sibs this->testPath = testPath; } - virtual const std::vector<PackageListDependency*>& getPackageListDependencies() const + virtual const std::vector<PackageListDependency>& getPackageListDependencies() const { return packageListDependencies; } @@ -340,30 +340,24 @@ namespace sibs } // Get cmake args for all builds. This is args under [cmake] only - const FileString& getCmakeArgs() const + const std::vector<FileString>& getCmakeArgs() const { return cmakeArgsGlobal; } // Get cmake args for static build. This is a combination of args under [cmake] and under [cmake.static] - FileString getCmakeArgsStatic() const + std::vector<FileString> getCmakeArgsStatic() const { - FileString result; - result.reserve(cmakeArgsGlobal.size() + 1 + cmakeArgsStatic.size()); - result += cmakeArgsGlobal; - result += TINYDIR_STRING(" "); - result += cmakeArgsStatic; + std::vector<FileString> result = cmakeArgsGlobal; + result.insert(result.end(), cmakeArgsStatic.begin(), cmakeArgsStatic.end()); return result; } // Get cmake args for dynamic build. This is a combination of args under [cmake] and under [cmake.dynamic] - FileString getCmakeArgsDynamic() const + std::vector<FileString> getCmakeArgsDynamic() const { - FileString result; - result.reserve(cmakeArgsGlobal.size() + 1 + cmakeArgsDynamic.size()); - result += cmakeArgsGlobal; - result += TINYDIR_STRING(" "); - result += cmakeArgsDynamic; + std::vector<FileString> result = cmakeArgsGlobal; + result.insert(result.end(), cmakeArgsDynamic.begin(), cmakeArgsDynamic.end()); return result; } @@ -451,7 +445,7 @@ namespace sibs std::string parsePlatformConfigStatic(const StringView &fieldName, const ConfigValue &fieldValue); void parsePlatformConfigStaticDebug(const StringView &fieldName, const ConfigValue &fieldValue); void parsePlatformConfigStaticRelease(const StringView &fieldName, const ConfigValue &fieldValue); - void parseCmake(const StringView &fieldName, const ConfigValue &fieldValue, FileString &cmakeDir, FileString &cmakeArgs); + void parseCmake(const StringView &fieldName, const ConfigValue &fieldValue, FileString &cmakeDir, std::vector<FileString> &cmakeArgs); void validatePackageName() const; protected: StringView currentObject; @@ -460,7 +454,7 @@ namespace sibs std::string packageName; FileString testPath; PackageType packageType; - std::vector<PackageListDependency*> packageListDependencies; + std::vector<PackageListDependency> packageListDependencies; std::vector<Platform> platforms; std::unordered_map<std::string, std::string> defines; OptimizationLevel optimizationLevel; @@ -469,9 +463,9 @@ namespace sibs FileString cmakeDirGlobal; FileString cmakeDirStatic; FileString cmakeDirDynamic; - FileString cmakeArgsGlobal; - FileString cmakeArgsStatic; - FileString cmakeArgsDynamic; + std::vector<FileString> cmakeArgsGlobal; + std::vector<FileString> cmakeArgsStatic; + std::vector<FileString> cmakeArgsDynamic; CVersion cVersion; CPPVersion cppVersion; bool useCmake; diff --git a/include/Exec.hpp b/include/Exec.hpp index 93ce307..538f0dc 100644 --- a/include/Exec.hpp +++ b/include/Exec.hpp @@ -4,6 +4,7 @@ #include "Result.hpp" #include "../include/FileUtil.hpp" #include <string> +#include <vector> namespace sibs { @@ -13,8 +14,7 @@ namespace sibs int exitCode; }; - Result<ExecResult> exec(const _tinydir_char_t *cmd, bool print = false); - Result<ExecResult> exec(const FileString &cmd, bool print = false); + Result<ExecResult> exec(const std::vector<FileString> &args, bool print_instead_of_pipe = false); } #endif //SIBS_EXEC_HPP diff --git a/include/FileUtil.hpp b/include/FileUtil.hpp index f2a1799..5bd4d33 100644 --- a/include/FileUtil.hpp +++ b/include/FileUtil.hpp @@ -38,6 +38,30 @@ namespace sibs void replaceChar(FileString &input, wchar_t charToReplace, wchar_t charToReplaceWith); #endif + class Path { + public: + Path(const FileString &str) : data(str) {} + + Path& join(const _tinydir_char_t *str) { + data += TINYDIR_STRING("/"); + data += str; + return *this; + } + + Path& join(const Path &other) { + data += TINYDIR_STRING("/"); + data += other.data; + return *this; + } + + Path& append(const FileString &str) { + data += str; + return *this; + } + + FileString data; + }; + // Return true if you want to continue iterating the remaining files, return false if you want to stop using FileWalkCallbackFunc = std::function<bool(tinydir_file*)>; diff --git a/include/GlobalLib.hpp b/include/GlobalLib.hpp index 92739df..953df7e 100644 --- a/include/GlobalLib.hpp +++ b/include/GlobalLib.hpp @@ -18,10 +18,10 @@ namespace sibs DEPENDENCY_VERSION_NO_MATCH = 20 }; - static Result<bool> getLibs(const std::vector<PackageListDependency*> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); + static Result<bool> getLibs(const std::vector<PackageListDependency> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); static Result<bool> validatePackageExists(const FileString &globalLibRootDir, const std::string &name); static Result<bool> getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const PackageVersionRange &versionRange, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); - static Result<bool> downloadDependency(PackageListDependency *dependency, Platform platform); + static Result<bool> downloadDependency(const PackageListDependency &dependency, Platform platform); private: static Result<bool> getLibsLinkerFlagsCommon(const SibsConfig &parentConfig, const FileString &packageDir, const std::string &dependencyName, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback); }; diff --git a/include/PkgConfig.hpp b/include/PkgConfig.hpp index 24aeb78..23c1b12 100644 --- a/include/PkgConfig.hpp +++ b/include/PkgConfig.hpp @@ -22,13 +22,12 @@ namespace sibs { public: static void setPkgConfigPath(const FileString &path); - static Result<bool> validatePkgConfigPackageVersionExists(PackageListDependency *dependency); + static Result<bool> validatePkgConfigPackageVersionExists(const PackageListDependency &dependency); static Result<bool> validatePackageExists(const std::string &name); - static Result<bool> validatePackageVersionAtLeast(const std::string &name, const std::string &version); static Result<PackageVersion> getPackageVersion(const std::string &name); - static Result<std::string> getDynamicLibsLinkerFlags(const std::vector<PackageListDependency*> &libs); - static Result<std::string> getDynamicLibsCflags(const std::vector<PackageListDependency*> &libs); - static Result<PkgConfigFlags> getDynamicLibsFlags(const std::vector<PackageListDependency*> &libs); + static Result<std::string> getDynamicLibsLinkerFlags(const std::vector<PackageListDependency> &libs); + static Result<std::string> getDynamicLibsCflags(const std::vector<PackageListDependency> &libs); + static Result<PkgConfigFlags> getDynamicLibsFlags(const std::vector<PackageListDependency> &libs); }; } diff --git a/src/CmakeModule.cpp b/src/CmakeModule.cpp index 5bf9400..a706e64 100644 --- a/src/CmakeModule.cpp +++ b/src/CmakeModule.cpp @@ -131,10 +131,10 @@ namespace sibs // CMakeLists.txt may contain: set(CMAKE_MODULE_PATH "PathToModules"). This needs to be replaced with list append, // otherwise our added module path is replaced. // It may work to do like vcpkg instead - to use -DCMAKE_TOOLCHAIN_FILE program argument to specify path to script (https://github.com/Microsoft/vcpkg/blob/master/docs/examples/using-sqlite.md) - vector<PackageListDependency*> globalLibDependencies; + vector<PackageListDependency> globalLibDependencies; #if OS_FAMILY == OS_FAMILY_POSIX - vector<PackageListDependency*> pkgConfigDependencies; - for(PackageListDependency *dependency : config.getPackageListDependencies()) + vector<PackageListDependency> pkgConfigDependencies; + for(const PackageListDependency &dependency : config.getPackageListDependencies()) { Result<bool> pkgConfigDependencyValidation = PkgConfig::validatePkgConfigPackageVersionExists(dependency); if(pkgConfigDependencyValidation.isOk()) @@ -153,7 +153,7 @@ namespace sibs { printf("%s, using global lib...\n", pkgConfigLinkerFlagsResult.getErrMsg().c_str()); globalLibDependencies.reserve(globalLibDependencies.size() + pkgConfigDependencies.size()); - for (PackageListDependency *pkgConfigDependency : pkgConfigDependencies) + for (const PackageListDependency &pkgConfigDependency : pkgConfigDependencies) { globalLibDependencies.push_back(pkgConfigDependency); } @@ -165,7 +165,7 @@ namespace sibs dynamicLinkerFlagCallbackFunc(pkgConfigLinkerFlagsResult.unwrap()); } #else - for (PackageListDependency *dependency : config.getPackageListDependencies()) + for (const PackageListDependency &dependency : config.getPackageListDependencies()) { globalLibDependencies.push_back(dependency); } @@ -194,8 +194,7 @@ namespace sibs _putenv("CXXFLAGS=-fPIC"); #endif #endif - FileString cmd = cmakePath; - cmd += TINYDIR_STRING(" "); + std::vector<FileString> cmd = { cmakePath }; FileString cflags = TINYDIR_STRING("-fPIC"); FileString cxxflags; @@ -240,56 +239,57 @@ namespace sibs } cxxflags = cflags; - cmd += TINYDIR_STRING(" \"-DCMAKE_C_FLAGS=") + cflags + TINYDIR_STRING("\""); - cmd += TINYDIR_STRING(" \"-DCMAKE_CXX_FLAGS=") + cxxflags + TINYDIR_STRING("\" "); + cmd.push_back(TINYDIR_STRING("-DCMAKE_C_FLAGS=") + cflags); + cmd.push_back(TINYDIR_STRING("-DCMAKE_CXX_FLAGS=") + cxxflags); switch(config.getPackageType()) { case PackageType::EXECUTABLE: { - cmd += config.getCmakeArgs(); + auto cmake_args = config.getCmakeArgs(); + cmd.insert(cmd.end(), cmake_args.begin(), cmake_args.end()); break; } case PackageType::STATIC: { - cmd += config.getCmakeArgsStatic(); + auto cmake_args = config.getCmakeArgsStatic(); + cmd.insert(cmd.end(), cmake_args.begin(), cmake_args.end()); break; } case PackageType::DYNAMIC: case PackageType::LIBRARY: { - cmd += config.getCmakeArgsDynamic(); + auto cmake_args = config.getCmakeArgsDynamic(); + cmd.insert(cmd.end(), cmake_args.begin(), cmake_args.end()); break; } } - //cmd += TINYDIR_STRING(" -DCMAKE_SKIP_RPATH=\"1\""); - cmd += TINYDIR_STRING(" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"); - cmd += TINYDIR_STRING(" \"-B"); - cmd += buildPath; - cmd += TINYDIR_STRING("\" \"-H"); + //cmd.push_back(TINYDIR_STRING("-DCMAKE_SKIP_RPATH=1")); + cmd.push_back(TINYDIR_STRING("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON")); + cmd.push_back(TINYDIR_STRING("-B") + buildPath); + cmd.push_back(TINYDIR_STRING("-H")); switch(config.getPackageType()) { case PackageType::EXECUTABLE: { - cmd += config.getCmakeDir(); + cmd.back() += config.getCmakeDir(); break; } case PackageType::STATIC: { - cmd += config.getCmakeDirStatic(); + cmd.back() += config.getCmakeDirStatic(); break; } case PackageType::DYNAMIC: case PackageType::LIBRARY: { - cmd += config.getCmakeDirDynamic(); + cmd.back() += config.getCmakeDirDynamic(); break; } } - cmd += TINYDIR_STRING("\""); - nprintf("Compiling with cmake with arguments: %s\n", cmd.c_str()); + //nprintf("Compiling with cmake with arguments: %s\n", cmd.c_str()); - Result<ExecResult> execResult = exec(cmd.c_str(), true); + Result<ExecResult> execResult = exec(cmd, true); if(execResult.isOk()) { if(execResult.unwrap().exitCode != 0) @@ -298,11 +298,8 @@ namespace sibs else return Result<bool>::Err(execResult); - FileString ninjaCommand = TINYDIR_STRING("ninja -C \""); - ninjaCommand += buildPath; - ninjaCommand += TINYDIR_STRING("\""); - nprintf("Compiling cmake generated ninja file: %s\n", ninjaCommand.c_str()); - execResult = exec(ninjaCommand.c_str(), true); + //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) diff --git a/src/Conf.cpp b/src/Conf.cpp index 12421e0..543c8d9 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -643,13 +643,7 @@ namespace sibs SibsConfig::~SibsConfig() { - // TODO: Fix this shit.. why does this cause segfault? - /* - for(PackageListDependency *dependency : packageListDependencies) - { - //delete dependency; - } - */ + } bool SibsConfig::isDefined(const std::string &name) const @@ -1102,10 +1096,10 @@ namespace sibs if(!dependencyVersionResult) throw ParserException("Dependency " + string(name.data, name.size) + " version is in invalid format, error: " + dependencyVersionResult.getErrMsg()); - PackageListDependency *dependency = new PackageListDependency(); - dependency->name = string(name.data, name.size); - dependency->version = dependencyVersionResult.unwrap(); - packageListDependencies.emplace_back(dependency); + PackageListDependency dependency; + dependency.name = string(name.data, name.size); + dependency.version = dependencyVersionResult.unwrap(); + packageListDependencies.push_back(std::move(dependency)); } else throw ParserException("Expected field under dependencies to be a single value or an object, was a list"); @@ -1287,7 +1281,7 @@ namespace sibs getLibFiles(parsePlatformConfigStatic(fieldName, fieldValue), releaseStaticLibs); } - void SibsConfig::parseCmake(const StringView &fieldName, const ConfigValue &fieldValue, FileString &cmakeDir, FileString &cmakeArgs) + void SibsConfig::parseCmake(const StringView &fieldName, const ConfigValue &fieldValue, FileString &cmakeDir, std::vector<FileString> &cmakeArgs) { if(fieldName.equals("dir")) { @@ -1314,13 +1308,7 @@ namespace sibs { for(const StringView &arg : fieldValue.asList()) { - bool prependSpace = !cmakeArgs.empty(); - cmakeArgs.reserve(cmakeArgs.size() + 4 + (prependSpace ? 1 : 0) + arg.size); - if(prependSpace) - cmakeArgs += TINYDIR_STRING(" "); - cmakeArgs += TINYDIR_STRING("\"-D"); - cmakeArgs += toFileString(arg); - cmakeArgs += TINYDIR_STRING("\""); + cmakeArgs.push_back(TINYDIR_STRING("-D") + toFileString(arg)); } } else diff --git a/src/Exec.cpp b/src/Exec.cpp index 169858f..12e373d 100644 --- a/src/Exec.cpp +++ b/src/Exec.cpp @@ -3,6 +3,7 @@ #if OS_FAMILY == OS_FAMILY_POSIX #include <sys/wait.h> +#include <unistd.h> #endif using namespace std; @@ -13,60 +14,129 @@ const int BUFSIZE = 4096; namespace sibs { #if OS_FAMILY == OS_FAMILY_POSIX - Result<ExecResult> exec(const _tinydir_char_t *cmd, bool print) + Result<ExecResult> exec(const std::vector<FileString> &args, bool print_instead_of_pipe) { char buffer[BUFSIZE]; std::string execStdout; - FILE *pipe = popen(cmd, "r"); - if(!pipe) - return Result<ExecResult>::Err("popen() failed"); - while(!feof(pipe)) - { - if(fgets(buffer, BUFSIZE, pipe)) - { - int bytesRead = strlen(buffer); - execStdout.append(buffer, bytesRead); - if(print) - printf("%.*s", bytesRead, buffer); + if(args.empty()) + return Result<ExecResult>::Err("exec requires at least one argument (the program name)"); + + std::vector<const char*> exec_args; + for(const FileString &arg : args) { + exec_args.push_back(arg.c_str()); + } + exec_args.push_back(nullptr); + + int fd[2]; + if(!print_instead_of_pipe && pipe(fd) == -1) + return Result<ExecResult>::Err(strerror(errno)); + + pid_t pid = fork(); + if(pid == -1) { + if(!print_instead_of_pipe) { + close(fd[0]); + close(fd[1]); } + return Result<ExecResult>::Err("Failed to exec " + args[0] + " (failed to fork)"); + } else if(pid == 0) { // child + if(!print_instead_of_pipe) { + dup2(fd[1], STDOUT_FILENO); + close(fd[0]); + close(fd[1]); + } + execvp(exec_args[0], (char* const*)exec_args.data()); + perror("execvp"); + _exit(127); + } else { // parent + if(!print_instead_of_pipe) + close(fd[1]); } - int processCloseResult = pclose(pipe); - if(WIFEXITED(processCloseResult)) + if(!print_instead_of_pipe) { + for(;;) { + ssize_t bytes_read = read(fd[0], buffer, sizeof(buffer)); + if(bytes_read == 0) { + break; + } else if(bytes_read == -1) { + std::string err_msg = "Failed to read from pipe to program " + args[0] + ", error: " + strerror(errno); + kill(pid, SIGTERM); + close(fd[0]); + return Result<ExecResult>::Err(err_msg); + } + + execStdout.append(buffer, bytes_read); + } + } + + int status = 0; + if(waitpid(pid, &status, 0) == -1) { + std::string err_msg = std::string("waitpid failed, error: ") + strerror(errno); + if(!print_instead_of_pipe) + close(fd[0]); + return Result<ExecResult>::Err(err_msg); + } + if(!print_instead_of_pipe) + close(fd[0]); + + if(WIFEXITED(status)) { - int returned = WEXITSTATUS(processCloseResult); + int returned = WEXITSTATUS(status); ExecResult execResult; execResult.execStdout = move(execStdout); execResult.exitCode = returned; return Result<ExecResult>::Ok(execResult); } - else if(WIFSIGNALED(processCloseResult)) + else if(WIFSIGNALED(status)) { - int signum = WSTOPSIG(processCloseResult); + int signum = WSTOPSIG(status); string errMsg = "Exited due to receiving signal "; errMsg += to_string(signum); return Result<ExecResult>::Err(errMsg); } - else if(WIFSTOPPED(processCloseResult)) + else if(WIFSTOPPED(status)) { - int signum = WSTOPSIG(processCloseResult); + int signum = WSTOPSIG(status); string errMsg = "Stopped due to receiving signal "; errMsg += to_string(signum); return Result<ExecResult>::Err(errMsg); } else { - string errMsg = "exec unexpected error on pclose: "; - errMsg += to_string(processCloseResult); + string errMsg = "exec unexpected error on waitpid: "; + errMsg += to_string(status); return Result<ExecResult>::Err(errMsg); } } + #else + static FileString escape_arg(const FileString &arg) { + FileString escaped = TINYDIR_STRING("\""); + for(_tinydir_char_t c : arg) { + if(c == '"') { + escaped += TINYDIR_STRING("\"\""); + } else { + escaped += c; + } + } + escaped += TINYDIR_STRING("\""); + return escaped; + } + + static FileString command_list_to_command_string(const std::vector<FileString> &args) { + FileString cmd; + for(size_t i = 0; i < args.size(); ++i) { + if(i > 0) + cmd += TINYDIR_STRING(" "); + cmd += escape_arg(args[i]); + } + return cmd; + } + // Currently stdout is read in text mode so \n is replaced with \r\n, should we read in binary mode instead? - Result<ExecResult> exec(const _tinydir_char_t *cmd, bool print) + Result<ExecResult> exec(const std::vector<FileString> &args, bool print_instead_of_pipe) { - FileString cmdNonConst = cmd; + FileString cmdNonConst = command_list_to_command_string(args); std::string execStdout; SECURITY_ATTRIBUTES saAttr; @@ -77,15 +147,17 @@ namespace sibs HANDLE childReadHandle = nullptr; HANDLE childStdoutHandle = nullptr; - if (!CreatePipe(&childReadHandle, &childStdoutHandle, &saAttr, 0)) - { - string errMsg = "exec unexpected error: "; - errMsg += toUtf8(getLastErrorAsString()); - return Result<ExecResult>::Err(errMsg); - } + if(!print_instead_of_pipe) { + if (!CreatePipe(&childReadHandle, &childStdoutHandle, &saAttr, 0)) + { + string errMsg = "exec unexpected error: "; + errMsg += toUtf8(getLastErrorAsString()); + return Result<ExecResult>::Err(errMsg); + } - if (!SetHandleInformation(childReadHandle, HANDLE_FLAG_INHERIT, 0)) - goto cleanupAndExit; + if (!SetHandleInformation(childReadHandle, HANDLE_FLAG_INHERIT, 0)) + goto cleanupAndExit; + } PROCESS_INFORMATION piProcInfo; ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); @@ -94,7 +166,7 @@ namespace sibs ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = nullptr; - siStartInfo.hStdOutput = childStdoutHandle; + siStartInfo.hStdOutput = print_instead_of_pipe ? nullptr : childStdoutHandle; siStartInfo.hStdInput = nullptr; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; @@ -103,19 +175,22 @@ namespace sibs if (!CreateProcessW(nullptr, (LPWSTR)cmdNonConst.data(), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo)) goto cleanupAndExit; - CloseHandle(childStdoutHandle); - childStdoutHandle = nullptr; - DWORD bytesRead; - CHAR buffer[BUFSIZE]; - while (true) - { - BOOL bSuccess = ReadFile(childReadHandle, buffer, BUFSIZE, &bytesRead, nullptr); - if (!bSuccess || bytesRead == 0) - break; + if(!print_instead_of_pipe) { + CloseHandle(childStdoutHandle); + childStdoutHandle = nullptr; + + DWORD bytesRead; + CHAR buffer[BUFSIZE]; + while (true) + { + BOOL bSuccess = ReadFile(childReadHandle, buffer, BUFSIZE, &bytesRead, nullptr); + if (!bSuccess || bytesRead == 0) + break; - execStdout.append(buffer, bytesRead); - if (print) - printf("%.*s", bytesRead, buffer); + execStdout.append(buffer, bytesRead); + if (print) + printf("%.*s", bytesRead, buffer); + } } WaitForSingleObject(piProcInfo.hProcess, INFINITE); @@ -134,13 +209,11 @@ namespace sibs cleanupAndExit: string errMsg = "exec unexpected error: "; errMsg += toUtf8(getLastErrorAsString()); - CloseHandle(childReadHandle); - CloseHandle(childStdoutHandle); + if(childReadHandle) + CloseHandle(childReadHandle); + if(childStdoutHandle) + CloseHandle(childStdoutHandle); return Result<ExecResult>::Err(errMsg); } #endif - Result<ExecResult> exec(const FileString &cmd, bool print) - { - return exec(cmd.c_str(), print); - } } diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index 8ad1678..b390571 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -15,25 +15,20 @@ namespace sibs { Result<bool> GlobalLib::validatePackageExists(const FileString &globalLibRootDir, const std::string &name) { - FileString packageDir = globalLibRootDir + TINYDIR_STRING("/"); -#if OS_FAMILY == OS_FAMILY_POSIX - packageDir += name; -#else - packageDir += utf8To16(name); -#endif - FileType packageDirFileType = getFileType(packageDir.c_str()); + Path packageDir = Path(globalLibRootDir).join(toFileString(name)); + FileType packageDirFileType = getFileType(packageDir.data.c_str()); switch(packageDirFileType) { case FileType::FILE_NOT_FOUND: { string errMsg = "Global lib dependency not found: "; - errMsg += toUtf8(packageDir); + errMsg += toUtf8(packageDir.data); return Result<bool>::Err(errMsg, DependencyError::DEPENDENCY_NOT_FOUND); } case FileType::REGULAR: { string errMsg = "Corrupt library directory. "; - errMsg += toUtf8(packageDir); + errMsg += toUtf8(packageDir.data); errMsg += " is a file, expected it to be a directory"; return Result<bool>::Err(errMsg); } @@ -54,10 +49,8 @@ namespace sibs if (!libPathResult) return Result<bool>::Err(libPathResult); - FileString libArchivedFilePath = libPathResult.unwrap(); - libArchivedFilePath += TINYDIR_STRING("/.cache/sibs/archive/"); - libArchivedFilePath += toFileString(name); - FileType archive_path_file_type = getFileType(libArchivedFilePath.c_str()); + Path libArchivedFilePath = Path(libPathResult.unwrap()).join(".cache/sibs/archive").join(toFileString(name)); + FileType archive_path_file_type = getFileType(libArchivedFilePath.data.c_str()); if(archive_path_file_type == FileType::FILE_NOT_FOUND) return Result<bool>::Ok(true); @@ -66,7 +59,7 @@ namespace sibs return Result<bool>::Err("A previous download of package is corrupt, attempting to redownload..."); bool isEmpty = true; - walkDir(libArchivedFilePath.c_str(), [&isEmpty](tinydir_file *file) + walkDir(libArchivedFilePath.data.c_str(), [&isEmpty](tinydir_file *file) { isEmpty = false; return false; @@ -78,13 +71,13 @@ namespace sibs return Result<bool>::Ok(true); } - Result<bool> GlobalLib::getLibs(const std::vector<PackageListDependency*> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) + Result<bool> GlobalLib::getLibs(const std::vector<PackageListDependency> &libs, const SibsConfig &parentConfig, const FileString &globalLibRootDir, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { - for(PackageListDependency *globalLibDependency : libs) + for(const PackageListDependency &globalLibDependency : libs) { if(!parentConfig.packaging) - printf("Dependency %s in version range %s is missing from pkg-config, trying global lib\n", globalLibDependency->name.c_str(), globalLibDependency->version.toString().c_str()); - Result<bool> globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency->name, globalLibDependency->version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + printf("Dependency %s in version range %s is missing from pkg-config, trying global lib\n", globalLibDependency.name.c_str(), globalLibDependency.version.toString().c_str()); + Result<bool> globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); if(!globalLibLinkerFlagsResult) { if(globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_NOT_FOUND || globalLibLinkerFlagsResult.getErrorCode() == GlobalLib::DependencyError::DEPENDENCY_VERSION_NO_MATCH) @@ -103,7 +96,7 @@ namespace sibs if(!downloadDependencyResult) return downloadDependencyResult; - globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency->name, globalLibDependency->version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + globalLibLinkerFlagsResult = GlobalLib::getLibsLinkerFlags(parentConfig, globalLibRootDir, globalLibDependency.name, globalLibDependency.version, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); if(!globalLibLinkerFlagsResult) return globalLibLinkerFlagsResult; } @@ -122,17 +115,9 @@ namespace sibs if (packageExistsResult.isErr()) return packageExistsResult; -#if OS_FAMILY == OS_FAMILY_POSIX - FileString namePlatformNative = name; -#else - FileString namePlatformNative = utf8To16(name); -#endif - - FileString packageDir = globalLibRootDir + TINYDIR_STRING("/"); - packageDir += namePlatformNative; - + Path packageDir = Path(globalLibRootDir).join(toFileString(name)); FileString foundVersion; - walkDir(packageDir.c_str(), [&foundVersion, &versionRange](tinydir_file *file) + walkDir(packageDir.data.c_str(), [&foundVersion, &versionRange](tinydir_file *file) { if(file->is_dir) { @@ -150,10 +135,8 @@ namespace sibs if(foundVersion.empty()) return Result<bool>::Err("Global lib dependency found, but version isn't in range of version", DependencyError::DEPENDENCY_VERSION_NO_MATCH); - packageDir += TINYDIR_STRING("/"); - packageDir += foundVersion; - - return GlobalLib::getLibsLinkerFlagsCommon(parentConfig, packageDir, name, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); + packageDir.join(foundVersion); + return GlobalLib::getLibsLinkerFlagsCommon(parentConfig, packageDir.data, name, staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); } Result<bool> GlobalLib::getLibsLinkerFlagsCommon(const SibsConfig &parentConfig, const FileString &packageDir, const string &dependencyName, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) @@ -237,9 +220,9 @@ namespace sibs } } - Result<bool> GlobalLib::downloadDependency(PackageListDependency *dependency, Platform platform) + Result<bool> GlobalLib::downloadDependency(const PackageListDependency &dependency, Platform platform) { - Result<PackageMetadata> packageResult = Package::getPackage(dependency->name.c_str(), dependency->version, platform); + Result<PackageMetadata> packageResult = Package::getPackage(dependency.name.c_str(), dependency.version, platform); if(!packageResult) return Result<bool>::Err(packageResult); @@ -248,41 +231,35 @@ namespace sibs Result<FileString> libPathResult = getHomeDir(); if (!libPathResult) return Result<bool>::Err(libPathResult); - FileString libPath = libPathResult.unwrap(); - libPath += TINYDIR_STRING("/.cache/sibs/lib/"); - libPath += toFileString(asString(platform)); - libPath += TINYDIR_STRING("/"); - libPath += toFileString(dependency->name); - libPath += TINYDIR_STRING("/"); - libPath += toFileString(package.version.toString()); + Path libPath = Path(libPathResult.unwrap()) + .join(TINYDIR_STRING(".cache/sibs/lib")) + .join(toFileString(asString(platform))) + .join(toFileString(dependency.name)) + .join(toFileString(package.version.toString())); - FileString libArchivedFilePath = libPathResult.unwrap(); - libArchivedFilePath += TINYDIR_STRING("/.cache/sibs/archive/"); - libArchivedFilePath += toFileString(dependency->name); - Result<bool> createArchiveDirResult = createDirectoryRecursive(libArchivedFilePath.c_str()); + Path libArchivedFilePath = Path(libPathResult.unwrap()).join(TINYDIR_STRING("/.cache/sibs/archive/")).join(toFileString(dependency.name)); + Result<bool> createArchiveDirResult = createDirectoryRecursive(libArchivedFilePath.data.c_str()); if(!createArchiveDirResult) return createArchiveDirResult; - FileString libArchivedDir = libArchivedFilePath; - libArchivedFilePath += TINYDIR_STRING("/"); - libArchivedFilePath += toFileString(package.version.toString()); - Result<bool> downloadResult = curl::downloadFile(package.urls[0].c_str(), libArchivedFilePath.c_str()); + Path libArchivedDir = Path(libArchivedFilePath).join(toFileString(package.version.toString())); + Result<bool> downloadResult = curl::downloadFile(package.urls[0].c_str(), libArchivedFilePath.data.c_str()); if(!downloadResult) return downloadResult; // Create build path. This is done here because we dont want to create it if download fails - Result<bool> createLibDirResult = createDirectoryRecursive(libPath.c_str()); + Result<bool> createLibDirResult = createDirectoryRecursive(libPath.data.c_str()); if(!createLibDirResult) return createLibDirResult; - Result<bool> archiveExtractResult = Archive::extract(libArchivedFilePath.c_str(), libPath.c_str()); + Result<bool> archiveExtractResult = Archive::extract(libArchivedFilePath.data.c_str(), libPath.data.c_str()); // We have extracted the archive, we dont need to cache it. If remove fails, it doesn't really matter, user can remove it himself #if OS_FAMILY == OS_FAMILY_POSIX - remove(libArchivedFilePath.c_str()); - remove(libArchivedDir.c_str()); + remove(libArchivedFilePath.data.c_str()); + remove(libArchivedDir.data.c_str()); #else - _wremove(libArchivedFilePath.c_str()); - _wremove(libArchivedDir.c_str()); + _wremove(libArchivedFilePath.data.c_str()); + _wremove(libArchivedDir.data.c_str()); #endif return archiveExtractResult; } diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp index 89d3a44..73df008 100644 --- a/src/PkgConfig.cpp +++ b/src/PkgConfig.cpp @@ -27,28 +27,25 @@ namespace sibs pkgConfigPath = path; } - Result<bool> PkgConfig::validatePkgConfigPackageVersionExists(PackageListDependency *dependency) + Result<bool> PkgConfig::validatePkgConfigPackageVersionExists(const PackageListDependency &dependency) { - Result<bool> dependencyValidationResult = PkgConfig::validatePackageExists(dependency->name); + Result<bool> dependencyValidationResult = PkgConfig::validatePackageExists(dependency.name); if(dependencyValidationResult.isErr()) return Result<bool>::Err(dependencyValidationResult.getErrMsg()); - Result<PackageVersion> dependencyVersionResult = PkgConfig::getPackageVersion(dependency->name); + Result<PackageVersion> dependencyVersionResult = PkgConfig::getPackageVersion(dependency.name); if(!dependencyVersionResult) return Result<bool>::Err(dependencyVersionResult); - if(!dependency->version.isInRange(dependencyVersionResult.unwrap())) - return Result<bool>::Err("pkg-config package " + dependency->name + " exists but the version does not match our expected version range"); + if(!dependency.version.isInRange(dependencyVersionResult.unwrap())) + return Result<bool>::Err("pkg-config package " + dependency.name + " exists but the version does not match our expected version range"); return Result<bool>::Ok(true); } Result<bool> PkgConfig::validatePackageExists(const string &name) { - FileString command = pkgConfigPath + TINYDIR_STRING(" --exists '"); - command += toFileString(name); - command += TINYDIR_STRING("'"); - Result<ExecResult> execResult = exec(command.c_str()); + Result<ExecResult> execResult = exec({ pkgConfigPath, TINYDIR_STRING("--exists"), TINYDIR_STRING("--"), toFileString(name) }); if(execResult.isErr()) return Result<bool>::Err(execResult.getErrMsg()); @@ -72,45 +69,9 @@ namespace sibs return Result<bool>::Ok(true); } - Result<bool> PkgConfig::validatePackageVersionAtLeast(const string &name, const string &version) - { - FileString command = pkgConfigPath + TINYDIR_STRING(" '--atleast-version="); - command += toFileString(version); - command += TINYDIR_STRING("' '"); - command += toFileString(name); - command += TINYDIR_STRING("'"); - Result<ExecResult> execResult = exec(command.c_str()); - if(execResult.isErr()) - return Result<bool>::Err(execResult.getErrMsg()); - - if(execResult.unwrap().exitCode == 1) - { - string errMsg = "Dependency "; - errMsg += name; - errMsg += " is installed but the version older than "; - errMsg += version; - return Result<bool>::Err(errMsg); - } - else if(execResult.unwrap().exitCode == 127) - { - return Result<bool>::Err("pkg-config is not installed"); - } - else if(execResult.unwrap().exitCode != 0) - { - string errMsg = "Failed to check pkg-config package version, Unknown error, exit code: "; - errMsg += to_string(execResult.unwrap().exitCode); - return Result<bool>::Err(errMsg); - } - - return Result<bool>::Ok(true); - } - Result<PackageVersion> PkgConfig::getPackageVersion(const std::string &name) { - FileString command = pkgConfigPath + TINYDIR_STRING(" --modversion '"); - command += toFileString(name); - command += TINYDIR_STRING("'"); - Result<ExecResult> execResult = exec(command.c_str()); + Result<ExecResult> execResult = exec({ pkgConfigPath, TINYDIR_STRING("--modversion"), TINYDIR_STRING("--"), toFileString(name) }); if(!execResult) return Result<PackageVersion>::Err(execResult.getErrMsg()); @@ -136,21 +97,17 @@ namespace sibs return parsePackageVersion({ execResult.unwrap().execStdout.data(), execResult.unwrap().execStdout.size() }, nullptr); } - Result<string> PkgConfig::getDynamicLibsLinkerFlags(const vector<PackageListDependency*> &libs) + Result<string> PkgConfig::getDynamicLibsLinkerFlags(const vector<PackageListDependency> &libs) { if(libs.empty()) return Result<string>::Ok(""); - string args; - for(PackageListDependency *lib : libs) + std::vector<FileString> args = { pkgConfigPath, TINYDIR_STRING("--libs"), TINYDIR_STRING("--") }; + for(const PackageListDependency &lib : libs) { - args += " '"; - args += lib->name; - args += "'"; + args.push_back(toFileString(lib.name)); } - FileString command = pkgConfigPath + TINYDIR_STRING(" --libs"); - command += toFileString(args); - Result<ExecResult> execResult = exec(command.c_str()); + Result<ExecResult> execResult = exec(args); if(execResult.isErr()) return Result<string>::Err(execResult.getErrMsg()); @@ -179,21 +136,17 @@ namespace sibs } } - Result<string> PkgConfig::getDynamicLibsCflags(const vector<PackageListDependency*> &libs) + Result<string> PkgConfig::getDynamicLibsCflags(const vector<PackageListDependency> &libs) { if(libs.empty()) return Result<string>::Ok(""); - string args; - for(PackageListDependency *lib : libs) + std::vector<FileString> args = { pkgConfigPath, TINYDIR_STRING("--cflags"), TINYDIR_STRING("--") }; + for(const PackageListDependency &lib : libs) { - args += " '"; - args += lib->name; - args += "'"; + args.push_back(toFileString(lib.name)); } - FileString command = pkgConfigPath + TINYDIR_STRING(" --cflags"); - command += toFileString(args); - Result<ExecResult> execResult = exec(command.c_str()); + Result<ExecResult> execResult = exec(args); if(execResult.isErr()) return Result<string>::Err(execResult.getErrMsg()); @@ -222,7 +175,7 @@ namespace sibs } } - Result<PkgConfigFlags> PkgConfig::getDynamicLibsFlags(const vector<PackageListDependency*> &libs) + Result<PkgConfigFlags> PkgConfig::getDynamicLibsFlags(const vector<PackageListDependency> &libs) { PkgConfigFlags flags; diff --git a/src/main.cpp b/src/main.cpp index 713514d..ecb0e99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -80,9 +80,6 @@ using namespace std::chrono; // TODO: Add program command for generating compile_commands.json without compiling code, without using Ninja -// TODO: Make Process::exec safe to use. Currently you pass an argument and it's run as a command, but the string can be escaped to perform malicious acts. -// Process::exec should be modified to take a list of arguments to execute command with. - // TODO: Verify paths (test path, ignore dirs, include dirs, expose include dir, sibs test --file dir) are sub directory of the project // TODO: When creating a package with `sibs package` copy LICENSE files into archive. @@ -97,6 +94,8 @@ using namespace std::chrono; // TODO: If dependencies are using a version that is not within our dependency version range then ask the user if they still want to use the dependency (the closest matching dependency). // Currently if dependency version does not match, build will always fail with no option to ignore version mismatch. +// TODO: Check if ninja args are properly escaped. + #if OS_FAMILY == OS_FAMILY_POSIX #define fout std::cout #define ferr std::cerr @@ -314,7 +313,8 @@ struct MicrosoftBuildTool static MicrosoftBuildTool locateLatestMicrosoftBuildTool() { MicrosoftBuildTool result = { 0 }; - Result<ExecResult> execResult = exec(TINYDIR_STRING("locate_windows_sdk x64")); + // TODO: x86 + Result<ExecResult> execResult = exec({ TINYDIR_STRING("locate_windows_sdk"), TINYDIR_STRING("x64") }); if (execResult && execResult.unwrap().exitCode == 0) { auto &str = execResult.unwrap().execStdout; @@ -363,7 +363,7 @@ static void appendBuildToolToPathEnv() #endif } -static int buildProject(const FileString &projectPath, const FileString &projectConfFilePath, SibsConfig &sibsConfig, bool run, FileString run_args) +static int buildProject(const FileString &projectPath, const FileString &projectConfFilePath, SibsConfig &sibsConfig, bool run, const std::vector<FileString> &run_args) { FileString buildPath; readSibsConfig(projectPath, projectConfFilePath, sibsConfig, buildPath); @@ -455,7 +455,11 @@ static int buildProject(const FileString &projectPath, const FileString &project FileString executableName = toFileString(sibsConfig.getPackageName()); if(isSamePlatformFamily(sibsConfig.platform, PLATFORM_WIN)) executableName += TINYDIR_STRING(".exe"); - auto exec_result = exec(buildPath + TINYDIR_STRING("/") + executableName + TINYDIR_STRING(" ") + run_args, true); + + std::vector<FileString> args = { Path(buildPath).join(executableName).data }; + args.insert(args.end(), run_args.begin(), run_args.end()); + + auto exec_result = exec(args, true); if(!exec_result) { ferr << "Failed to execute" << (buildPath + TINYDIR_STRING("/") + executableName) << ", error: " << toFileString(exec_result.getErrMsg()) << endl; return 1; @@ -466,35 +470,6 @@ static int buildProject(const FileString &projectPath, const FileString &project return 0; } -#if OS_FAMILY == OS_FAMILY_WINDOWS -#define NATIVE_CHAR_PREFIX L -#else -#define NATIVE_CHAR_PREFIX -#endif - -static FileString replace_all(const _tinydir_char_t *str) { - FileString result = TINYDIR_STRING("'"); - while(*str != NATIVE_CHAR_PREFIX'\0') { - if(*str == NATIVE_CHAR_PREFIX'\'') - result += TINYDIR_STRING("\\'"); - else - result += *str; - ++str; - } - result += NATIVE_CHAR_PREFIX'\''; - return result; -} - -static FileString escape_args(const std::vector<const _tinydir_char_t*> &args) { - FileString result; - for(const _tinydir_char_t *arg : args) { - if(!result.empty()) - result += NATIVE_CHAR_PREFIX' '; - result += replace_all(arg); - } - return result; -} - static Sanitize sanitize_string_to_type(const _tinydir_char_t *str) { if(strcmp(str, TINYDIR_STRING("address")) == 0) return Sanitize::ADDRESS; @@ -517,7 +492,7 @@ static int buildProject(int argc, const _tinydir_char_t **argv, bool run) Sanitize sanitize = Sanitize::NONE; FileString platformName; bool use_lto = false; - std::vector<const _tinydir_char_t*> run_args; + std::vector<FileString> run_args; for(int i = 0; i < argc; ++i) { @@ -652,7 +627,7 @@ static int buildProject(int argc, const _tinydir_char_t **argv, bool run) sibsConfig.platform = platform; sibsConfig.setSanitize(sanitize); sibsConfig.use_lto = use_lto; - return buildProject(projectPath, projectConfFilePath, sibsConfig, run, escape_args(run_args)); + return buildProject(projectPath, projectConfFilePath, sibsConfig, run, run_args); } static int testProject(int argc, const _tinydir_char_t **argv) @@ -797,7 +772,7 @@ static int testProject(int argc, const _tinydir_char_t **argv) sibsConfig.zigTestFiles = move(filesToTest); sibsConfig.zigTestAllFiles = testAllFiles; - return buildProject(projectPath, projectConfFilePath, sibsConfig, false, TINYDIR_STRING("")); + return buildProject(projectPath, projectConfFilePath, sibsConfig, false, {}); } // Returns nullptr if @charToFind is not found @@ -844,10 +819,7 @@ static void createProjectFile(const FileString &projectFilePath, const string &f // so there is no reason to do it (right now) static Result<ExecResult> gitInitProject(const FileString &projectPath) { - FileString cmd = TINYDIR_STRING("git init \""); - cmd += projectPath; - cmd += TINYDIR_STRING("\""); - return exec(cmd.c_str()); + return exec({ TINYDIR_STRING("git"), TINYDIR_STRING("init"), TINYDIR_STRING("--"), projectPath }); } static bool gitIgnoreContainsSibs(const FileString &gitIgnoreFilePath) @@ -1236,7 +1208,7 @@ static int packageProject(int argc, const _tinydir_char_t **argv) sibsConfig.packaging = packagingType == PackagingType::STATIC; sibsConfig.bundling = (packagingType == PackagingType::BUNDLE) || (packagingType == PackagingType::BUNDLE_INSTALL); sibsConfig.use_lto = true; - int result = buildProject(projectPath, projectConfFilePath, sibsConfig, false, TINYDIR_STRING("")); + int result = buildProject(projectPath, projectConfFilePath, sibsConfig, false, {}); if(result != 0) return result; @@ -1244,8 +1216,8 @@ static int packageProject(int argc, const _tinydir_char_t **argv) { case PackagingType::STATIC: { - string packagePath = toUtf8(projectPath + TINYDIR_STRING("/sibs-build/") + toFileString(asString(sibsConfig.platform)) + TINYDIR_STRING("/package")); - printf("Project %s was successfully packaged and can be found at %s\n", sibsConfig.getPackageName().c_str(), packagePath.c_str()); + Path packagePath = Path(projectPath).join(TINYDIR_STRING("sibs-build")).join(toFileString(asString(sibsConfig.platform))).join(TINYDIR_STRING("package")); + printf("Project %s was successfully packaged and can be found at %s\n", sibsConfig.getPackageName().c_str(), packagePath.data.c_str()); break; } case PackagingType::BUNDLE: @@ -1262,21 +1234,11 @@ static int packageProject(int argc, const _tinydir_char_t **argv) break; } - FileString packagePath = projectPath + TINYDIR_STRING("/sibs-build/") + toFileString(asString(sibsConfig.platform)) + TINYDIR_STRING("/package"); - FileString executablePath = projectPath + TINYDIR_STRING("/sibs-build/") + toFileString(asString(sibsConfig.platform)) + TINYDIR_STRING("/release/")+ toFileString(sibsConfig.getPackageName()); + Path packagePath = Path(projectPath).join(TINYDIR_STRING("sibs-build")).join(toFileString(asString(sibsConfig.platform))).join(TINYDIR_STRING("package")); + Path executablePath = Path(projectPath).join(TINYDIR_STRING("sibs-build")).join(toFileString(asString(sibsConfig.platform))).join(TINYDIR_STRING("release")).join(toFileString(sibsConfig.getPackageName())); printf("Creating a package from project and dependencies...\n"); // args: executable_path program_version destination_path <--bundle|--bundle-install> - FileString cmd = TINYDIR_STRING("python3 \"") + - packageScriptPath + - TINYDIR_STRING("\" \"") + - executablePath + - TINYDIR_STRING("\" \"") + - toFileString(sibsConfig.version.toString()) + - TINYDIR_STRING("\" \"") + - packagePath + - TINYDIR_STRING("\" ") + - bundleType; - Result<ExecResult> bundleResult = exec(cmd.c_str(), true); + Result<ExecResult> bundleResult = exec({ TINYDIR_STRING("python3"), packageScriptPath, executablePath.data, toFileString(sibsConfig.version.toString()), packagePath.data, bundleType }, true); if(!bundleResult) { fprintf(stderr, "Error: failed to package project as a bundle, reason: %s\n", bundleResult.getErrMsg().c_str()); diff --git a/tests/src/confTest/confTest.cpp b/tests/src/confTest/confTest.cpp index 68e2225..c770266 100644 --- a/tests/src/confTest/confTest.cpp +++ b/tests/src/confTest/confTest.cpp @@ -3,6 +3,16 @@ using namespace sibs; +static FileString arg_list_to_string(const std::vector<FileString> &args) { + FileString result; + for(const FileString &arg : args) { + if(!result.empty()) + result += TINYDIR_STRING(" "); + result += TINYDIR_STRING("\"") + arg + TINYDIR_STRING("\""); + } + return result; +} + TEST_CASE("parse config") { SibsConfig sibsConfig(Compiler::GCC, TINYDIR_STRING("tests/src/confTest"), OPT_LEV_DEBUG, false); @@ -20,28 +30,28 @@ TEST_CASE("parse config") REQUIRE(containsPlatform(sibsConfig.getPlatforms(), PLATFORM_WIN64)); REQUIRE(sibsConfig.getPackageListDependencies().size() == 2); - for(auto *dep : sibsConfig.getPackageListDependencies()) + for(const auto &dep : sibsConfig.getPackageListDependencies()) { - REQUIRE(dep->getSource() == Dependency::Source::PACKAGE_LIST); + REQUIRE(dep.getSource() == Dependency::Source::PACKAGE_LIST); } - auto *xxhashDependency = sibsConfig.getPackageListDependencies()[0]->asPackageListDependency(); + auto *xxhashDependency = sibsConfig.getPackageListDependencies()[0].asPackageListDependency(); REQUIRE(xxhashDependency->name == "xxhash"); REQUIRE(xxhashDependency->version.toString() == ">=0.1.0 and <1.0.0"); - const auto &catch2Dependency = sibsConfig.getPackageListDependencies()[1]->asPackageListDependency(); + const auto &catch2Dependency = sibsConfig.getPackageListDependencies()[1].asPackageListDependency(); REQUIRE(catch2Dependency->name == "catch2"); REQUIRE(catch2Dependency->version.toString() == ">=1.0.0 and <2.0.0"); REQUIRE(sibsConfig.shouldUseCmake()); REQUIRE(sibsConfig.getCmakeDir() == TINYDIR_STRING("tests/src/confTest/cmakeGlobal")); - REQUIRE(sibsConfig.getCmakeArgs() == "-G Ninja \"-DCMAKE_BUILD_TYPE=Debug\" \"-DENTITYX_RUN_BENCHMARKS=0\""); + REQUIRE(arg_list_to_string(sibsConfig.getCmakeArgs()) == "\"-G\" \"Ninja\" \"-DCMAKE_BUILD_TYPE=Debug\" \"-DENTITYX_RUN_BENCHMARKS=0\""); REQUIRE(sibsConfig.getCmakeDirStatic() == TINYDIR_STRING("tests/src/confTest/cmakeStatic")); - REQUIRE(sibsConfig.getCmakeArgsStatic() == "-G Ninja \"-DCMAKE_BUILD_TYPE=Debug\" \"-DENTITYX_RUN_BENCHMARKS=0\" \"-DENTITYX_BUILD_TESTING=0\""); + REQUIRE(arg_list_to_string(sibsConfig.getCmakeArgsStatic()) == "\"-G\" \"Ninja\" \"-DCMAKE_BUILD_TYPE=Debug\" \"-DENTITYX_RUN_BENCHMARKS=0\" \"-DENTITYX_BUILD_TESTING=0\""); REQUIRE(sibsConfig.getCmakeDirDynamic() == TINYDIR_STRING("tests/src/confTest/cmakeDynamic")); - REQUIRE(sibsConfig.getCmakeArgsDynamic() == "-G Ninja \"-DCMAKE_BUILD_TYPE=Debug\" \"-DENTITYX_RUN_BENCHMARKS=0\" \"-DENTITYX_BUILD_TESTING=0\" \"-DENTITYX_BUILD_SHARED=1\""); + REQUIRE(arg_list_to_string(sibsConfig.getCmakeArgsDynamic()) == "\"-G\" \"Ninja\" \"-DCMAKE_BUILD_TYPE=Debug\" \"-DENTITYX_RUN_BENCHMARKS=0\" \"-DENTITYX_BUILD_TESTING=0\" \"-DENTITYX_BUILD_SHARED=1\""); } TEST_CASE("parse config - invalid object") |