From 6b45a982ddc55826605fc9be6294694ded7ed58d Mon Sep 17 00:00:00 2001 From: Vitalii Koshura Date: Thu, 12 Dec 2024 10:11:49 +0100 Subject: [PATCH] Refactor boinc.json structure a little bit. Moved RadioButton definitions closer to the relevant controls. Locale files now shoulw be defined in the main JSON file. Add possibility to pass main JSON file as a parameter to the installer.exe. Signed-off-by: Vitalii Koshura --- installer/Control.cpp | 7 ++ installer/Control.h | 3 + installer/ControlTable.cpp | 5 + installer/Installer.cpp | 13 +-- installer/InstallerStrings.cpp | 35 ++++++- installer/InstallerStrings.h | 2 +- installer/RadioButton.cpp | 4 +- installer/RadioButton.h | 2 +- installer/RadioButtonTable.cpp | 10 +- installer/RadioButtonTable.h | 4 +- installer/boinc.json | 165 +++++++++++++----------------- installer/locale/en.json | 12 --- installer/main.cpp | 13 ++- win_build/installer_setup.vcxproj | 2 +- 14 files changed, 144 insertions(+), 133 deletions(-) diff --git a/installer/Control.cpp b/installer/Control.cpp index 6473403a78..c5ec225929 100644 --- a/installer/Control.cpp +++ b/installer/Control.cpp @@ -43,6 +43,9 @@ Control::Control(const nlohmann::json& json, JsonHelper::handle(json, "EventMappings", [&](const auto& eventMapping) { eventMappings.emplace_back(eventMapping, dialog, control); }); + JsonHelper::handle(json, "RadioButtons", [&](const auto& radioButton) { + radioButtons.emplace_back(radioButton, property, installerStrings); + }); } const std::vector& Control::get_conditions() const noexcept { @@ -57,6 +60,10 @@ const std::vector& Control::get_event_mappings() const noexcept { return eventMappings; } +const std::vector& Control::get_radio_buttons() const noexcept { + return radioButtons; +} + MSIHANDLE Control::getRecord() const { return MsiHelper::MsiRecordSet({ dialog, control, type, x, y, width, height, attributes, property, text, next, help }); diff --git a/installer/Control.h b/installer/Control.h index f65f974e07..db935504ec 100644 --- a/installer/Control.h +++ b/installer/Control.h @@ -25,6 +25,7 @@ #include "ControlCondition.h" #include "ControlEvent.h" #include "EventMapping.h" +#include "RadioButton.h" #include "InstallerStrings.h" class Control : public Record { @@ -35,6 +36,7 @@ class Control : public Record { const std::vector& get_conditions() const noexcept; const std::vector& get_events() const noexcept; const std::vector& get_event_mappings() const noexcept; + const std::vector& get_radio_buttons() const noexcept; MSIHANDLE getRecord() const override; private: std::string dialog{}; @@ -52,4 +54,5 @@ class Control : public Record { std::vector conditions{}; std::vector events{}; std::vector eventMappings{}; + std::vector radioButtons{}; }; diff --git a/installer/ControlTable.cpp b/installer/ControlTable.cpp index 3d585b8894..57b7fd4300 100644 --- a/installer/ControlTable.cpp +++ b/installer/ControlTable.cpp @@ -20,6 +20,7 @@ #include "ControlConditionTable.h" #include "ControlEventTable.h" #include "EventMappingTable.h" +#include "RadioButtonTable.h" #include "ControlTable.h" ControlTable::ControlTable(const std::vector& dialogs) noexcept : @@ -46,6 +47,10 @@ bool ControlTable::generate(MSIHANDLE hDatabase) std::cerr << "Failed to generate EventMappingTable" << std::endl; return false; } + if (!RadioButtonTable(controls).generate(hDatabase)) { + std::cerr << "Failed to generate RadioButtonTable" << std::endl; + return false; + } std::cout << "Generating ControlTable..." << std::endl; diff --git a/installer/Installer.cpp b/installer/Installer.cpp index 3ff1f7504d..10c177e985 100644 --- a/installer/Installer.cpp +++ b/installer/Installer.cpp @@ -51,10 +51,6 @@ Installer::Installer(const std::filesystem::path& output_path, } bool Installer::load(const std::filesystem::path& json) { - if (!installer_strings.load(json.parent_path())) { - return false; - } - std::ifstream file(json); if (!file.is_open()) { std::cerr << "Could not open file " << json << std::endl; @@ -77,6 +73,11 @@ bool Installer::load_from_json(const nlohmann::json& json, const std::filesystem::path& path) { try { + if (JsonHelper::exists(json, "Locale")) { + if (!installer_strings.load(json["Locale"], path)) { + return false; + } + } if (JsonHelper::exists(json, "Summary")) { tables["Summary"] = std::make_shared( json["Summary"], installer_strings, platform); @@ -150,10 +151,6 @@ bool Installer::load_from_json(const nlohmann::json& json, tables["Property"] = std::make_shared( json["Property"], installer_strings); } - if (JsonHelper::exists(json, "RadioButton")) { - tables["RadioButton"] = std::make_shared( - json["RadioButton"], installer_strings); - } if (JsonHelper::exists(json, "TextStyle")) { tables["TextStyle"] = std::make_shared( json["TextStyle"]); diff --git a/installer/InstallerStrings.cpp b/installer/InstallerStrings.cpp index b80daaf87d..d188a82db3 100644 --- a/installer/InstallerStrings.cpp +++ b/installer/InstallerStrings.cpp @@ -24,7 +24,7 @@ InstallerStrings::~InstallerStrings() { for (const auto& key : strings) { if (keys_used.find(key.first) == keys_used.end()) { - std::cerr << "WARNING: Key " << key.first<< " not used." + std::cerr << "WARNING: Key " << key.first << " not used." << std::endl; } } @@ -38,8 +38,33 @@ const std::string& InstallerStrings::get(const std::string& key) { keys_used.insert(key); return strings.at(key); }; -bool InstallerStrings::load(const std::filesystem::path& path) { - const auto filename = path / "locale/en.json"; +bool InstallerStrings::load(const nlohmann::json& json, + const std::filesystem::path& path) { + std::string en_path{}; + for (const auto& item : json) { + std::string p{}; + std::string language{}; + std::string title{}; + JsonHelper::get(item, "Path", p); + JsonHelper::get(item, "Language", language); + JsonHelper::get(item, "Title", title); + + if (language == "en") { + en_path = p; + } + else { + std::cerr << "WARNING: Unsupported language " << language + << std::endl; + } + + } + + if (en_path.empty()) { + std::cerr << "No English locale specified." << std::endl; + return false; + } + + const auto filename = path / en_path; std::ifstream file(filename); if (!file.is_open()) { std::cerr << "Could not open file " << filename << std::endl; @@ -47,7 +72,9 @@ bool InstallerStrings::load(const std::filesystem::path& path) { } nlohmann::json j; file >> j; - return load_from_json(j); + const auto result = load_from_json(j); + file.close(); + return result; } bool InstallerStrings::load_from_json(const nlohmann::json& json) { for (const auto& item : json) { diff --git a/installer/InstallerStrings.h b/installer/InstallerStrings.h index 121c06dd97..5614a3105f 100644 --- a/installer/InstallerStrings.h +++ b/installer/InstallerStrings.h @@ -30,7 +30,7 @@ class InstallerStrings { ~InstallerStrings(); const std::string& get(const std::string& key); - bool load(const std::filesystem::path& path); + bool load(const nlohmann::json& json, const std::filesystem::path& path); private: std::map strings{}; std::unordered_set keys_used{}; diff --git a/installer/RadioButton.cpp b/installer/RadioButton.cpp index f51990dc8e..2c877db94b 100644 --- a/installer/RadioButton.cpp +++ b/installer/RadioButton.cpp @@ -20,8 +20,8 @@ #include "JsonHelper.h" RadioButton::RadioButton(const nlohmann::json& json, - InstallerStrings& installerStrings) { - JsonHelper::get(json, "Property", property); + const std::string& property, InstallerStrings& installerStrings) + : property(property) { JsonHelper::get(json, "Order", order); JsonHelper::get(json, "Value", value); JsonHelper::get(json, "X", x); diff --git a/installer/RadioButton.h b/installer/RadioButton.h index 89db01602f..8983b41d6a 100644 --- a/installer/RadioButton.h +++ b/installer/RadioButton.h @@ -25,7 +25,7 @@ class RadioButton : public Record { public: explicit RadioButton(const nlohmann::json& json, - InstallerStrings& installerStrings); + const std::string& property, InstallerStrings& installerStrings); ~RadioButton() = default; MSIHANDLE getRecord() const override; private: diff --git a/installer/RadioButtonTable.cpp b/installer/RadioButtonTable.cpp index a5a5b7fce1..8ee1afd410 100644 --- a/installer/RadioButtonTable.cpp +++ b/installer/RadioButtonTable.cpp @@ -17,11 +17,11 @@ #include "RadioButtonTable.h" -RadioButtonTable::RadioButtonTable(const nlohmann::json& json, - InstallerStrings& installerStrings) { - std::cout << "Loading RadioButtonTable..." << std::endl; - for (const auto& item : json) { - properties.emplace_back(item, installerStrings); +RadioButtonTable::RadioButtonTable(const std::vector& controls) { + for (const auto& control : controls) { + for (const auto& radioButton : control.get_radio_buttons()) { + properties.emplace_back(radioButton); + } } } diff --git a/installer/RadioButtonTable.h b/installer/RadioButtonTable.h index bc972a3a39..7a5a118eed 100644 --- a/installer/RadioButtonTable.h +++ b/installer/RadioButtonTable.h @@ -19,12 +19,12 @@ #include "Generator.h" #include "RadioButton.h" +#include "Control.h" #include "InstallerStrings.h" class RadioButtonTable : public Generator { public: - explicit RadioButtonTable(const nlohmann::json& json, - InstallerStrings& installerStrings); + explicit RadioButtonTable(const std::vector& controls); ~RadioButtonTable() = default; bool generate(MSIHANDLE hDatabase) override; private: diff --git a/installer/boinc.json b/installer/boinc.json index 4688c1d000..4dc9d779d4 100644 --- a/installer/boinc.json +++ b/installer/boinc.json @@ -14,6 +14,13 @@ "appname": "BOINC", "security": 1 }, + "Locale": [ + { + "Path": "locale/en.json", + "Language": "en", + "Title": "English (United States)" + } + ], "ActionText": [ { "Action": "Advertise", @@ -2338,7 +2345,27 @@ "Height": 40, "Attributes": 3, "Property": "AgreeToLicense", - "Control_Next": "Back" + "Control_Next": "Back", + "RadioButtons": [ + { + "Order": 1, + "Value": "No", + "X": 0, + "Y": 15, + "Width": 291, + "Height": 15, + "Text": "IDS__AgreeToLicense_0" + }, + { + "Order": 2, + "Value": "Yes", + "X": 0, + "Y": 0, + "Width": 291, + "Height": 15, + "Text": "IDS__AgreeToLicense_1" + } + ] }, { "Control": "Back", @@ -2656,7 +2683,27 @@ "Attributes": 3, "Property": "_IsMaintenance", "Text": "IDS__IsMaintenanceDlg_11", - "Control_Next": "Back" + "Control_Next": "Back", + "RadioButtons": [ + { + "Order": 2, + "Value": "Reinstall", + "X": 0, + "Y": 0, + "Width": 290, + "Height": 14, + "Text": "IDS__IsMaintenanceDlg_Repair" + }, + { + "Order": 3, + "Value": "Remove", + "X": 0, + "Y": 60, + "Width": 290, + "Height": 14, + "Text": "IDS__IsMaintenanceDlg_Remove" + } + ] }, { "Control": "Text2", @@ -2916,7 +2963,27 @@ "Height": 40, "Attributes": 3, "Property": "RestartManagerOption", - "Control_Next": "List" + "Control_Next": "List", + "RadioButtons": [ + { + "Order": 1, + "Value": "CloseRestart", + "X": 6, + "Y": 9, + "Width": 331, + "Height": 14, + "Text": "IDS__IsMsiRMFilesInUse_CloseRestart" + }, + { + "Order": 2, + "Value": "Reboot", + "X": 6, + "Y": 21, + "Width": 331, + "Height": 14, + "Text": "IDS__IsMsiRMFilesInUse_RebootAfter" + } + ] } ] }, @@ -10141,98 +10208,6 @@ "Value": "Install" } ], - "RadioButton": [ - { - "Property": "AgreeToLicense", - "Order": 1, - "Value": "No", - "X": 0, - "Y": 15, - "Width": 291, - "Height": 15, - "Text": "IDS__AgreeToLicense_0" - }, - { - "Property": "AgreeToLicense", - "Order": 2, - "Value": "Yes", - "X": 0, - "Y": 0, - "Width": 291, - "Height": 15, - "Text": "IDS__AgreeToLicense_1" - }, - { - "Property": "RestartManagerOption", - "Order": 1, - "Value": "CloseRestart", - "X": 6, - "Y": 9, - "Width": 331, - "Height": 14, - "Text": "IDS__IsMsiRMFilesInUse_CloseRestart" - }, - { - "Property": "RestartManagerOption", - "Order": 2, - "Value": "Reboot", - "X": 6, - "Y": 21, - "Width": 331, - "Height": 14, - "Text": "IDS__IsMsiRMFilesInUse_RebootAfter" - }, - { - "Property": "SETUPTYPE", - "Order": 1, - "Value": "Single", - "X": 0, - "Y": 0, - "Width": 270, - "Height": 15, - "Text": "IDS__IsSetupTypeDlg_Single" - }, - { - "Property": "SETUPTYPE", - "Order": 2, - "Value": "Shared", - "X": 0, - "Y": 38, - "Width": 270, - "Height": 15, - "Text": "IDS__IsSetupTypeDlg_Shared" - }, - { - "Property": "SETUPTYPE", - "Order": 3, - "Value": "Service", - "X": 0, - "Y": 75, - "Width": 270, - "Height": 15, - "Text": "IDS__IsSetupTypeDlg_Service" - }, - { - "Property": "_IsMaintenance", - "Order": 2, - "Value": "Reinstall", - "X": 0, - "Y": 0, - "Width": 290, - "Height": 14, - "Text": "IDS__IsMaintenanceDlg_Repair" - }, - { - "Property": "_IsMaintenance", - "Order": 3, - "Value": "Remove", - "X": 0, - "Y": 60, - "Width": 290, - "Height": 14, - "Text": "IDS__IsMaintenanceDlg_Remove" - } - ], "TextStyle": [ { "TextStyle": "Tahoma8", diff --git a/installer/locale/en.json b/installer/locale/en.json index d2b61a6450..4367a60db7 100644 --- a/installer/locale/en.json +++ b/installer/locale/en.json @@ -3723,18 +3723,6 @@ "Id": "IDS__IsResumeDlg_WizardResume", "Value": "The BOINC Installer Wizard will complete the upgrade of [ProductName] on your computer. To continue, click Next." }, - { - "Id": "IDS__IsSetupTypeDlg_Service", - "Value": "{&TahomaBold8}Ser&vice Installation" - }, - { - "Id": "IDS__IsSetupTypeDlg_Shared", - "Value": "{&TahomaBold8}S&hared Installation" - }, - { - "Id": "IDS__IsSetupTypeDlg_Single", - "Value": "{&TahomaBold8}&Single-User Installation" - }, { "Id": "IDS__IsUserExit_ClickFinish", "Value": "Click Finish to exit the wizard." diff --git a/installer/main.cpp b/installer/main.cpp index ce2ec29edf..614cad16ac 100644 --- a/installer/main.cpp +++ b/installer/main.cpp @@ -32,8 +32,9 @@ int main(int argc, char** argv) { #else "x64"; #endif + std::string project_path{}; - for (int i = 0; i < argc; i++) { + for (int i = 1; i < argc; i++) { auto arg = std::string(argv[i]); if (arg == "-c") { if (++i >= argc) { @@ -62,13 +63,21 @@ int main(int argc, char** argv) { } platform = arg; } + else if (project_path.empty()) { + project_path = arg; + } + else { + std::cerr << "Unknown argument: " << arg << std::endl; + return 1; + } } const auto output_path = std::filesystem::current_path() / "Build" / platform / configuration; Installer installer(output_path, platform, configuration); if (!installer.load( - std::filesystem::current_path() / "../installer/boinc.json")) { + std::filesystem::current_path() / (project_path.empty() ? + "../installer/boinc.json" : project_path))) { return 1; } return installer.create_msi(output_path / "boinc.msi") ? 0 : 1; diff --git a/win_build/installer_setup.vcxproj b/win_build/installer_setup.vcxproj index 5d01b1b2c4..3e41e65e92 100644 --- a/win_build/installer_setup.vcxproj +++ b/win_build/installer_setup.vcxproj @@ -43,7 +43,7 @@ $(SolutionDir)Build\x64\$(Configuration)\installer.exe -p $(Platform) - $(MSBuildThisFileDirectory)..\installer\boinc.json;$(MSBuildThisFileDirectory)..\installer\locale\en.json;$(OutDir)boinccas.dll + $(MSBuildThisFileDirectory)..\installer\boinc.json;$(MSBuildThisFileDirectory)..\installer\locale\en.json;$(OutDir)boinccas.dll;$(SolutionDir)Build\x64\$(Configuration)\installer.exe $(OutDir)boinc.msi