aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Exec.cpp98
-rw-r--r--src/FileUtil.cpp49
-rw-r--r--src/main.cpp126
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)