diff --git a/src/conf.cpp b/src/conf.cpp index 82a937a..706d4fa 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -11,7 +11,7 @@ static std::map g_conf = {"vs_year", "2019"}, {"sevenzip", "19.00"}, {"zlib", "1.2.11"}, - {"boost", "1.72.0"}, + {"boost", "1.72.0-b1-rc1"}, {"boost_vs", "14.2"}, {"python", "3.8.1"}, {"fmt", "6.1.2"}, @@ -19,6 +19,7 @@ static std::map g_conf = {"libbsarch", "0.0.8"}, {"libloot", "0.15.1"}, {"libloot_hash", "gf725dd7"}, + {"openssl", "1.1.1d"}, {"prefix", R"(C:\dev\projects\mobuild-out)"}, }; @@ -93,6 +94,9 @@ const std::string& versions::gtest() { return get_conf("gtest"); } const std::string& versions::libbsarch() { return get_conf("libbsarch"); } const std::string& versions::libloot() { return get_conf("libloot"); } const std::string& versions::libloot_hash() { return get_conf("libloot_hash"); } +const std::string& versions::openssl() { return get_conf("openssl"); } + +bool prebuilt::boost() { return false; } fs::path paths::prefix() { return get_conf("prefix"); } fs::path paths::cache() { return prefix() / "downloads"; } @@ -101,6 +105,7 @@ fs::path paths::install() { return prefix() / "install"; } fs::path paths::install_bin( ) { return install() / "bin"; } fs::path paths::install_dlls() { return install_bin() / "dlls"; } fs::path paths::install_loot() { return install_bin() / "loot"; } +fs::path paths::install_pdbs() { return install_bin() / "pdbs"; } fs::path paths::patches() { @@ -175,23 +180,26 @@ fs::path paths::temp_file() } +fs::path find_third_party_directory() +{ + static fs::path path = find_in_root("third-party"); + return path; +} + fs::path third_party::sevenz() { - static fs::path path = find_in_root("third-party/bin/7z.exe"); - return path; + return "7z"; } fs::path third_party::jom() { - static fs::path path = find_in_root("third-party/bin/jom.exe"); - return path; + return "jom"; } fs::path third_party::patch() { - static fs::path path = find_in_root("third-party/bin/patch.exe"); - return path; + return "patch"; } fs::path third_party::git() @@ -204,10 +212,15 @@ fs::path third_party::cmake() return "cmake"; } +fs::path third_party::perl() +{ + return "perl"; +} + bool conf::verbose() { - return true; + return false; } bool conf::dry() diff --git a/src/conf.h b/src/conf.h index 45fd6e9..0812c42 100644 --- a/src/conf.h +++ b/src/conf.h @@ -17,6 +17,12 @@ struct versions static const std::string& libbsarch(); static const std::string& libloot(); static const std::string& libloot_hash(); + static const std::string& openssl(); +}; + +struct prebuilt +{ + static bool boost(); }; struct paths @@ -28,6 +34,7 @@ struct paths static fs::path install(); static fs::path install_bin(); static fs::path install_dlls(); + static fs::path install_pdbs(); static fs::path install_loot(); static fs::path program_files_x86(); @@ -43,8 +50,11 @@ struct third_party static fs::path patch(); static fs::path git(); static fs::path cmake(); + static fs::path perl(); }; +fs::path find_third_party_directory(); + struct conf { static bool verbose(); diff --git a/src/main.cpp b/src/main.cpp index 1e1f3af..d7144ba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,9 +8,18 @@ namespace builder { +class interrupted {}; + class task; std::vector> g_tasks; +template +bool add_task() +{ + g_tasks.push_back(std::make_unique()); + return true; +} + class task { @@ -51,14 +60,9 @@ public: { try { - if (interrupted_) - return; - + check_interrupted(); fetch(); - - if (interrupted_) - return; - + check_interrupted(); build_and_install(); } catch(bailed e) @@ -66,6 +70,10 @@ public: error(name_ + " bailed out, interrupting all tasks"); interrupt_all(); } + catch(interrupted) + { + return; + } catch(std::exception& e) { error(name_ + " uncaught exception: " + e.what()); @@ -106,12 +114,18 @@ protected: { } + void check_interrupted() + { + if (interrupted_) + throw interrupted(); + } + virtual void do_fetch() {}; virtual void do_build_and_install() {}; virtual fs::path source_path() const = 0; template - auto run_tool(Args&&... args) + std::unique_ptr run_tool(Args&&... args) { Tool* p = nullptr; @@ -121,8 +135,15 @@ protected: tool_.reset(p); } + check_interrupted(); p->run(); - return p->result(); + check_interrupted(); + + { + std::scoped_lock lock(tool_mutex_); + tool_.release(); + return std::unique_ptr(p); + } } private: @@ -158,7 +179,7 @@ protected: const auto nodots = replace_all(versions::sevenzip(), ".", ""); const auto file = run_tool( - "https://www.7-zip.org/a/7z" + nodots + "-src.7z"); + "https://www.7-zip.org/a/7z" + nodots + "-src.7z")->file(); run_tool(file, source_path()); } @@ -168,7 +189,7 @@ protected: const fs::path src = source_path() / "CPP" / "7zip" / "Bundles" / "Format7zF"; - run_tool(src, + run_tool(src, "", "/NOLOGO CPU=x64 NEW_COMPILER=1 " "MY_STATIC_LINK=1 NO_BUFFEROVERFLOWU=1"); @@ -194,7 +215,7 @@ protected: void do_fetch() override { const auto file = run_tool( - "http://zlib.net/zlib-" + versions::zlib() + ".tar.gz"); + "http://zlib.net/zlib-" + versions::zlib() + ".tar.gz")->file(); run_tool(file, source_path()); } @@ -202,7 +223,7 @@ protected: void do_build_and_install() override { run_tool(source_path(), "", source_path()); - run_tool(source_path() / cmake_for_nmake::build_path()); + run_tool(source_path() / cmake_for_nmake::build_path(), "install", ""); op::copy_file_to_dir_if_better( source_path() / cmake_for_nmake::build_path() / "zconf.h", @@ -227,23 +248,72 @@ protected: } void do_fetch() override + { + if (prebuilt::boost()) + fetch_prebuilt(); + else + fetch_from_source(); + } + + void do_build_and_install() override + { + if (prebuilt::boost()) + build_and_install_prebuilt(); + else + build_and_install_from_source(); + } + +private: + std::smatch parse_boost_version() const + { + // 1.72.0-b1-rc1 + // everything but 1.72 is optional + std::regex re(R"((\d+)\.(\d+)(?:\.(\d+)(?:-(\w+)(?:-(\w+))?)?)?)"); + std::smatch m; + + if (!std::regex_match(versions::boost(), m, re)) + bail_out("bad boost version '" + versions::boost() + "'"); + + return m; + } + + std::string source_download_filename() const + { + return boost_version_all_underscores() + ".zip"; + } + + void fetch_prebuilt() { const auto underscores = replace_all(versions::boost(), ".", "_"); const auto file = run_tool( "https://github.com/ModOrganizer2/modorganizer-umbrella/" - "releases/download/1.1/boost_prebuilt_" + underscores + ".7z"); + "releases/download/1.1/boost_prebuilt_" + underscores + ".7z") + ->file(); run_tool(file, source_path()); } - void do_build_and_install() override + void build_and_install_prebuilt() { op::copy_file_to_dir_if_better( lib_path() / python_dll(), paths::install_bin()); } -private: + void fetch_from_source() + { + const auto file = run_tool( + "https://dl.bintray.com/boostorg/release/" + + boost_version_no_tags() + "/source/" + + boost_version_all_underscores() + ".zip")->file(); + + run_tool(file, source_path()); + } + + void build_and_install_from_source() + { + } + fs::path lib_path() const { const std::string lib = "lib64-msvc-" + versions::boost_vs(); @@ -263,7 +333,7 @@ private: oss << "vc" + replace_all(versions::boost_vs(), ".", "") << "-"; // mt-x64-1_72 - oss << "mt-x64-" << boost_version_no_patch(); + oss << "mt-x64-" << boost_version_no_patch_underscores(); oss << ".dll"; @@ -283,18 +353,45 @@ private: return m[1].str() + m[2].str(); } - std::string boost_version_no_patch() const + std::string boost_version_no_patch_underscores() const { - // matches 1.71 or 1.71.0 - std::regex re(R"((\d+)\.(\d+)(?:\.(\d+))?)"); + const auto m = parse_boost_version(); - std::smatch m; - if (!std::regex_match(versions::boost(), m, re)) - bail_out("bad boost version '" + versions::boost() + "'"); - - // 1_71 + // 1_72 return m[1].str() + "_" + m[2].str(); } + + std::string boost_version_no_tags() const + { + const auto m = parse_boost_version(); + + // 1.72.1 + std::string s = m[1].str() + "." + m[2].str(); + + if (m.size() > 3) + s += "." + m[3].str(); + + return s; + } + + std::string boost_version_all_underscores() const + { + const auto m = parse_boost_version(); + + // boost_1_72_0_b1_rc1 + std::string s = "boost_" + m[1].str() + "_" + m[2].str(); + + if (m.size() > 3) + s += "_" + m[3].str(); + + if (m.size() > 4) + s += "_" + m[4].str(); + + if (m.size() > 5) + s += "_" + m[5].str(); + + return s; + } }; @@ -316,7 +413,7 @@ protected: { const auto file = run_tool( "https://github.com/fmtlib/fmt/releases/download/" + - versions::fmt() + "/fmt-" + versions::fmt() + ".zip"); + versions::fmt() + "/fmt-" + versions::fmt() + ".zip")->file(); run_tool(file, source_path()); } @@ -324,7 +421,7 @@ protected: void do_build_and_install() override { run_tool(source_path(), "-DFMT_TEST=OFF -DFMT_DOC=OFF"); - run_tool(source_path() / cmake_for_nmake::build_path()); + run_tool(source_path() / cmake_for_nmake::build_path(), "", ""); } }; @@ -351,7 +448,7 @@ protected: void do_build_and_install() override { run_tool(source_path()); - run_tool(source_path() / cmake_for_nmake::build_path()); + run_tool(source_path() / cmake_for_nmake::build_path(), "", ""); } }; @@ -379,7 +476,7 @@ protected: { const auto file = run_tool( "https://github.com/ModOrganizer2/libbsarch/releases/download/" + - versions::libbsarch() + "/" + dir_name() + ".7z"); + versions::libbsarch() + "/" + dir_name() + ".7z")->file(); run_tool(file, source_path()); } @@ -422,7 +519,220 @@ protected: { const auto file = run_tool( "https://github.com/loot/libloot/releases/download/" + - versions::libloot() + "/" + dir_name() + ".7z"); + versions::libloot() + "/" + dir_name() + ".7z")->file(); + + run_tool(file, source_path()); + } + + void do_build_and_install() override + { + op::copy_file_to_dir_if_better( + source_path() / "loot.dll", + paths::install_loot()); + } +}; + + +class libffi : public task +{ +public: + libffi() + : task("libffi") + { + } + +protected: + fs::path source_path() const override + { + return paths::build() / "libffi"; + } + + void do_fetch() override + { + run_tool( + "python", "cpython-bin-deps", "libffi", + source_path()); + } +}; + + +class openssl : public task +{ +public: + openssl() + : task("openssl") + { + } + +protected: + fs::path source_path() const override + { + return paths::build() / ("openssl-" + versions::openssl()); + } + + fs::path build_path() const + { + return source_path() / "build"; + } + + void do_fetch() override + { + const std::string filename = "openssl-" + versions::openssl() + ".tar.gz"; + const auto file = run_tool( + "https://www.openssl.org/source/" + filename)->file(); + + run_tool(file, source_path()); + } + + void do_build_and_install() override + { + if (fs::exists(source_path() / "makefile")) + debug("openssl already configured"); + else + configure(); + + install_engines(); + info("openssl built successfully"); + + copy_files(); + } + +private: + void configure() + { + run_tool( + "perl", + "\"" + third_party::perl().string() + "\" " + + "Configure " + "--openssldir=\"" + build_path().string() + "\" " + "--prefix=\"" + build_path().string() + "\" " + "-FS -MP1 " + "VC-WIN64A", + source_path()); + } + + void install_engines() + { + for (int tries=0; tries<3; ++tries) + { + const auto tool = run_tool( + source_path(), "install_engines", "", jom::accept_failure); + + if (tool->exit_code() == 0) + return; + } + + run_tool(source_path(), "install_engines", "", jom::single_job); + } + + void copy_files() + { + copy_dlls_to(paths::install_bin()); + copy_dlls_to(paths::install_dlls()); + copy_pdbs_to(paths::install_pdbs()); + } + + void copy_dlls_to(const fs::path& dir) + { + for (auto&& name : output_names()) + { + op::copy_file_to_dir_if_better( + build_path() / "bin" / (name + ".dll"), dir); + } + } + + void copy_pdbs_to(const fs::path& dir) + { + for (auto&& name : output_names()) + { + op::copy_file_to_dir_if_better( + build_path() / "bin" / (name + ".pdb"), dir); + } + } + + std::vector output_names() const + { + return + { + "libcrypto-" + version_no_minor_underscores() + "-x64", + "libssl-" + version_no_minor_underscores() + "-x64" + }; + } + + std::smatch parse_version() const + { + // 1.1.1d + // everything but 1 is optional + std::regex re(R"((\d+)(?:\.(\d+)(?:\.(\d+)([a-zA-Z]+)?)?)?)"); + std::smatch m; + + if (!std::regex_match(versions::openssl(), m, re)) + bail_out("bad openssl version '" + versions::openssl() + "'"); + + return m; + } + + std::string version_no_tags() const + { + auto m = parse_version(); + + // up to 4 so the tag is skipped if present + const std::size_t count = std::min(m.size(), 4); + + std::string s; + for (std::size_t i=1; i( + "https://github.com/loot/libloot/releases/download/" + + versions::libloot() + "/" + dir_name() + ".7z")->file(); run_tool(file, source_path()); } @@ -480,14 +790,18 @@ int run(int argc, char** argv) ::SetConsoleCtrlHandler(signal_handler, TRUE); vcvars(); + prepend_to_path(find_third_party_directory() / "bin"); - g_tasks.push_back(std::make_unique()); - g_tasks.push_back(std::make_unique()); - g_tasks.push_back(std::make_unique()); - g_tasks.push_back(std::make_unique()); - g_tasks.push_back(std::make_unique()); - g_tasks.push_back(std::make_unique()); - g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + //g_tasks.push_back(std::make_unique()); + g_tasks.push_back(std::make_unique()); if (argc > 1) { diff --git a/src/net.cpp b/src/net.cpp index e674467..701a8ed 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -47,48 +47,35 @@ std::string url::file() const } -curl_downloader::curl_downloader(fs::path where, url u) : - where_(std::move(where)), url_(std::move(u)), file_(nullptr), - interrupt_(false) +curl_downloader::curl_downloader() + : interrupt_(false), ok_(false) { - path_ = where_ / url_.file(); } -void curl_downloader::start() +fs::path curl_downloader::path_for_url(const fs::path& where, const url& u) { + return where / u.file(); +} + +void curl_downloader::start(const fs::path& where, const url& u) +{ + where_ = where; + url_ = u; + path_ = path_for_url(where, u); + ok_ = false; + debug("downloading " + url_.string() + " to " + path_.string()); - op::create_directories(where_); - - if (fs::exists(path_)) - { - debug("download " + path_.string() + " already exists"); - return; - } - if (conf::dry()) return; - thread_ = std::thread([&] - { - try - { - run(); - } - catch(bailed e) - { - bailed_ = e; - } - }); + thread_ = std::thread([&] { run(); }); } void curl_downloader::join() { if (thread_.joinable()) thread_.join(); - - if (bailed_) - throw *bailed_; } void curl_downloader::interrupt() @@ -96,6 +83,11 @@ void curl_downloader::interrupt() interrupt_ = true; } +bool curl_downloader::ok() const +{ + return ok_; +} + fs::path curl_downloader::file() const { return path_; @@ -110,37 +102,35 @@ void curl_downloader::run() curl_easy_setopt(c, CURLOPT_WRITEDATA, this); curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1l); + file_deleter output_deleter(path_); const auto r = curl_easy_perform(c); + file_.reset(); if (interrupt_) - { - if (file_) - { - std::fclose(file_); - op::delete_file(path_); - } - return; - } if (r == 0) { long h = 0; curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &h); - if (h != 200) - bail_out(url_.string() + " http code: " + std::to_string(h)); + if (h == 200) + { + ok_ = true; + output_deleter.cancel(); + } + else + { + error(url_.string() + " http code: " + std::to_string(h)); + } } else { - bail_out(url_.string() + " curl: " + curl_easy_strerror(r)); + error(url_.string() + " curl: " + curl_easy_strerror(r)); } curl_easy_cleanup(c); - - if (file_) - std::fclose(file_); } size_t curl_downloader::on_write_static( @@ -161,9 +151,12 @@ size_t curl_downloader::on_write_static( void curl_downloader::on_write(char* ptr, std::size_t n) noexcept { if (!file_) - file_ = _wfopen(path_.native().c_str(), L"wb"); + { + op::create_directories(where_); + file_ .reset(_wfopen(path_.native().c_str(), L"wb")); + } - std::fwrite(ptr, n, 1, file_); + std::fwrite(ptr, n, 1, file_.get()); } } // namespace diff --git a/src/net.h b/src/net.h index 0cb96f1..3b70f63 100644 --- a/src/net.h +++ b/src/net.h @@ -24,22 +24,25 @@ private: class curl_downloader { public: - curl_downloader(fs::path where, url u); + curl_downloader(); - void start(); + void start(const fs::path& where, const url& u); void join(); void interrupt(); + bool ok() const; fs::path file() const; + static fs::path path_for_url(const fs::path& where, const url& u); + private: fs::path where_; url url_; fs::path path_; - std::FILE* file_; + file_ptr file_; std::thread thread_; std::atomic interrupt_; - std::optional bailed_; + bool ok_; void run(); diff --git a/src/op.cpp b/src/op.cpp index 6c8a176..5bb03bc 100644 --- a/src/op.cpp +++ b/src/op.cpp @@ -228,6 +228,40 @@ bool is_source_better(const fs::path& src, const fs::path& dest) return false; } +void op::rename(const fs::path& src, const fs::path& dest) +{ + check(src); + check(dest); + + if (fs::exists(dest)) + { + bail_out( + "can't rename " + src.string() + " to " + dest.string() + ", " + "already exists"); + } + + debug("renaming " + src.string() + " to " + dest.string()); + do_rename(src, dest); +} + +void op::move_to_directory(const fs::path& src, const fs::path& dest_dir) +{ + check(src); + check(dest_dir); + + const auto target = dest_dir / src.filename(); + + if (fs::exists(target)) + { + bail_out( + "can't move " + src.string() + " to " + dest_dir.string() + ", " + + src.filename().string() + " already exists"); + } + + debug("moving " + src.string() + " to " + target.string()); + do_rename(src, target); +} + void op::copy_file_to_dir_if_better(const fs::path& file, const fs::path& dir) { check(file); @@ -245,7 +279,7 @@ void op::copy_file_to_dir_if_better(const fs::path& file, const fs::path& dir) const auto target = dir / file.filename(); if (is_source_better(file, target)) { - info(file.string() + " -> " + dir.string()); + debug(file.string() + " -> " + dir.string()); if (!conf::dry()) do_copy_file_to_dir(file, dir); @@ -271,7 +305,11 @@ process op::run(const std::string& cmd, const fs::path& cwd) void op::do_touch(const fs::path& p) { + create_directories(p.parent_path()); + std::ofstream out(p); + if (!out) + bail_out("failed to touch " + p.string()); } void op::do_create_directories(const fs::path& p) @@ -337,6 +375,15 @@ void op::do_remove_readonly(const fs::path& p) bail_out("can't remove read-only flag on " + p.string(), ec); } +void op::do_rename(const fs::path& src, const fs::path& dest) +{ + std::error_code ec; + fs::rename(src, dest, ec); + + if (ec) + bail_out("can't rename " + src.string() + " to " + dest.string(), ec); +} + process op::do_run(const std::string& what, const fs::path& cwd) { STARTUPINFOA si = { .cb=sizeof(si) }; diff --git a/src/op.h b/src/op.h index b3eb53a..fcf0263 100644 --- a/src/op.h +++ b/src/op.h @@ -40,7 +40,9 @@ public: static void delete_directory(const fs::path& p); static void delete_file(const fs::path& p); static void remove_readonly(const fs::path& first); - static void copy_file_to_dir_if_better(const fs::path& file, const fs::path& dir); + static void rename(const fs::path& src, const fs::path& dest); + static void move_to_directory(const fs::path& src, const fs::path& dest_dir); + static void copy_file_to_dir_if_better(const fs::path& file, const fs::path& dest_dir); static process run(const std::string& cmd, const fs::path& cwd={}); private: @@ -50,6 +52,7 @@ private: static void do_delete_file(const fs::path& p); static void do_copy_file_to_dir(const fs::path& f, const fs::path& d); static void do_remove_readonly(const fs::path& p); + static void do_rename(const fs::path& src, const fs::path& dest); static process do_run(const std::string& cmd, const fs::path& cwd); static void check(const fs::path& p); }; diff --git a/src/tools.cpp b/src/tools.cpp index 310e5b4..7419665 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -91,25 +91,6 @@ process do_cmake( return op::run(cmd, build); } -process do_nmake( - const fs::path& dir, - const std::string& what, const std::string& args) -{ - std::ostringstream oss; - oss << "\"" << third_party::jom().string() << "\""; - - if (!conf::verbose()) - oss << " /C /S"; - - if (!args.empty()) - oss << " " << args; - - if (!what.empty()) - oss << " " << what; - - return op::run(oss.str(), dir); -} - tool::tool(std::string name) : name_(std::move(name)), interrupted_(false) @@ -179,21 +160,57 @@ void process_runner::join(bool check_exit_code) } } +int process_runner::exit_code() const +{ + return process_.exit_code(); +} + downloader::downloader(url u) - : tool("downloader"), dl_(paths::cache(), std::move(u)) + : tool("downloader") +{ + urls_.push_back(std::move(u)); +} + +downloader::downloader(std::vector urls) + : tool("downloader"), urls_(std::move(urls)) { } -fs::path downloader::result() const +fs::path downloader::file() const { - return dl_.file(); + return file_; } void downloader::do_run() { - dl_.start(); - dl_.join(); + // check if one the urls has already been downloaded + for (auto&& u : urls_) + { + const auto file = curl_downloader::path_for_url(paths::cache(), u); + if (fs::exists(file)) + { + file_ = file; + debug("download " + file_.string() + " already exists"); + return; + } + } + + // try them in order + for (auto&& u : urls_) + { + dl_.start(paths::cache(), std::move(u)); + dl_.join(); + + if (dl_.ok()) + { + file_ = dl_.file(); + return; + } + } + + // all failed + bail_out("all urls failed"); } void downloader::do_interrupt() @@ -282,25 +299,44 @@ void decompresser::do_run() const auto sevenz = "\"" + third_party::sevenz().string() + "\""; - //op::delete_directory(where_); op::create_directories(where_); + directory_deleter delete_output(where_); process p; + // the -spe from 7z is supposed to figure out if there's a folder in the + // archive with the same name as the target and extract its content to + // avoid duplicating the folder + // + // however, it fails miserably if there are files along with that folder, + // which is the case for openssl: + // + // openssl-1.1.1d.tar/ + // +- openssl-1.1.1d/ + // +- pax_global_header + // + // that pax_global_header makes 7z fail with "unspecified error" + // + // so the handling of a duplicate directory is done manually in + // check_duplicate_directory() below + if (file_.string().ends_with(".tar.gz")) { p = op::run( sevenz + " x -so \"" + file_.string() + "\" | " + - sevenz + " x -aoa -spe -si -ttar -o\"" + where_.string() + "\" " + redir_nul()); + sevenz + " x -aoa -si -ttar -o\"" + where_.string() + "\" " + redir_nul()); } else { p = op::run( - sevenz + " x -aoa -spe -bd -bb0 -o\"" + where_.string() + "\" " + sevenz + " x -aoa -bd -bb0 -o\"" + where_.string() + "\" " "\"" + file_.string() + "\" " + redir_nul()); } execute_and_join(std::move(p)); + check_duplicate_directory(); + + delete_output.cancel(); if (!interrupted()) op::delete_file(interrupt_file()); @@ -308,7 +344,58 @@ void decompresser::do_run() fs::path decompresser::interrupt_file() const { - return where_ / "_builder_interrupted"; + return where_ / "_mob_interrupted"; +} + +void decompresser::check_duplicate_directory() +{ + const auto dir_name = where_.filename().string(); + + // check for a folder with the same name + if (!fs::exists(where_ / dir_name)) + return; + + // the archive contained a directory with the same name as the output + // directory + + // delete anything other than this directory; some archives have + // useless files along with it + for (auto e : fs::directory_iterator(where_)) + { + // but don't delete the directory itself + if (e.path().filename() == dir_name) + continue; + + // or the interrupt file + if (e.path().filename() == interrupt_file().filename()) + continue; + + if (!fs::is_regular_file(e.path())) + { + // don't know what to do with archives that have the + // same directory _and_ other directories + bail_out( + "check_duplicate_directory: " + e.path().string() + " is " + "yet another directory"); + } + + op::delete_file(e.path()); + } + + // now there should only be two things in this directory: another + // directory with the same name and the interrupt file + + // give it a temp name in case there's yet another directory with the + // same name in it + const auto temp_dir_name = where_ / ("_mob_" + dir_name ); + op::rename(where_ / dir_name, where_ / temp_dir_name); + + // move the content of the directory up + for (auto e : fs::directory_iterator(where_ / temp_dir_name)) + op::move_to_directory(e.path(), where_); + + // delete the old directory, which should be empty now + op::delete_directory(where_ / temp_dir_name); } @@ -411,25 +498,36 @@ void cmake_for_vs::do_run() } -nmake::nmake(fs::path dir, std::string args) - : process_runner("nmake"), dir_(std::move(dir)), args_(std::move(args)) +jom::jom(fs::path dir, std::string target, std::string args, flags f) : + process_runner("jom " + target), + dir_(std::move(dir)), target_(std::move(target)), + args_(std::move(args)), flags_(f) { } -void nmake::do_run() +void jom::do_run() { - execute_and_join(do_nmake(dir_, "", args_)); -} + std::ostringstream oss; + oss << "\"" << third_party::jom().string() << "\""; -nmake_install::nmake_install(fs::path dir, std::string args) : - process_runner("nmake_install"), - dir_(std::move(dir)), args_(std::move(args)) -{ -} + if (!conf::verbose()) + oss << " /C /S"; -void nmake_install::do_run() -{ - execute_and_join(do_nmake(dir_, "install", args_)); + oss << " /K "; + + if (flags_ & single_job) + oss << " /J 1"; + + if (!args_.empty()) + oss << " " << args_; + + if (!target_.empty()) + oss << " " << target_; + + oss << redir_nul(); + + const bool check_exit_code = !(flags_ & accept_failure); + execute_and_join(op::run(oss.str(), dir_), check_exit_code); } } // namespace diff --git a/src/tools.h b/src/tools.h index e2c0ab4..92f2b4b 100644 --- a/src/tools.h +++ b/src/tools.h @@ -16,7 +16,6 @@ public: void run(); void interrupt(); - void result() {} protected: tool(std::string name); @@ -36,7 +35,9 @@ class downloader : public tool { public: downloader(url u); - fs::path result() const; + downloader(std::vector urls); + + fs::path file() const; protected: void do_run() override; @@ -44,6 +45,8 @@ protected: private: curl_downloader dl_; + fs::path file_; + std::vector urls_; }; @@ -51,7 +54,9 @@ class process_runner : public tool { public: process_runner(std::string name, std::string cmd, fs::path cwd={}); + void join(bool check_exit_code=true); + int exit_code() const; protected: process_runner(std::string name); @@ -101,6 +106,7 @@ private: fs::path where_; fs::path interrupt_file() const; + void check_duplicate_directory(); }; @@ -150,31 +156,28 @@ private: }; -class nmake : public process_runner +class jom : public process_runner { public: - nmake(fs::path dir, std::string args={}); - -protected: - void do_run() override; - -private: - fs::path dir_; - std::string args_; -}; - - -class nmake_install : public process_runner -{ -public: - nmake_install(fs::path dir, std::string args={}); + enum flags + { + default_flags = 0x00, + single_job = 0x01, + accept_failure = 0x02 + }; + + jom( + fs::path dir, std::string target, std::string args, + flags f=default_flags); protected: void do_run() override; private: fs::path dir_; + std::string target_; std::string args_; + flags flags_; }; } // namespace diff --git a/src/utility.cpp b/src/utility.cpp index c9cdb5c..65a414c 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -121,7 +121,23 @@ std::string env(const std::string& name) name.c_str(), buffer.get(), static_cast(buffer_size)); return buffer.get(); +} +void env(const std::string& name, const std::string& value) +{ + ::SetEnvironmentVariableA(name.c_str(), value.c_str()); +} + + +void prepend_to_path(const fs::path& dir) +{ + if (!fs::exists(dir)) + bail_out("can't add " + dir.string() + " to path, doesn't exist"); + + if (!fs::is_directory(dir)) + bail_out("can't add " + dir.string() + " to path, not a directory"); + + env("PATH", dir.string() + ";" + env("PATH")); } std::string read_text_file(const fs::path& p) @@ -157,4 +173,50 @@ std::string replace_all( return s; } + +file_deleter::file_deleter(fs::path p) + : p_(std::move(p)), delete_(true) +{ +} + +file_deleter::~file_deleter() +{ + if (delete_) + delete_now(); +} + +void file_deleter::delete_now() +{ + if (fs::exists(p_)) + op::delete_file(p_); +} + +void file_deleter::cancel() +{ + delete_ = false; +} + + +directory_deleter::directory_deleter(fs::path p) + : p_(std::move(p)), delete_(true) +{ +} + +directory_deleter::~directory_deleter() +{ + if (delete_) + delete_now(); +} + +void directory_deleter::delete_now() +{ + if (fs::exists(p_)) + op::delete_directory(p_); +} + +void directory_deleter::cancel() +{ + delete_ = false; +} + } // namespace diff --git a/src/utility.h b/src/utility.h index f774185..2303a82 100644 --- a/src/utility.h +++ b/src/utility.h @@ -21,21 +21,65 @@ private: }; -struct handle_deleter +struct handle_closer { using pointer = HANDLE; void operator()(HANDLE h) { - if (h != INVALID_HANDLE_VALUE) { + if (h != INVALID_HANDLE_VALUE) ::CloseHandle(h); - } } }; -using handle_ptr = std::unique_ptr; +using handle_ptr = std::unique_ptr; +struct file_closer +{ + void operator()(std::FILE* f) + { + if (f) + std::fclose(f); + } +}; + +using file_ptr = std::unique_ptr; + + +class file_deleter +{ +public: + file_deleter(fs::path p); + file_deleter(const file_deleter&) = delete; + file_deleter& operator=(const file_deleter&) = delete; + ~file_deleter(); + + void delete_now(); + void cancel(); + +private: + fs::path p_; + bool delete_; +}; + + +class directory_deleter +{ +public: + directory_deleter(fs::path p); + directory_deleter(const directory_deleter&) = delete; + directory_deleter& operator=(const directory_deleter&) = delete; + ~directory_deleter(); + + void delete_now(); + void cancel(); + +private: + fs::path p_; + bool delete_; +}; + enum class level { @@ -82,6 +126,8 @@ void debug(Args&&... args) std::string env(const std::string& name); +void env(const std::string& name, const std::string& value); +void prepend_to_path(const fs::path& dir); std::string redir_nul(); diff --git a/third-party/bin/nasm.exe b/third-party/bin/nasm.exe new file mode 100644 index 0000000..3960353 Binary files /dev/null and b/third-party/bin/nasm.exe differ diff --git a/vs/props.props b/vs/props.props index 3152912..4d647e0 100644 --- a/vs/props.props +++ b/vs/props.props @@ -19,7 +19,7 @@ Use pch.h $(IntDir)fake\%(RelativeDir) - _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;CURL_STATICLIB;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;CURL_STATICLIB;NOMINMAX;%(PreprocessorDefinitions) ..\third-party\include;%(AdditionalIncludeDirectories)