diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Exec.cpp | 98 | ||||
-rw-r--r-- | src/FileUtil.cpp | 49 | ||||
-rw-r--r-- | src/main.cpp | 126 |
3 files changed, 194 insertions, 79 deletions
diff --git a/src/Exec.cpp b/src/Exec.cpp index 2eac8bc..6c1a72e 100644 --- a/src/Exec.cpp +++ b/src/Exec.cpp @@ -3,24 +3,28 @@ using namespace std; +const int BUFSIZE = 1024; + +// TODO: Redirect stderr to namespace sibs { #if OS_FAMILY == OS_FAMILY_POSIX Result<ExecResult> exec(const _tinydir_char_t *cmd, bool print) { - char buffer[128]; - std::string result; + 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, 128, pipe)) + int bytesRead = fgets(buffer, BUFSIZE, pipe); + if(bytesRead > 0) { - result += buffer; + execStdout.append(buffer, bytesRead); if(print) - printf("%s", buffer); + printf("%.*s", bytesRead, buffer); } } @@ -29,7 +33,7 @@ namespace sibs { int returned = WEXITSTATUS(processCloseResult); ExecResult execResult; - execResult.execStdout = result; + execResult.execStdout = move(execStdout); execResult.exitCode = returned; return Result<ExecResult>::Ok(execResult); } @@ -55,48 +59,80 @@ namespace sibs } } #else - // TODO(Windows): Redirect stdout (and stderr) to string + // 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) { - char buffer[128]; - std::string result; + FileString cmdNonConst = cmd; + std::string execStdout; - STARTUPINFO startupInfo; - ZeroMemory(&startupInfo, sizeof(startupInfo)); - startupInfo.cb = sizeof(startupInfo); + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = nullptr; - PROCESS_INFORMATION processInfo; - ZeroMemory(&processInfo, sizeof(processInfo)); + HANDLE childReadHandle = nullptr; + HANDLE childStdoutHandle = nullptr; - DWORD exitCode; - - if (!CreateProcess(NULL, (LPWSTR)cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) + if (!CreatePipe(&childReadHandle, &childStdoutHandle, &saAttr, 0)) { string errMsg = "exec unexpected error: "; errMsg += toUtf8(getLastErrorAsString()); return Result<ExecResult>::Err(errMsg); } - WaitForSingleObject(processInfo.hProcess, INFINITE); - GetExitCodeProcess(processInfo.hProcess, &exitCode); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); + if (!SetHandleInformation(childReadHandle, HANDLE_FLAG_INHERIT, 0)) + goto cleanupAndExit; + + PROCESS_INFORMATION piProcInfo; + ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); + + STARTUPINFO siStartInfo; + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = nullptr; + siStartInfo.hStdOutput = childStdoutHandle; + siStartInfo.hStdInput = nullptr; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + DWORD exitCode; + + if (!CreateProcessW(nullptr, (LPWSTR)cmdNonConst.data(), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo)) + goto cleanupAndExit; + + WaitForSingleObject(piProcInfo.hProcess, INFINITE); + GetExitCodeProcess(piProcInfo.hProcess, &exitCode); + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + 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); + } - if (exitCode == 0) { ExecResult execResult; - execResult.execStdout = result; + execResult.execStdout = move(execStdout); execResult.exitCode = exitCode; + CloseHandle(childReadHandle); return Result<ExecResult>::Ok(execResult); } - else - { - string errMsg = "Exited with non-zero exit code ("; - errMsg += to_string(exitCode); - errMsg += "): "; - errMsg += toUtf8(getLastErrorAsString()); - return Result<ExecResult>::Err(errMsg); - } + + cleanupAndExit: + string errMsg = "exec unexpected error: "; + errMsg += toUtf8(getLastErrorAsString()); + CloseHandle(childReadHandle); + CloseHandle(childStdoutHandle); + return Result<ExecResult>::Err(errMsg); } #endif } diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index 53430c4..77669e5 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -489,25 +489,38 @@ namespace sibs } #endif - // TODO: Support better path equality check. For example if path contains several slashes in a row: /home/userName/.sibs//lib////libraryName - // then it should equal: /home/userName/.sibs/lib/libraryName - // Maybe check with OS operation if they refer to the same inode? + // TODO: Support better path equality check. Maybe check with OS operation if they refer to the same inode? bool pathEquals(const std::string &path, const std::string &otherPath) { - if(path.size() != otherPath.size()) - return false; - - size_t size = path.size(); - for(size_t i = 0; i < size; ++i) - { - char c = path[i]; - char otherC = otherPath[i]; - if(c == '\\') c = '/'; - if(otherC == '\\') otherC = '/'; - if(c != otherC) - return false; - } - - return true; + size_t pathIndex = 0; + size_t otherPathIndex = 0; + + while (true) + { + while (pathIndex < path.size() && (path[pathIndex] == '/' || path[pathIndex] == '\\')) + { + ++pathIndex; + } + + while (otherPathIndex < otherPath.size() && (otherPath[otherPathIndex] == '/' || otherPath[otherPathIndex] == '\\')) + { + ++otherPathIndex; + } + + if (pathIndex < path.size() && otherPathIndex < otherPath.size()) + { + if (path[pathIndex] != otherPath[otherPathIndex]) + return false; + + ++pathIndex; + ++otherPathIndex; + } + else + { + break; + } + } + + return pathIndex == path.size() && otherPathIndex == otherPath.size(); } } diff --git a/src/main.cpp b/src/main.cpp index 38e68f0..cb1c611 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,7 +112,7 @@ static string SIBS_GITIGNORE_FILES = "tests/sibs-build/\n" "tests/compile_commands.json\n"; -void usage() +static void usage() { printf("Usage: sibs COMMAND\n\n"); printf("Simple Build System for Native Languages\n\n"); @@ -124,7 +124,7 @@ void usage() exit(1); } -void usageBuild() +static void usageBuild() { printf("Usage: sibs build [project_path] [--debug|--release] [--sanitize]\n\n"); printf("Build a sibs project\n\n"); @@ -141,7 +141,7 @@ void usageBuild() exit(1); } -void usageNew() +static void usageNew() { printf("Usage: sibs new <project_name> <--exec|--static|--dynamic> [--lang c|c++|zig]\n\n"); printf("Create a new sibs project\n\n"); @@ -156,7 +156,7 @@ void usageNew() exit(1); } -void usageTest() +static void usageTest() { printf("Usage: sibs test [project_path] [--no-sanitize] [--file filepath...|--all-files]\n\n"); printf("Build and run tests for a sibs project\n\n"); @@ -171,7 +171,7 @@ void usageTest() exit(1); } -void usageInit() +static void usageInit() { printf("Usage: sibs init [project_path] <--exec|--static|--dynamic> [--lang c|c++|zig]\n\n"); printf("Create sibs project structure in an existing directory\n\n"); @@ -187,7 +187,7 @@ void usageInit() exit(1); } -void validateDirectoryPath(const _tinydir_char_t *projectPath) +static void validateDirectoryPath(const _tinydir_char_t *projectPath) { FileType projectPathFileType = getFileType(projectPath); if(projectPathFileType == FileType::FILE_NOT_FOUND) @@ -204,7 +204,7 @@ void validateDirectoryPath(const _tinydir_char_t *projectPath) } } -void validateFilePath(const _tinydir_char_t *projectConfPath) +static void validateFilePath(const _tinydir_char_t *projectConfPath) { FileType projectConfFileType = getFileType(projectConfPath); if(projectConfFileType == FileType::FILE_NOT_FOUND) @@ -221,20 +221,84 @@ void validateFilePath(const _tinydir_char_t *projectConfPath) } } -bool isPathSubPathOf(const FileString &path, const FileString &subPathOf) +static bool isPathSubPathOf(const FileString &path, const FileString &subPathOf) { return _tinydir_strncmp(path.c_str(), subPathOf.c_str(), subPathOf.size()) == 0; } -static bool doesProgramExist(const _tinydir_char_t *programName) +#if OS_FAMILY == OS_FAMILY_WINDOWS +static char* join(const char *str1, const char *str2, const char separator) { - FileString cmd = FileString(programName) + TINYDIR_STRING(" --version"); - Result<sibs::ExecResult> result = exec(cmd.c_str()); - bool programNotFound = !result && result.getErrorCode() == 127; - return !programNotFound; + int str1len = strlen(str1); + int str2len = strlen(str2); + char *result = new char[str1len + 1 + str2len + 1]; + memcpy(result, str1, str1len); + result[str1len] = separator; + memcpy(result + str1len + 1, str2, str2len); + result[str1len + 1 + str2len] = '\0'; + return result; } -int buildProject(const FileString &projectPath, const FileString &projectConfFilePath, SibsConfig &sibsConfig) +struct MicrosoftBuildTool +{ + // 0 if version not found + int version; + // empty if not found + char binPath[_TINYDIR_PATH_MAX]; + // empty if not found + char umLibPath[_TINYDIR_PATH_MAX]; + // empty if not found + char ucrtLibPath[_TINYDIR_PATH_MAX]; + + bool found() + { + return version != 0; + } +}; + +static MicrosoftBuildTool locateLatestMicrosoftBuildTool() +{ + MicrosoftBuildTool result = { 0 }; + Result<ExecResult> execResult = exec(TINYDIR_STRING("locate_windows_sdk")); + if (execResult && execResult.unwrap().exitCode == 0) + { + auto &str = execResult.unwrap().execStdout; + sscanf(execResult.unwrap().execStdout.c_str(), "%d %[^\r\n] %[^\r\n] %[^\r\n]", &result.version, result.binPath, result.umLibPath, result.ucrtLibPath); + } + return result; +} + +static void appendMicrosoftBuildToolToPathEnv() +{ + MicrosoftBuildTool msBuildTool = locateLatestMicrosoftBuildTool(); + if (msBuildTool.found()) + { + fprintf(stderr, "Located microsoft build tools at %s\n", msBuildTool.binPath); + fprintf(stderr, "Located microsoft build libraries at %s;%s\n", msBuildTool.umLibPath, msBuildTool.ucrtLibPath); + + if (const char *pathEnv = getenv("PATH")) + { + // We do not free this because it needs to live as long as it's used as env (in _putenv) + if (_putenv_s("PATH", join(pathEnv, msBuildTool.binPath, ';')) != 0) + fprintf(stderr, "Warning: Failed to add microsoft build tools to PATH env\n"); + } + + // We do not free this because it needs to live as long as it's used as env (in _putenv) + if (_putenv_s("LIB", join(msBuildTool.umLibPath, msBuildTool.ucrtLibPath, ';')) != 0) + fprintf(stderr, "Warning: Failed to add microsoft build libraries to LIB env\n"); + } +} +#endif + +static void appendBuildToolToPathEnv() +{ +#if OS_FAMILY == OS_FAMILY_WINDOWS + // TODO: We shouldn't do this if user wants to compile with clang/mingw? + appendMicrosoftBuildToolToPathEnv(); +#endif +} + +static int buildProject(const FileString &projectPath, const FileString &projectConfFilePath, SibsConfig &sibsConfig) { FileString buildPath; readSibsConfig(projectPath, projectConfFilePath, sibsConfig, buildPath); @@ -317,7 +381,7 @@ int buildProject(const FileString &projectPath, const FileString &projectConfFil return 0; } -int buildProject(int argc, const _tinydir_char_t **argv) +static int buildProject(int argc, const _tinydir_char_t **argv) { if(argc > 3) usageBuild(); @@ -404,7 +468,7 @@ int buildProject(int argc, const _tinydir_char_t **argv) return buildProject(projectPath, projectConfFilePath, sibsConfig); } -int testProject(int argc, const _tinydir_char_t **argv) +static int testProject(int argc, const _tinydir_char_t **argv) { if(argc > 2) usageTest(); @@ -546,7 +610,7 @@ int testProject(int argc, const _tinydir_char_t **argv) } // Returns nullptr if @charToFind is not found -const _tinydir_char_t* findLastOf(const _tinydir_char_t *str, const int strSize, const char charToFind) +static const _tinydir_char_t* findLastOf(const _tinydir_char_t *str, const int strSize, const char charToFind) { for(int i = strSize; i >= 0; --i) { @@ -556,7 +620,7 @@ const _tinydir_char_t* findLastOf(const _tinydir_char_t *str, const int strSize, return nullptr; } -Result<bool> newProjectCreateConf(const string &projectName, const string &projectType, const FileString &projectPath) +static Result<bool> newProjectCreateConf(const string &projectName, const string &projectType, const FileString &projectPath) { string projectConfStr = "[package]\n"; projectConfStr += "name = \"" + projectName + "\"\n"; @@ -574,12 +638,12 @@ Result<bool> newProjectCreateConf(const string &projectName, const string &proje return fileWrite(projectConfPath.c_str(), projectConfStr.c_str()); } -Result<bool> createDirectoryRecursive(const FileString &dir) +static Result<bool> createDirectoryRecursive(const FileString &dir) { return createDirectoryRecursive(dir.c_str()); } -void createProjectFile(const FileString &projectFilePath, const string &fileContent) +static void createProjectFile(const FileString &projectFilePath, const string &fileContent) { Result<bool> fileOverwriteResult = fileOverwrite(projectFilePath.c_str(), fileContent.c_str()); if(fileOverwriteResult.isErr()) @@ -591,7 +655,7 @@ void createProjectFile(const FileString &projectFilePath, const string &fileCont // This can be replaced with createDirectory and fileOverwrite, but it's not important // so there is no reason to do it (right now) -Result<ExecResult> gitInitProject(const FileString &projectPath) +static Result<ExecResult> gitInitProject(const FileString &projectPath) { FileString cmd = TINYDIR_STRING("git init \""); cmd += projectPath; @@ -599,7 +663,7 @@ Result<ExecResult> gitInitProject(const FileString &projectPath) return exec(cmd.c_str()); } -bool gitIgnoreContainsSibs(const FileString &gitIgnoreFilePath) +static bool gitIgnoreContainsSibs(const FileString &gitIgnoreFilePath) { Result<StringView> fileContentResult = getFileContent(gitIgnoreFilePath.c_str()); if(!fileContentResult) return false; @@ -611,7 +675,7 @@ bool gitIgnoreContainsSibs(const FileString &gitIgnoreFilePath) return containsSibs; } -void gitIgnoreAppendSibs(const FileString &gitIgnoreFilePath) +static void gitIgnoreAppendSibs(const FileString &gitIgnoreFilePath) { Result<StringView> fileContentResult = getFileContent(gitIgnoreFilePath.c_str()); string fileContentNew; @@ -627,10 +691,10 @@ void gitIgnoreAppendSibs(const FileString &gitIgnoreFilePath) fileContentNew += SIBS_GITIGNORE_FILES; Result<bool> result = fileOverwrite(gitIgnoreFilePath.c_str(), { fileContentNew.data(), fileContentNew.size() }); if(!result) - ferr << "Failed to add sibs to .gitignore, reason: " << result.getErrMsg() << endl; + ferr << "Failed to add sibs to .gitignore, reason: " << toFileString(result.getErrMsg()) << endl; } -int initProject(int argc, const _tinydir_char_t **argv) +static int initProject(int argc, const _tinydir_char_t **argv) { FileString projectPath; const _tinydir_char_t *projectType = nullptr; @@ -785,7 +849,7 @@ int initProject(int argc, const _tinydir_char_t **argv) auto createProjectConfResult = newProjectCreateConf(projectName, projectTypeConf, projectPath); if(!createProjectConfResult) { - ferr << "A project already exists in the directory " << projectPath << ". Error: failed to create project.conf, reason: " << createProjectConfResult.getErrMsg() << endl; + ferr << "A project already exists in the directory " << projectPath << ". Error: failed to create project.conf, reason: " << toFileString(createProjectConfResult.getErrMsg()) << endl; exit(20); } createDirectoryRecursive(projectPath + TINYDIR_STRING("/src")); @@ -830,7 +894,7 @@ int initProject(int argc, const _tinydir_char_t **argv) return 0; } -void newProjectCreateMainDir(const FileString &projectPath) +static void newProjectCreateMainDir(const FileString &projectPath) { Result<bool> createProjectDirResult = createDirectoryRecursive(projectPath.c_str()); if(createProjectDirResult.isErr()) @@ -840,7 +904,7 @@ void newProjectCreateMainDir(const FileString &projectPath) } } -void checkFailCreateSubDir(Result<bool> createSubDirResult) +static void checkFailCreateSubDir(Result<bool> createSubDirResult) { if(!createSubDirResult) { @@ -849,7 +913,7 @@ void checkFailCreateSubDir(Result<bool> createSubDirResult) } } -int newProject(int argc, const _tinydir_char_t **argv) +static int newProject(int argc, const _tinydir_char_t **argv) { string projectName; const _tinydir_char_t *projectType = nullptr; @@ -918,7 +982,7 @@ int newProject(int argc, const _tinydir_char_t **argv) { if(!projectName.empty()) { - ferr << "Error: Project name was defined more than once. First defined as " << projectName << " then as " << arg << endl; + ferr << "Error: Project name was defined more than once. First defined as " << toFileString(projectName) << " then as " << arg << endl; usageNew(); } projectName = toUtf8(arg); @@ -1041,6 +1105,7 @@ int wmain(int argc, const _tinydir_char_t **argv) const _tinydir_char_t **subCommandArgPtr = argv + i + 1; if(_tinydir_strcmp(arg, TINYDIR_STRING("build")) == 0) { + appendBuildToolToPathEnv(); return buildProject(subCommandArgCount, subCommandArgPtr); } else if(_tinydir_strcmp(arg, TINYDIR_STRING("new")) == 0) @@ -1049,6 +1114,7 @@ int wmain(int argc, const _tinydir_char_t **argv) } else if(_tinydir_strcmp(arg, TINYDIR_STRING("test")) == 0) { + appendBuildToolToPathEnv(); return testProject(subCommandArgCount, subCommandArgPtr); } else if(_tinydir_strcmp(arg, TINYDIR_STRING("init")) == 0) |