From 98ad7dd049a366e21d60a34548736a3c8ef72877 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 30 Dec 2017 04:32:49 +0100 Subject: Add support for windows (ugly fast solution) --- src/Archive.cpp | 38 +++++---- src/Conf.cpp | 16 ++-- src/Exec.cpp | 49 ++++++++++- src/FileUtil.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++++++-------- src/GlobalLib.cpp | 157 ++++++++++++++++++++++------------ src/PkgConfig.cpp | 4 +- src/curl.cpp | 21 +++-- src/main.cpp | 160 ++++++++++++++++++++--------------- 8 files changed, 502 insertions(+), 189 deletions(-) (limited to 'src') diff --git a/src/Archive.cpp b/src/Archive.cpp index 08ab42b..506b020 100644 --- a/src/Archive.cpp +++ b/src/Archive.cpp @@ -6,6 +6,14 @@ using namespace std; +#if OS_FAMILY == OS_FAMILY_POSIX +#define archive_read_open_filename_native archive_read_open_filename +#define archive_entry_pathname_native archive_entry_pathname +#else +#define archive_read_open_filename_native archive_read_open_filename_w +#define archive_entry_pathname_native archive_entry_pathname_w +#endif + class FileHandler { DISABLE_COPY(FileHandler) @@ -53,7 +61,7 @@ namespace sibs } } - Result Archive::extract(const char *source, const char *destination) + Result Archive::extract(const _tinydir_char_t *source, const _tinydir_char_t *destination) { struct archive *a; struct archive *ext; @@ -67,7 +75,7 @@ namespace sibs flags |= ARCHIVE_EXTRACT_ACL; flags |= ARCHIVE_EXTRACT_FFLAGS; - string rootName; + FileString rootName; a = archive_read_new(); archive_read_support_format_all(a); @@ -77,10 +85,10 @@ namespace sibs archive_write_disk_set_standard_lookup(ext); - if ((r = archive_read_open_filename(a, source, 10240))) + if ((r = archive_read_open_filename_native(a, source, 10240))) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); return Result::Err(errMsg); } @@ -92,7 +100,7 @@ namespace sibs else if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(a); return Result::Err(errMsg); @@ -100,25 +108,27 @@ namespace sibs else if (r < ARCHIVE_WARN) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(a); return Result::Err(errMsg); } - const char* currentFile = archive_entry_pathname(entry); + const _tinydir_char_t* currentFile = archive_entry_pathname_native(entry); if(rootName.empty()) rootName = currentFile; - std::string fullOutputPath = destination; + FileString fullOutputPath = destination; fullOutputPath += (currentFile + (rootName.empty() ? 0 : rootName.size() - 1)); - archive_entry_set_pathname(entry, fullOutputPath.c_str()); + // TODO: Verify if this really works. Why doesn't libarchive have wide string version of archive_entry_set_pathname? + string fullOutputPathUtf8 = toUtf8(fullOutputPath); + archive_entry_set_pathname(entry, fullOutputPathUtf8.c_str()); r = archive_write_header(ext, entry); if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); @@ -128,7 +138,7 @@ namespace sibs if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); @@ -136,7 +146,7 @@ namespace sibs else if (r < ARCHIVE_WARN) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); @@ -147,7 +157,7 @@ namespace sibs if (r < ARCHIVE_OK) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); @@ -155,7 +165,7 @@ namespace sibs else if (r < ARCHIVE_WARN) { string errMsg = "Failed to extract archive: "; - errMsg += source; + errMsg += toUtf8(source); errMsg += "; reason: "; errMsg += archive_error_string(ext); return Result::Err(errMsg); diff --git a/src/Conf.cpp b/src/Conf.cpp index d6aee2c..743942a 100644 --- a/src/Conf.cpp +++ b/src/Conf.cpp @@ -1,5 +1,5 @@ #include "../include/Conf.hpp" -#include "../include/FileUtil.hpp" +#include "../include/types.hpp" #include "../external/utf8/unchecked.h" using namespace std; @@ -7,8 +7,6 @@ using u8string = utf8::unchecked::iterator; namespace sibs { - u32 min(u32 a, u32 b) { return a < b ? a : b; } - class UnexpectedTokenException : public std::runtime_error { public: @@ -316,7 +314,7 @@ namespace sibs bool objectDefined; }; - Result Config::readFromFile(const char *filepath, const ConfigCallback &callback) + Result Config::readFromFile(const _tinydir_char_t *filepath, const ConfigCallback &callback) { Result fileContentResult = getFileContent(filepath); if(fileContentResult.isErr()) @@ -409,9 +407,13 @@ namespace sibs if (value.isSingle()) { testPath = projectPath; - testPath += "/"; - testPath += string(value.asSingle().data, value.asSingle().size); - Result testRealPathResult = getRealPath(testPath.c_str()); + testPath += TINYDIR_STRING("/"); +#if OS_FAMILY == OS_FAMILY_POSIX + testPath += FileString(value.asSingle().data, value.asSingle().size); +#else + testPath += utf8To16(value.asSingle()); +#endif + Result testRealPathResult = getRealPath(testPath.c_str()); if(!testRealPathResult) { string errMsg = "Failed to resolve package.tests path: "; diff --git a/src/Exec.cpp b/src/Exec.cpp index 37ffef5..e0ae306 100644 --- a/src/Exec.cpp +++ b/src/Exec.cpp @@ -1,10 +1,12 @@ #include "../include/Exec.hpp" +#include "../include/env.hpp" using namespace std; namespace sibs { - Result exec(const char *cmd, bool print) +#if OS_FAMILY == OS_FAMILY_POSIX + Result exec(const _tinydir_char_t *cmd, bool print) { char buffer[128]; std::string result; @@ -52,4 +54,49 @@ namespace sibs return Result::Err(errMsg); } } +#else + // TODO(Windows): Redirect stdout (and stderr) to string + Result exec(const _tinydir_char_t *cmd, bool print) + { + char buffer[128]; + std::string result; + + STARTUPINFO startupInfo; + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + PROCESS_INFORMATION processInfo; + ZeroMemory(&processInfo, sizeof(processInfo)); + + DWORD exitCode; + + if (!CreateProcess(NULL, (LPWSTR)cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) + { + string errMsg = "exec unexpected error: "; + errMsg += toUtf8(getLastErrorAsString()); + return Result::Err(errMsg); + } + + WaitForSingleObject(processInfo.hProcess, INFINITE); + GetExitCodeProcess(processInfo.hProcess, &exitCode); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + + if (exitCode == 0) + { + ExecResult execResult; + execResult.execStdout = result; + execResult.exitCode = exitCode; + return Result::Ok(execResult); + } + else + { + string errMsg = "Exited with non-zero exit code ("; + errMsg += to_string(exitCode); + errMsg += "): "; + errMsg += toUtf8(getLastErrorAsString()); + return Result::Err(errMsg); + } + } +#endif } \ No newline at end of file diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp index d075f2b..5cf8377 100644 --- a/src/FileUtil.cpp +++ b/src/FileUtil.cpp @@ -1,5 +1,4 @@ #include "../include/FileUtil.hpp" -#include "../include/env.hpp" #include #if OS_FAMILY == OS_FAMILY_POSIX @@ -7,27 +6,92 @@ #include #include #include +#else +#include +// Copied from linux libc sys/stat.h: +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif using namespace std; namespace sibs { - FileType getFileType(const char *path) +#if OS_FAMILY == OS_FAMILY_POSIX +#define toUtf8(input) input + FileString toFileString(const std::string &utf8Str) { - tinydir_file file; - if(tinydir_file_open(&file, path) == 0) - { - return file.is_dir ? FileType::DIRECTORY : FileType::REGULAR; - } + return utf8Str; + } +#else + std::string toUtf8(const sibs::FileString &input) + { + std::string result; + utf8::utf16to8(input.data(), input.data() + input.size(), std::back_inserter(result)); + return result; + } + + std::string toUtf8(const TCHAR *input) + { + size_t inputSize = wcslen(input); + std::string result; + utf8::utf16to8(input, input + inputSize, std::back_inserter(result)); + return result; + } + + FileString utf8To16(const StringView &utf8Str) + { + FileString result; + utf8::utf8to16(utf8Str.data, utf8Str.data + utf8Str.size, std::back_inserter(result)); + return result; + } + + FileString utf8To16(const std::string &utf8Str) + { + FileString result; + utf8::utf8to16(utf8Str.data(), utf8Str.data() + utf8Str.size(), std::back_inserter(result)); + return result; + } + + FileString toFileString(const std::string &utf8Str) + { + return utf8To16(utf8Str); + } + + FileString getLastErrorAsString() + { + DWORD errorMessageId = GetLastError(); + if (errorMessageId == 0) return TINYDIR_STRING(""); + LPWSTR messageBuffer = nullptr; + size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorMessageId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); + FileString message(messageBuffer, size); + LocalFree(messageBuffer); + return message; + } +#endif + +#if OS_FAMILY == OS_FAMILY_POSIX + FileType getFileType(const _tinydir_char_t *path) + { + struct stat64 fileStat; + if (stat64(path, &fileStat) == 0) + return S_ISREG(fileStat.st_mode) ? FileType::REGULAR : FileType::DIRECTORY; else - { return FileType::FILE_NOT_FOUND; - } } +#else + FileType getFileType(const _tinydir_char_t *path) + { + struct _stat64i32 fileStat; + if (_wstat(path, &fileStat) == 0) + return S_ISREG(fileStat.st_mode) ? FileType::REGULAR : FileType::DIRECTORY; + else + return FileType::FILE_NOT_FOUND; + } +#endif // TODO: Handle failure (directory doesn't exist, no permission etc) - void walkDir(const char *directory, FileWalkCallbackFunc callbackFunc) + void walkDir(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc) { tinydir_dir dir; tinydir_open(&dir, directory); @@ -36,7 +100,7 @@ namespace sibs { tinydir_file file; tinydir_readfile(&dir, &file); - if(_tinydir_strcmp(file.name, ".") != 0 && _tinydir_strcmp(file.name, "..") != 0) + if(_tinydir_strcmp(file.name, TINYDIR_STRING(".")) != 0 && _tinydir_strcmp(file.name, TINYDIR_STRING("..")) != 0) callbackFunc(&file); tinydir_next(&dir); } @@ -45,7 +109,7 @@ namespace sibs } // TODO: Handle failure (directory doesn't exist, no permission etc) - void walkDirFiles(const char *directory, FileWalkCallbackFunc callbackFunc) + void walkDirFiles(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc) { tinydir_dir dir; tinydir_open(&dir, directory); @@ -63,7 +127,7 @@ namespace sibs } // TODO: Handle failure (directory doesn't exist, no permission etc) - void walkDirFilesRecursive(const char *directory, FileWalkCallbackFunc callbackFunc) + void walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc) { tinydir_dir dir; tinydir_open(&dir, directory); @@ -74,7 +138,7 @@ namespace sibs tinydir_readfile(&dir, &file); if(file.is_reg) callbackFunc(&file); - else if(_tinydir_strcmp(file.name, ".") != 0 && _tinydir_strcmp(file.name, "..") != 0) + else if(_tinydir_strcmp(file.name, TINYDIR_STRING(".")) != 0 && _tinydir_strcmp(file.name, TINYDIR_STRING("..")) != 0) walkDirFilesRecursive(file.path, callbackFunc); tinydir_next(&dir); } @@ -82,14 +146,18 @@ namespace sibs tinydir_close(&dir); } - Result getFileContent(const char *filepath) + Result getFileContent(const _tinydir_char_t *filepath) { +#if OS_FAMILY == OS_FAMILY_POSIX FILE *file = fopen(filepath, "rb"); +#else + FILE *file = _wfopen(filepath, TINYDIR_STRING("rb")); +#endif if(!file) { int error = errno; string errMsg = "Failed to open file: "; - errMsg += filepath; + errMsg += toUtf8(filepath); errMsg += "; reason: "; errMsg += strerror(error); return Result::Err(errMsg); @@ -104,7 +172,7 @@ namespace sibs if(!result) { std::string errMsg = "Failed to load file content from file: "; - errMsg += filepath; + errMsg += toUtf8(filepath); throw std::runtime_error(errMsg); } result[fileSize] = '\0'; @@ -113,14 +181,18 @@ namespace sibs return Result::Ok(StringView(result, fileSize)); } - Result fileOverwrite(const char *filepath, StringView data) + Result fileOverwrite(const _tinydir_char_t *filepath, StringView data) { +#if OS_FAMILY == OS_FAMILY_POSIX FILE *file = fopen(filepath, "wb"); +#else + FILE *file = _wfopen(filepath, TINYDIR_STRING("wb")); +#endif if(!file) { int error = errno; string errMsg = "Failed to overwrite file: "; - errMsg += filepath; + errMsg += toUtf8(filepath); errMsg += "; reason: "; errMsg += strerror(error); return Result::Err(errMsg); @@ -130,8 +202,8 @@ namespace sibs fclose(file); return Result::Ok(true); } - - const char* getHomeDir() +#if OS_FAMILY == OS_FAMILY_POSIX + Result getHomeDir() { const char *homeDir = getenv("HOME"); if(!homeDir) @@ -139,26 +211,25 @@ namespace sibs passwd *pw = getpwuid(getuid()); homeDir = pw->pw_dir; } - return homeDir; + return Result::Ok(homeDir); } - Result getCwd() + Result getCwd() { - string cwd; - cwd.reserve(PATH_MAX); - if(getcwd(&cwd[0], PATH_MAX) != 0) + FileString cwd; + cwd.resize(_TINYDIR_PATH_MAX); + if(getcwd(&cwd[0], _TINYDIR_PATH_MAX) != 0) { if(cwd.empty()) cwd = "."; - return Result::Ok(cwd); + cwd.resize(_tinydir_strlen(cwd.c_str())); + return Result::Ok(cwd); } - - return Result::Err(strerror(errno)); + return Result::Err(strerror(errno)); } -#if OS_FAMILY == OS_FAMILY_POSIX - Result createDirectoryRecursive(const char *path) + Result createDirectoryRecursive(const _tinydir_char_t *path) { - char pathBuffer[PATH_MAX]; + char pathBuffer[_TINYDIR_PATH_MAX]; size_t pathLength = strlen(path); if(pathLength > sizeof(pathBuffer) - 1) { @@ -207,25 +278,126 @@ namespace sibs return Result::Ok(true); } - Result getRealPath(const char *path) + Result getRealPath(const _tinydir_char_t *path) { // TODO: Verify NULL can be passed as 'resolved' argument with different compilers and operating systems (clang, freebsd etc) char *resolved = realpath(path, nullptr); if(!resolved) { int error = errno; - string errMsg = "Failed to get real path for \""; + FileString errMsg = "Failed to get real path for \""; errMsg += path; errMsg += "\": "; errMsg += strerror(error); - return Result::Err(errMsg, error); + return Result::Err(errMsg, error); } string result = resolved; free(resolved); - return Result::Ok(result); + return Result::Ok(result); } #else -#error "TODO: Implement createDirectoryRecursive and getRealPath on windows" + +#pragma comment(lib, "Userenv.lib") + + Result getHomeDir() + { + BOOL ret; + HANDLE hToken; + FileString homeDir; + DWORD homeDirLen = _TINYDIR_PATH_MAX; + homeDir.resize(homeDirLen); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) + return Result::Err("Failed to open process token"); + + if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) + return Result::Err("Failed to get home directory"); + + CloseHandle(hToken); + homeDir.resize(_tinydir_strlen(homeDir.c_str())); + return Result::Ok(homeDir); + } + + Result getCwd() + { + FileString cwd; + cwd.resize(_TINYDIR_PATH_MAX); + if (GetCurrentDirectory(_TINYDIR_PATH_MAX, &cwd[0]) == 0) + { + FileString lastErrStr = getLastErrorAsString(); + return Result::Err(toUtf8(lastErrStr)); + } + cwd.resize(_tinydir_strlen(cwd.c_str())); + return Result::Ok(cwd); + } + + Result createDirectoryRecursive(const _tinydir_char_t *path) + { + _tinydir_char_t pathBuffer[_TINYDIR_PATH_MAX]; + size_t pathLength = _tinydir_strlen(path); + if (pathLength > sizeof(pathBuffer) - 1) + { + string errMsg = "Directory path too long: "; + errMsg += toUtf8(FileString(path, pathLength)); + return Result::Err(errMsg, ENAMETOOLONG); + } + _tinydir_strcpy(pathBuffer, path); + + _tinydir_char_t *p = pathBuffer; + for (size_t i = 0; i < pathLength; ++i) + { + if (i > 0 && *p == '/') + { + *p = '\0'; + if (_wmkdir(pathBuffer) != 0) + { + int error = errno; + if (error != EEXIST) + { + string errMsg = "Failed to create directory: "; + errMsg += toUtf8(pathBuffer); + errMsg += "; reason: "; + errMsg += strerror(error); + return Result::Err(errMsg, error); + } + } + *p = '/'; + } + ++p; + } + + if (_wmkdir(pathBuffer) != 0) + { + int error = errno; + if (error != EEXIST) + { + string errMsg = "Failed to create directory: "; + errMsg += toUtf8(pathBuffer); + errMsg += "; reason: "; + errMsg += strerror(error); + return Result::Err(errMsg, error); + } + } + + return Result::Ok(true); + } + + Result getRealPath(const _tinydir_char_t *path) + { + FileString fullPath; + fullPath.resize(_TINYDIR_PATH_MAX); + if (GetFullPathName(path, _TINYDIR_PATH_MAX, &fullPath[0], nullptr) == 0) + { + int error = GetLastError(); + string errMsg = "Failed to get real path for \""; + errMsg += toUtf8(path); + errMsg += "\": "; + errMsg += toUtf8(getLastErrorAsString()); + return Result::Err(errMsg, error); + } + fullPath.resize(_tinydir_strlen(fullPath.c_str())); + return Result::Ok(fullPath); + } #endif } \ No newline at end of file diff --git a/src/GlobalLib.cpp b/src/GlobalLib.cpp index d994a27..db85abd 100644 --- a/src/GlobalLib.cpp +++ b/src/GlobalLib.cpp @@ -9,23 +9,27 @@ using namespace std; namespace sibs { - Result GlobalLib::validatePackageExists(const string &globalLibRootDir, const string &name) + Result GlobalLib::validatePackageExists(const FileString &globalLibRootDir, const std::string &name) { - string packageDir = globalLibRootDir + "/"; + FileString packageDir = globalLibRootDir + TINYDIR_STRING("/"); +#if OS_FAMILY == OS_FAMILY_POSIX packageDir += name; +#else + packageDir += utf8To16(name); +#endif FileType packageDirFileType = getFileType(packageDir.c_str()); switch(packageDirFileType) { case FileType::FILE_NOT_FOUND: { string errMsg = "Global lib dependency not found: "; - errMsg += name; + errMsg += toUtf8(packageDir); return Result::Err(errMsg, DependencyError::DEPENDENCY_NOT_FOUND); } case FileType::REGULAR: { string errMsg = "Corrupt library directory. "; - errMsg += packageDir; + errMsg += toUtf8(packageDir); errMsg += " is a file, expected it to be a directory"; return Result::Err(errMsg); } @@ -40,13 +44,13 @@ namespace sibs } } - const char *sourceFileExtensions[] = { "c", "cc", "cpp", "cxx" }; + const _tinydir_char_t *sourceFileExtensions[] = { TINYDIR_STRING("c"),TINYDIR_STRING("cc"), TINYDIR_STRING("cpp"), TINYDIR_STRING("cxx") }; bool isSourceFile(tinydir_file *file) { if(!file->is_reg) return false; - for(const char *sourceFileExtension : sourceFileExtensions) + for(const _tinydir_char_t *sourceFileExtension : sourceFileExtensions) { if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) return true; @@ -55,29 +59,37 @@ namespace sibs return false; } - bool isPathSubPathOf(const char *path, const string &subPathOf) + bool isPathSubPathOf(const _tinydir_char_t *path, const FileString &subPathOf) { return _tinydir_strncmp(path, subPathOf.c_str(), subPathOf.size()) == 0; } - Result GlobalLib::getLibsLinkerFlags(const SibsConfig &parentConfig, const string &globalLibRootDir, const string &name, const 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) { Result packageExistsResult = validatePackageExists(globalLibRootDir, name); if(packageExistsResult.isErr()) return Result::Err(packageExistsResult); - string packageDir = globalLibRootDir + "/"; - packageDir += name; +#if OS_FAMILY == OS_FAMILY_POSIX + FileString namePlatformNative = name; + FileString versionPlatformNative = version; +#else + FileString namePlatformNative = utf8To16(name); + FileString versionPlatformNative = utf8To16(version); +#endif + + FileString packageDir = globalLibRootDir + TINYDIR_STRING("/"); + packageDir += namePlatformNative; // TODO: Instead of checking if version is exact match, check if package has same major version // and same or newer minor version - string foundVersion; - walkDir(packageDir.c_str(), [&foundVersion, &version](tinydir_file *file) + FileString foundVersion; + walkDir(packageDir.c_str(), [&foundVersion, &versionPlatformNative](tinydir_file *file) { if(file->is_dir) { //printf("version: %s\n", file->name); - if(_tinydir_strcmp(version.c_str(), file->name) == 0) + if(_tinydir_strcmp(versionPlatformNative.c_str(), file->name) == 0) foundVersion = file->name; } }); @@ -85,11 +97,11 @@ namespace sibs if(foundVersion.empty()) return Result::Err("Global lib dependency found, but version doesn't match dependency version", DependencyError::DEPENDENCY_VERSION_NO_MATCH); - packageDir += "/"; - packageDir += version; + packageDir += TINYDIR_STRING("/"); + packageDir += versionPlatformNative; - string projectConfFilePath = packageDir; - projectConfFilePath += "/project.conf"; + FileString projectConfFilePath = packageDir; + projectConfFilePath += TINYDIR_STRING("/project.conf"); FileType projectConfFileType = getFileType(projectConfFilePath.c_str()); switch(projectConfFileType) @@ -97,20 +109,20 @@ namespace sibs case FileType::FILE_NOT_FOUND: { string errMsg = "Global lib dependency found: "; - errMsg += packageDir; + errMsg += toUtf8(packageDir); errMsg += ", but it's missing a project.conf file"; return Result::Err(errMsg); } case FileType::DIRECTORY: { string errMsg = "Global lib dependency found: "; - errMsg += packageDir; + errMsg += toUtf8(packageDir); errMsg += ", but it's corrupt (Found directory instead of file)"; return Result::Err(errMsg); } } - SibsConfig sibsConfig(packageDir, parentConfig.getOptimizationLevel()); + SibsConfig sibsConfig(parentConfig.getCompiler(), packageDir, parentConfig.getOptimizationLevel()); Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); if(result.isErr()) return Result::Err(result.getErrMsg()); @@ -148,7 +160,8 @@ namespace sibs { if (isSourceFile(file)) { - ninja.addSourceFile(file->path + sibsConfig.getProjectPath().size() + 1); + string fileNameNative = toUtf8(file->path + sibsConfig.getProjectPath().size() + 1); + ninja.addSourceFile(fileNameNative.c_str()); } else { @@ -160,8 +173,11 @@ 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())) - ninja.addTestSourceDir(file->path); + if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(file->path, sibsConfig.getTestPath())) + { + string filePathUtf8 = toUtf8(file->path); + ninja.addTestSourceDir(filePathUtf8.c_str()); + } else walkDir(file->path, collectSourceFiles); } @@ -175,36 +191,64 @@ namespace sibs } else { - string buildPath = packageDir + "/sibs-build/"; + FileString buildPath = packageDir + TINYDIR_STRING("/sibs-build/"); switch(sibsConfig.getOptimizationLevel()) { case OPT_LEV_DEBUG: - buildPath += "debug"; + buildPath += TINYDIR_STRING("debug"); break; case OPT_LEV_RELEASE: - buildPath += "release"; + buildPath += TINYDIR_STRING("release"); break; } - - string libPath = buildPath; - libPath += "/lib"; - libPath += name; - if(libraryType == backend::Ninja::LibraryType::STATIC) - { - libPath += ".a"; - string libPathCmd = "'"; - libPathCmd += libPath; - libPathCmd += "'"; - staticLinkerFlagCallbackFunc(libPathCmd); - libPath += ".a"; - } - else + + string libPath = toUtf8(buildPath); + switch (sibsConfig.getCompiler()) { - libPath += ".so"; - string libPathCmd = "'"; - libPathCmd += libPath; - libPathCmd += "'"; - dynamicLinkerFlagCallbackFunc(libPathCmd); + case Compiler::GCC: + { + libPath += "/lib"; + libPath += name; + if (libraryType == backend::Ninja::LibraryType::STATIC) + { + libPath += ".a"; + string libPathCmd = "'"; + libPathCmd += libPath; + libPathCmd += "'"; + staticLinkerFlagCallbackFunc(libPathCmd); + } + else + { + libPath += ".so"; + string libPathCmd = "'"; + libPathCmd += libPath; + libPathCmd += "'"; + dynamicLinkerFlagCallbackFunc(libPathCmd); + } + break; + } + case Compiler::MSVC: + { + libPath += "/"; + libPath += name; + if (libraryType == backend::Ninja::LibraryType::STATIC) + { + libPath += ".lib"; + string libPathCmd = "\""; + libPathCmd += libPath; + libPathCmd += "\""; + staticLinkerFlagCallbackFunc(libPathCmd); + } + else + { + libPath += ".lib"; + string libPathCmd = "\""; + libPathCmd += libPath; + libPathCmd += "\""; + dynamicLinkerFlagCallbackFunc(libPathCmd); + } + break; + } } // TODO: Use different directories depending on the project type, but .o build files should be in the same directory @@ -229,21 +273,24 @@ namespace sibs url += dependency.version; url += ".tar.gz"; - string libPath = getHomeDir(); - libPath += "/.sibs/lib/"; - libPath += dependency.name; - libPath += "/"; - libPath += dependency.version; + Result libPathResult = getHomeDir(); + if (!libPathResult) + return Result::Err(libPathResult); + FileString libPath = libPathResult.unwrap(); + libPath += TINYDIR_STRING("/.sibs/lib/"); + libPath += toFileString(dependency.name); + libPath += TINYDIR_STRING("/"); + libPath += toFileString(dependency.version); - string libArchivedFilePath = getHomeDir(); - libArchivedFilePath += "/.sibs/archive/"; - libArchivedFilePath += dependency.name; + FileString libArchivedFilePath = libPathResult.unwrap(); + libArchivedFilePath += TINYDIR_STRING("/.sibs/archive/"); + libArchivedFilePath += toFileString(dependency.name); Result createArchiveDirResult = createDirectoryRecursive(libArchivedFilePath.c_str()); if(createArchiveDirResult.isErr()) return createArchiveDirResult; - libArchivedFilePath += "/"; - libArchivedFilePath += dependency.version; + libArchivedFilePath += TINYDIR_STRING("/"); + libArchivedFilePath += toFileString(dependency.version); Result downloadResult = curl::downloadFile(url.c_str(), libArchivedFilePath.c_str()); if(downloadResult.isErr()) return downloadResult; diff --git a/src/PkgConfig.cpp b/src/PkgConfig.cpp index 620b1a7..a66d5b3 100644 --- a/src/PkgConfig.cpp +++ b/src/PkgConfig.cpp @@ -1,4 +1,5 @@ #include "../include/PkgConfig.hpp" +#if OS_FAMILY == OS_FAMILY_POSIX #include "../include/Exec.hpp" using namespace std; @@ -127,4 +128,5 @@ namespace sibs return Result::Err(errMsg); } } -} \ No newline at end of file +} +#endif // OS_FAMILY_POSIX diff --git a/src/curl.cpp b/src/curl.cpp index 56c19ec..a3c5e28 100644 --- a/src/curl.cpp +++ b/src/curl.cpp @@ -5,6 +5,12 @@ using namespace std; +#if OS_FAMILY == OS_FAMILY_WINDOWS +#pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "Wldap32.lib") +#pragma comment(lib, "Crypt32.lib") +#endif + #ifdef DEBUG #define CURL_DEBUG #endif @@ -44,7 +50,7 @@ namespace sibs return size * nmemb; } - Result curl::downloadFile(const char *url, const char *filepath) + Result curl::downloadFile(const char *url, const _tinydir_char_t *filepath) { CURL *curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, url); @@ -60,15 +66,19 @@ namespace sibs curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeToFile); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, true); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SIBS"); - + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, FALSE); +#if OS_FAMILY == OS_FAMILY_POSIX FILE *file = fopen(filepath, "wb"); +#else + FILE *file = _wfopen(filepath, L"wb"); +#endif if(!file) { int error = errno; curl_easy_cleanup(curl_handle); string errMsg = "Failed to open file for writing: "; - errMsg += filepath; + errMsg += toUtf8(filepath); if(error != 0) { errMsg += "; Reason: "; @@ -131,8 +141,9 @@ namespace sibs curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, noProgressMeter); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeToString); curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, true); - curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "Hacker"); - + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SIBS"); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &result.str); printf("Downloading from url: %s\n", url); CURLcode curlResponse = curl_easy_perform(curl_handle); diff --git a/src/main.cpp b/src/main.cpp index 0f20ea0..f49ba59 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,12 @@ using namespace sibs; // TODO: Places that use PATH_MAX should be modified. A path CAN be longer than PATH_MAX... (does this include replacing tinydir.h?) +#if OS_FAMILY == OS_FAMILY_POSIX +#define ferr std::cerr +#else +#define ferr std::wcerr +#endif + void usage() { printf("Usage: sibs COMMAND\n\n"); @@ -59,47 +65,47 @@ void usageNew() exit(1); } -void validateDirectoryPath(const char *projectPath) +void validateDirectoryPath(const _tinydir_char_t *projectPath) { FileType projectPathFileType = getFileType(projectPath); if(projectPathFileType == FileType::FILE_NOT_FOUND) { string errMsg = "Invalid project path: "; - errMsg += projectPath; + errMsg += toUtf8(projectPath); perror(errMsg.c_str()); exit(2); } else if(projectPathFileType == FileType::REGULAR) { - cerr <<"Expected project path (" << projectPath << ") to be a directory, was a file" << endl; + ferr <<"Expected project path (" << projectPath << ") to be a directory, was a file" << endl; exit(3); } } -void validateFilePath(const char *projectConfPath) +void validateFilePath(const _tinydir_char_t *projectConfPath) { FileType projectConfFileType = getFileType(projectConfPath); if(projectConfFileType == FileType::FILE_NOT_FOUND) { string errMsg = "Invalid project.conf path: "; - errMsg += projectConfPath; + errMsg += toUtf8(projectConfPath); perror(errMsg.c_str()); exit(4); } else if(projectConfFileType == FileType::DIRECTORY) { - cerr << "Expected project path (" << projectConfPath << ") to be a file, was a directory" << endl; + ferr << "Expected project path (" << projectConfPath << ") to be a file, was a directory" << endl; exit(5); } } -const char *sourceFileExtensions[] = { "c", "cc", "cpp", "cxx" }; +const _tinydir_char_t *sourceFileExtensions[] = { TINYDIR_STRING("c"), TINYDIR_STRING("cc"), TINYDIR_STRING("cpp"), TINYDIR_STRING("cxx") }; bool isSourceFile(tinydir_file *file) { if(!file->is_reg) return false; - for(const char *sourceFileExtension : sourceFileExtensions) + for(const _tinydir_char_t *sourceFileExtension : sourceFileExtensions) { if(_tinydir_strcmp(sourceFileExtension, file->extension) == 0) return true; @@ -108,36 +114,36 @@ bool isSourceFile(tinydir_file *file) return false; } -bool isPathSubPathOf(const string &path, const string &subPathOf) +bool isPathSubPathOf(const FileString &path, const FileString &subPathOf) { return _tinydir_strncmp(path.c_str(), subPathOf.c_str(), subPathOf.size()) == 0; } -int buildProject(int argc, const char **argv) +int buildProject(int argc, const _tinydir_char_t **argv) { if(argc > 2) usageBuild(); OptimizationLevel optimizationLevel = OPT_LEV_NONE; - string projectPath; + FileString projectPath; for(int i = 0; i < argc; ++i) { - const char *arg = argv[i]; - if(strcmp(arg, "--debug") == 0) + const _tinydir_char_t *arg = argv[i]; + if(_tinydir_strcmp(arg, TINYDIR_STRING("--debug")) == 0) { if(optimizationLevel != OPT_LEV_NONE) { - fprintf(stderr, "Error: Optimization level defined more than once. First defined as %s then as %s\n", asString(optimizationLevel), "debug"); + ferr << "Error: Optimization level defined more than once. First defined as " << asString(optimizationLevel) << " then as debug" << endl; usageBuild(); } optimizationLevel = OPT_LEV_DEBUG; } - else if(strcmp(arg, "--release") == 0) + else if(_tinydir_strcmp(arg, TINYDIR_STRING("--release")) == 0) { if(optimizationLevel != OPT_LEV_NONE) { - fprintf(stderr, "Error: Optimization level defined more than once. First defined as %s then as %s\n", asString(optimizationLevel), "debug"); + ferr << "Error: Optimization level defined more than once. First defined as " << asString(optimizationLevel) << " then as release" << endl; usageBuild(); } optimizationLevel = OPT_LEV_RELEASE; @@ -146,7 +152,7 @@ int buildProject(int argc, const char **argv) { if(!projectPath.empty()) { - fprintf(stderr, "Error: Project path was defined more than once. First defined as %s then as %s\n", projectPath.c_str(), arg); + ferr << "Error: Project path was defined more than once. First defined as " << projectPath << " then as " << arg << endl; usageBuild(); } projectPath = arg; @@ -158,35 +164,43 @@ int buildProject(int argc, const char **argv) // TODO: If projectPath is not defined and working directory does not contain project.conf, then search every parent directory until one is found if(projectPath.empty()) - projectPath = "."; + projectPath = TINYDIR_STRING("."); validateDirectoryPath(projectPath.c_str()); if(projectPath.back() != '/') - projectPath += "/"; + projectPath += TINYDIR_STRING("/"); - Result projectRealPathResult = getRealPath(projectPath.c_str()); + Result projectRealPathResult = getRealPath(projectPath.c_str()); if(!projectRealPathResult) { - cerr << "Failed to get real path for: '" << projectPath << "': " << projectRealPathResult.getErrMsg() << endl; + ferr << "Failed to get real path for: '" << projectPath.c_str() << "': " << toFileString(projectRealPathResult.getErrMsg()) << endl; exit(40); } projectPath = projectRealPathResult.unwrap(); - string projectConfFilePath = projectPath; - projectConfFilePath += "/project.conf"; + FileString projectConfFilePath = projectPath; + projectConfFilePath += TINYDIR_STRING("/project.conf"); validateFilePath(projectConfFilePath.c_str()); - SibsConfig sibsConfig(projectPath, optimizationLevel); + // TODO: Detect compiler to use at runtime. Should also be configurable + // by passing argument to `sibs build` +#if OS_FAMILY == OS_FAMILY_POSIX + Compiler compiler = Compiler::GCC; +#else + Compiler compiler = Compiler::MSVC; +#endif + + SibsConfig sibsConfig(compiler, projectPath, optimizationLevel); Result result = Config::readFromFile(projectConfFilePath.c_str(), sibsConfig); if(result.isErr()) { - cerr << "Failed to read config: " << result.getErrMsg() << endl; + ferr << "Failed to read config: " << toFileString(result.getErrMsg()) << endl; exit(6); } if(sibsConfig.getPackageName().empty()) { - cerr << "project.conf is missing required field package.name" << endl; + ferr << "project.conf is missing required field package.name" << endl; exit(10); } @@ -216,7 +230,8 @@ int buildProject(int argc, const char **argv) { if (isSourceFile(file)) { - ninja.addSourceFile(file->path + sibsConfig.getProjectPath().size()); + string filePathUtf8 = toUtf8(file->path + sibsConfig.getProjectPath().size()); + ninja.addSourceFile(filePathUtf8.c_str()); } else { @@ -226,66 +241,69 @@ int buildProject(int argc, const char **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())) - ninja.addTestSourceDir(file->path); + if (!sibsConfig.getTestPath().empty() && isPathSubPathOf(file->path, sibsConfig.getTestPath())) + { + string filePathUtf8 = toUtf8(file->path); + ninja.addTestSourceDir(filePathUtf8.c_str()); + } else walkDir(file->path, collectSourceFiles); } }; walkDir(projectPath.c_str(), collectSourceFiles); - string buildPath = projectPath + "/sibs-build/"; + FileString buildPath = projectPath + TINYDIR_STRING("/sibs-build/"); switch(sibsConfig.getOptimizationLevel()) { case OPT_LEV_DEBUG: - buildPath += "debug"; + buildPath += TINYDIR_STRING("debug"); break; case OPT_LEV_RELEASE: - buildPath += "release"; + buildPath += TINYDIR_STRING("release"); break; } Result buildFileResult = ninja.build(sibsConfig, buildPath.c_str()); if(buildFileResult.isErr()) { - cerr << "Failed to build ninja file: " << buildFileResult.getErrMsg() << endl; + ferr << "Failed to build ninja file: " << toFileString(buildFileResult.getErrMsg()) << endl; exit(7); } return 0; } -void newProjectCreateMainDir(const string &projectPath) +void newProjectCreateMainDir(const FileString &projectPath) { Result createProjectDirResult = createDirectoryRecursive(projectPath.c_str()); if(createProjectDirResult.isErr()) { - cerr << "Failed to create project main directory: " << createProjectDirResult.getErrMsg() << endl; + ferr << "Failed to create project main directory: " << toFileString(createProjectDirResult.getErrMsg()) << endl; exit(20); } } -void createProjectSubDir(const string &dir) +void createProjectSubDir(const FileString &dir) { Result createProjectDirResult = createDirectoryRecursive(dir.c_str()); if(createProjectDirResult.isErr()) { - cerr << "Failed to create directory in project: " << createProjectDirResult.getErrMsg() << endl; + ferr << "Failed to create directory in project: " << toFileString(createProjectDirResult.getErrMsg()) << endl; exit(20); } } -void createProjectFile(const string &projectFilePath, const string &fileContent) +void createProjectFile(const FileString &projectFilePath, const string &fileContent) { Result fileOverwriteResult = fileOverwrite(projectFilePath.c_str(), fileContent.c_str()); if(fileOverwriteResult.isErr()) { - cerr << "Failed to create project file: " << fileOverwriteResult.getErrMsg() << endl; + ferr << "Failed to create project file: " << toFileString(fileOverwriteResult.getErrMsg()) << endl; exit(20); } } -void newProjectCreateConf(const string &projectName, const string &projectType, const string &projectPath) +void newProjectCreateConf(const string &projectName, const string &projectType, const FileString &projectPath) { string projectConfStr = "[package]\n"; projectConfStr += "name = \"" + projectName + "\"\n"; @@ -293,98 +311,102 @@ void newProjectCreateConf(const string &projectName, const string &projectType, projectConfStr += "version = \"0.1.0\"\n\n"; projectConfStr += "[dependencies]\n"; - string projectConfPath = projectPath; - projectConfPath += "/project.conf"; + FileString projectConfPath = projectPath; + projectConfPath += TINYDIR_STRING("/project.conf"); Result fileOverwriteResult = fileOverwrite(projectConfPath.c_str(), projectConfStr.c_str()); if(fileOverwriteResult.isErr()) { - cerr << "Failed to create project.conf: " << fileOverwriteResult.getErrMsg() << endl; + ferr << "Failed to create project.conf: " << toFileString(fileOverwriteResult.getErrMsg()) << endl; exit(20); } } // This can be replaced with createDirectory and fileOverwrite, but it's not important // so there is no reason to do it (right now) -Result gitInitProject(const string &projectPath) +Result gitInitProject(const FileString &projectPath) { - string cmd = "git init '"; + FileString cmd = TINYDIR_STRING("git init \""); cmd += projectPath; - cmd += "'"; + cmd += TINYDIR_STRING("\""); return exec(cmd.c_str()); } -int newProject(int argc, const char **argv) +int newProject(int argc, const _tinydir_char_t **argv) { if(argc != 2) { - cerr << "Expected 'new' command to be followed by two arguments - project name and type of project (--exec, --static or --dynamic)" << endl << endl; + ferr << "Expected 'new' command to be followed by two arguments - project name and type of project (--exec, --static or --dynamic)" << endl << endl; usageNew(); } - Result cwdResult = getCwd(); + Result cwdResult = getCwd(); if(cwdResult.isErr()) { - cerr << "Failed to get current working directory: " << cwdResult.getErrMsg() << endl; + ferr << "Failed to get current working directory: " << toFileString(cwdResult.getErrMsg()) << endl; exit(20); } - string projectName = argv[0]; - string projectPath = cwdResult.unwrap(); - projectPath += "/"; - projectPath += projectName; + string projectName = toUtf8(argv[0]); + FileString projectPath = cwdResult.unwrap(); + projectPath += TINYDIR_STRING("/"); + projectPath += toFileString(projectName); bool projectPathExists = getFileType(projectPath.c_str()) != FileType::FILE_NOT_FOUND; if(projectPathExists) { - cerr << "Unable to create a new project at path '" << projectPath << "'. A file or directory already exists in the same location" << endl; + ferr << "Unable to create a new project at path '" << projectPath << "'. A file or directory already exists in the same location" << endl; exit(20); } - const char *projectType = argv[1]; + const _tinydir_char_t *projectType = argv[1]; string projectTypeConf; - if(strcmp(projectType, "--exec") == 0) + if(_tinydir_strcmp(projectType, TINYDIR_STRING("--exec")) == 0) projectTypeConf = "executable"; - else if(strcmp(projectType, "--static") == 0) + else if(_tinydir_strcmp(projectType, TINYDIR_STRING("--static")) == 0) projectTypeConf = "static"; - else if(strcmp(projectType, "--dynamic") == 0) + else if(_tinydir_strcmp(projectType, TINYDIR_STRING("--dynamic")) == 0) projectTypeConf = "dynamic"; else { - cerr << "Expected project type to be either --exec, --static or --dynamic; was: " << projectType << endl << endl; + ferr << "Expected project type to be either --exec, --static or --dynamic; was: " << projectType << endl << endl; usageNew(); } newProjectCreateMainDir(projectPath); newProjectCreateConf(projectName, projectTypeConf, projectPath); - createProjectSubDir(projectPath + "/src"); - createProjectSubDir(projectPath + "/include"); - createProjectFile(projectPath + "/src/main.cpp", "#include \n\nint main()\n{\n return 0;\n}\n"); + createProjectSubDir(projectPath + TINYDIR_STRING("/src")); + createProjectSubDir(projectPath + TINYDIR_STRING("/include")); + createProjectFile(projectPath + TINYDIR_STRING("/src/main.cpp"), "#include \n\nint main()\n{\n return 0;\n}\n"); // We are ignoring git init result on purpose. If it fails, just ignore it; not important gitInitProject(projectPath); return 0; } -int main(int argc, const char **argv) +#if OS_FAMILY == OS_FAMILY_POSIX +int main(int argc, const _tinydir_char_t **argv) +#else +int wmain(int argc, const _tinydir_char_t **argv) +#endif { unordered_map param; unordered_set flags; for(int i = 1; i < argc; ++i) { - const char *arg = argv[i]; + const _tinydir_char_t *arg = argv[i]; int subCommandArgCount = argc - i - 1; - const char **subCommandArgPtr = argv + i + 1; - if(strcmp(arg, "build") == 0) + const _tinydir_char_t **subCommandArgPtr = argv + i + 1; + if(_tinydir_strcmp(arg, TINYDIR_STRING("build")) == 0) { return buildProject(subCommandArgCount, subCommandArgPtr); } - else if(strcmp(arg, "new") == 0) + else if(_tinydir_strcmp(arg, TINYDIR_STRING("new")) == 0) { return newProject(subCommandArgCount, subCommandArgPtr); } else { - cerr << "Expected command to be either 'build' or 'new', was: " << arg << endl << endl; + ferr << "Expected command to be either 'build' or 'new', was: " << arg << endl << endl; usage(); } } -- cgit v1.2.3