Name the missing parent when a dropped preset is resolved from the CLI

When load_presets() drops a preset because its parent does not exist, the
only trace is a log line. The CLI then resolves --load-settings /
--load-filaments against the loaded bundle and reports "Preset was not found
in the loaded bundle", which points at the resolver rather than at the real
cause.

Record the presets dropped for a missing parent in the collection, keyed by
file, and have resolve_preset_config() report that parent by name when the
source file is one of them.
This commit is contained in:
Hanif Koh
2026-09-09 17:32:32 +08:00
parent fb44569b8e
commit f7812bf0f1
4 changed files with 33 additions and 2 deletions

View File

@@ -1624,6 +1624,7 @@ void PresetCollection::reset(bool delete_files)
unlock();
m_map_alias_to_profile_name.clear();
m_map_system_profile_renamed.clear();
m_unresolved_parents.clear();
}
void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name)
@@ -1875,6 +1876,7 @@ void PresetCollection::load_presets(
if (presets_loaded.empty()) {
for (const auto &entry : deferred) {
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent %1% for config %2%!")%entry.second %entry.first.string();
m_unresolved_parents[entry.first.string()] = entry.second;
++m_errors;
}
break;
@@ -3345,6 +3347,16 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
return first_visible_if_not_found ? &this->first_visible() : nullptr;
}
std::string PresetCollection::unresolved_parent(const boost::filesystem::path &file) const
{
for (const auto &[dropped_file, parent] : m_unresolved_parents) {
boost::system::error_code ec;
if (boost::filesystem::equivalent(file, boost::filesystem::path(dropped_file), ec) && !ec)
return parent;
}
return {};
}
Preset* PresetCollection::find_preset2(const std::string& name, bool auto_match/* = true */)
{
auto preset = find_preset(name, false, true);

View File

@@ -2,6 +2,7 @@
#define slic3r_Preset_hpp_
#include <deque>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
@@ -728,6 +729,9 @@ public:
{
return const_cast<PresetCollection*>(this)->find_preset2(name, auto_match);
}
// Name of the parent that kept the preset file from loading, or empty if the file loaded
// (or was never seen). Lets a caller that fails to find a preset explain why it is missing.
std::string unresolved_parent(const boost::filesystem::path &file) const;
size_t first_visible_idx() const;
// Return the index of the first visible, compatible, system base preset
@@ -965,6 +969,8 @@ private:
// Orca: used for validation only
int m_errors = 0;
// Preset files dropped by load_presets() because their parent does not exist, keyed by file path.
std::map<std::string, std::string> m_unresolved_parents;
};
// Printer supports the FFF and SLA technologies, with different set of configuration values,

View File

@@ -506,6 +506,11 @@ bool PresetBundle::resolve_preset_config(DynamicPrintConfig &config, Preset::Typ
}
if (error == "Preset identity is ambiguous")
return false;
// The file was seen but dropped at load time; say so rather than reporting it as unknown.
if (const std::string parent = collection->unresolved_parent(source_path); !parent.empty()) {
error = "Preset was not loaded because its parent preset \"" + parent + "\" was not found";
return false;
}
if (!allow_source_manifest) {
error = "Preset was not found in the loaded bundle";
return false;

View File

@@ -411,8 +411,8 @@ TEST_CASE("A preset whose parent exists nowhere is reported, not loaded", "[Pres
ScopedTemporaryDir temp_dir;
PresetBundle bundle;
write_sparse_preset(temp_dir.path() / PRESET_PRINT_NAME / "Orphan.json", "Orphan", "No Such Parent",
{{"layer_height", "0.15"}});
const fs::path orphan_file = temp_dir.path() / PRESET_PRINT_NAME / "Orphan.json";
write_sparse_preset(orphan_file, "Orphan", "No Such Parent", {{"layer_height", "0.15"}});
PresetsConfigSubstitutions substitutions;
bundle.prints.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions,
@@ -420,6 +420,14 @@ TEST_CASE("A preset whose parent exists nowhere is reported, not loaded", "[Pres
CHECK(bundle.prints.find_preset("Orphan") == nullptr);
CHECK(bundle.has_errors());
CHECK(bundle.prints.unresolved_parent(orphan_file) == "No Such Parent");
// Resolving the dropped file names the missing parent instead of reporting the file as unknown.
DynamicPrintConfig config;
std::string error;
CHECK_FALSE(bundle.resolve_preset_config(config, Preset::TYPE_PRINT, orphan_file.string(),
ForwardCompatibilitySubstitutionRule::Disable, error, false));
CHECK(error == "Preset was not loaded because its parent preset \"No Such Parent\" was not found");
}
TEST_CASE("Presets inheriting each other in a cycle are reported, not loaded", "[Preset][Inherits][Regression]")