mirror of
https://github.com/ModOrganizer2/mob.git
synced 2026-07-27 14:07:05 -07:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50da6e0238 | ||
|
|
49857a23f0 | ||
|
|
1e4f026324 | ||
|
|
74f8ea9c08 | ||
|
|
263ebd1461 | ||
|
|
524a1fb46a | ||
|
|
cad0eca997 | ||
|
|
ca4c2ed474 | ||
|
|
5258109aa5 | ||
|
|
2de230cf11 | ||
|
|
77bd21028d | ||
|
|
a3a33128ee | ||
|
|
f7b95abeab | ||
|
|
d1965acd1a | ||
|
|
27483d6ddc | ||
|
|
73d7d3dd2b | ||
|
|
11dafd063b | ||
|
|
b0688f81b9 | ||
|
|
eaae1e44c9 |
@@ -10,9 +10,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run clang-format
|
||||
uses: jidicula/clang-format-action@v4.11.0
|
||||
- name: Check format
|
||||
uses: ModOrganizer2/check-formatting-action@master
|
||||
with:
|
||||
clang-format-version: "16"
|
||||
check-path: "."
|
||||
exclude-regex: "third-party"
|
||||
|
||||
@@ -22,13 +22,14 @@ super = cmake_common modorganizer* githubpp
|
||||
plugins = check_fnis bsapacker bsa_extractor diagnose_basic installer_* plugin_python preview_base preview_bsa tool_* game_*
|
||||
|
||||
[task]
|
||||
enabled = true
|
||||
mo_org = ModOrganizer2
|
||||
mo_branch = master
|
||||
mo_fallback =
|
||||
no_pull = false
|
||||
ignore_ts = false
|
||||
revert_ts = false
|
||||
enabled = true
|
||||
mo_org = ModOrganizer2
|
||||
mo_branch = master
|
||||
mo_fallback =
|
||||
no_pull = false
|
||||
ignore_ts = false
|
||||
revert_ts = false
|
||||
configuration = RelWithDebInfo
|
||||
|
||||
git_url_prefix = https://github.com/
|
||||
git_shallow = true
|
||||
@@ -89,37 +90,35 @@ lz4 = true
|
||||
openssl = true
|
||||
pyqt = true
|
||||
python = true
|
||||
usvfs = false
|
||||
|
||||
[versions]
|
||||
vs = 17
|
||||
vs_year = 2022
|
||||
vs_toolset = 14.3
|
||||
sdk = 10.0.22621.0
|
||||
sevenz = 23.01
|
||||
boost = 1.83.0
|
||||
sevenz = 24.05
|
||||
boost = 1.85.0
|
||||
boost_vs = 14.3
|
||||
fmt = 8.1.1
|
||||
fmt = 10.2.1
|
||||
gtest = main
|
||||
libloot = 0.22.3
|
||||
libloot = 0.22.4
|
||||
lz4 = v1.9.4
|
||||
nmm = 0.71.2
|
||||
openssl = 3.2.0
|
||||
pyqt = 6.5.3.dev2309101249
|
||||
pyqt_builder = 1.15.3
|
||||
pyqt_sip = 13.6.0
|
||||
python = v3.11.5
|
||||
pybind11 = v2.11.1
|
||||
openssl = 3.3.0
|
||||
pyqt = 6.7.0
|
||||
pyqt_builder = 1.16.2
|
||||
pyqt_sip = 13.7.0
|
||||
python = v3.12.3
|
||||
pybind11 = v2.12.0
|
||||
bzip2 = 1.0.8
|
||||
sip = 6.7.12
|
||||
spdlog = v1.10.0
|
||||
qt = 6.5.0
|
||||
sip = 6.8.3
|
||||
spdlog = v1.14.1
|
||||
qt = 6.7.0
|
||||
qt_vs = 2019
|
||||
zlib = v1.3
|
||||
zlib = v1.3.1
|
||||
libbsarch = 0.0.9
|
||||
usvfs = qt6
|
||||
explorerpp = 1.3.5
|
||||
|
||||
usvfs = master
|
||||
explorerpp = 1.4.0
|
||||
ss_paper_lad_6788 = 7.2
|
||||
ss_paper_automata_6788 = 3.2
|
||||
ss_paper_mono_6788 = 3.2
|
||||
@@ -130,6 +129,11 @@ ss_starfield_trosski = V1.11
|
||||
ss_fallout3_trosski = v1.11
|
||||
ss_fallout4_trosski = v1.11
|
||||
|
||||
[build-types]
|
||||
libbsarch = release
|
||||
pyqt = release
|
||||
python = release
|
||||
|
||||
[paths]
|
||||
third_party =
|
||||
prefix =
|
||||
|
||||
@@ -25,26 +25,51 @@
|
||||
|
||||
|
||||
## Quick start
|
||||
```
|
||||
```powershell
|
||||
> git clone https://github.com/ModOrganizer2/mob
|
||||
> cd mob
|
||||
> bootstrap
|
||||
> mob -d c:\somewhere build
|
||||
```
|
||||
|
||||
## Slow start
|
||||
- Install Qt 6.5.1 ([Installer](https://download.qt.io/official_releases/online_installers/qt-unified-windows-x64-online.exe)) and select these components:
|
||||
## Extended start
|
||||
|
||||
### Qt - CLI based install using [aqt](https://github.com/miurahr/aqtinstall)
|
||||
|
||||
[aqt](https://github.com/miurahr/aqtinstall) is a CLI installer for Qt, it makes installing Qt extremely quick and painless, and doesn't require a login
|
||||
|
||||
#### Installing aqt via package manager
|
||||
- Start an administrative terminal
|
||||
- Install aqt using one of the two popular packaage managers
|
||||
- `winget install --id=miurahr.aqtinstall -e`
|
||||
- `choco install aqt`
|
||||
|
||||
#### Installing aqt via python/pip
|
||||
|
||||
- Install [Python 3.9](https://www.python.org/downloads/release/python-390/)
|
||||
- Install [aqt](https://github.com/miurahr/aqtinstall) using `pip`
|
||||
|
||||
#### Using aqt
|
||||
- Open a terminal with administrative rights, and run
|
||||
```powershell
|
||||
aqt install-qt --outputdir "C:\Qt" windows desktop 6.7.0 win64_msvc2019_64 -m qtwebengine qtimageformats qtpositioning qtserialport qtwebchannel qtwebsockets
|
||||
```
|
||||
|
||||
### Qt - Manual installation
|
||||
- Install Qt 6.7.0 ([Installer](https://download.qt.io/official_releases/online_installers/qt-unified-windows-x64-online.exe)) and select these components:
|
||||
- MSVC 2019 64-bit
|
||||
- Additional Libraries:
|
||||
- Qt WebEngine (display nexus pages)
|
||||
- Qt Image Formats (display images in image tab and preview)
|
||||
- Qt Positioning (required by QtWebEngine)
|
||||
- Qt Serial Port (required by Qt Core)
|
||||
- Qt Web Channel (required by QtWebEngine)
|
||||
- Qt WebChannel (required by QtWebEngine)
|
||||
- Qt WebSockets (Nexus api/download)
|
||||
- Optional:
|
||||
- Qt Source Files
|
||||
- Qt Debug Files
|
||||
|
||||
### Visual Studio
|
||||
- Install Visual Studio 2022 ([Installer](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false))
|
||||
- Desktop development with C++
|
||||
- Desktop .NET desktop development (needed by OMOD and FOMOD installers)
|
||||
@@ -56,22 +81,20 @@
|
||||
- C++ /CLI support for v143 build Tools (Latest) (for OMOD and FOMOD installers)
|
||||
- Windows 11 SDK (get latest)
|
||||
- C++ Build Tools core features
|
||||
- Optional:
|
||||
- Git for Windows (if you don't have git already installed)
|
||||
- Start a shell
|
||||
- If you have git in your path, a regular cmd shell works.
|
||||
- If not, start the _x64 Native Tools Command Prompt for VS 2019_, it'll use Git for Windows installed above.
|
||||
- Do:
|
||||
```
|
||||
mkdir C:\dev
|
||||
cd C:\dev
|
||||
git clone https://github.com/ModOrganizer2/mob
|
||||
cd mob
|
||||
bootstrap
|
||||
mob -d C:\dev\modorganizer build
|
||||
```
|
||||
- Once `mob` is finished, everything will be in `C:\dev\modorganizer`. Mod Organizer can be run from `install\bin\ModOrganizer.exe`. The Visual Studio solution for Mod Organizer itself is `build\modorganizer_super\modorganizer\vsbuild\organizer.sln`.
|
||||
- Git for Windows (Skip if you have this already installed outside of the VS installer)
|
||||
- CMake tools for Windows (Skip if you have this already installed outside of the VS installer)
|
||||
|
||||
## Setting up MOB
|
||||
- Open a shell. If you've installed Git or CMake through the VS installer, they won't be on your PATH, so you must use x64 Native Tools Command Prompt for VS 2022.- Pick a folder to develop MO2 in, for these steps it'll be `C:\dev`
|
||||
- Execute these commands
|
||||
```powershell
|
||||
mkdir C:\dev
|
||||
cd C:\dev
|
||||
git clone https://github.com/ModOrganizer2/mob
|
||||
./bootstrap.ps1
|
||||
mob -d C:\dev\modorganizer build
|
||||
```
|
||||
- Once `mob` is finished, everything will be in `C:\dev\modorganizer`. Mod Organizer can be run from `install\bin\ModOrganizer.exe`. The Visual Studio solution for Mod Organizer itself is `build\modorganizer_super\modorganizer\vsbuild\organizer.sln`.
|
||||
|
||||
### Prebuilts
|
||||
Some third-parties are not normally built from source, but this can be changed in the options by setting the various tasks in `[prebuilt]` to `false`. If OpenSSL is built from source, perl is required ([Strawberry Perl 5.30.2.1 installer](http://strawberryperl.com/download/5.30.2.1/strawberry-perl-5.30.2.1-64bit.msi)).
|
||||
@@ -118,9 +141,10 @@ Inside the INI file are `[sections]` and `key = value` pairs. The `[task]` secti
|
||||
### `[task]`
|
||||
Options for individual tasks. Can be `[task_name:task]`, where `task_name` is the name of a task (see `mob list`) , `super` for all MO tasks or a glob like `installer_*`.
|
||||
|
||||
| Option | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `enabled` | bool | Whether this task is enabled. Disabled tasks are never built. When specifying task names with `mob build task1 task2...`, all tasks except those given are turned off. |
|
||||
| Option | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `enabled` | bool | Whether this task is enabled. Disabled tasks are never built. When specifying task names with `mob build task1 task2...`, all tasks except those given are turned off. |
|
||||
| `configuration` | enum | Which configuration to build, should be one of Debug, Release or RelWithDebInfo with RelWithDebInfo being the default.|
|
||||
|
||||
#### Common git options
|
||||
Unless otherwise stated, applies to any task that is a git repo.
|
||||
|
||||
+5
-8
@@ -122,7 +122,7 @@ namespace mob {
|
||||
git_wrap g(task->source_path());
|
||||
|
||||
g.fetch(task->git_url().string(),
|
||||
fmt::format("pull/{}/head", pr.number));
|
||||
std::format("pull/{}/head", pr.number));
|
||||
|
||||
g.checkout("FETCH_HEAD");
|
||||
}
|
||||
@@ -208,13 +208,10 @@ namespace mob {
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
constexpr auto* pattern =
|
||||
"https://api.github.com/search/issues?per_page=100&q="
|
||||
"is:pr+org:{org:}+author:{author:}+is:open+head:{branch:}";
|
||||
constexpr auto* pattern = "https://api.github.com/search/issues?per_page=100&q="
|
||||
"is:pr+org:{0:}+author:{1:}+is:open+head:{2:}";
|
||||
|
||||
const auto search_url =
|
||||
fmt::format(pattern, fmt::arg("org", org), fmt::arg("author", author),
|
||||
fmt::arg("branch", branch));
|
||||
const auto search_url = std::format(pattern, org, author, branch);
|
||||
|
||||
u8cout << "search url is " << search_url << "\n";
|
||||
|
||||
@@ -275,7 +272,7 @@ namespace mob {
|
||||
return {};
|
||||
}
|
||||
|
||||
const url u(fmt::format("https://api.github.com/repos/{}/{}/pulls/{}",
|
||||
const url u(std::format("https://api.github.com/repos/{}/{}/pulls/{}",
|
||||
task->org(), task->repo(), pr));
|
||||
|
||||
curl_downloader dl;
|
||||
|
||||
+2
-2
@@ -367,7 +367,7 @@ namespace mob {
|
||||
}
|
||||
|
||||
const auto q =
|
||||
fmt::format("prefix {} already exists, delete?", path_to_utf8(prefix));
|
||||
std::format("prefix {} already exists, delete?", path_to_utf8(prefix));
|
||||
|
||||
if (ask_yes_no(q, yn::no) != yn::yes)
|
||||
return false;
|
||||
@@ -489,7 +489,7 @@ namespace mob {
|
||||
const auto* lcp = static_cast<LANGANDCODEPAGE*>(value_pointer);
|
||||
|
||||
const auto sub_block =
|
||||
fmt::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileVersion", lcp->wLanguage,
|
||||
std::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileVersion", lcp->wLanguage,
|
||||
lcp->wCodePage);
|
||||
|
||||
ret = VerQueryValueW(buffer.get(), sub_block.c_str(), &value_pointer,
|
||||
|
||||
+90
-38
@@ -14,6 +14,11 @@ namespace mob::details {
|
||||
using key_value_map = std::map<std::string, std::string, std::less<>>;
|
||||
using section_map = std::map<std::string, key_value_map, std::less<>>;
|
||||
|
||||
static std::unordered_map<mob::config, std::string_view> s_configuration_values{
|
||||
{mob::config::release, "Release"},
|
||||
{mob::config::debug, "Debug"},
|
||||
{mob::config::relwithdebinfo, "RelWithDebInfo"}};
|
||||
|
||||
// holds all the options not related to tasks (global, tools, paths, etc.)
|
||||
static section_map g_conf;
|
||||
|
||||
@@ -27,6 +32,18 @@ namespace mob::details {
|
||||
static int g_file_log_level = 5;
|
||||
static bool g_dry = false;
|
||||
|
||||
// check if the two given string are equals case-insensitive
|
||||
//
|
||||
bool case_insensitive_equals(std::string_view lhs, std::string_view rhs)
|
||||
{
|
||||
// _strcmpi does not have a n-overload, and since string_view is not
|
||||
// necessarily null-terminated, _strmcpi cannot be safely used
|
||||
return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
|
||||
std::end(rhs), [](auto&& c1, auto&& c2) {
|
||||
return ::tolower(c1) == ::tolower(c2);
|
||||
});
|
||||
}
|
||||
|
||||
bool bool_from_string(std::string_view s)
|
||||
{
|
||||
return (s == "true" || s == "yes" || s == "1");
|
||||
@@ -85,6 +102,17 @@ namespace mob::details {
|
||||
kitor->second = value;
|
||||
}
|
||||
|
||||
config string_to_config(std::string_view value)
|
||||
{
|
||||
for (const auto& [c, v] : s_configuration_values) {
|
||||
if (case_insensitive_equals(value, v)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
gcx().bail_out(context::conf, "invalid configuration '{}'", value);
|
||||
}
|
||||
|
||||
// sets the given option, adds it if it doesn't exist; used when setting options
|
||||
// from the master ini
|
||||
//
|
||||
@@ -184,6 +212,28 @@ namespace mob::details {
|
||||
g_tasks[task_name][key] = std::move(value);
|
||||
}
|
||||
|
||||
// read a CMake constant from the configuration
|
||||
//
|
||||
template <typename T>
|
||||
T parse_cmake_value(std::string_view section, std::string_view key,
|
||||
std::string_view value,
|
||||
std::unordered_map<T, std::string_view> const& values)
|
||||
{
|
||||
for (const auto& [value_c, value_s] : values) {
|
||||
if (case_insensitive_equals(value_s, value)) {
|
||||
return value_c;
|
||||
}
|
||||
}
|
||||
|
||||
// build a string containing allowed value for logging
|
||||
std::vector<std::string_view> values_s;
|
||||
for (const auto& [value_c, value_s] : values) {
|
||||
values_s.push_back(value_s);
|
||||
}
|
||||
gcx().bail_out(context::conf, "bad value '{}' for {}/{} (expected one of {})",
|
||||
value, section, key, join(values_s, ", ", std::string{}));
|
||||
}
|
||||
|
||||
} // namespace mob::details
|
||||
|
||||
namespace mob {
|
||||
@@ -644,6 +694,11 @@ namespace mob {
|
||||
return {};
|
||||
}
|
||||
|
||||
conf_build_types conf::build_types()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
conf_paths conf::path()
|
||||
{
|
||||
return {};
|
||||
@@ -666,6 +721,34 @@ namespace mob {
|
||||
return details::g_dry;
|
||||
}
|
||||
|
||||
// use appropriate case for the below constants since we will be using them in
|
||||
// to_string, although most of cmake and msbuild is case-insensitive so it will
|
||||
// not matter much in the end
|
||||
|
||||
static std::unordered_map<conf_cmake::constant, std::string_view> constant_values{
|
||||
{conf_cmake::always, "ALWAYS"},
|
||||
{conf_cmake::lazy, "LAZY"},
|
||||
{conf_cmake::never, "NEVER"}};
|
||||
|
||||
std::string conf_cmake::to_string(constant c)
|
||||
{
|
||||
return std::string{constant_values.at(c)};
|
||||
}
|
||||
|
||||
conf_cmake::conf_cmake() : conf_section("cmake") {}
|
||||
|
||||
conf_cmake::constant conf_cmake::install_message() const
|
||||
{
|
||||
return details::parse_cmake_value(
|
||||
name(), "install_message", details::get_string(name(), "install_message"),
|
||||
constant_values);
|
||||
}
|
||||
|
||||
std::string conf_cmake::host() const
|
||||
{
|
||||
return details::get_string(name(), "host");
|
||||
}
|
||||
|
||||
conf_task::conf_task(std::vector<std::string> names) : names_(std::move(names)) {}
|
||||
|
||||
std::string conf_task::get(std::string_view key) const
|
||||
@@ -678,45 +761,12 @@ namespace mob {
|
||||
return details::get_bool_for_task(names_, key);
|
||||
}
|
||||
|
||||
bool conf_cmake::cmake_constant::is_equivalent(std::string_view other) const
|
||||
mob::config conf_task::configuration() const
|
||||
{
|
||||
// _strcmpi does not have a n-overload, and since string_view is not
|
||||
// necessarily null-terminated, _strmcpi cannot be safely used
|
||||
return std::equal(std::begin(value_), std::end(value_), std::begin(other),
|
||||
std::end(other), [](auto&& c1, auto&& c2) {
|
||||
return ::tolower(c1) == ::tolower(c2);
|
||||
});
|
||||
}
|
||||
|
||||
conf_cmake::conf_cmake() : conf_section("cmake") {}
|
||||
|
||||
const conf_cmake::cmake_constant conf_cmake::ALWAYS{"always"};
|
||||
const conf_cmake::cmake_constant conf_cmake::LAZY{"lazy"};
|
||||
const conf_cmake::cmake_constant conf_cmake::NEVER{"never"};
|
||||
|
||||
conf_cmake::cmake_constant conf_cmake::install_message() const
|
||||
{
|
||||
return read_cmake_constant("install_message", {ALWAYS, LAZY, NEVER});
|
||||
}
|
||||
|
||||
std::string conf_cmake::host() const
|
||||
{
|
||||
return details::get_string(name(), "host");
|
||||
}
|
||||
|
||||
conf_cmake::cmake_constant
|
||||
conf_cmake::read_cmake_constant(std::string_view key,
|
||||
std::vector<cmake_constant> const& allowed) const
|
||||
{
|
||||
const auto value = details::get_string(name(), key);
|
||||
for (const auto& constant : allowed) {
|
||||
if (constant.is_equivalent(value)) {
|
||||
return constant;
|
||||
}
|
||||
}
|
||||
|
||||
gcx().bail_out(context::conf, "bad value '{}' for {}/{} (expected one of {})",
|
||||
value, name(), key, join(allowed, ", ", std::string{}));
|
||||
return details::parse_cmake_value(
|
||||
names_[0], "configuration",
|
||||
details::get_string_for_task(names_, "configuration"),
|
||||
details::s_configuration_values);
|
||||
}
|
||||
|
||||
conf_tools::conf_tools() : conf_section("tools") {}
|
||||
@@ -725,6 +775,8 @@ namespace mob {
|
||||
|
||||
conf_versions::conf_versions() : conf_section("versions") {}
|
||||
|
||||
conf_build_types::conf_build_types() : conf_section("build-types") {}
|
||||
|
||||
conf_prebuilt::conf_prebuilt() : conf_section("prebuilt") {}
|
||||
|
||||
conf_paths::conf_paths() : conf_section("paths") {}
|
||||
|
||||
+57
-53
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../utility.h"
|
||||
|
||||
// these shouldn't be called directly, they're used by some of the template
|
||||
// below
|
||||
//
|
||||
@@ -9,6 +11,18 @@ namespace mob::details {
|
||||
//
|
||||
std::string get_string(std::string_view section, std::string_view key);
|
||||
|
||||
// convert a string to the given type
|
||||
template <class T>
|
||||
T string_to(std::string_view value);
|
||||
|
||||
config string_to_config(std::string_view value);
|
||||
|
||||
template <>
|
||||
inline config string_to<config>(std::string_view value)
|
||||
{
|
||||
return string_to_config(value);
|
||||
}
|
||||
|
||||
// calls get_string(), converts to bool
|
||||
//
|
||||
bool get_bool(std::string_view section, std::string_view key);
|
||||
@@ -48,7 +62,14 @@ namespace mob {
|
||||
public:
|
||||
DefaultType get(std::string_view key) const
|
||||
{
|
||||
return details::get_string(name_, key);
|
||||
const auto value = details::get_string(name_, key);
|
||||
|
||||
if constexpr (std::is_convertible_v<std::string, DefaultType>) {
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
return details::string_to<DefaultType>(value);
|
||||
}
|
||||
}
|
||||
|
||||
// undefined
|
||||
@@ -102,6 +123,29 @@ namespace mob {
|
||||
bool build() const { return get<bool>("build_task"); }
|
||||
};
|
||||
|
||||
// options in [cmake]
|
||||
//
|
||||
class conf_cmake : conf_section<std::string> {
|
||||
public:
|
||||
enum class constant { always, lazy, never };
|
||||
using enum constant;
|
||||
|
||||
static std::string to_string(constant m);
|
||||
|
||||
conf_cmake();
|
||||
|
||||
// specify the value for CMAKE_INSTALL_MESSAGE
|
||||
//
|
||||
constant install_message() const;
|
||||
|
||||
// specify the toolset host configuration, if any, this is equivalent
|
||||
// to -T host=XXX on the command line
|
||||
//
|
||||
// an empty string means no host configured
|
||||
//
|
||||
std::string host() const;
|
||||
};
|
||||
|
||||
// options in [task] or [task_name:task]
|
||||
//
|
||||
class conf_task {
|
||||
@@ -141,64 +185,16 @@ namespace mob {
|
||||
return get<bool>("remote_push_default_origin");
|
||||
}
|
||||
|
||||
// specify the configuration to build
|
||||
//
|
||||
mob::config configuration() const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> names_;
|
||||
|
||||
bool get_bool(std::string_view name) const;
|
||||
};
|
||||
|
||||
// options in [task] or [task_name:task]
|
||||
//
|
||||
class conf_cmake : conf_section<std::string> {
|
||||
public:
|
||||
class cmake_constant {
|
||||
std::string value_;
|
||||
|
||||
// check if the given string is equivalent to this constant
|
||||
//
|
||||
bool is_equivalent(std::string_view other) const;
|
||||
|
||||
friend class conf_cmake;
|
||||
|
||||
public:
|
||||
constexpr cmake_constant(std::string_view value) : value_{value} {}
|
||||
constexpr const auto& value() const { return value_; }
|
||||
constexpr operator const std::string&() const { return value(); }
|
||||
|
||||
friend bool operator==(cmake_constant const& lhs, cmake_constant const& rhs)
|
||||
{
|
||||
return lhs.is_equivalent(rhs.value());
|
||||
}
|
||||
friend bool operator!=(cmake_constant const& lhs, cmake_constant const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static const cmake_constant ALWAYS;
|
||||
static const cmake_constant LAZY;
|
||||
static const cmake_constant NEVER;
|
||||
|
||||
public:
|
||||
conf_cmake();
|
||||
|
||||
// specify the value for CMAKE_INSTALL_MESSAGE
|
||||
//
|
||||
cmake_constant install_message() const;
|
||||
|
||||
// specify the toolset host configuration, if any, this is equivalent
|
||||
// to -T host=XXX on the command line
|
||||
//
|
||||
// an empty string means no host configured
|
||||
//
|
||||
std::string host() const;
|
||||
|
||||
private:
|
||||
cmake_constant
|
||||
read_cmake_constant(std::string_view key,
|
||||
std::vector<cmake_constant> const& allowed) const;
|
||||
};
|
||||
|
||||
// options in [tools]
|
||||
//
|
||||
class conf_tools : public conf_section<fs::path> {
|
||||
@@ -220,6 +216,13 @@ namespace mob {
|
||||
conf_versions();
|
||||
};
|
||||
|
||||
// options in [build-types]
|
||||
//
|
||||
class conf_build_types : public conf_section<config> {
|
||||
public:
|
||||
conf_build_types();
|
||||
};
|
||||
|
||||
// options in [prebuilt]
|
||||
//
|
||||
class conf_prebuilt : public conf_section<std::string> {
|
||||
@@ -284,6 +287,7 @@ namespace mob {
|
||||
conf_transifex transifex();
|
||||
conf_prebuilt prebuilt();
|
||||
conf_versions version();
|
||||
conf_build_types build_types();
|
||||
conf_paths path();
|
||||
|
||||
// opens the log file, creates the directory if needed
|
||||
|
||||
@@ -5,25 +5,6 @@
|
||||
#include "../utility.h"
|
||||
#include "conf.h"
|
||||
|
||||
namespace mob::details {
|
||||
|
||||
std::string converter<std::wstring>::convert(const std::wstring& s)
|
||||
{
|
||||
return utf16_to_utf8(s);
|
||||
}
|
||||
|
||||
std::string converter<fs::path>::convert(const fs::path& s)
|
||||
{
|
||||
return utf16_to_utf8(s.native());
|
||||
}
|
||||
|
||||
std::string converter<url>::convert(const url& u)
|
||||
{
|
||||
return u.string();
|
||||
}
|
||||
|
||||
} // namespace mob::details
|
||||
|
||||
namespace mob {
|
||||
|
||||
// timestamps are relative to this
|
||||
|
||||
+14
-52
@@ -1,45 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../utility.h"
|
||||
|
||||
// T to std::string converters
|
||||
//
|
||||
// those are kept in this namespace so they don't leak all over the place;
|
||||
// they're used directly by context::do_log() below
|
||||
|
||||
namespace mob::details {
|
||||
|
||||
class mob::url;
|
||||
|
||||
template <class T, class = void>
|
||||
struct converter {
|
||||
static const T& convert(const T& t) { return t; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct converter<std::wstring> {
|
||||
static std::string convert(const std::wstring& s);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct converter<fs::path> {
|
||||
static std::string convert(const fs::path& s);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct converter<url> {
|
||||
static std::string convert(const url& u);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct converter<T, std::enable_if_t<std::is_enum_v<T>>> {
|
||||
static std::string convert(T e)
|
||||
{
|
||||
return std::to_string(static_cast<std::underlying_type_t<T>>(e));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mob::details
|
||||
#include "./formatters.h"
|
||||
|
||||
namespace mob {
|
||||
|
||||
@@ -57,7 +19,7 @@ namespace mob {
|
||||
// in places where there is no context available, there's a global one can that
|
||||
// be retrieved with gcx() for logging
|
||||
//
|
||||
// all log functions will use fmt::format() internally, so they can be used
|
||||
// all log functions will use std::format() internally, so they can be used
|
||||
// like:
|
||||
//
|
||||
// cx.log(context::generic, "eat more {}", "potatoes");
|
||||
@@ -161,7 +123,7 @@ namespace mob {
|
||||
// logs a formatted string with the dump level
|
||||
//
|
||||
template <class... Args>
|
||||
void dump(reason r, const char* f, Args&&... args) const
|
||||
void dump(reason r, std::format_string<Args...> f, Args&&... args) const
|
||||
{
|
||||
do_log(false, r, level::dump, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -169,7 +131,7 @@ namespace mob {
|
||||
// logs a formatted string with the trace level
|
||||
//
|
||||
template <class... Args>
|
||||
void trace(reason r, const char* f, Args&&... args) const
|
||||
void trace(reason r, std::format_string<Args...> f, Args&&... args) const
|
||||
{
|
||||
do_log(false, r, level::trace, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -177,7 +139,7 @@ namespace mob {
|
||||
// logs a formatted string with the debug level
|
||||
//
|
||||
template <class... Args>
|
||||
void debug(reason r, const char* f, Args&&... args) const
|
||||
void debug(reason r, std::format_string<Args...> f, Args&&... args) const
|
||||
{
|
||||
do_log(false, r, level::debug, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -185,7 +147,7 @@ namespace mob {
|
||||
// logs a formatted string with the info level
|
||||
//
|
||||
template <class... Args>
|
||||
void info(reason r, const char* f, Args&&... args) const
|
||||
void info(reason r, std::format_string<Args...> f, Args&&... args) const
|
||||
{
|
||||
do_log(false, r, level::info, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -193,7 +155,7 @@ namespace mob {
|
||||
// logs a formatted string with the warning level
|
||||
//
|
||||
template <class... Args>
|
||||
void warning(reason r, const char* f, Args&&... args) const
|
||||
void warning(reason r, std::format_string<Args...> f, Args&&... args) const
|
||||
{
|
||||
do_log(false, r, level::warning, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -201,7 +163,7 @@ namespace mob {
|
||||
// logs a formatted string with the error level
|
||||
//
|
||||
template <class... Args>
|
||||
void error(reason r, const char* f, Args&&... args) const
|
||||
void error(reason r, std::format_string<Args...> f, Args&&... args) const
|
||||
{
|
||||
do_log(false, r, level::error, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -211,7 +173,8 @@ namespace mob {
|
||||
// tasks
|
||||
//
|
||||
template <class... Args>
|
||||
[[noreturn]] void bail_out(reason r, const char* f, Args&&... args) const
|
||||
[[noreturn]] void bail_out(reason r, std::format_string<Args...> f,
|
||||
Args&&... args) const
|
||||
{
|
||||
do_log(true, r, level::error, f, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -227,7 +190,8 @@ namespace mob {
|
||||
// bailed exception after logging
|
||||
//
|
||||
template <class... Args>
|
||||
void do_log(bool bail, reason r, level lv, const char* f, Args&&... args) const
|
||||
void do_log(bool bail, reason r, level lv, std::format_string<Args...> f,
|
||||
Args&&... args) const
|
||||
{
|
||||
// discard log if it's not enabled and it's not bailing out
|
||||
if (!bail && !enabled(lv))
|
||||
@@ -235,9 +199,7 @@ namespace mob {
|
||||
|
||||
try {
|
||||
// formatting string
|
||||
const std::string s =
|
||||
fmt::format(f, details::converter<std::decay_t<Args>>::convert(
|
||||
std::forward<Args>(args))...);
|
||||
const std::string s = std::format(f, std::forward<Args>(args)...);
|
||||
|
||||
do_log_impl(bail, r, lv, s);
|
||||
}
|
||||
@@ -253,7 +215,7 @@ namespace mob {
|
||||
// a ghetto conversion and hope it gives enough info
|
||||
std::wstring s;
|
||||
|
||||
const char* p = f;
|
||||
const char* p = f.get().data();
|
||||
while (*p) {
|
||||
s += (wchar_t)*p;
|
||||
++p;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "../utility/string.h"
|
||||
|
||||
template <>
|
||||
struct std::formatter<std::wstring, char> : std::formatter<std::string, char> {
|
||||
template <class FmtContext>
|
||||
FmtContext::iterator format(std::wstring const& s, FmtContext& ctx) const
|
||||
{
|
||||
return std::formatter<std::string, char>::format(mob::utf16_to_utf8(s), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::formatter<std::filesystem::path, char>
|
||||
: std::formatter<std::basic_string<std::filesystem::path::value_type>, char> {
|
||||
template <class FmtContext>
|
||||
FmtContext::iterator format(std::filesystem::path const& s, FmtContext& ctx) const
|
||||
{
|
||||
return std::formatter<std::basic_string<std::filesystem::path::value_type>,
|
||||
char>::format(s.native(), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Enum, class CharT>
|
||||
requires std::is_enum_v<Enum>
|
||||
struct std::formatter<Enum, CharT>
|
||||
: std::formatter<std::underlying_type_t<Enum>, CharT> {
|
||||
template <class FmtContext>
|
||||
FmtContext::iterator format(Enum v, FmtContext& ctx) const
|
||||
{
|
||||
return std::formatter<std::underlying_type_t<Enum>, CharT>::format(
|
||||
static_cast<std::underlying_type_t<Enum>>(v), ctx);
|
||||
}
|
||||
};
|
||||
+2
-2
@@ -10,11 +10,11 @@
|
||||
namespace mob {
|
||||
|
||||
template <class... Args>
|
||||
void ini_error(const ini_data& ini, std::size_t line, std::string_view f,
|
||||
void ini_error(const ini_data& ini, std::size_t line, std::format_string<Args...> f,
|
||||
Args&&... args)
|
||||
{
|
||||
gcx().bail_out(context::conf, "{}:{}: {}", path_to_utf8(ini.path), (line + 1),
|
||||
fmt::format(f, std::forward<Args>(args)...));
|
||||
std::format(f, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
ini_data::kv_map& ini_data::get_section(std::string_view name)
|
||||
|
||||
+1
-1
@@ -326,7 +326,7 @@ namespace mob {
|
||||
|
||||
// check known installation paths for a bunch of versions
|
||||
for (int v : {5, 6, 7, 8}) {
|
||||
const fs::path inno_dir = fmt::format("Inno Setup {}", v);
|
||||
const fs::path inno_dir = std::format("Inno Setup {}", v);
|
||||
|
||||
// check for both architectures
|
||||
for (fs::path pf : {conf().path().pf_x86(), conf().path().pf_x64()}) {
|
||||
|
||||
@@ -29,12 +29,10 @@ namespace mob {
|
||||
add_task<parallel_tasks>()
|
||||
.add_task<sevenz>()
|
||||
.add_task<zlib>()
|
||||
.add_task<fmt>()
|
||||
.add_task<gtest>()
|
||||
.add_task<libbsarch>()
|
||||
.add_task<libloot>()
|
||||
.add_task<openssl>()
|
||||
.add_task<libffi>()
|
||||
.add_task<bzip2>();
|
||||
|
||||
add_task<parallel_tasks>()
|
||||
|
||||
@@ -115,3 +115,12 @@ namespace mob {
|
||||
};
|
||||
|
||||
} // namespace mob
|
||||
|
||||
template <>
|
||||
struct std::formatter<mob::url, char> : std::formatter<std::string, char> {
|
||||
template <class FmtContext>
|
||||
FmtContext::iterator format(mob::url const& u, FmtContext& ctx) const
|
||||
{
|
||||
return std::formatter<std::string, char>::format(u.string(), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#include "pch.h"
|
||||
#include <fmt/format.cc>
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <atomic>
|
||||
#include <charconv>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
@@ -69,7 +70,6 @@
|
||||
|
||||
#include <clipp.h>
|
||||
#include <curl/curl.h>
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
+15
-10
@@ -218,23 +218,28 @@ namespace mob::tasks {
|
||||
bootstrap();
|
||||
}
|
||||
|
||||
// some libraries only need some variants, avoid building the unnecessary
|
||||
// ones
|
||||
// we do not need all variants of all components but since people should
|
||||
// usually be using the pre-built, we can build everything without losing too
|
||||
// much time and it is much easier to deal when mixing
|
||||
//
|
||||
// note: filesystem is only required by USVFS I think, so maybe think about
|
||||
// removing it if we switch to std::filesystem in USVFS
|
||||
//
|
||||
clipp::arg_list components{"thread", "date_time", "filesystem", "locale",
|
||||
"program_options"};
|
||||
|
||||
// static link, static runtime, x64
|
||||
do_b2({"thread", "date_time", "filesystem", "locale", "program_options"},
|
||||
"static", "static", arch::x64);
|
||||
do_b2(components, "static", "static", arch::x64);
|
||||
|
||||
// static link, static runtime, x86, required by usvfs 32-bit
|
||||
do_b2({"thread", "date_time", "filesystem", "locale"}, "static", "static",
|
||||
arch::x86);
|
||||
// static link, static/shared runtime, x86, required by usvfs 32-bit
|
||||
do_b2(components, "static", "static", arch::x86);
|
||||
do_b2(components, "static", "shared", arch::x86);
|
||||
|
||||
// static link, shared runtime, x64
|
||||
do_b2({"thread", "date_time", "locale", "program_options"}, "static", "shared",
|
||||
arch::x64);
|
||||
do_b2(components, "static", "shared", arch::x64);
|
||||
|
||||
// shared link, shared runtime, x64
|
||||
do_b2({"thread", "date_time", "atomic"}, "shared", "shared", arch::x64);
|
||||
do_b2(components, "shared", "shared", arch::x64);
|
||||
}
|
||||
|
||||
void boost::do_b2(const std::vector<std::string>& components,
|
||||
|
||||
@@ -7,9 +7,8 @@ namespace mob::tasks {
|
||||
|
||||
url source_url()
|
||||
{
|
||||
return "https://explorerplusplus.com/software/"
|
||||
"explorer++_" +
|
||||
explorerpp::version() + "_x64.zip";
|
||||
return "https://download.explorerplusplus.com/stable/" +
|
||||
explorerpp::version() + "/explorerpp_x64.zip";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "tasks.h"
|
||||
|
||||
namespace mob::tasks {
|
||||
|
||||
namespace {
|
||||
|
||||
url source_url()
|
||||
{
|
||||
return "https://github.com/fmtlib/fmt/releases/download/" + fmt::version() +
|
||||
"/fmt-" + fmt::version() + ".zip";
|
||||
}
|
||||
|
||||
cmake create_cmake_tool(const fs::path& src_path,
|
||||
cmake::ops o = cmake::ops::generate)
|
||||
{
|
||||
return std::move(cmake(o)
|
||||
.generator(cmake::vs)
|
||||
.root(src_path)
|
||||
.prefix(src_path / "build")
|
||||
.def("FMT_TEST", "OFF")
|
||||
.def("FMT_DOC", "OFF"));
|
||||
}
|
||||
|
||||
fs::path solution_path()
|
||||
{
|
||||
const auto build_path = create_cmake_tool(fmt::source_path()).build_path();
|
||||
|
||||
return build_path / "INSTALL.vcxproj";
|
||||
}
|
||||
|
||||
msbuild create_msbuild_tool(msbuild::ops o = msbuild::ops::build)
|
||||
{
|
||||
return std::move(msbuild(o).solution(solution_path()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
fmt::fmt() : basic_task("fmt") {}
|
||||
|
||||
std::string fmt::version()
|
||||
{
|
||||
return conf().version().get("fmt");
|
||||
}
|
||||
|
||||
bool fmt::prebuilt()
|
||||
{
|
||||
// no prebuilts available
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::path fmt::source_path()
|
||||
{
|
||||
return conf().path().build() / ("fmt-" + version());
|
||||
}
|
||||
|
||||
void fmt::do_clean(clean c)
|
||||
{
|
||||
// delete download
|
||||
if (is_set(c, clean::redownload))
|
||||
run_tool(downloader(source_url(), downloader::clean));
|
||||
|
||||
// delete the whole directory
|
||||
if (is_set(c, clean::reextract)) {
|
||||
cx().trace(context::reextract, "deleting {}", source_path());
|
||||
op::delete_directory(cx(), source_path(), op::optional);
|
||||
|
||||
// no need to do anything else
|
||||
return;
|
||||
}
|
||||
|
||||
// cmake clean
|
||||
if (is_set(c, clean::reconfigure))
|
||||
run_tool(create_cmake_tool(source_path(), cmake::clean));
|
||||
|
||||
// msbuild clean
|
||||
if (is_set(c, clean::rebuild))
|
||||
run_tool(create_msbuild_tool(msbuild::clean));
|
||||
}
|
||||
|
||||
void fmt::do_fetch()
|
||||
{
|
||||
const auto file = run_tool(downloader(source_url()));
|
||||
|
||||
run_tool(extractor().file(file).output(source_path()));
|
||||
}
|
||||
|
||||
void fmt::do_build_and_install()
|
||||
{
|
||||
run_tool(create_cmake_tool(source_path()));
|
||||
run_tool(create_msbuild_tool());
|
||||
}
|
||||
|
||||
} // namespace mob::tasks
|
||||
+32
-16
@@ -5,25 +5,24 @@ namespace mob::tasks {
|
||||
|
||||
namespace {
|
||||
|
||||
cmake create_cmake_tool(arch a, cmake::ops o = cmake::generate)
|
||||
cmake create_cmake_tool(arch a, config config, cmake::ops o = cmake::generate)
|
||||
{
|
||||
const std::string build_dir = (a == arch::x64 ? "build" : "build_32");
|
||||
|
||||
return std::move(cmake(o)
|
||||
.generator(cmake::vs)
|
||||
.architecture(a)
|
||||
.arg("-Wno-deprecated")
|
||||
.arg("-Dgtest_force_shared_crt=ON")
|
||||
.prefix(gtest::source_path() / build_dir)
|
||||
.prefix(gtest::build_path(a, config))
|
||||
.root(gtest::source_path()));
|
||||
}
|
||||
|
||||
msbuild create_msbuild_tool(arch a, msbuild::ops o = msbuild::build)
|
||||
msbuild create_msbuild_tool(arch a, config config,
|
||||
msbuild::ops o = msbuild::build)
|
||||
{
|
||||
const fs::path build_path = create_cmake_tool(a).build_path();
|
||||
const fs::path build_path = create_cmake_tool(a, config).build_path();
|
||||
|
||||
return std::move(
|
||||
msbuild(o).architecture(a).solution(build_path / "INSTALL.vcxproj"));
|
||||
return std::move(msbuild(o).architecture(a).configuration(config).solution(
|
||||
build_path / "INSTALL.vcxproj"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -45,6 +44,12 @@ namespace mob::tasks {
|
||||
return conf().path().build() / "googletest";
|
||||
}
|
||||
|
||||
fs::path gtest::build_path(arch a, config c)
|
||||
{
|
||||
return source_path() / "build" / (a == arch::x64 ? "x64" : "Win32") /
|
||||
msbuild::configuration_name(c);
|
||||
}
|
||||
|
||||
void gtest::do_clean(clean c)
|
||||
{
|
||||
if (is_set(c, clean::reclone)) {
|
||||
@@ -53,13 +58,15 @@ namespace mob::tasks {
|
||||
}
|
||||
|
||||
if (is_set(c, clean::reconfigure)) {
|
||||
run_tool(create_cmake_tool(arch::x86, cmake::clean));
|
||||
run_tool(create_cmake_tool(arch::x64, cmake::clean));
|
||||
run_tool(create_cmake_tool(arch::x86, config::release, cmake::clean));
|
||||
run_tool(create_cmake_tool(arch::x64, config::release, cmake::clean));
|
||||
}
|
||||
|
||||
if (is_set(c, clean::rebuild)) {
|
||||
run_tool(create_msbuild_tool(arch::x86, msbuild::clean));
|
||||
run_tool(create_msbuild_tool(arch::x64, msbuild::clean));
|
||||
run_tool(create_msbuild_tool(arch::x86, config::release, msbuild::clean));
|
||||
run_tool(create_msbuild_tool(arch::x86, config::debug, msbuild::clean));
|
||||
run_tool(create_msbuild_tool(arch::x64, config::release, msbuild::clean));
|
||||
run_tool(create_msbuild_tool(arch::x64, config::debug, msbuild::clean));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,15 +80,24 @@ namespace mob::tasks {
|
||||
|
||||
void gtest::do_build_and_install()
|
||||
{
|
||||
op::create_directories(cx(), gtest::build_path(arch::x64).parent_path());
|
||||
op::create_directories(cx(), gtest::build_path(arch::x86).parent_path());
|
||||
|
||||
parallel({{"gtest64",
|
||||
[&] {
|
||||
run_tool(create_cmake_tool(arch::x64));
|
||||
run_tool(create_msbuild_tool(arch::x64));
|
||||
run_tool(create_cmake_tool(arch::x64, config::release));
|
||||
run_tool(create_msbuild_tool(arch::x64, config::release));
|
||||
|
||||
run_tool(create_cmake_tool(arch::x64, config::debug));
|
||||
run_tool(create_msbuild_tool(arch::x64, config::debug));
|
||||
}},
|
||||
|
||||
{"gtest32", [&] {
|
||||
run_tool(create_cmake_tool(arch::x86));
|
||||
run_tool(create_msbuild_tool(arch::x86));
|
||||
run_tool(create_cmake_tool(arch::x86, config::release));
|
||||
run_tool(create_msbuild_tool(arch::x86, config::release));
|
||||
|
||||
run_tool(create_cmake_tool(arch::x86, config::debug));
|
||||
run_tool(create_msbuild_tool(arch::x86, config::debug));
|
||||
}}});
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user