diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .gitmodules | 6 | ||||
-rw-r--r-- | Jenkinsfile | 25 | ||||
-rw-r--r-- | bootstrap.bat | 25 | ||||
-rwxr-xr-x | clrun.sh | 27 | ||||
-rw-r--r-- | ctor.cc | 22 | ||||
m--------- | getopt-for-windows | 0 | ||||
m--------- | json | 0 | ||||
-rw-r--r-- | src/configure.cc | 81 | ||||
-rw-r--r-- | src/ctor.h | 1 | ||||
-rw-r--r-- | src/deps.cc | 74 | ||||
-rw-r--r-- | src/execute.cc | 248 | ||||
-rw-r--r-- | src/rebuild.cc | 13 | ||||
-rw-r--r-- | src/task_ar.cc | 2 | ||||
-rw-r--r-- | src/task_cc.cc | 2 | ||||
-rw-r--r-- | src/task_ld.cc | 24 | ||||
-rw-r--r-- | src/task_so.cc | 2 | ||||
-rw-r--r-- | src/tools.cc | 295 | ||||
-rw-r--r-- | src/util.cc | 53 | ||||
-rw-r--r-- | test/ctor.cc | 41 | ||||
-rw-r--r-- | test/execute_test.cc | 14 | ||||
-rw-r--r-- | test/source_type_test.cc | 8 | ||||
-rwxr-xr-x | test/suite/test.sh | 2 | ||||
-rw-r--r-- | test/testprog.cc | 38 | ||||
-rw-r--r-- | test/tmpfile.h | 44 | ||||
-rw-r--r-- | test/tools_test.cc | 3 |
26 files changed, 984 insertions, 67 deletions
@@ -1,6 +1,7 @@ drumgizmo/ build/ ctor +ctor.exe *.a *.o *.d diff --git a/.gitmodules b/.gitmodules index c765825..383739b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,9 @@ [submodule "test/uunit"] path = test/uunit url = git://git.drumgizmo.org/uunit.git +[submodule "getopt-for-windows"] + path = getopt-for-windows + url = https://github.com/Chunde/getopt-for-windows.git +[submodule "json"] + path = json + url = https://github.com/nlohmann/json.git diff --git a/Jenkinsfile b/Jenkinsfile index a558359..cc659ab 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,6 +5,31 @@ pipeline { { parallel { //////////////////////////////////////////////////// + stage('Windows msvc') { + agent { label 'win10 && msvc' } + environment + { + VSDEVCMD="C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\Common7\\Tools\\VsDevCmd.bat" + } + steps { + echo 'Cleaning workspace ...' + bat 'git clean -d -x -f' + dir ('build/test') { + writeFile file:'dummy', text:'' + } + bat '"%VSDEVCMD%" && bootstrap.bat && ctor.exe' + echo 'Testing (msvc) ...' + bat 'ctor.exe check' + } + post { + always { + xunit(thresholds: [ skipped(failureThreshold: '0'), + failed(failureThreshold: '0') ], + tools: [ CppUnit(pattern: 'build/test/*.xml') ]) + } + } + } + //////////////////////////////////////////////////// stage('MacOSX clang') { agent { label 'macos' } steps { diff --git a/bootstrap.bat b/bootstrap.bat new file mode 100644 index 0000000..3802663 --- /dev/null +++ b/bootstrap.bat @@ -0,0 +1,25 @@ +@echo off + +set CXX=cl.exe +set CC=cl.exe +set AR=lib.exe +set LD=link.exe + +echo Bootstrapping... +%CXX% /nologo /MT /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build\ ctor.cc src\bootstrap.cc /link /out:ctor.exe +@if %errorlevel% neq 0 exit /b %errorlevel% + +ctor.exe +@if %errorlevel% neq 0 exit /b %errorlevel% + +%CXX% /nologo /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build\ ctor.cc /c +@if %errorlevel% neq 0 exit /b %errorlevel% +%CXX% /nologo /std:c++20 /D_X86_ /EHsc /Isrc /Fo:build\test\ test\ctor.cc /c +@if %errorlevel% neq 0 exit /b %errorlevel% +%LD% /nologo build\libctor.lib build\ctor.obj build\test\ctor.obj /subsystem:console /out:ctor.exe +@if %errorlevel% neq 0 exit /b %errorlevel% + +ctor.exe configure --ctor-includedir=src --ctor-libdir=build +@if %errorlevel% neq 0 exit /b %errorlevel% + +@echo Done. Now run ctor.exe to (re)build. diff --git a/clrun.sh b/clrun.sh new file mode 100755 index 0000000..d0f3cbb --- /dev/null +++ b/clrun.sh @@ -0,0 +1,27 @@ +#!/bin/bash +#set -e +#set -x +export WINEDEBUG=-all + +rm -f ~/.wine/drive_c/number + +export BASE='C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133' +export ONECORELIB="$BASE\lib\onecore\x86" +export WINEPATH="$BASE\bin\Hostx86\x86" +export INCLUDE="$BASE\include" +export UCRT="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt" +export UCRTLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x86" +export UM="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um" +export UMLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86" +export SHARED="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared" + +export CL="/I\"$BASE\include\" /I\"$UCRT\" /I\"$UM\" /I\"$SHARED\" /link /LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\"" +export LINK="/LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\"" +export LIB="/LIBPATH:\"$UMLIB\" /LIBPATH:\"$ONECORELIB\" /LIBPATH:\"$UCRTLIB\"" + + + +#wine ctor.exe + +$* + @@ -31,8 +31,23 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings) "src/tools.cc", "src/util.cc", "src/unittest.cc", + {ctor::toolchain::msvc, "getopt-for-windows/getopt.c"}, }, .flags = { + .cflags = { + "-O3", +// "-g", +// "-Wall", +// "-Wconversion", +// "-Wextra", + //"-Werror", + "-Isrc", + {ctor::toolchain::msvc, ctor::c_opt::custom, "/Igetopt-for-windows"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/D_X86_"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/external:W0"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, + }, .cxxflags = { "-std=c++20", "-O3", @@ -44,6 +59,13 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings) "-Wconversion", // "-Wnrvo", "-Isrc", + "-Ijson/include", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Igetopt-for-windows"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_X86_"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/external:W0"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Dstrdup=_strdup"}, }, }, } diff --git a/getopt-for-windows b/getopt-for-windows new file mode 160000 +Subproject 76fe2df86f84771775fd2f1a641a09adbce28df diff --git a/json b/json new file mode 160000 +Subproject 6be4e8560023098fdb6d2047e6e6e5bc5dd5287 diff --git a/src/configure.cc b/src/configure.cc index a43152f..b52824e 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -8,6 +8,8 @@ #include <fstream> #include <optional> #include <span> +#include <vector> +#include <deque> #include <getoptpp/getoptpp.hpp> @@ -25,7 +27,11 @@ const std::filesystem::path configHeaderFile("config.h"); std::map<std::string, std::string> external_includedir; std::map<std::string, std::string> external_libdir; +#if !defined(_WIN32) const ctor::configuration& __attribute__((weak)) ctor::get_configuration() +#else +const ctor::configuration& default_get_configuration() +#endif { static ctor::configuration cfg; static bool initialised{false}; @@ -44,6 +50,14 @@ const ctor::configuration& __attribute__((weak)) ctor::get_configuration() } return cfg; } +#if defined(_WIN32) +// Hack to make ctor::get_configuration "weak" linked +// See: +// https://stackoverflow.com/questions/2290587/gcc-style-weak-linking-in-visual-studio +// and +// https://stackoverflow.com/questions/11849853/how-to-list-functions-present-in-object-file +#pragma comment(linker, "/alternatename:?get_configuration@ctor@@YAABUconfiguration@1@XZ=?default_get_configuration@@YAABUconfiguration@ctor@@XZ") +#endif namespace ctor { std::optional<std::string> includedir; @@ -100,11 +114,47 @@ std::string ctor::configuration::get(const std::string& key, return ctor::conf_values[key]; } - if(has(key)) + if(tools.find(key) != tools.end()) { return tools.at(key); } + if(key == ctor::cfg::build_cxx) + { + auto e = std::getenv("CXX"); + if(e) + { + return e; + } + } + + if(key == ctor::cfg::build_cc) + { + auto e = std::getenv("CC"); + if(e) + { + return e; + } + } + + if(key == ctor::cfg::build_ld) + { + auto e = std::getenv("LD"); + if(e) + { + return e; + } + } + + if(key == ctor::cfg::build_ar) + { + auto e = std::getenv("AR"); + if(e) + { + return e; + } + } + return default_value; } @@ -162,6 +212,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) case ctor::toolchain::gcc: stream << "ctor::toolchain::gcc"; break; + case ctor::toolchain::msvc: + stream << "ctor::toolchain::msvc"; + break; case ctor::toolchain::clang: stream << "ctor::toolchain::clang"; break; @@ -676,6 +729,7 @@ int regenerateCache(ctor::settings& settings, { ctor::conf_values[ctor::cfg::builddir] = builddir; } + ctor::conf_values[ctor::cfg::host_cxx] = host_cxx; ctor::conf_values[ctor::cfg::build_cxx] = build_cxx; @@ -906,6 +960,31 @@ int configure(const ctor::settings& global_settings, int argc, char* argv[]) env["PATH"] = path_env; } + // Env vars for msvc + auto cl_env = getenv("CL"); + if(cl_env) + { + env["CL"] = cl_env; + } + + auto lib_env = getenv("LIB"); + if(lib_env) + { + env["LIB"] = lib_env; + } + + auto link_env = getenv("LINK"); + if(link_env) + { + env["LINK"] = link_env; + } + + auto include_env = getenv("INCLUDE"); + if(include_env) + { + env["INCLUDE"] = include_env; + } + auto ret = regenerateCache(settings, args_span[0], args, env); if(ret != 0) { @@ -59,6 +59,7 @@ enum class toolchain none, gcc, clang, + msvc, }; struct source diff --git a/src/deps.cc b/src/deps.cc index 9400b35..599be8c 100644 --- a/src/deps.cc +++ b/src/deps.cc @@ -5,6 +5,8 @@ #include "util.h" +#include <nlohmann/json.hpp> + #include <fstream> namespace { @@ -94,6 +96,74 @@ std::vector<std::string> readDepsMake(const std::string& dep_file) return output; } + +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html +// https://devblogs.microsoft.com/cppblog/introducing-source-dependency-reporting-with-msvc-in-visual-studio-2019-version-16-7/ +/* Format examples: +{ + "Version": "1.1", + "Data": { + "Source": "z:\\home\\deva\\docs\\c\\ctor\\src\\libctor.cc", + "ProvidedModule": "", + "Includes": [ + "c:\\program files (x86)\\microsoft visual studio\\2019\\buildtools\\vc\\tools\\msvc\\14.29.30133\\include\\vector", + "c:\\program files (x86)\\microsoft visual studio\\2019\\buildtools\\vc\\tools\\msvc\\14.29.30133\\include\\yvals_core.h", + "c:\\program files (x86)\\microsoft visual studio\\2019\\buildtools\\vc\\tools\\msvc\\14.29.30133\\include\\vcruntime.h", +. +. +. + "z:\\home\\deva\\docs\\c\\ctor\\src\\unittest.h" + ], + "ImportedModules": [], + "ImportedHeaderUnits": [] + } +} +*/ +/* +{ + "Version": "1.2", + "Data": { + "Source": "c:\\jenkins\\workspace\\ctor-linux64\\src\\build.cc", + "ProvidedModule": "", + "Includes": [ + "c:\\jenkins\\workspace\\ctor-linux64\\src\\build.h", + "c:\\program files (x86)\\microsoft visual studio\\2022\\buildtools\\vc\\tools\\msvc\\14.42.34433\\include\\string", + "c:\\program files (x86)\\microsoft visual studio\\2022\\buildtools\\vc\\tools\\msvc\\14.42.34433\\include\\yvals_core.h", +. +. +. + "c:\\program files (x86)\\microsoft visual studio\\2022\\buildtools\\vc\\tools\\msvc\\14.42.34433\\include\\iostream" + ], + "ImportedModules": [], + "ImportedHeaderUnits": [] + } +} +*/ + +std::vector<std::string> readDepsJson(const std::string& dep_file) +{ + std::ifstream stream(dep_file); + auto json = nlohmann::json::parse(stream); + for(const auto& [key, value] : json.items()) + { + if(key == "Data" && value.is_object()) + { + for(const auto& [inner_key, inner_value] : value.items()) + { + if(inner_key == "Includes" && inner_value.is_array()) + { + std::vector<std::string> deps; + for(const auto& dep : inner_value) + { + deps.emplace_back(dep); + } + return deps; + } + } + } + } + return {}; +} } std::vector<std::string> readDeps(const std::string& dep_file, @@ -110,6 +180,10 @@ std::vector<std::string> readDeps(const std::string& dep_file, case ctor::toolchain::none: return {}; + // json based: + case ctor::toolchain::msvc: + return readDepsJson(dep_file); + // makefile .d file based: case ctor::toolchain::gcc: case ctor::toolchain::clang: diff --git a/src/execute.cc b/src/execute.cc index c050732..4ed274f 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -6,12 +6,24 @@ #include "ctor.h" #include "pointerlist.h" -#include <unistd.h> -#include <cstring> -#include <sys/types.h> -#include <sys/wait.h> -#include <spawn.h> +#if !defined(_WIN32) + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> + #include <spawn.h> + extern char **environ; +#else + #define WINDOWS_LEAN_AND_MEAN + #include <windows.h> + #undef max + #include <thread> +#endif + #include <iostream> +#include <cstring> +#include <vector> +#include <deque> +#include <filesystem> /* https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ @@ -23,7 +35,7 @@ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork- namespace { - +#if !defined(_WIN32) int parent_waitpid(pid_t pid) { int status{}; @@ -61,9 +73,80 @@ int parent_waitpid(pid_t pid) // Should never happen... return 1; } -} // namespace :: +#endif //_WIN32 + +#if defined(_WIN32) +std::string getLastErrorAsString() +{ + DWORD errorMessageID = ::GetLastError(); + if(errorMessageID == 0) + { + return {}; + } + + LPSTR message_buffer{}; + auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message_buffer, 0, nullptr); + + std::string message(message_buffer, size); + LocalFree(message_buffer); + return message; +} + +int moveSelf(const ctor::settings& settings) +{ + int cnt{0}; + char source[MAX_PATH]; + HMODULE module = GetModuleHandle(0); + GetModuleFileNameA(module, source, MAX_PATH); + + while(true) + { + if(cnt > 10) // If we need to try more than 10 times something is wrong + { + return 1; + } + + std::filesystem::path tmp_file = settings.builddir; + tmp_file /= "tmp"; + std::string target = tmp_file.string() + "-" + std::to_string(cnt); + if(MoveFileA(source, target.data())) + { + break; // success + } + + auto err = GetLastError(); + if(err == ERROR_ALREADY_EXISTS) + { + if(DeleteFileA(target.data())) + { + continue; // Try again + } + + err = GetLastError(); + if(err != ERROR_ACCESS_DENIED) + { + std::cerr << "Could not delete file\n"; + return err; + } -extern char **environ; // see 'man environ' + cnt++; + continue; // Increment and try again + } + else + { + std::cerr << "Could not move file\n"; + return err; + } + } + return 0; +} +#endif // _WIN32 +} // namespace :: int execute(const ctor::settings& settings, const std::string& command, @@ -98,11 +181,13 @@ int execute(const ctor::settings& settings, std::cout << cmd << std::endl; } +#if !defined(_WIN32) + #if 1 auto pid = vfork(); if(pid == 0) { - EnvMap envmap((const char**)environ); + EnvMap envmap(environ); for(const auto& [key, value] : env) { envmap.insert(key + "=" + value); @@ -120,15 +205,16 @@ int execute(const ctor::settings& settings, } return parent_waitpid(pid); #elif 0 - pid_t pid; - std::vector<std::string> venv; + pid_t pid{}; + EnvMap envmap(environ); for(const auto& [key, value] : env) { - venv.push_back(key + "=" + value); + envmap.insert(key + "=" + value); } - Env penv(venv); + + auto [_, envv] = envmap.get(); if(posix_spawn(&pid, command.data(), nullptr, nullptr, - (char**)argv.data(), penv.data())) + (char**)argv.data(), const_cast<char* const *>(envv))) { return 1; } @@ -138,5 +224,139 @@ int execute(const ctor::settings& settings, return system(cmd.data()); #endif +#else // _WIN32 + if(terminate) + { + auto ret = moveSelf(settings); + if(ret != 0) + { + return ret; + } + } + + auto env_strings = GetEnvironmentStrings(); + EnvMap envmap(env_strings); + FreeEnvironmentStrings(env_strings); + for(const auto& [key, value] : env) + { + envmap.insert(key + "=" + value); + } + + // TODO: Use SetDllDirectory(...) to set DLL search directory? + + SECURITY_ATTRIBUTES security_attr; + // Set the bInheritHandle flag so pipe handles are inherited. + security_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attr.bInheritHandle = TRUE; + security_attr.lpSecurityDescriptor = nullptr; + + HANDLE stream_in_read{}; + HANDLE stream_in_write{}; + HANDLE stream_out_read{}; + HANDLE stream_out_write{}; + + if(!CreatePipe(&stream_out_read, &stream_out_write, &security_attr, 0)) + { + std::cout << "Error CreatePipe (out): " << getLastErrorAsString() << "\n"; + } + + if(!SetHandleInformation(stream_out_read, HANDLE_FLAG_INHERIT, 0)) + { + std::cout << "Error - SetHandleInformation (out): " << getLastErrorAsString() << "\n"; + } + + // Create a pipe for the child process's STDIN. + if(!CreatePipe(&stream_in_read, &stream_in_write, &security_attr, 0)) + { + std::cout << "Error CreatePipe (in): " << getLastErrorAsString() << "\n"; + } + + // Ensure the write handle to the pipe for STDIN is not inherited. + if(!SetHandleInformation(stream_in_write, HANDLE_FLAG_INHERIT, 0)) + { + std::cout << "Error - SetHandleInformation (in): " << getLastErrorAsString() << "\n"; + } + + STARTUPINFO si{}; + si.hStdInput = GetStdHandle(((DWORD)-10));//STD_INPUT_HANDLE + si.hStdOutput = stream_out_write; + si.hStdError = stream_out_write; + si.dwFlags |= STARTF_USESTDHANDLES; + + PROCESS_INFORMATION pi{}; + + si.cb = sizeof(si); + + if(!CreateProcess(nullptr, // lpApplicationName + (char*)cmd.data(), // lpCommandLine + nullptr, // lpProcessAttributes + nullptr, // lpThreadAttributes + TRUE, // bInheritHandles + INHERIT_PARENT_AFFINITY | + 0x00000200 | // CREATE_NEW_PROCESS_GROUP + 0, // dwCreationFlags + envmap.stringify().data(), // lpEnvironment + nullptr, // lpCurrentDirectory + &si, // lpStartupInfo + &pi)) // lpProcessInformation + { + std::cout << "Could not execute " << command << ": " << + getLastErrorAsString() << "\n"; + return 1; + } + + int ignore_lines{}; + std::filesystem::path cmd_path{command}; + if(cmd_path.filename() == "cl.exe") + { + // Ignore first line, avoiding the annoying msvc compiler of printing the filename + // of the file that is being compiled + ignore_lines = 1; + } + + std::atomic<bool> running{true}; + auto parent_waitpid = std::thread([&](){ + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(stream_out_write); + CloseHandle(stream_in_read); + running.store(false); + }); + + CHAR buf[1024]; + while(running.load()) + { + if(WaitForSingleObject(stream_out_read, 0) != WAIT_OBJECT_0) + { + Sleep(1); + continue; + } + DWORD read_bytes{}; + auto res = ReadFile(stream_out_read, buf, sizeof(buf), &read_bytes, nullptr); + if(res != TRUE || read_bytes == 0) + { + break; + } + std::string str; + str.append(buf, read_bytes); + if(ignore_lines == 0) + { + std::cout << str << std::flush; + } + if(str.find('\n') != std::string::npos) + { + ignore_lines = std::max(0, ignore_lines - 1); + } + } + + DWORD exit_code{}; + GetExitCodeProcess(pi.hProcess, &exit_code); + parent_waitpid.join(); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return exit_code; +#endif // _WIN32 + return 1; } diff --git a/src/rebuild.cc b/src/rebuild.cc index a2b7ddd..9a3fca0 100644 --- a/src/rebuild.cc +++ b/src/rebuild.cc @@ -38,7 +38,10 @@ int reg(ctor::build_configurations (*cb)(const ctor::settings&), { auto pwd = std::filesystem::current_path(); auto rel = std::filesystem::relative(loc, pwd); - configFiles[numConfigFiles].file = strdup(rel.string().data()); // NOTE: This intentionally leaks memory + auto str = rel.string(); + auto file = new char[str.size() + 1]; + strncpy(file, str.data(), str.size() + 1); + configFiles[numConfigFiles].file = file; // NOTE: This intentionally leaks memory } else { @@ -169,6 +172,8 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[ config.flags.cxxflags.emplace_back(ctor::cxx_opt::optimization, "3"); config.flags.cxxflags.emplace_back(ctor::cxx_opt::cpp_std, "c++20"); + config.flags.cxxflags.push_back({ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}); + const auto& c = ctor::get_configuration(); if(c.has(ctor::cfg::ctor_includedir)) { @@ -180,9 +185,13 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[ config.flags.ldflags.emplace_back(ctor::ld_opt::library_path, c.get(ctor::cfg::ctor_libdir)); } - config.flags.ldflags.emplace_back(ctor::ld_opt::link, "ctor"); + config.flags.ldflags.emplace_back(ctor::toolchain::msvc, ctor::ld_opt::link, "libctor.lib"); + config.flags.ldflags.emplace_back(ctor::toolchain::gcc, ctor::ld_opt::link, "ctor"); + config.flags.ldflags.emplace_back(ctor::toolchain::clang, ctor::ld_opt::link, "ctor"); config.flags.ldflags.emplace_back(ctor::ld_opt::threads); + config.flags.ldflags.push_back({ctor::toolchain::msvc, ctor::ld_opt::custom, "/subsystem:console"}); + ctor::settings settings{global_settings}; settings.verbose = -1; // Make check completely silent. diff --git a/src/task_ar.cc b/src/task_ar.cc index 3b45cc2..0365d51 100644 --- a/src/task_ar.cc +++ b/src/task_ar.cc @@ -93,6 +93,8 @@ int TaskAR::runInner() append(args, ar_option(toolchain, ctor::ar_opt::add_index)); append(args, ar_option(toolchain, ctor::ar_opt::create)); append(args, ar_option(toolchain, ctor::ar_opt::output, targetFile().string())); + append(args, to_strings(toolchain, {ctor::toolchain::msvc, ctor::ar_opt::custom, "/nologo"})); + for(const auto& task : getDependsTasks()) { args.push_back(task->targetFile().string()); diff --git a/src/task_cc.cc b/src/task_cc.cc index 9628455..b300cd6 100644 --- a/src/task_cc.cc +++ b/src/task_cc.cc @@ -305,12 +305,14 @@ std::vector<std::string> TaskCC::flags() const { append(flags, to_strings(toolchain, flag)); } + append(flags, to_strings(toolchain, {ctor::toolchain::msvc, ctor::c_opt::custom, "/nologo"})); return flags; case ctor::language::cpp: for(const auto& flag : config.flags.cxxflags) { append(flags, to_strings(toolchain, flag)); } + append(flags, to_strings(toolchain, {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/nologo"})); return flags; default: std::cerr << "Unknown CC target type\n"; diff --git a/src/task_ld.cc b/src/task_ld.cc index 03745be..af71bbb 100644 --- a/src/task_ld.cc +++ b/src/task_ld.cc @@ -115,6 +115,7 @@ int TaskLD::runInner() } append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string())); + append(args, to_strings(toolchain, {ctor::toolchain::msvc, ctor::ld_opt::custom, "/nologo"})); { // Write flags to file. std::ofstream flagsStream(flagsFile); @@ -129,11 +130,28 @@ int TaskLD::runInner() auto tool = compiler(); const auto& cfg = ctor::get_configuration(); - auto ldflags = cfg.getenv("LDFLAGS"); - if(!ldflags.empty()) + if(toolchain == ctor::toolchain::gcc || + toolchain == ctor::toolchain::clang) { - append(args, ld_option(toolchain, ctor::ld_opt::custom, ldflags)); + auto ldflags = cfg.getenv("LDFLAGS"); + if(!ldflags.empty()) + { + append(args, ld_option(toolchain, ctor::ld_opt::custom, ldflags)); + } } + else // msvc + { + switch(outputSystem()) + { + case ctor::output_system::host: + tool = cfg.get(ctor::cfg::host_ld, "/usr/bin/ld"); + break; + case ctor::output_system::build: + tool = cfg.get(ctor::cfg::build_ld, "/usr/bin/ld"); + break; + } + } + auto res = execute(settings, tool, args, cfg.env, is_self); if(res != 0) { diff --git a/src/task_so.cc b/src/task_so.cc index 92aeefe..db07c53 100644 --- a/src/task_so.cc +++ b/src/task_so.cc @@ -93,6 +93,8 @@ int TaskSO::runInner() append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string())); + append(args, to_strings(toolchain, {ctor::toolchain::msvc, ctor::ld_opt::custom, "/nologo"})); + for(const auto& task : getDependsTasks()) { args.push_back(task->targetFile().string()); diff --git a/src/tools.cc b/src/tools.cc index dfabdff..1a539be 100644 --- a/src/tools.cc +++ b/src/tools.cc @@ -6,6 +6,7 @@ #include <filesystem> #include <iostream> #include <sstream> +#include <algorithm> #include <array> #include <cassert> @@ -13,6 +14,11 @@ #include "util.h" +#if defined(_WIN32) +#define popen _popen +#define pclose _pclose +#endif + std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt) { // Adding to this enum should also imply adding to the unit-tests below @@ -129,6 +135,10 @@ ctor::toolchain getToolChain(const std::string& compiler) { return ctor::toolchain::gcc; } + else if(to_lower(cc_cmd).find("cl") != std::string::npos) + { + return ctor::toolchain::msvc; + } std::cerr << "Unsupported output system.\n"; return ctor::toolchain::gcc; @@ -154,12 +164,256 @@ ctor::toolchain getToolChain(ctor::output_system system) return getToolChain(cfg.get(ctor::cfg::build_cxx, "g++")); } } +namespace msvc { +std::string get_arch([[maybe_unused]] ctor::output_system system) +{ + return "windows"; +} + +ctor::arch get_arch([[maybe_unused]] const std::string& str) +{ + return ctor::arch::windows; +} + +ctor::c_flag c_option(const std::string& flag) +{ + if(flag.starts_with("/I")) + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::c_opt::include_path, path }; + } + + return { ctor::c_opt::custom, flag }; +} + +ctor::cxx_flag cxx_option(const std::string& flag) +{ + if(flag.starts_with("/I")) + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::cxx_opt::include_path, path }; + } + + return { ctor::cxx_opt::custom, flag }; +} + +ctor::ld_flag ld_option(const std::string& flag) +{ + if(flag.starts_with("/L")) + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::ld_opt::library_path, path }; + } + + return { ctor::ld_opt::custom, flag }; +} + +ctor::ar_flag ar_option(const std::string& flag) +{ + return { ctor::ar_opt::custom, flag }; +} + +std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg, + const std::string& arg2) +{ + switch(opt) + { + case ctor::cxx_opt::output: + return {"/Fo\"" + arg + "\""}; + case ctor::cxx_opt::debug: + return {"/DEBUG"}; + case ctor::cxx_opt::warn_all: + return {"/W4"}; + case ctor::cxx_opt::warn_conversion: + return {"/W4"}; // TODO: This is incorrect + case ctor::cxx_opt::warn_shadow: + return {"/W4"}; // TODO: This is incorrect + case ctor::cxx_opt::warn_extra: + return {"/W4"}; // TODO: This is incorrect + case ctor::cxx_opt::warnings_as_errors: + return {"/WX"}; + case ctor::cxx_opt::generate_dep_tree: + return {"/sourceDependencies", arg}; + case ctor::cxx_opt::no_link: + return {"/c"}; + case ctor::cxx_opt::include_path: + return {"/I" + arg}; + case ctor::cxx_opt::cpp_std: + return {"/std:" + arg}; + case ctor::cxx_opt::optimization: + { + int o{0}; + try + { + o = std::stoi(arg); + o = std::clamp(o, 0, 2); + } + catch(...) + { + // bad number? + } + return {"/O" + std::to_string(o)}; + } + case ctor::cxx_opt::position_independent_code: + return {}; // TODO? + case ctor::cxx_opt::position_independent_executable: + return {}; // TODO? + case ctor::cxx_opt::define: + if(!arg2.empty()) + { + return {"/D" + arg + "=" + esc(arg2)}; + } + else + { + return {"/D" + arg}; + } + case ctor::cxx_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg, + const std::string& arg2) +{ + switch(opt) + { + case ctor::c_opt::output: + return {"/Fo\"" + arg + "\""}; + case ctor::c_opt::debug: + return {"/DEBUG"}; + case ctor::c_opt::warn_all: + return {"/W4"}; + case ctor::c_opt::warn_conversion: + return {"/W4"}; // TODO: This is incorrect + case ctor::c_opt::warn_shadow: + return {"/W4"}; // TODO: This is incorrect + case ctor::c_opt::warn_extra: + return {"/W4"}; // TODO: This is incorrect + case ctor::c_opt::warnings_as_errors: + return {"/WX"}; + case ctor::c_opt::generate_dep_tree: + return {"/sourceDependencies", arg}; + case ctor::c_opt::no_link: + return {"/c"}; + case ctor::c_opt::include_path: + return {"/I" + arg}; + case ctor::c_opt::c_std: + return {"/std:" + arg}; + case ctor::c_opt::optimization: + { + int o{0}; + try + { + o = std::stoi(arg); + o = std::clamp(o, 0, 2); + } + catch(...) + { + // bad number? + } + return {"/O" + std::to_string(o)}; + } + case ctor::c_opt::position_independent_code: + return {}; // TODO? + case ctor::c_opt::position_independent_executable: + return {}; // TODO? + case ctor::c_opt::define: + if(!arg2.empty()) + { + return {"/D" + arg + "=" + esc(arg2)}; + } + else + { + return {"/D" + arg}; + } + case ctor::c_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg, + [[maybe_unused]]const std::string& arg2) +{ + switch(opt) + { + case ctor::ld_opt::output: + return {"/out:" + arg + ""}; + case ctor::ld_opt::warn_all: + return {"/Wall"}; + case ctor::ld_opt::warnings_as_errors: + return {"/WX"}; + case ctor::ld_opt::library_path: + return {"/LIBPATH:\""+arg+"\""}; + case ctor::ld_opt::link: + return {arg}; // TODO? + case ctor::ld_opt::cpp_std: + return {"/std:" + arg}; + case ctor::ld_opt::build_shared: + return {}; // TODO? + case ctor::ld_opt::threads: + return {}; // TODO? + case ctor::ld_opt::position_independent_code: + return {}; // TODO? + case ctor::ld_opt::position_independent_executable: + return {}; // TODO? + case ctor::ld_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg, + [[maybe_unused]]const std::string& arg2) +{ + switch(opt) + { + case ctor::ar_opt::replace: + return {}; + case ctor::ar_opt::add_index: + return {}; + case ctor::ar_opt::create: + return {}; + case ctor::ar_opt::output: + return {"/out:" + arg}; + case ctor::ar_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg, + [[maybe_unused]]const std::string& arg2) +{ + switch(opt) + { + case ctor::asm_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} +} // msvc:: + namespace gcc { -std::string get_arch(ctor::output_system system) +std::string get_arch([[maybe_unused]] ctor::output_system system) { + std::string arch; std::string cmd; - const auto& c = ctor::get_configuration(); switch(system) { @@ -180,7 +434,6 @@ std::string get_arch(ctor::output_system system) return {};//ctor::arch::unknown; } - std::string arch; while(!feof(pipe)) { constexpr auto buffer_size{1024}; @@ -571,6 +824,8 @@ std::string get_arch(ctor::output_system system) case ctor::toolchain::clang: case ctor::toolchain::gcc: return gcc::get_arch(system); + case ctor::toolchain::msvc: + return msvc::get_arch(system); case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -586,6 +841,8 @@ ctor::arch get_arch(ctor::output_system system, const std::string& str) case ctor::toolchain::clang: case ctor::toolchain::gcc: return gcc::get_arch(str); + case ctor::toolchain::msvc: + return msvc::get_arch(str); case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -603,6 +860,8 @@ std::vector<std::string> c_option(ctor::toolchain toolchain, case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::c_option(opt, arg, arg2); + case ctor::toolchain::msvc: + return msvc::c_option(opt, arg, arg2); case ctor::toolchain::any: { std::ostringstream ss; @@ -636,6 +895,8 @@ std::vector<std::string> cxx_option(ctor::toolchain toolchain, case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::cxx_option(opt, arg, arg2); + case ctor::toolchain::msvc: + return msvc::cxx_option(opt, arg, arg2); case ctor::toolchain::any: { std::ostringstream ss; @@ -669,6 +930,8 @@ std::vector<std::string> ld_option(ctor::toolchain toolchain, case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::ld_option(opt, arg, arg2); + case ctor::toolchain::msvc: + return msvc::ld_option(opt, arg, arg2); case ctor::toolchain::any: { std::ostringstream ss; @@ -702,6 +965,8 @@ std::vector<std::string> ar_option(ctor::toolchain toolchain, case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::ar_option(opt, arg, arg2); + case ctor::toolchain::msvc: + return msvc::ar_option(opt, arg, arg2); case ctor::toolchain::any: { std::ostringstream ss; @@ -735,6 +1000,8 @@ std::vector<std::string> asm_option(ctor::toolchain toolchain, case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::asm_option(opt, arg, arg2); + case ctor::toolchain::msvc: + return msvc::asm_option(opt, arg, arg2); case ctor::toolchain::any: { std::ostringstream ss; @@ -766,6 +1033,8 @@ ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain) case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::c_option(flag); + case ctor::toolchain::msvc: + return msvc::c_option(flag); case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -781,6 +1050,8 @@ ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain) case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::cxx_option(flag); + case ctor::toolchain::msvc: + return msvc::cxx_option(flag); case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -796,6 +1067,8 @@ ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain) case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::ld_option(flag); + case ctor::toolchain::msvc: + return msvc::ld_option(flag); case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -811,6 +1084,8 @@ ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain) case ctor::toolchain::gcc: case ctor::toolchain::clang: return gcc::ar_option(flag); + case ctor::toolchain::msvc: + return msvc::ar_option(flag); case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -825,6 +1100,7 @@ ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain) { case ctor::toolchain::gcc: case ctor::toolchain::clang: + case ctor::toolchain::msvc: case ctor::toolchain::any: case ctor::toolchain::none: break; @@ -908,10 +1184,11 @@ ctor::toolchain guess_toolchain(const std::string& opt) return ctor::toolchain::gcc; } - //if(opt[0] == '/') - //{ - // return ctor::toolchain::msvc; - //} + if(opt[0] == '/') + { + return ctor::toolchain::msvc; + } + return ctor::toolchain::any; } } @@ -982,8 +1259,8 @@ ctor::target_type target_type_from_extension(ctor::toolchain toolchain, } } - if(toolchain == ctor::toolchain::any// || - //toolchain == ctor::toolchain::msvc || + if(toolchain == ctor::toolchain::any || + toolchain == ctor::toolchain::msvc// || //toolchain == ctor::toolchain::mingw || ) { diff --git a/src/util.cc b/src/util.cc index 6fc650a..20dea71 100644 --- a/src/util.cc +++ b/src/util.cc @@ -8,10 +8,17 @@ #include <algorithm> #include <sstream> +namespace { +char to_lower_c(char ch) +{ + return static_cast<char>(::tolower(ch)); +} +} + std::string to_lower(const std::string& str) { std::string out{str}; - std::transform(out.begin(), out.end(), out.begin(), ::tolower); + std::transform(out.begin(), out.end(), out.begin(), to_lower_c); return out; } @@ -20,13 +27,18 @@ std::string readFile(const std::string& fileName) std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - std::ifstream::pos_type fileSize = ifs.tellg(); + auto tell = ifs.tellg(); + if(tell < 0) + { + return {}; + } + auto fileSize = static_cast<std::size_t>(tell); ifs.seekg(0, std::ios::beg); - std::vector<char> bytes(static_cast<std::size_t>(fileSize)); - ifs.read(bytes.data(), fileSize); + std::vector<char> bytes(fileSize); + ifs.read(bytes.data(), static_cast<long>(bytes.size())); - return {bytes.data(), static_cast<std::size_t>(fileSize)}; + return { bytes.data(), bytes.size() }; } ctor::language languageFromExtension(const std::filesystem::path& file) @@ -71,7 +83,7 @@ namespace { bool isClean(char c) { - return c != '.' && c != '/'; + return c != '.' && c != '/' && c != '\\'; } } @@ -170,15 +182,36 @@ std::string locate(const std::string& prog, } } + if(std::filesystem::exists(program + ".exe")) + { + if(check_executable(program + ".exe")) + { + return program + ".exe"; + } + } + for(const auto& path_str : paths) { std::filesystem::path path(path_str); - auto prog_path = path / program; - if(std::filesystem::exists(prog_path)) { - if(check_executable(prog_path)) + auto prog_path = path / program; + if(std::filesystem::exists(prog_path)) + { + if(check_executable(prog_path)) + { + return prog_path.string(); + } + } + } + + { + auto prog_path = path / (program + ".exe"); + if(std::filesystem::exists(prog_path)) { - return prog_path.string(); + if(check_executable(prog_path)) + { + return prog_path.string(); + } } } } diff --git a/test/ctor.cc b/test/ctor.cc index b7bcc6d..fa53802 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -23,6 +23,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"argsplit\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, }, }, @@ -40,6 +42,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"pointerlist\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, }, }, @@ -58,7 +62,10 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) .cxxflags = { "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", + "-I../json/include", "-DOUTPUT=\"deps\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, }, }, @@ -72,6 +79,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) .flags = { .cxxflags = { "-std=c++20", "-O3", "-Wall", "-Werror", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, }, }, @@ -92,6 +101,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"execute\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, .ldflags = { "-pthread" }, }, @@ -110,6 +121,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"tasks\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, .ldflags = { "-pthread" }, }, @@ -128,6 +141,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"cycle\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, .ldflags = { "-pthread" }, }, @@ -146,6 +161,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"source_type\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, .ldflags = { "-pthread" }, }, @@ -166,6 +183,8 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"tools\"", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, }, }, }, @@ -190,11 +209,33 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) "../src/tools.cc", "../src/util.cc", "../src/externals_manual.cc", + {ctor::toolchain::msvc, "../getopt-for-windows/getopt.c"}, }, .flags = { + .cflags = { + "-O3", +// "-g", +// "-Wall", +// "-Wconversion", +// "-Wextra", + //"-Werror", + "-I../src", + {ctor::toolchain::msvc, ctor::c_opt::custom, "/I../getopt-for-windows"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/D_X86_"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/external:W0"}, + {ctor::toolchain::msvc, ctor::c_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, + }, .cxxflags = { "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", + "-I../json/include", + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/I../getopt-for-windows"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_X86_"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/EHsc"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/external:W0"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/D_CRT_SECURE_NO_WARNINGS"}, + {ctor::toolchain::msvc, ctor::cxx_opt::custom, "/Dstrdup=_strdup"}, }, .ldflags = { "-pthread" }, }, diff --git a/test/execute_test.cc b/test/execute_test.cc index 722b6ea..4084084 100644 --- a/test/execute_test.cc +++ b/test/execute_test.cc @@ -36,11 +36,23 @@ public: value = execute(s, "no-such-binary", {}, {}, false); uASSERT_EQUAL(1, value); value = execute(s, cmd, {"segfault"}, {}, false); +#if !defined(_WIN32) uASSERT_EQUAL(11, value); +#else + uASSERT_EQUAL(3, value); +#endif value = execute(s, cmd, {"throw"}, {}, false); +#if !defined(_WIN32) uASSERT_EQUAL(6, value); +#else + uASSERT_EQUAL(-1073740791, value); +#endif value = execute(s, cmd, {"abort"}, {}, false); +#if !defined(_WIN32) uASSERT_EQUAL(6, value); +#else + uASSERT_EQUAL(-1073740791, value); +#endif } void env() @@ -53,7 +65,7 @@ public: auto cmd = locate("testprog", paths); uASSERT(!cmd.empty()); - tmp_file tmp; + TmpFile tmp; std::map<std::string, std::string> env; diff --git a/test/source_type_test.cc b/test/source_type_test.cc index 288f1e5..657260e 100644 --- a/test/source_type_test.cc +++ b/test/source_type_test.cc @@ -26,6 +26,14 @@ std::ostream& operator<<(std::ostream& stream, const ctor::language& lang) return stream; } +const ctor::configuration& ctor::get_configuration() +{ + static ctor::configuration cfg{}; + cfg.build_toolchain = ctor::toolchain::gcc; + cfg.build_arch = ctor::arch::unix; + return cfg; +} + class TestableTaskCC : public TaskCC { diff --git a/test/suite/test.sh b/test/suite/test.sh index 97d2551..d57580f 100755 --- a/test/suite/test.sh +++ b/test/suite/test.sh @@ -1,4 +1,6 @@ #!/bin/bash +#set -x + : ${CXX:=g++} : ${CTORDIR:=../../build} : ${BUILDDIR:=build} diff --git a/test/testprog.cc b/test/testprog.cc index 93edc3f..18c2c74 100644 --- a/test/testprog.cc +++ b/test/testprog.cc @@ -5,6 +5,14 @@ extern char **environ; +#if defined(_WIN32) +#define WINDOWS_LEAN_AND_MEAN +#include <windows.h> +#undef max +#else +extern char **environ; // see 'man environ' +#endif + int main(int argc, const char* argv[]) { if(argc < 2) @@ -20,11 +28,41 @@ int main(int argc, const char* argv[]) { return 0; } + std::ofstream ostrm(argv[2], std::ios::binary); + if(ostrm.bad()) + { + std::cout << "Error: Could not write to " << argv[2] << "\n"; + } +#if defined(_WIN32) + auto env_strings = GetEnvironmentStrings(); + const char* ptr = env_strings; + std::string env; + while(true) + { + if(*ptr == '\0') + { + if(env.empty()) + { + // no more + break; + } + ostrm << env << "\n"; + env.clear(); + ++ptr; + continue; + } + + env += *ptr; + ++ptr; + } + FreeEnvironmentStrings(env_strings); +#else for(auto current = environ; *current; ++current) { ostrm << (*current) << "\n"; } +#endif } if(cmd == "retval") diff --git a/test/tmpfile.h b/test/tmpfile.h index 5d114d0..0f83a20 100644 --- a/test/tmpfile.h +++ b/test/tmpfile.h @@ -3,39 +3,29 @@ // See accompanying file LICENSE for details. #pragma once -#include <cstdlib> -#include <unistd.h> +#include <cstdio> -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#endif - -struct tmp_file +class TmpFile { - tmp_file(const std::string& data = {}) +public: + TmpFile(const std::string& data = {}) { - int fd; -#ifdef _WIN32 - char templ[] = "ctor_tmp_file-XXXXXX"; // buffer for filename - _mktemp_s(templ, sizeof(templ)); - fd = open(templ, O_CREAT | O_RDWR); -#else - char templ[] = "/tmp/ctor_tmp_file-XXXXXX"; // buffer for filename - fd = mkstemp(templ); -#endif - filename = templ; - auto sz = write(fd, data.data(), data.size()); - (void)sz; - close(fd); + auto tmp_dir = std::filesystem::temp_directory_path(); + auto tmp_file_template = tmp_dir / "ctor_tmp_file-"; + std::FILE* fp{nullptr}; + int counter{}; + while(!fp) + { + filename = tmp_file_template.string() + std::to_string(counter++); + fp = std::fopen(filename.data(), "wx"); + } + std::fwrite(data.data(), data.size(), 1, fp); + std::fclose(fp); } - ~tmp_file() + ~TmpFile() { - unlink(filename.data()); + std::filesystem::remove(filename); } const std::string& get() const diff --git a/test/tools_test.cc b/test/tools_test.cc index 5ae04c3..15270b3 100644 --- a/test/tools_test.cc +++ b/test/tools_test.cc @@ -22,6 +22,9 @@ std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) case ctor::toolchain::clang: stream << "ctor::toolchain::clang"; break; + case ctor::toolchain::msvc: + stream << "ctor::toolchain::msvc"; + break; } return stream; } |