Add CI\CD step to prepare cache file in ahead of time so user does not need to wait

This commit is contained in:
ExPikaPaka
2026-06-15 09:19:53 +02:00
parent d861e8af22
commit 5d0c640f7b
6 changed files with 149 additions and 0 deletions

View File

@@ -117,6 +117,15 @@ jobs:
run: |
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15
- name: Generate system presets cache (macOS)
if: runner.os == 'macOS' && !inputs.macos-combine-only
working-directory: ${{ github.workspace }}
shell: bash
run: |
tool=$(find build/${{ inputs.arch }} -name generate_system_cache -type f | head -1)
profiles=$(find build/${{ inputs.arch }} -path "*/Resources/profiles" -type d | head -1)
"$tool" --path "$profiles" --log_level 2
- name: Pack macOS app bundle ${{ inputs.arch }}
if: runner.os == 'macOS' && !inputs.macos-combine-only
working-directory: ${{ github.workspace }}
@@ -292,6 +301,14 @@ jobs:
# WindowsSDKVersion: '10.0.26100.0\'
run: .\build_release_vs.bat slicer
- name: Generate system presets cache (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$profiles = Get-ChildItem -Recurse -Path build -Directory -Filter profiles |
Where-Object { $_.FullName -match 'resources' } | Select-Object -First 1
.\build\src\Release\generate_system_cache.exe --path $profiles.FullName --log_level 2
- name: Create installer Win
if: runner.os == 'Windows' && !vars.SELF_HOSTED
working-directory: ${{ github.workspace }}/build
@@ -400,6 +417,20 @@ jobs:
retention-days: 5
if-no-files-found: error
- name: Generate system presets cache (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
./build/src/Release/generate_system_cache --path build/package/resources/profiles --log_level 2
# Re-pack the AppImage so the cache is included
appimage=$(find build -maxdepth 1 -name "OrcaSlicer_Linux_AppImage*.AppImage" | head -1)
chmod +x "$appimage"
"$appimage" --appimage-extract
cp build/package/resources/profiles/system_presets_cache.cache squashfs-root/resources/profiles/
appimagetool=$(find build -name "appimagetool.AppImage" | head -1)
ARCH=$(uname -m) "$appimagetool" --appimage-extract-and-run squashfs-root "$appimage"
rm -rf squashfs-root
- name: Run external slicer regression tests
if: runner.os == 'Linux'
timeout-minutes: 20

View File

@@ -111,6 +111,11 @@ if(ORCA_TOOLS)
endif()
target_link_libraries(OrcaSlicer_profile_validator libslic3r boost_headeronly libcurl OpenSSL::SSL OpenSSL::Crypto)
target_compile_definitions(OrcaSlicer_profile_validator PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
# generate_system_cache: pre-generates resources/profiles/system_presets_cache.cache for CI bundling.
add_executable(generate_system_cache dev-utils/generate_system_cache.cpp)
target_link_libraries(generate_system_cache libslic3r boost_headeronly)
target_compile_definitions(generate_system_cache PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
endif()
# Create a slic3r executable

View File

@@ -0,0 +1,86 @@
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/PresetBundleCache.hpp"
#include "libslic3r/Utils.hpp"
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/program_options.hpp>
#include <iostream>
using namespace Slic3r;
namespace fs = boost::filesystem;
namespace po = boost::program_options;
int main(int argc, char* argv[])
{
po::options_description desc("OrcaSlicer System Cache Generator\nUsage");
// clang-format off
desc.add_options()
("help,h", "Show help")
#ifdef __APPLE__
("path,p", po::value<std::string>()->default_value("../../../../../../../resources/profiles"), "Path to profiles directory")
#else
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "Path to profiles directory")
#endif
("log_level,l", po::value<int>()->default_value(2), "Log level (0=trace, 2=info, 4=error)");
// clang-format on
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) { std::cout << desc << "\n"; return 0; }
po::notify(vm);
} catch (const po::error& e) {
std::cerr << "Error: " << e.what() << "\n" << desc << "\n";
return 1;
}
const std::string profiles_path = vm["path"].as<std::string>();
const int log_level = vm["log_level"].as<int>();
if (!fs::exists(profiles_path) || !fs::is_directory(profiles_path)) {
std::cerr << "Error: '" << profiles_path << "' is not a valid directory\n";
return 1;
}
set_logging_level(log_level);
// In validation_mode, load_system_presets_from_json uses data_dir() directly
// (no /system/ suffix), so point data_dir at the profiles directory.
set_data_dir(profiles_path);
set_resources_dir(fs::path(profiles_path).parent_path().make_preferred().string());
// load_presets creates user preset dirs under data_dir().
const fs::path user_dir = fs::path(data_dir()) / PRESET_USER_DIR;
if (!fs::exists(user_dir))
fs::create_directories(user_dir);
AppConfig app_config;
app_config.set("preset_folder", "default");
auto preset_bundle = std::make_unique<PresetBundle>();
preset_bundle->set_is_validation_mode(true);
preset_bundle->set_default_suppressed(true);
std::cout << "Loading system presets from: " << profiles_path << "\n";
try {
preset_bundle->load_presets(app_config, ForwardCompatibilitySubstitutionRule::EnableSilent);
} catch (const std::exception& ex) {
std::cerr << "Failed to load presets: " << ex.what() << "\n";
return 1;
}
const std::string cache_path =
(fs::path(profiles_path) / "system_presets_cache.cache").make_preferred().string();
PresetBundleCache::SystemPresetsCache cache;
cache.capture(*preset_bundle, profiles_path);
cache.save(cache_path);
std::cout << "Cache written: " << cache_path << "\n"
<< " Vendor profiles: " << cache.vendor_profiles.size() << "\n"
<< " Print presets: " << cache.print_presets.size() << "\n"
<< " Filament presets: " << cache.filament_presets.size() << "\n"
<< " Printer presets: " << cache.printer_presets.size() << "\n";
return 0;
}

View File

@@ -2208,6 +2208,26 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""};
}
// Try bundled cache shipped with the installer (first launch, before user cache exists).
// Validates against resources/profiles/ — the same directory it was generated from in CI.
{
const std::string bundled_dir =
(boost::filesystem::path(resources_dir()) / "profiles").make_preferred().string();
PresetBundleCache::SystemPresetsCache bundled;
if (bundled.load(PresetBundleCache::SystemPresetsCache::bundled_cache_path()) &&
bundled.is_valid(bundled_dir)) {
bundled.apply(*this);
update_system_maps();
// Promote to user cache so subsequent launches skip this check.
bundled.save(cache_file);
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - t0).count();
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from bundled cache in " << ms << " ms";
return {PresetsConfigSubstitutions{}, ""};
}
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cache miss, falling back to JSON load";
}

View File

@@ -116,6 +116,12 @@ std::string SystemPresetsCache::cache_path()
.make_preferred().string();
}
std::string SystemPresetsCache::bundled_cache_path()
{
return (boost::filesystem::path(resources_dir()) / "profiles" / "system_presets_cache.cache")
.make_preferred().string();
}
bool SystemPresetsCache::is_valid(const std::string& system_dir) const
{
if (format_version != FORMAT_VERSION)

View File

@@ -119,6 +119,7 @@ struct SystemPresetsCache {
}
static std::string cache_path();
static std::string bundled_cache_path();
bool is_valid(const std::string& system_dir) const;
void capture(const PresetBundle& bundle, const std::string& system_dir);
void apply(PresetBundle& bundle) const;