From 017ec45e94204f977dcd7b04c8035d48f230ded3 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 31 Dec 2017 05:24:40 +0100 Subject: Sibs can now build itself on windows Fixed several bugs. The windows implementation IS QUICK AND DIRTY! It links things as static even if you wish to link as dynamic etc..... NEED TO FIX THIS !!! --- src/Conf.cpp | 226 +++++++++++++++++++++++++++++++++++++++++++++++------- src/FileUtil.cpp | 14 +++- src/GlobalLib.cpp | 97 +++++++++++++---------- src/main.cpp | 21 ++++- 4 files changed, 287 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/Conf.cpp b/src/Conf.cpp index 5282bb5..4217846 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -24,7 +24,8 @@ namespace sibs OPEN_BRACKET, CLOSING_BRACKET, EQUALS, - STRING + STRING, + COMMA }; const char *getTokenName(Token token) @@ -38,6 +39,7 @@ namespace sibs case Token::CLOSING_BRACKET: return "]"; case Token::EQUALS: return "="; case Token::STRING: return "string"; + case Token::COMMA: return ","; default: return "Unknown"; } } @@ -61,12 +63,12 @@ namespace sibs c = *code; } - if(isAlpha(c) || c == '_') + if(isIdentifierChar(c)) { char *startOfIdentifier = code.base(); ++code; c = *code; - while(isAlpha(c) || isDigit(c) || c == '_' || c == '-') + while(isIdentifierChar(c)) { ++code; c = *code; @@ -115,6 +117,11 @@ namespace sibs ++code; return Token::STRING; } + else if (c == ',') + { + ++code; + return Token::COMMA; + } else if(c == '\0') { return Token::END_OF_FILE; @@ -144,6 +151,11 @@ namespace sibs { return c >= '0' && c <= '9'; } + + bool isIdentifierChar(u32 c) + { + return isAlpha(c) || isDigit(c) || c == '_' || c == '-' || c == '.'; + } private: Token currentToken; u8string code; @@ -238,7 +250,7 @@ namespace sibs } } - void parseConfigFieldRhs(StringView fieldName) + void parseConfigFieldRhs(const StringView &fieldName) { Token token = tokenizer.nextToken(); if(token == Token::STRING) @@ -247,39 +259,48 @@ namespace sibs } else if(token == Token::OPEN_BRACKET) { + parseConfigFieldRhsList(fieldName); + } + else + { + string errMsg = "Expected string on right-hand side of field '"; + errMsg += string(fieldName.data, fieldName.size); + errMsg += "', got: "; + errMsg += getTokenName(token); + throw ParserException(errMsg); + } + } + + void parseConfigFieldRhsList(const StringView &fieldName) + { + Token token = tokenizer.nextToken(); + if (token == Token::CLOSING_BRACKET) return; + + vector values; + while (true) + { + if (token == Token::STRING) + values.push_back(tokenizer.getString()); + token = tokenizer.nextToken(); - if(token == Token::STRING) + if (token == Token::COMMA) { - StringView str = tokenizer.getString(); token = tokenizer.nextToken(); - if(token == Token::CLOSING_BRACKET) - { - vector values; - values.push_back(str); - callback->processField(fieldName, values); - } - else - { - string errMsg = "Expected ']' to close value list, got: "; - errMsg += getTokenName(token); - throw ParserException(errMsg); - } + continue; + } + else if (token == Token::CLOSING_BRACKET) + { + break; } else { - string errMsg = "Expected string value inside list in field definition, got: "; + string errMsg = "Expected list value to be followed by ']' or ',', got: "; errMsg += getTokenName(token); throw ParserException(errMsg); } } - else - { - string errMsg = "Expected string on right-hand side of field '"; - errMsg += string(fieldName.data, fieldName.size); - errMsg += "', got: "; - errMsg += getTokenName(token); - throw ParserException(errMsg); - } + + callback->processField(fieldName, values); } void parseConfigObject() @@ -330,6 +351,28 @@ namespace sibs return Parser::parse(code, callback); } + bool containsPlatform(const vector &platforms, Platform platform) + { + for (Platform vecPlatform : platforms) + { + if (vecPlatform == platform || vecPlatform == PLATFORM_ANY) + return true; + } + return false; + } + + const char* asString(Platform platform) + { + switch (platform) + { + case PLATFORM_ANY: return "any"; + case PLATFORM_LINUX32: return "linux32"; + case PLATFORM_LINUX64: return "linux64"; + case PLATFORM_WIN32: return "win32"; + case PLATFORM_WIN64: return "win64"; + } + } + const char* asString(OptimizationLevel optLevel) { switch(optLevel) @@ -361,6 +404,33 @@ namespace sibs return defines; } + void getLibFiles(const string &libPath, vector &outputFiles) + { + FileString nativePath = toFileString(libPath); + FileType fileType = getFileType(nativePath.c_str()); + switch (fileType) + { + case FileType::FILE_NOT_FOUND: + { + string errMsg = "Path not found: "; + errMsg += libPath; + throw ParserException(errMsg); + } + case FileType::REGULAR: + { + string errMsg = "Expected path "; + errMsg += libPath; + errMsg += " to be a directory, was a regular file"; + throw ParserException(errMsg); + } + } + + walkDirFiles(nativePath.c_str(), [&outputFiles](tinydir_file *file) + { + outputFiles.push_back(toUtf8(file->path)); + }); + } + void SibsConfig::processObject(StringView name) { currentObject = name; @@ -462,6 +532,108 @@ namespace sibs else throw ParserException("Expected package.include_dirs to be a list, was a single value"); } + else if (name.equals("platforms")) + { + if (value.isList()) + { + // TODO: Checking for duplicate declaration should be done in the config parser + if (!platforms.empty()) + throw ParserException("Found duplicate declaration of package.platforms"); + + for (const StringView &platform : value.asList()) + { + if (platform.equals("any")) + { + platforms.push_back(PLATFORM_ANY); + } + else if (platform.equals("linux32")) + { + platforms.push_back(PLATFORM_LINUX32); + } + else if (platform.equals("linux64")) + { + platforms.push_back(PLATFORM_LINUX64); + } + else if (platform.equals("win32")) + { + platforms.push_back(PLATFORM_WIN32); + } + else if (platform.equals("win64")) + { + platforms.push_back(PLATFORM_WIN64); + } + else + { + string errMsg = "package.platforms contains invalid platform \""; + errMsg += string(platform.data, platform.size); + errMsg += "\". Expected platform to be one of: any, linux32, linux64, win32 or win64"; + throw ParserException(errMsg); + } + } + } + else + throw ParserException("Expected package.platforms to be a list, was a single value"); + } + } + else if (currentObject.equals(CONFIG_SYSTEM_PLATFORM)) + { + if (name.equals("expose_include_dirs")) + { + if (value.isList()) + { + for (const StringView &includeDir : value.asList()) + { + exposeIncludeDirs.emplace_back(string(includeDir.data, includeDir.size)); + } + } + else + { + string errMsg = "Expected "; + errMsg += string(currentObject.data, currentObject.size); + errMsg += " to be a list, was a single value"; + throw ParserException(errMsg); + } + } + } + else if (currentObject.equals(CONFIG_STATIC_DEBUG_PLATFORM)) + { + if (name.equals("lib")) + { + if (value.isSingle()) + { + string debugStaticLibPath = toUtf8(projectPath); + debugStaticLibPath += "/"; + debugStaticLibPath += string(value.asSingle().data, value.asSingle().size); + getLibFiles(debugStaticLibPath, debugStaticLibs); + } + else + { + string errMsg = "Expected "; + errMsg += string(currentObject.data, currentObject.size); + errMsg += " to be a single value, was a list"; + throw ParserException(errMsg); + } + } + } + else if (currentObject.equals(CONFIG_STATIC_RELEASE_PLATFORM)) + { + if (name.equals("lib")) + { + if (value.isSingle()) + { + string releaseStaticLibPath = toUtf8(projectPath); + releaseStaticLibPath += "/"; + releaseStaticLibPath += string(value.asSingle().data, value.asSingle().size); + getLibFiles(releaseStaticLibPath, releaseStaticLibs); + } + else + { + string errMsg = "Expected "; + errMsg += string(currentObject.data, currentObject.size); + errMsg += " to be a single value, was a list"; + throw ParserException(errMsg); + } + } } else if(currentObject.equals("dependencies")) { diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index 5cf8377..db68bb4 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -68,6 +68,16 @@ namespace sibs LocalFree(messageBuffer); return message; } + + void replaceChar(FileString &input, wchar_t charToReplace, wchar_t charToReplaceWith) + { + for (int i = 0; i < input.size(); ++i) + { + wchar_t c = input[i]; + if (c == charToReplace) + input[i] = charToReplaceWith; + } + } #endif #if OS_FAMILY == OS_FAMILY_POSIX @@ -100,7 +110,7 @@ namespace sibs { tinydir_file file; tinydir_readfile(&dir, &file); - if(_tinydir_strcmp(file.name, TINYDIR_STRING(".")) != 0 && _tinydir_strcmp(file.name, TINYDIR_STRING("..")) != 0) + if(_tinydir_strncmp(file.name, TINYDIR_STRING("."), 1) != 0) callbackFunc(&file); tinydir_next(&dir); } @@ -138,7 +148,7 @@ namespace sibs tinydir_readfile(&dir, &file); if(file.is_reg) callbackFunc(&file); - else if(_tinydir_strcmp(file.name, TINYDIR_STRING(".")) != 0 && _tinydir_strcmp(file.name, TINYDIR_STRING("..")) != 0) + else if(_tinydir_strncmp(file.name, TINYDIR_STRING("."), 1) != 0) walkDirFilesRecursive(file.path, callbackFunc); tinydir_next(&dir); } diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index fde7797..ac8fd59 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -64,11 +64,11 @@ namespace sibs return _tinydir_strncmp(path, subPathOf.c_str(), subPathOf.size()) == 0; } - Result GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc) + Result GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const FileString &globalLibRootDir, const std::string &name, const std::string &version, LinkerFlagCallbackFunc staticLinkerFlagCallbackFunc, LinkerFlagCallbackFunc dynamicLinkerFlagCallbackFunc, GlobalIncludeDirCallbackFunc globalIncludeDirCallback) { Result packageExistsResult = validatePackageExists(globalLibRootDir, name); - if(packageExistsResult.isErr()) - return Result::Err(packageExistsResult); + if (packageExistsResult.isErr()) + return packageExistsResult; #if OS_FAMILY == OS_FAMILY_POSIX FileString namePlatformNative = name; @@ -95,7 +95,7 @@ namespace sibs }); if(foundVersion.empty()) - return Result::Err("Global lib dependency found, but version doesn't match dependency version", DependencyError::DEPENDENCY_VERSION_NO_MATCH); + return Result::Err("Global lib dependency found, but version doesn't match dependency version", DependencyError::DEPENDENCY_VERSION_NO_MATCH); packageDir += TINYDIR_STRING("/"); packageDir += versionPlatformNative; @@ -111,41 +111,55 @@ namespace sibs string errMsg = "Global lib dependency found: "; errMsg += toUtf8(packageDir); errMsg += ", but it's missing a project.conf file"; - return Result::Err(errMsg); + return Result::Err(errMsg); } case FileType::DIRECTORY: { string errMsg = "Global lib dependency found: "; errMsg += toUtf8(packageDir); errMsg += ", but it's corrupt (Found directory instead of file)"; - return Result::Err(errMsg); + return Result::Err(errMsg); } } SibsConfig sibsConfig(parentConfig.getCompiler(), packageDir, parentConfig.getOptimizationLevel()); Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); - if(result.isErr()) - return Result::Err(result.getErrMsg()); + if (result.isErr()) + return result; if(sibsConfig.getPackageName().empty()) - return Result::Err("project.conf is missing required field package.name"); + return Result::Err("project.conf is missing required field package.name"); if(sibsConfig.getPackageType() == PackageType::EXECUTABLE) { string errMsg = "The dependency "; errMsg += name; errMsg += " is an executable. Only libraries can be dependencies"; - return Result::Err(errMsg); + return Result::Err(errMsg); + } + + if (!containsPlatform(sibsConfig.getPlatforms(), SYSTEM_PLATFORM)) + { + string errMsg = "The dependency "; + errMsg += name; + errMsg += " does not support your platform ("; + errMsg += asString(SYSTEM_PLATFORM); + errMsg += ")"; + return Result::Err(errMsg); } backend::Ninja ninja; FileWalkCallbackFunc collectSourceFiles = [&ninja, &sibsConfig, &collectSourceFiles](tinydir_file *file) { + FileString pathNative = file->path; +#if OS_FAMILY == OS_FAMILY_WINDOWS + replaceChar(pathNative, L'/', L'\\'); +#endif if(file->is_reg) { if (isSourceFile(file)) { - string fileNameNative = toUtf8(file->path + sibsConfig.getProjectPath().size() + 1); + string fileNameNative = toUtf8(pathNative.c_str() + sibsConfig.getProjectPath().size() + 1); ninja.addSourceFile(fileNameNative.c_str()); } else @@ -158,9 +172,9 @@ namespace sibs // TODO: If compiling without "test" option, do not add test source dir to ninja. Ninja logic will then not build tests... // OR I believe there is no reason to run tests in dependencies. The main projects tests should cover that? // But you might want to know exactly which dependency is causing issue and which part of it... - if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(file->path, sibsConfig.getTestPath())) + if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(pathNative.c_str(), sibsConfig.getTestPath())) { - string filePathUtf8 = toUtf8(file->path); + string filePathUtf8 = toUtf8(pathNative.c_str()); ninja.addTestSourceDir(filePathUtf8.c_str()); } else @@ -169,24 +183,39 @@ namespace sibs }; walkDir(packageDir.c_str(), collectSourceFiles); - // TODO: Dont do this. Unit tests wont be built. Need to call the below ninja.createBuildFile - if(ninja.getSourceFiles().empty()) - { - return Result::Ok("No source files in dependency (header only library?)"); - } - else + FileString buildPath = packageDir + TINYDIR_STRING("/sibs-build/"); + switch (sibsConfig.getOptimizationLevel()) { - FileString buildPath = packageDir + TINYDIR_STRING("/sibs-build/"); - switch(sibsConfig.getOptimizationLevel()) + case OPT_LEV_DEBUG: { - case OPT_LEV_DEBUG: - buildPath += TINYDIR_STRING("debug"); - break; - case OPT_LEV_RELEASE: - buildPath += TINYDIR_STRING("release"); - break; + buildPath += TINYDIR_STRING("debug"); + // TODO: Check if this dependency is static or dynamic and decide which lib path to use from that + for(const string &staticLib : sibsConfig.getDebugStaticLibs()) + { + string staticLibCmd = "\""; + staticLibCmd += staticLib; + staticLibCmd += "\""; + staticLinkerFlagCallbackFunc(staticLibCmd); + } + break; + } + case OPT_LEV_RELEASE: + { + buildPath += TINYDIR_STRING("release"); + // TODO: Check if this dependency is static or dynamic and decide which lib path to use from that + for (const string &staticLib : sibsConfig.getReleaseStaticLibs()) + { + string staticLibCmd = "\""; + staticLibCmd += staticLib; + staticLibCmd += "\""; + staticLinkerFlagCallbackFunc(staticLibCmd); + } + break; } + } + if (!ninja.getSourceFiles().empty()) + { string libPath = toUtf8(buildPath); switch (sibsConfig.getCompiler()) { @@ -235,19 +264,9 @@ namespace sibs break; } } - - // TODO: Use different directories depending on the project type, but .o build files should be in the same directory - // no matter what project type, since they are used for executables, static/dynamic libraries - Result createBuildDirResult = createDirectoryRecursive(buildPath.c_str()); - if(createBuildDirResult.isErr()) - return Result::Err(createBuildDirResult); - - Result buildFileResult = ninja.build(sibsConfig, buildPath.c_str(), staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc); - if (buildFileResult.isErr()) - return Result::Err(buildFileResult.getErrMsg()); - - return Result::Ok(libPath); } + + return ninja.build(sibsConfig, buildPath.c_str(), staticLinkerFlagCallbackFunc, dynamicLinkerFlagCallbackFunc, globalIncludeDirCallback); } Result GlobalLib::downloadDependency(const Dependency &dependency) diff --git a/src/main.cpp b/src/main.cpp index 855b1dc..1c2492c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -208,17 +208,32 @@ int buildProject(int argc, const _tinydir_char_t **argv) exit(10); } + if (!containsPlatform(sibsConfig.getPlatforms(), SYSTEM_PLATFORM)) + { + string errMsg = "The project "; + errMsg += sibsConfig.getPackageName(); + errMsg += " does not support your platform ("; + errMsg += asString(SYSTEM_PLATFORM); + errMsg += ")"; + cerr << errMsg << endl; + exit(11); + } + //string projectSrcPath = projectPath + "/src"; //validateDirectoryPath(projectSrcPath.c_str()); backend::Ninja ninja; FileWalkCallbackFunc collectSourceFiles = [&ninja, &sibsConfig, &collectSourceFiles](tinydir_file *file) { + FileString pathNative = file->path; +#if OS_FAMILY == OS_FAMILY_WINDOWS + replaceChar(pathNative, L'/', L'\\'); +#endif if(file->is_reg) { if (isSourceFile(file)) { - string filePathUtf8 = toUtf8(file->path + sibsConfig.getProjectPath().size()); + string filePathUtf8 = toUtf8(pathNative.c_str() + sibsConfig.getProjectPath().size()); ninja.addSourceFile(filePathUtf8.c_str()); } else @@ -229,9 +244,9 @@ int buildProject(int argc, const _tinydir_char_t **argv) else { // TODO: If compiling without "test" option, do not add test source dir to ninja. Ninja logic will then not build tests - if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(file->path, sibsConfig.getTestPath())) + if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(pathNative.c_str(), sibsConfig.getTestPath())) { - string filePathUtf8 = toUtf8(file->path); + string filePathUtf8 = toUtf8(pathNative.c_str()); ninja.addTestSourceDir(filePathUtf8.c_str()); } else -- cgit v1.2.3