From 7436d83ef371d4fee4a66bec235e102ed80275db Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 19 Aug 2022 18:09:25 +0200 Subject: Add support for msvc tool-chain (cl.exe/link.exe and lib.exe) on windows. --- .gitignore | 5 +- bootstrap.bat | 44 ++++++++++++ bootstrap.sh | 9 +-- clbootstrap.sh | 50 +++++++++++++ clrun.sh | 22 ++++++ ctor.cc | 29 ++++++-- src/bootstrap.cc | 22 +++++- src/configure.cc | 215 ++++++++++++++++++++++++++++++++++++++++++++++++------- src/execute.cc | 133 +++++++++++++++++++++++++++++----- src/execute.h | 2 + src/libctor.cc | 9 +++ src/libctor.h | 1 + src/rebuild.cc | 149 ++++++++++++++++++++++++++++++++++++-- src/rebuild.h | 3 +- src/task.cc | 23 ++++-- src/task.h | 1 + src/task_ar.cc | 12 ++-- src/task_cc.cc | 31 ++++++-- src/task_fn.cc | 2 +- src/task_ld.cc | 12 ++-- src/task_so.cc | 3 +- src/tasks.cc | 7 +- src/tasks.h | 6 +- src/tools.cc | 63 +++++++++++++++- src/tools.h | 1 + src/unittest.cc | 3 +- src/unittest.h | 2 +- src/util.cc | 1 + 28 files changed, 763 insertions(+), 97 deletions(-) create mode 100644 bootstrap.bat create mode 100755 clbootstrap.sh create mode 100755 clrun.sh diff --git a/.gitignore b/.gitignore index 4a8a9cb..393e55a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ drumgizmo/ build/ ctor -*.a -*.o -*.d +ctor.exe configuration.cc config.h +examples diff --git a/bootstrap.bat b/bootstrap.bat new file mode 100644 index 0000000..2a8820a --- /dev/null +++ b/bootstrap.bat @@ -0,0 +1,44 @@ +@echo off +:: Run with: +:: WINEDEBUG=-all wine start bootstrap.bat + +set BASE=C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133 +set ONECORELIB="%BASE%\lib\onecore\x86" +set path=%path%;%BASE%\bin\Hostx86\x86 +set INCLUDE="%BASE%\include" +set UCRT="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt" +set UCRTLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\ucrt\x86" +set UM="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um" +set UMLIB="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86" +set SHARED="C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared" +echo Bootstrapping... + +set CL=/I"%BASE%\include" /I%UCRT% /I%UM% /I%SHARED% /link /LIBPATH:%UMLIB% /LIBPATH:%ONECORELIB% /LIBPATH:%UCRTLIB% +set LINK=/LIBPATH:%UMLIB% /LIBPATH:%ONECORELIB% /LIBPATH:%UCRTLIB% +set LIB=/LIBPATH:%UMLIB% /LIBPATH:%ONECORELIB% /LIBPATH:%UCRTLIB% + +:: https://docs.microsoft.com/en-us/cpp/build/reference/cl-environment-variables?view=msvc-170 + +:: set INCLUDE="%BASE%\include";%UCRT%;%UM%;%SHARED% +:: set LIB= +:: set LIBPATH=%UMLIB%;%ONECORELIB%;%UCRTLIB% + +set CXX=cl.exe +set CC=cl.exe +set AR=lib.exe +set LD=link.exe +cl /nologo /std:c++20 /D_X86_ /EHsc /Isrc src/bootstrap.cc /link /SUBSYSTEM:CONSOLE /out:ctor.exe + +set /p DUMMY=Hit ENTER to continue... + +ctor.exe + +set /p DUMMY=Hit ENTER to continue... + +cl /nologo /std:c++20 /D_X86_ /EHsc /Isrc ctor.cc build/libctor.lib /link /SUBSYSTEM:CONSOLE /out:ctor.exe + +set /p DUMMY=Hit ENTER to continue... + +ctor.exe configure --name ctor.exe --ctor-includedir=src --ctor-libdir=build --cxx=cl.exe --cc=cl.exe --ar=lib.exe --ld=link.exe + +set /p DUMMY=!!!!!!!!!!!!!!!!!Hit ENTER to continue... diff --git a/bootstrap.sh b/bootstrap.sh index a5c11ac..3a9c454 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,7 +1,8 @@ #!/bin/sh +set -e echo "Bootstrapping..." -g++ -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor && \ -./ctor && \ -g++ -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -Lbuild -lctor -o ctor && \ -./ctor configure --ctor-includedir=src --ctor-libdir=build && \ +g++ -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor +./ctor +g++ -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -Lbuild -lctor -o ctor +./ctor configure --ctor-includedir=src --ctor-libdir=build echo "Done. Now run ./ctor to (re)build." diff --git a/clbootstrap.sh b/clbootstrap.sh new file mode 100755 index 0000000..e3a420f --- /dev/null +++ b/clbootstrap.sh @@ -0,0 +1,50 @@ +#!/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" +echo Bootstrapping... + +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\"" + +# https://docs.microsoft.com/en-us/cpp/build/reference/cl-environment-variables?view=msvc-170 + +# set INCLUDE="$BASE\include";$UCRT;$UM;$SHARED +# set LIB= +# set LIBPATH=$UMLIB;$ONECORELIB;$UCRTLIB + +rm -Rf ctor.exe build/ configuration.cc + +export CXX="cl.exe" +export CC="cl.exe" +export AR="lib.exe" +export LD="link.exe" +wine cl /nologo /std:c++20 /D_X86_ /EHsc /Isrc src/bootstrap.cc /link /SUBSYSTEM:CONSOLE /out:ctor.exe + +#echo -e "\n\n\nHit ENTER to continue...\n\n\n"; read + +wine ctor.exe + +#echo -e "\n\n\nHit ENTER to continue...\n\n\n"; read + +wine cl /nologo /std:c++20 /D_X86_ /EHsc /Isrc ctor.cc build/libctor.lib /link /SUBSYSTEM:CONSOLE /out:ctor.exe + +#echo -e "\n\n\nHit ENTER to continue...\n\n\n"; read + +wine ctor.exe configure --ctor-includedir=src --ctor-libdir=build --cxx=cl.exe --cc=cl.exe --ar=lib.exe --ld=link.exe + +#echo -e "\n\n\nHit ENTER to continue...\n\n\n"; read + diff --git a/clrun.sh b/clrun.sh new file mode 100755 index 0000000..e920372 --- /dev/null +++ b/clrun.sh @@ -0,0 +1,22 @@ +#!/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 + diff --git a/ctor.cc b/ctor.cc index 445f18a..baf22c1 100644 --- a/ctor.cc +++ b/ctor.cc @@ -11,7 +11,7 @@ BuildConfigurations ctorConfigs(const Settings& settings) { { .type = TargetType::StaticLibrary, - .target = "libctor.a", + .target = "libctor.lib", .sources = { "src/build.cc", "src/configure.cc", @@ -29,15 +29,30 @@ BuildConfigurations ctorConfigs(const Settings& settings) "src/tools.cc", "src/util.cc", "src/unittest.cc", + "getopt-for-windows/getopt.c", }, .flags = { .cxxflags = { - "-std=c++20", - "-O3", - "-g", - "-Wall", - "-Werror", - "-Isrc", + "/std:c++20", +// "/O2", +// "/Wall", +// "/WX", + "/Isrc", + "/nologo", + "/EHsc", + "/D_X86_", + "/Igetopt-for-windows", + }, + .cflags = { + "/std:c++20", +// "/O2", +// "/Wall", +// "/WX", + "/Isrc", + "/nologo", + "/EHsc", + "/D_X86_", + "/Igetopt-for-windows", }, }, } diff --git a/src/bootstrap.cc b/src/bootstrap.cc index 9a3c321..97fe6fa 100644 --- a/src/bootstrap.cc +++ b/src/bootstrap.cc @@ -3,6 +3,7 @@ // See accompanying file LICENSE for details. #include #include +#include #define BOOTSTRAP @@ -17,6 +18,7 @@ #include "tasks.cc" #include "build.cc" #include "tools.cc" +#include "../ctor.cc" std::filesystem::path configurationFile("configuration.cc"); std::filesystem::path configHeaderFile("config.h"); @@ -35,6 +37,24 @@ bool hasConfiguration(const std::string& key) const std::string& getConfiguration(const std::string& key, const std::string& defaultValue) { + if(key == cfg::host_cxx && std::getenv("CXX")) + { + static std::string s = std::getenv("CXX"); + return s; + } + + if(key == cfg::host_cc && std::getenv("CC")) + { + static std::string s = std::getenv("CC"); + return s; + } + + if(key == cfg::host_ar && std::getenv("AR")) + { + static std::string s = std::getenv("AR"); + return s; + } + return defaultValue; } @@ -51,7 +71,7 @@ int main(int argc, char* argv[]) settings.builddir = getConfiguration(cfg::builddir, "build"); settings.parallel_processes = - std::max(1u, std::thread::hardware_concurrency() * 2 - 1); + (std::max)(1u, std::thread::hardware_concurrency() * 2 - 1); settings.verbose = 0; auto all_tasks = getTasks(settings, {}, false); for(auto task : all_tasks) diff --git a/src/configure.cc b/src/configure.cc index a81f8ad..bb94202 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -23,15 +23,73 @@ std::map external_includedir; std::map external_libdir; const Configuration default_configuration{}; +#if !defined(_WIN32) const Configuration& __attribute__((weak)) configuration() { return default_configuration; } +#else +//extern const char * pWeakValue; +//extern const char * pDefaultWeakValue = NULL; +// +//#pragma comment(linker, "/alternatename:_pWeakValue=_pDefaultWeakValue") +const Configuration& defConfiguration() +{ + return default_configuration; +} +#pragma comment(linker, "/alternatename:?configuration@@YAABUConfiguration@@XZ=?defConfiguration@@YAABUConfiguration@@XZ") +//https://stackoverflow.com/questions/2290587/gcc-style-weak-linking-in-visual-studio +//https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024 + +//extern "C" +//{ +//void default_error_log() { /* do nothing */ } +//} +//// For expository simplification: assume x86 cdecl +//#pragma comment(linker, "/alternatename:_error_log=_default_error_log") + + +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +void _copyAndRelaunch(int argc, char* argv[], + const std::string& copy_src, + const std::string& copy_tar, + const std::string& compilation_name) +{ +#ifdef _WIN32 + CopyFile(copy_src.data(), copy_tar.data(), false); + SetFileAttributes(copy_tar.data(), FILE_ATTRIBUTE_HIDDEN); + auto t = std::filesystem::last_write_time(copy_src); + std::filesystem::last_write_time(copy_tar, t); + + STARTUPINFO si{}; + PROCESS_INFORMATION pi{}; + si.cb = sizeof(pi); + + std::string args = copy_tar + " configure"; + for(int i = 1; i < argc; ++i) + { + args += " "; + args += argv[i]; + } + args += " --name "; + args += compilation_name; + + CreateProcess(nullptr, args.data(), 0,0,0,0,0,0,&si,&pi); + exit(1); +#endif // _WIN32 +} namespace ctor { std::optional includedir; std::optional libdir; +std::map conf_values; } bool hasConfiguration(const std::string& key) @@ -46,6 +104,11 @@ bool hasConfiguration(const std::string& key) return true; } + if(ctor::conf_values.find(key) != ctor::conf_values.end()) + { + return true; + } + const auto& c = configuration(); return c.tools.find(key) != c.tools.end(); } @@ -63,6 +126,11 @@ const std::string& getConfiguration(const std::string& key, return *ctor::libdir; } + if(ctor::conf_values.find(key) != ctor::conf_values.end()) + { + return ctor::conf_values[key]; + } + const auto& c = configuration(); if(hasConfiguration(key)) { @@ -88,11 +156,24 @@ std::string locate(const std::string& arch, const std::string& app) { std::stringstream ss(path_env); std::string path; - while (std::getline(ss, path, ':')) + while (std::getline(ss, path, ';')) { paths.push_back(path); } } + { + const auto& cfg = configuration(); + auto it = cfg.env.find("ctorPATH"); + if(it != cfg.env.end()) + { + std::stringstream ss(it->second); + std::string path; + while (std::getline(ss, path, ';')) + { + paths.push_back(path); + } + } + } for(const auto& path_str : paths) { std::filesystem::path path(path_str); @@ -127,10 +208,10 @@ class Args : public std::vector { public: - Args(const std::vector& args) + Args(const std::string& name, const std::vector& args) { resize(args.size() + 1); - (*this)[0] = strdup("./ctor"); + (*this)[0] = strdup(name.data()); for(std::size_t i = 0; i < size() - 1; ++i) { (*this)[i + 1] = strdup(args[i].data()); @@ -146,15 +227,32 @@ public: } }; +std::string esc(const std::string& in) +{ + std::string out; + for(auto c : in) + { + switch(c) + { + case '\\': out += "\\\\"; break; + case '"': out += "\\\""; break; + default: + out += c; + break; + } + } + return out; +} + // helper constant for the visitor template inline constexpr bool always_false_v = false; -int regenerateCache(const Settings& default_settings, +int regenerateCache(Settings& settings, + const std::string& name, const std::vector& args, const std::map& env) { - Settings settings{default_settings}; - Args vargs(args); + Args vargs(name, args); dg::Options opt; int key{128}; @@ -185,6 +283,13 @@ int regenerateCache(const Settings& default_settings, return 0; }); + opt.add("name", required_argument, 'n', + "Set the output name of the ctor command to override the current one.", + [&]() { + settings.name = optarg; + return 0; + }); + opt.add("cc", required_argument, key++, "Use specified c-compiler instead of gcc.", [&]() { @@ -311,6 +416,24 @@ int regenerateCache(const Settings& default_settings, opt.process(vargs.size(), vargs.data()); + std::filesystem::path settings_file(settings.name); + std::filesystem::path argv0_file(vargs[0]); + + if(settings_file.is_relative()) + { + settings_file = std::filesystem::current_path() / settings_file; + } + if(argv0_file.is_relative()) + { + argv0_file = std::filesystem::current_path() / argv0_file; + } + + if(settings_file.string() == argv0_file.string()) + { + _copyAndRelaunch(vargs.size(), vargs.data(), vargs[0], "ctor-configure-tmp.exe", settings.name); + exit(0); + } + if(host_arch.empty()) { host_arch = build_arch; @@ -376,6 +499,20 @@ int regenerateCache(const Settings& default_settings, std::string build_ar = locate(build_arch, ar_prog); std::string build_ld = locate(build_arch, ld_prog); + // Store current values for execution in this execution context. + if(!ctor_includedir.empty()) + { + ctor::conf_values[cfg::ctor_includedir] = ctor_includedir; + } + if(!ctor_libdir.empty()) + { + ctor::conf_values[cfg::ctor_libdir] = ctor_libdir; + } + ctor::conf_values[cfg::host_cxx] = host_cxx; + ctor::conf_values[cfg::build_cxx] = build_cxx; + ctor::conf_values[cfg::host_ld] = host_ld; + ctor::conf_values[cfg::build_ld] = build_ld; + std::cout << "Writing results to: " << configurationFile.string() << "\n"; { std::ofstream istr(configurationFile); @@ -387,34 +524,34 @@ int regenerateCache(const Settings& default_settings, istr << " .args = {"; for(const auto& arg : args) { - istr << "\"" << arg << "\","; + istr << "\"" << esc(arg) << "\","; } istr << "},\n"; - istr << " .env = {"; + istr << " .env = {\n"; for(const auto& e : env) { - istr << "{\"" << e.first << "\", \"" << e.second << "\"}, "; + istr << " {\"" << e.first << "\", \"" << esc(e.second) << "\"},\n"; } - istr << "},\n"; + istr << " },\n"; istr << " .tools = {\n"; - istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; - istr << " { \"" << cfg::host_cc << "\", \"" << host_cc << "\" },\n"; - istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; - istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n"; - istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n"; - istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n"; - istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; - istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n"; - istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n"; + istr << " { \"" << cfg::builddir << "\", \"" << esc(settings.builddir) << "\" },\n"; + istr << " { \"" << cfg::host_cc << "\", \"" << esc(host_cc) << "\" },\n"; + istr << " { \"" << cfg::host_cxx << "\", \"" << esc(host_cxx) << "\" },\n"; + istr << " { \"" << cfg::host_ar << "\", \"" << esc(host_ar) << "\" },\n"; + istr << " { \"" << cfg::host_ld << "\", \"" << esc(host_ld) << "\" },\n"; + istr << " { \"" << cfg::build_cc << "\", \"" << esc(build_cc) << "\" },\n"; + istr << " { \"" << cfg::build_cxx << "\", \"" << esc(build_cxx) << "\" },\n"; + istr << " { \"" << cfg::build_ar << "\", \"" << esc(build_ar) << "\" },\n"; + istr << " { \"" << cfg::build_ld << "\", \"" << esc(build_ld) << "\" },\n"; if(!ctor_includedir.empty()) { - istr << " { \"" << cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n"; + istr << " { \"" << cfg::ctor_includedir << "\", \"" << esc(ctor_includedir) << "\" },\n"; ctor::includedir = ctor_includedir; } if(!ctor_libdir.empty()) { - istr << " { \"" << cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n"; + istr << " { \"" << cfg::ctor_libdir << "\", \"" << esc(ctor_libdir) << "\" },\n"; ctor::libdir = ctor_libdir; } @@ -508,7 +645,7 @@ int configure(const Settings& global_settings, int argc, char* argv[]) args.push_back(argv[i]); } - std::map env; + auto env = configuration().env; auto cc_env = getenv("CC"); if(cc_env) { @@ -533,7 +670,32 @@ int configure(const Settings& global_settings, int argc, char* argv[]) env["LD"] = ld_env; } - auto ret = regenerateCache(settings, args, 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 path_env = getenv("PATH"); + if(path_env) + { + env["ctorPATH"] = path_env; + } + + auto ret = regenerateCache(settings, argv[0], args, env); if(ret != 0) { return ret; @@ -544,8 +706,9 @@ int configure(const Settings& global_settings, int argc, char* argv[]) return 0; } -int reconfigure(const Settings& settings, int argc, char* argv[]) +int reconfigure(const Settings& global_settings, int argc, char* argv[]) { + Settings settings{global_settings}; bool no_rerun{false}; std::vector args; @@ -573,7 +736,7 @@ int reconfigure(const Settings& settings, int argc, char* argv[]) } std::cout << "\n"; - auto ret = regenerateCache(settings, cfg.args, cfg.env); + auto ret = regenerateCache(settings, argv[0], cfg.args, cfg.env); if(ret != 0) { return ret; @@ -586,5 +749,5 @@ int reconfigure(const Settings& settings, int argc, char* argv[]) return 0; // this was originally invoked by configure, don't loop } - return execute(argv[0], args); + return execute(argv[0], args, {}); } diff --git a/src/execute.cc b/src/execute.cc index 610ccdd..5e59ff6 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -3,11 +3,17 @@ // See accompanying file LICENSE for details. #include "execute.h" -#include #include +#if !defined(_WIN32) +#include #include #include #include +#else +#include +#include +#include +#endif #include /* @@ -18,6 +24,7 @@ https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork- */ +#if !defined(_WIN32) namespace { int parent_waitpid(pid_t pid) @@ -32,9 +39,11 @@ int parent_waitpid(pid_t pid) return WEXITSTATUS(status); } } // namespace :: +#endif int execute(const std::string& command, const std::vector& args, + const std::map& env, bool verbose) { std::vector argv; @@ -45,46 +54,134 @@ int execute(const std::string& command, } argv.push_back(nullptr); - if(verbose) + std::string cmd; + for(const auto& arg : argv) { - std::string cmd; - for(const auto& arg : argv) + if(arg == nullptr) { - if(arg == nullptr) - { - break; - } - if(!cmd.empty()) - { - cmd += " "; - } - cmd += arg; + break; + } + if(!cmd.empty()) + { + cmd += " "; } + cmd += arg; + } + if(verbose) + { std::cout << cmd << "\n"; } +#if !defined(_WIN32) + #if 1 auto pid = vfork(); if(pid == 0) { + for(const auto& [key, value] : env) + { + setenv(key, value); + } + // TODO: use execvpe to set LD_LIBRARY_PATH execv(command.data(), (char**)argv.data()); std::cout << "Could not execute " << command << ": " << strerror(errno) << "\n"; _exit(1); // execv only returns if an error occurred } - auto ret = parent_waitpid(pid); + + return parent_waitpid(pid); #elif 0 pid_t pid; + const char * envp[] = {nullptr}; // TODO: use this to set LD_LIBRARY_PATH if(posix_spawn(&pid, command.data(), nullptr, nullptr, - (char**)argv.data(), nullptr)) + (char**)argv.data(), envp)) { return 1; } - auto ret = parent_waitpid(pid); + return parent_waitpid(pid); #else - auto ret = system(cmd.data()); + return system(cmd.data()); #endif - return ret; +#else + std::map new_env; + auto env_strings = GetEnvironmentStrings(); + const char* ptr = env_strings; + std::string key; + std::string value; + bool key_state{true}; + while(true) + { + if(key_state) // searching for key + { + if(*ptr == '\0') + { + break; + } + + if(*ptr != '=') + { + key += *ptr; + } + else + { + key_state = false; + } + } + else + { + if(*ptr != '\0') + { + value += *ptr; + } + else + { + new_env.insert({key, value}); + key = {}; + value = {}; + key_state = true; + } + } + ++ptr; + } + FreeEnvironmentStrings(env_strings); + + // add new env vars (if any) + for(const auto& [key, value] : env) + { + //if(key == "CL" || key == "LINK" || key == "LIB") + { + new_env[key] = value; + } + } + + std::string env_str; + for(const auto& [k,v] : new_env) + { + env_str += k + "=" + v; + env_str += '\0'; + } + env_str += '\0'; + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si,sizeof(si)); + si.cb=sizeof(si); + ZeroMemory(&pi,sizeof(pi)); + // TODO: Use SetDllDirectory(...) to set DLL search directory + if(!CreateProcess(nullptr, //"C:/WINDOWS/notepad.exe", + //"notepad.exe c:/readme.txt",0,0,0,0,0,0,&si,&pi)) + (char*)cmd.data(),0,0,0,0,env_str.data(),0,&si,&pi)) + { + return 1; // Could not start process; + } + + // Now 'pi.hProcess' contains the process HANDLE, which you can use to + // wait for it like this: + const DWORD infinite = 0xFFFFFFFF; + WaitForSingleObject(pi.hProcess, infinite); + DWORD exitCode{0}; + GetExitCodeProcess(pi.hProcess, &exitCode); + return exitCode; +#endif } diff --git a/src/execute.h b/src/execute.h index c750a83..2ad81d1 100644 --- a/src/execute.h +++ b/src/execute.h @@ -5,7 +5,9 @@ #include #include +#include int execute(const std::string& command, const std::vector& args, + const std::map& env, bool verbose = true); diff --git a/src/libctor.cc b/src/libctor.cc index d188771..bfa09d6 100644 --- a/src/libctor.cc +++ b/src/libctor.cc @@ -33,6 +33,7 @@ int main(int argc, char* argv[]) settings.builddir = getConfiguration(cfg::builddir, settings.builddir); settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency()) * 2 - 1; + settings.name = argv[0]; if(argc > 1 && std::string(argv[1]) == "configure") { @@ -96,6 +97,14 @@ int main(int argc, char* argv[]) return 0; }); + opt.add("name", required_argument, 'n', + "Set the output name of the ctor command to override the current one.", + [&]() { + std::this_thread::sleep_for(std::chrono::seconds(3)); + settings.name = optarg; + return 0; + }); + opt.add("add", required_argument, 'a', "Add specified file to the build configurations.", [&]() { diff --git a/src/libctor.h b/src/libctor.h index 96e1115..e4ea7e7 100644 --- a/src/libctor.h +++ b/src/libctor.h @@ -69,6 +69,7 @@ struct Settings std::string builddir{"build"}; std::size_t parallel_processes{1}; int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... + std::string name; }; struct BuildConfiguration; diff --git a/src/rebuild.cc b/src/rebuild.cc index 9ddf5ba..4b0c8e5 100644 --- a/src/rebuild.cc +++ b/src/rebuild.cc @@ -143,6 +143,92 @@ bool contains(const std::vector& sources, const std::string& file) } } +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +// From: http://www.catch22.net/tuts/win32/self-deleting-executables +void DeleteSelf() +{ +//#ifdef Windows NT/2000 +#if 1 + char buf[MAX_PATH]; + HMODULE module; + + module = GetModuleHandle(0); + GetModuleFileName(module, buf, MAX_PATH); + CloseHandle((HANDLE)4); + + __asm + { + lea eax, buf + push 0 + push 0 + push eax + push ExitProcess + push module + push DeleteFile + push UnmapViewOfFile + ret + } +#endif +//#elif Windows 95/98 +#if 0 + char buf[MAX_PATH]; + HMODULE module; + + module = GetModuleHandle(0); + GetModuleFileName(module, buf, MAX_PATH); + + __asm + { + lea eax, buf + push 0 + push 0 + push eax + push ExitProcess + push module + push DeleteFile + push FreeLibrary + ret + } +#endif +//endif +} +#endif // _WIN32 + +void copyAndRelaunch(bool dirty_tasks, bool delete_me, + int argc, char* argv[], + const std::string& copy_src, + const std::string& copy_tar, + const std::string& compilation_name) +{ +#ifdef _WIN32 + if(dirty_tasks && !delete_me) + { + CopyFile(copy_src.data(), copy_tar.data(), false); + SetFileAttributes(copy_tar.data(), FILE_ATTRIBUTE_HIDDEN); + auto t = std::filesystem::last_write_time(copy_src); + std::filesystem::last_write_time(copy_tar, t); + + STARTUPINFO si{}; + PROCESS_INFORMATION pi{}; + si.cb = sizeof(pi); + std::string args = copy_tar; + for(int i = 1; i < argc; ++i) + { + args += " "; + args += argv[i]; + } + args += " --name "; + args += compilation_name; + + CreateProcess(nullptr, args.data(), 0,0,0,0,0,0,&si,&pi); + exit(0); + } +#endif // _WIN32 +} + bool recompileCheck(const Settings& global_settings, int argc, char* argv[], bool relaunch_allowed) { @@ -152,40 +238,65 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[], { std::cout << "Recompile check (" << numConfigFiles << "):\n"; } + std::filesystem::path settings_file(global_settings.name); + std::filesystem::path argv0_file(argv[0]); + + if(settings_file.is_relative()) + { + settings_file = std::filesystem::current_path() / settings_file; + } + if(argv0_file.is_relative()) + { + argv0_file = std::filesystem::current_path() / argv0_file; + } + + bool delete_me = settings_file.string() != argv0_file.string(); BuildConfiguration config; + config.type = TargetType::Executable; config.name = "ctor"; config.system = OutputSystem::Build; auto tool_chain = getToolChain(config.system); append(config.flags.cxxflags, - getOption(tool_chain, opt::optimization, "3")); + getOption(tool_chain, opt::optimization, "2")); append(config.flags.cxxflags, getOption(tool_chain, opt::cpp_std, "c++20")); + config.flags.cxxflags.push_back("/nologo"); if(hasConfiguration(cfg::ctor_includedir)) { append(config.flags.cxxflags, getOption(tool_chain, opt::include_path, getConfiguration(cfg::ctor_includedir))); } + if(hasConfiguration(cfg::ctor_libdir)) { append(config.flags.ldflags, getOption(tool_chain, opt::library_path, getConfiguration(cfg::ctor_libdir))); } - append(config.flags.ldflags, getOption(tool_chain, opt::link, "ctor")); +// append(config.flags.ldflags, getOption(tool_chain, opt::link, "ctor")); + config.flags.ldflags.push_back("build/libctor.lib"); append(config.flags.ldflags, getOption(tool_chain, opt::threads)); + config.flags.ldflags.push_back("/nologo"); + config.flags.ldflags.push_back("/SUBSYSTEM:CONSOLE"); + Settings settings{global_settings}; - settings.verbose = -1; // Make check completely silent. + settings.parallel_processes = 1; + settings.verbose = 2;//-1; // Make check completely silent. settings.builddir += "/ctor"; // override builddir to use ctor subdir - { std::filesystem::path buildfile = settings.builddir; - std::filesystem::path currentfile = argv[0]; + std::filesystem::path currentfile = settings.name; + + if(currentfile.is_relative()) + { + currentfile = std::filesystem::current_path() / currentfile; + } config.target = std::filesystem::relative(currentfile, buildfile).string(); } @@ -247,35 +358,61 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[], } auto dirty_tasks = build(settings, "ctor", tasks, true); // dryrun + + copyAndRelaunch(dirty_tasks, delete_me, argc, argv, + settings.name/*argv[0]*/, "ctor-rebuild-tmp.exe", settings.name); + + bool was_rebuilt{false}; if(dirty_tasks) { std::cout << "Rebuilding config.\n"; auto ret = build(settings, "ctor", tasks); // run for real if(ret != 0) { + if(delete_me) + { + DeleteSelf(); + } return ret; } + was_rebuilt = true; } if(reconfigure) { + copyAndRelaunch(true, delete_me, argc, argv, + settings.name, + "ctor-reconfigure-tmp.exe", settings.name); std::vector args; args.push_back("reconfigure"); if(!relaunch_allowed) { args.push_back("--no-rerun"); } + if(delete_me) + { + args.push_back("--name " + settings.name); + } for(int i = 1; i < argc; ++i) { args.push_back(argv[i]); } - auto ret = execute(argv[0], args); + auto ret = execute(settings.name, args, {}); //if(ret != 0) { + if(delete_me) + { + DeleteSelf(); + } exit(ret); } } + if(delete_me) + { + DeleteSelf(); + } + return dirty_tasks; } diff --git a/src/rebuild.h b/src/rebuild.h index f1255c6..7b0ebb8 100644 --- a/src/rebuild.h +++ b/src/rebuild.h @@ -7,8 +7,7 @@ #include #include "libctor.h" - -class Settings; +//class Settings; struct BuildConfigurationEntry { diff --git a/src/task.cc b/src/task.cc index fb50765..4736565 100644 --- a/src/task.cc +++ b/src/task.cc @@ -3,7 +3,7 @@ // See accompanying file LICENSE for details. #include "task.h" -#include +//#include #include Task::Task(const BuildConfiguration& config, const Settings& settings, @@ -147,11 +147,24 @@ std::string Task::compiler() const case OutputSystem::Build: return getConfiguration(cfg::build_cxx, "/usr/bin/g++"); } - default: - std::cerr << "Unknown CC target type\n"; - exit(1); - break; } + + std::cerr << "Unknown CC target type\n"; + exit(1); +} + +std::string Task::linker() const +{ + switch(outputSystem()) + { + case OutputSystem::Host: + return getConfiguration(cfg::host_ld, "/usr/bin/ld"); + case OutputSystem::Build: + return getConfiguration(cfg::build_ld, "/usr/bin/ld"); + } + + std::cerr << "Unknown CC target type\n"; + exit(1); } std::set> Task::getDependsTasks() diff --git a/src/task.h b/src/task.h index be3995f..0e2a9f7 100644 --- a/src/task.h +++ b/src/task.h @@ -64,6 +64,7 @@ public: Language sourceLanguage() const; OutputSystem outputSystem() const; std::string compiler() const; + std::string linker() const; std::set> getDependsTasks(); diff --git a/src/task_ar.cc b/src/task_ar.cc index 3e1746c..3fcfcf4 100644 --- a/src/task_ar.cc +++ b/src/task_ar.cc @@ -78,13 +78,16 @@ bool TaskAR::dirtyInner() int TaskAR::runInner() { std::vector args; - args.push_back("rcs"); - args.push_back(targetFile().string()); + //args.push_back("rcs"); for(const auto& task : getDependsTasks()) { args.push_back(task->targetFile().string()); } + args.push_back("/nologo"); + args.push_back("/SUBSYSTEM:CONSOLE"); + args.push_back("/out:"+targetFile().string()); + { // Write flags to file. std::ofstream flagsStream(flagsFile); flagsStream << flagsString(); @@ -106,7 +109,8 @@ int TaskAR::runInner() break; } - return execute(tool, args, settings.verbose > 0); + const auto& cfg = configuration(); + return execute(tool, args, cfg.env, settings.verbose > 0); } int TaskAR::clean() @@ -168,7 +172,7 @@ std::string TaskAR::flagsString() const } flagsStr += flag; } - flagsStr += "\n"; + //flagsStr += "\n"; for(const auto& dep : config.depends) { diff --git a/src/task_cc.cc b/src/task_cc.cc index c4343b6..e9ef590 100644 --- a/src/task_cc.cc +++ b/src/task_cc.cc @@ -24,7 +24,6 @@ TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, sourceFile /= source.file; std::filesystem::path base = sourceFile.parent_path(); - std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base); base /= cleanUp(config.target); base += "-"; @@ -56,14 +55,17 @@ TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, if(source.output.empty()) { _targetFile = base; - _targetFile += ".o"; + _targetFile += ".obj"; } else { _targetFile = source.output; } + + std::filesystem::create_directories(targetFile().parent_path()); + depsFile = targetFile().parent_path() / targetFile().stem(); - depsFile += ".d"; + depsFile += ".deps"; flagsFile = targetFile().parent_path() / targetFile().stem(); flagsFile += ".flags"; } @@ -185,7 +187,8 @@ int TaskCC::runInner() targetFile().lexically_normal().string() << "\n"; } - return execute(compiler(), args, settings.verbose > 0); + const auto& cfg = configuration(); + return execute(compiler(), args, cfg.env, settings.verbose > 0); } int TaskCC::clean() @@ -285,8 +288,26 @@ std::vector TaskCC::getCompilerArgs() const auto compiler_flags = flags(); + // cl.exe (compiler - gcc/g++) + // https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-alphabetically?view=msvc-170 + + // /c Compiles without linking. + // /Fo Object File Name (or path if .cc basename is re-used) + // /F Output-File + // /Fe (Name EXE File) + // /sourceDependencies json file with dependenciy tree + // /I include path + // /LD Creates a dynamic-link library. + // /link