diff --git a/src/core/conf.cpp b/src/core/conf.cpp index 8913b28..80a8093 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -3,6 +3,7 @@ #include "context.h" #include "env.h" #include "ini.h" +#include "paths.h" #include "../utility.h" #include "../tasks/task.h" #include "../tools/tools.h" @@ -228,59 +229,6 @@ std::vector format_options() return lines; } - -fs::path find_in_path(std::string_view exe) -{ - const std::wstring wexe = utf8_to_utf16(exe); - - const std::size_t size = MAX_PATH; - wchar_t buffer[size + 1] = {}; - - if (SearchPathW(nullptr, wexe.c_str(), nullptr, size, buffer, nullptr)) - return buffer; - else - return {}; -} - -fs::path find_iscc() -{ - const auto tasks = find_tasks("installer"); - MOB_ASSERT(tasks.size() == 1); - - if (!tasks[0]->enabled()) - return {}; - - auto iscc = conf().tool().get("iscc"); - if (iscc.is_absolute()) - { - if (!fs::exists(iscc)) - { - gcx().bail_out(context::conf, - "{} doesn't exist (from ini, absolute path)", iscc); - } - - return iscc; - } - - fs::path p = find_in_path(path_to_utf8(iscc)); - if (fs::exists(p)) - return fs::canonical(fs::absolute(p)); - - for (int v : {5, 6, 7, 8}) - { - const fs::path inno = fmt::format("inno setup {}", v); - - for (fs::path pf : {conf().path().pf_x86(), conf().path().pf_x64()}) - { - p = pf / inno / iscc; - if (fs::exists(p)) - return fs::canonical(fs::absolute(p)); - } - } - - gcx().bail_out(context::conf, "can't find {} anywhere", iscc); -} - void set_special_options() { conf().global().set_output_log_level( @@ -341,356 +289,6 @@ void make_canonical_path( details::set_string("paths", key, path_to_utf8(p)); } -bool try_parts(fs::path& check, const std::vector& parts) -{ - for (std::size_t i=0; i(buffer_size + 1); - DWORD n = GetModuleFileNameW(0, buffer.get(), buffer_size); - - if (n == 0) { - const auto e = GetLastError(); - gcx().bail_out(context::conf, - "can't get module filename, {}", error_message(e)); - } - else if (n >= buffer_size) { - // buffer is too small, try again - buffer_size *= 2; - } else { - // if GetModuleFileName() works, `n` does not include the null - // terminator - const std::wstring s(buffer.get(), n); - return fs::canonical(s); - } - } - - gcx().bail_out(context::conf, "can't get module filename"); -} - -fs::path find_root(bool verbose) -{ - gcx().trace(context::conf, "looking for root directory"); - - fs::path mob_exe_dir = mob_exe_path().parent_path(); - - auto third_party = mob_exe_dir / "third-party"; - - if (!fs::exists(third_party)) - { - if (verbose) - u8cout << "no third-party here\n"; - - auto p = mob_exe_dir; - - if (p.filename().u8string() == u8"x64") - { - p = p.parent_path(); - if (p.filename().u8string() == u8"Debug" || - p.filename().u8string() == u8"Release") - { - if (verbose) - u8cout << "this is mob's build directory, looking up\n"; - - // mob_exe_dir is in the build directory - third_party = mob_exe_dir / ".." / ".." / ".." / "third-party"; - } - } - } - - if (!fs::exists(third_party)) - gcx().bail_out(context::conf, "root directory not found"); - - const auto p = fs::canonical(third_party.parent_path()); - gcx().trace(context::conf, "found root directory at {}", p); - return p; -} - -fs::path find_in_root(const fs::path& file) -{ - static fs::path root = find_root(); - - fs::path p = root / file; - if (!fs::exists(p)) - gcx().bail_out(context::conf, "{} not found", p); - - gcx().trace(context::conf, "found {}", p); - return p; -} - - -fs::path find_third_party_directory() -{ - static fs::path path = find_in_root("third-party"); - return path; -} - -bool find_qmake(fs::path& check) -{ - // try Qt/Qt5.14.2/msvc*/bin/qmake.exe - if (try_parts(check, { - "Qt", - "Qt" + qt::version(), - "msvc" + qt::vs_version() + "_64", - "bin", - "qmake.exe"})) - { - return true; - } - - // try Qt/5.14.2/msvc*/bin/qmake.exe - if (try_parts(check, { - "Qt", - qt::version(), - "msvc" + qt::vs_version() + "_64", - "bin", - "qmake.exe"})) - { - return true; - } - - return false; -} - -bool try_qt_location(fs::path& check) -{ - if (!find_qmake(check)) - return false; - - check = fs::absolute(check.parent_path() / ".."); - return true; -} - -fs::path find_qt() -{ - fs::path p = conf().path().qt_install(); - - if (!p.empty()) - { - p = fs::absolute(p); - - if (!try_qt_location(p)) - gcx().bail_out(context::conf, "no qt install in {}", p); - - return p; - } - - - std::vector locations = - { - conf().path().pf_x64(), - "C:\\", - "D:\\" - }; - - // look for qmake, which is in %qt%/version/msvc.../bin - fs::path qmake = find_in_path("qmake.exe"); - if (!qmake.empty()) - locations.insert(locations.begin(), qmake.parent_path() / "../../"); - - // look for qtcreator.exe, which is in %qt%/Tools/QtCreator/bin - fs::path qtcreator = find_in_path("qtcreator.exe"); - if (!qtcreator.empty()) - locations.insert(locations.begin(), qtcreator.parent_path() / "../../../"); - - for (fs::path loc : locations) - { - loc = fs::absolute(loc); - if (try_qt_location(loc)) - return loc; - } - - gcx().bail_out(context::conf, "can't find qt install"); -} - -void validate_qt() -{ - fs::path p = qt::installation_path(); - - if (!try_qt_location(p)) - gcx().bail_out(context::conf, "qt path {} doesn't exist", p); - - details::set_string("paths", "qt_install", path_to_utf8(p)); -} - -fs::path get_known_folder(const GUID& id) -{ - wchar_t* buffer = nullptr; - const auto r = ::SHGetKnownFolderPath(id, 0, 0, &buffer); - - if (r != S_OK) - return {}; - - fs::path p = buffer; - ::CoTaskMemFree(buffer); - - return p; -} - -fs::path find_program_files_x86() -{ - fs::path p = get_known_folder(FOLDERID_ProgramFilesX86); - - if (p.empty()) - { - const auto e = GetLastError(); - - p = fs::path(R"(C:\Program Files (x86))"); - - gcx().warning(context::conf, - "failed to get x86 program files folder, defaulting to {}, {}", - p, error_message(e)); - } - else - { - gcx().trace(context::conf, "x86 program files is {}", p); - } - - return p; -} - -fs::path find_program_files_x64() -{ - fs::path p = get_known_folder(FOLDERID_ProgramFilesX64); - - if (p.empty()) - { - const auto e = GetLastError(); - - p = fs::path(R"(C:\Program Files)"); - - gcx().warning(context::conf, - "failed to get x64 program files folder, defaulting to {}, {}", - p, error_message(e)); - } - else - { - gcx().trace(context::conf, "x64 program files is {}", p); - } - - return p; -} - -fs::path find_temp_dir() -{ - const std::size_t buffer_size = MAX_PATH + 2; - wchar_t buffer[buffer_size] = {}; - - if (GetTempPathW(static_cast(buffer_size), buffer) == 0) - { - const auto e = GetLastError(); - gcx().bail_out(context::conf, "can't get temp path", error_message(e)); - } - - fs::path p(buffer); - gcx().trace(context::conf, "temp dir is {}", p); - - return p; -} - -fs::path find_vs() -{ - if (conf::dry()) - return vs::vswhere(); - - const auto path = vswhere::find_vs(); - if (path.empty()) - gcx().bail_out(context::conf, "vswhere failed"); - - const auto lines = split(path_to_utf8(path), "\r\n"); - - if (lines.empty()) - { - gcx().bail_out(context::conf, "vswhere didn't output anything"); - } - else if (lines.size() > 1) - { - gcx().error(context::conf, "vswhere returned multiple installations:"); - - for (auto&& line : lines) - gcx().error(context::conf, " - {}", line); - - gcx().bail_out(context::conf, - "specify the `vs` path in the `[paths]` section of the INI, or " - "pass -s paths/vs=PATH` to pick an installation"); - } - - if (!fs::exists(path)) - { - gcx().bail_out(context::conf, - "the path given by vswhere doesn't exist: {}", path); - } - - return path; -} - -bool try_vcvars(fs::path& bat) -{ - if (!fs::exists(bat)) - return false; - - bat = fs::canonical(fs::absolute(bat)); - return true; -} - -void find_vcvars() -{ - fs::path bat = conf().tool().get("vcvars"); - - if (conf::dry()) - { - if (bat.empty()) - bat = "vcvars.bat"; - - return; - } - else - { - if (bat.empty()) - { - bat = vs::installation_path() - / "VC" / "Auxiliary" / "Build" / "vcvarsall.bat"; - - if (!try_vcvars(bat)) - gcx().bail_out(context::conf, "vcvars not found at {}", bat); - } - else - { - if (!try_vcvars(bat)) - gcx().bail_out(context::conf, "vcvars not found at {}", bat); - } - } - - details::set_string("tools", "vcvars", path_to_utf8(bat)); - gcx().trace(context::conf, "using vcvars at {}", bat); -} - - struct parsed_option { std::string task, section, key, value; @@ -929,23 +527,6 @@ void dump_available_options() } -fs::path make_temp_file() -{ - static fs::path dir = conf().path().temp_dir(); - - wchar_t name[MAX_PATH + 1] = {}; - if (GetTempFileNameW(dir.native().c_str(), L"mob", 0, name) == 0) - { - const auto e = GetLastError(); - - gcx().bail_out(context::conf, - "can't create temp file in {}, {}", dir, error_message(e)); - } - - return dir / name; -} - - conf_global conf::global() { return {}; diff --git a/src/core/conf.h b/src/core/conf.h index 91b8651..72fde68 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -22,10 +22,6 @@ bool verify_options(); void log_options(); void dump_available_options(); -fs::path find_root(bool verbose=false); -fs::path find_in_root(const fs::path& file); -fs::path make_temp_file(); - template class conf_section diff --git a/src/core/ini.cpp b/src/core/ini.cpp index 4a644ba..f8e9638 100644 --- a/src/core/ini.cpp +++ b/src/core/ini.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "ini.h" +#include "paths.h" #include "context.h" #include "env.h" #include "conf.h" diff --git a/src/core/paths.cpp b/src/core/paths.cpp new file mode 100644 index 0000000..729d86c --- /dev/null +++ b/src/core/paths.cpp @@ -0,0 +1,410 @@ +#include "pch.h" +#include "paths.h" +#include "../tasks/tasks.h" +#include "../utility/string.h" + +namespace mob +{ + +fs::path find_in_path(std::string_view exe) +{ + const std::wstring wexe = utf8_to_utf16(exe); + + const std::size_t size = MAX_PATH; + wchar_t buffer[size + 1] = {}; + + if (SearchPathW(nullptr, wexe.c_str(), nullptr, size, buffer, nullptr)) + return buffer; + else + return {}; +} + +fs::path find_iscc() +{ + const auto tasks = find_tasks("installer"); + MOB_ASSERT(tasks.size() == 1); + + if (!tasks[0]->enabled()) + return {}; + + auto iscc = conf().tool().get("iscc"); + if (iscc.is_absolute()) + { + if (!fs::exists(iscc)) + { + gcx().bail_out(context::conf, + "{} doesn't exist (from ini, absolute path)", iscc); + } + + return iscc; + } + + fs::path p = find_in_path(path_to_utf8(iscc)); + if (fs::exists(p)) + return fs::canonical(fs::absolute(p)); + + for (int v : {5, 6, 7, 8}) + { + const fs::path inno = fmt::format("inno setup {}", v); + + for (fs::path pf : {conf().path().pf_x86(), conf().path().pf_x64()}) + { + p = pf / inno / iscc; + if (fs::exists(p)) + return fs::canonical(fs::absolute(p)); + } + } + + gcx().bail_out(context::conf, "can't find {} anywhere", iscc); +} + +bool try_parts(fs::path& check, const std::vector& parts) +{ + for (std::size_t i=0; i(buffer_size + 1); + DWORD n = GetModuleFileNameW(0, buffer.get(), buffer_size); + + if (n == 0) { + const auto e = GetLastError(); + gcx().bail_out(context::conf, + "can't get module filename, {}", error_message(e)); + } + else if (n >= buffer_size) { + // buffer is too small, try again + buffer_size *= 2; + } else { + // if GetModuleFileName() works, `n` does not include the null + // terminator + const std::wstring s(buffer.get(), n); + return fs::canonical(s); + } + } + + gcx().bail_out(context::conf, "can't get module filename"); +} + +fs::path find_root(bool verbose) +{ + gcx().trace(context::conf, "looking for root directory"); + + fs::path mob_exe_dir = mob_exe_path().parent_path(); + + auto third_party = mob_exe_dir / "third-party"; + + if (!fs::exists(third_party)) + { + if (verbose) + u8cout << "no third-party here\n"; + + auto p = mob_exe_dir; + + if (p.filename().u8string() == u8"x64") + { + p = p.parent_path(); + if (p.filename().u8string() == u8"Debug" || + p.filename().u8string() == u8"Release") + { + if (verbose) + u8cout << "this is mob's build directory, looking up\n"; + + // mob_exe_dir is in the build directory + third_party = mob_exe_dir / ".." / ".." / ".." / "third-party"; + } + } + } + + if (!fs::exists(third_party)) + gcx().bail_out(context::conf, "root directory not found"); + + const auto p = fs::canonical(third_party.parent_path()); + gcx().trace(context::conf, "found root directory at {}", p); + return p; +} + +fs::path find_in_root(const fs::path& file) +{ + static fs::path root = find_root(); + + fs::path p = root / file; + if (!fs::exists(p)) + gcx().bail_out(context::conf, "{} not found", p); + + gcx().trace(context::conf, "found {}", p); + return p; +} + + +fs::path find_third_party_directory() +{ + static fs::path path = find_in_root("third-party"); + return path; +} + +bool find_qmake(fs::path& check) +{ + // try Qt/Qt5.14.2/msvc*/bin/qmake.exe + if (try_parts(check, { + "Qt", + "Qt" + qt::version(), + "msvc" + qt::vs_version() + "_64", + "bin", + "qmake.exe"})) + { + return true; + } + + // try Qt/5.14.2/msvc*/bin/qmake.exe + if (try_parts(check, { + "Qt", + qt::version(), + "msvc" + qt::vs_version() + "_64", + "bin", + "qmake.exe"})) + { + return true; + } + + return false; +} + +bool try_qt_location(fs::path& check) +{ + if (!find_qmake(check)) + return false; + + check = fs::absolute(check.parent_path() / ".."); + return true; +} + +fs::path find_qt() +{ + fs::path p = conf().path().qt_install(); + + if (!p.empty()) + { + p = fs::absolute(p); + + if (!try_qt_location(p)) + gcx().bail_out(context::conf, "no qt install in {}", p); + + return p; + } + + + std::vector locations = + { + conf().path().pf_x64(), + "C:\\", + "D:\\" + }; + + // look for qmake, which is in %qt%/version/msvc.../bin + fs::path qmake = find_in_path("qmake.exe"); + if (!qmake.empty()) + locations.insert(locations.begin(), qmake.parent_path() / "../../"); + + // look for qtcreator.exe, which is in %qt%/Tools/QtCreator/bin + fs::path qtcreator = find_in_path("qtcreator.exe"); + if (!qtcreator.empty()) + locations.insert(locations.begin(), qtcreator.parent_path() / "../../../"); + + for (fs::path loc : locations) + { + loc = fs::absolute(loc); + if (try_qt_location(loc)) + return loc; + } + + gcx().bail_out(context::conf, "can't find qt install"); +} + +void validate_qt() +{ + fs::path p = qt::installation_path(); + + if (!try_qt_location(p)) + gcx().bail_out(context::conf, "qt path {} doesn't exist", p); + + details::set_string("paths", "qt_install", path_to_utf8(p)); +} + +fs::path get_known_folder(const GUID& id) +{ + wchar_t* buffer = nullptr; + const auto r = ::SHGetKnownFolderPath(id, 0, 0, &buffer); + + if (r != S_OK) + return {}; + + fs::path p = buffer; + ::CoTaskMemFree(buffer); + + return p; +} + +fs::path find_program_files_x86() +{ + fs::path p = get_known_folder(FOLDERID_ProgramFilesX86); + + if (p.empty()) + { + const auto e = GetLastError(); + + p = fs::path(R"(C:\Program Files (x86))"); + + gcx().warning(context::conf, + "failed to get x86 program files folder, defaulting to {}, {}", + p, error_message(e)); + } + else + { + gcx().trace(context::conf, "x86 program files is {}", p); + } + + return p; +} + +fs::path find_program_files_x64() +{ + fs::path p = get_known_folder(FOLDERID_ProgramFilesX64); + + if (p.empty()) + { + const auto e = GetLastError(); + + p = fs::path(R"(C:\Program Files)"); + + gcx().warning(context::conf, + "failed to get x64 program files folder, defaulting to {}, {}", + p, error_message(e)); + } + else + { + gcx().trace(context::conf, "x64 program files is {}", p); + } + + return p; +} + +fs::path find_temp_dir() +{ + const std::size_t buffer_size = MAX_PATH + 2; + wchar_t buffer[buffer_size] = {}; + + if (GetTempPathW(static_cast(buffer_size), buffer) == 0) + { + const auto e = GetLastError(); + gcx().bail_out(context::conf, "can't get temp path", error_message(e)); + } + + fs::path p(buffer); + gcx().trace(context::conf, "temp dir is {}", p); + + return p; +} + +fs::path find_vs() +{ + if (conf::dry()) + return vs::vswhere(); + + const auto path = vswhere::find_vs(); + if (path.empty()) + gcx().bail_out(context::conf, "vswhere failed"); + + const auto lines = split(path_to_utf8(path), "\r\n"); + + if (lines.empty()) + { + gcx().bail_out(context::conf, "vswhere didn't output anything"); + } + else if (lines.size() > 1) + { + gcx().error(context::conf, "vswhere returned multiple installations:"); + + for (auto&& line : lines) + gcx().error(context::conf, " - {}", line); + + gcx().bail_out(context::conf, + "specify the `vs` path in the `[paths]` section of the INI, or " + "pass -s paths/vs=PATH` to pick an installation"); + } + + if (!fs::exists(path)) + { + gcx().bail_out(context::conf, + "the path given by vswhere doesn't exist: {}", path); + } + + return path; +} + +bool try_vcvars(fs::path& bat) +{ + if (!fs::exists(bat)) + return false; + + bat = fs::canonical(fs::absolute(bat)); + return true; +} + +void find_vcvars() +{ + fs::path bat = conf().tool().get("vcvars"); + + if (conf::dry()) + { + if (bat.empty()) + bat = "vcvars.bat"; + + return; + } + else + { + if (bat.empty()) + { + bat = vs::installation_path() + / "VC" / "Auxiliary" / "Build" / "vcvarsall.bat"; + + if (!try_vcvars(bat)) + gcx().bail_out(context::conf, "vcvars not found at {}", bat); + } + else + { + if (!try_vcvars(bat)) + gcx().bail_out(context::conf, "vcvars not found at {}", bat); + } + } + + details::set_string("tools", "vcvars", path_to_utf8(bat)); + gcx().trace(context::conf, "using vcvars at {}", bat); +} + +} // namespace diff --git a/src/core/paths.h b/src/core/paths.h new file mode 100644 index 0000000..b63fd3d --- /dev/null +++ b/src/core/paths.h @@ -0,0 +1,22 @@ +#pragma once + +namespace mob +{ + +fs::path find_root(bool verbose=false); +fs::path find_in_root(const fs::path& file); + +fs::path find_third_party_directory(); +fs::path find_program_files_x86(); +fs::path find_program_files_x64(); +fs::path find_vs(); +fs::path find_qt(); +fs::path find_iscc(); +fs::path find_temp_dir(); + +void find_vcvars(); +void validate_qt(); + +fs::path mob_exe_path(); + +} // namespace diff --git a/src/utility/fs.cpp b/src/utility/fs.cpp index f37056e..2c090dd 100644 --- a/src/utility/fs.cpp +++ b/src/utility/fs.cpp @@ -8,6 +8,23 @@ namespace mob { +fs::path make_temp_file() +{ + static fs::path dir = conf().path().temp_dir(); + + wchar_t name[MAX_PATH + 1] = {}; + if (GetTempFileNameW(dir.native().c_str(), L"mob", 0, name) == 0) + { + const auto e = GetLastError(); + + gcx().bail_out(context::conf, + "can't create temp file in {}, {}", dir, error_message(e)); + } + + return dir / name; +} + + file_deleter::file_deleter(const context& cx, fs::path p) : cx_(cx), p_(std::move(p)), delete_(true) { diff --git a/src/utility/fs.h b/src/utility/fs.h index 17f058f..a220117 100644 --- a/src/utility/fs.h +++ b/src/utility/fs.h @@ -6,6 +6,11 @@ namespace mob class context; +// returns a unique filename in the temp directory; requires inis to be loaded +// +fs::path make_temp_file(); + + struct handle_closer { using pointer = HANDLE; diff --git a/vs/mob.vcxproj b/vs/mob.vcxproj index b4a2e93..028d579 100644 --- a/vs/mob.vcxproj +++ b/vs/mob.vcxproj @@ -71,6 +71,7 @@ + @@ -128,6 +129,7 @@ + diff --git a/vs/mob.vcxproj.filters b/vs/mob.vcxproj.filters index 421ec1c..d8b1db4 100644 --- a/vs/mob.vcxproj.filters +++ b/vs/mob.vcxproj.filters @@ -198,6 +198,9 @@ src\core + + src\core + @@ -263,5 +266,8 @@ src\core + + src\core + \ No newline at end of file