diff --git a/src/App/FreeCADInit.py b/src/App/FreeCADInit.py index 685165c97f..9ab51ebbd0 100644 --- a/src/App/FreeCADInit.py +++ b/src/App/FreeCADInit.py @@ -200,10 +200,16 @@ def InitApplications(): MetadataFile = os.path.join(Dir, "package.xml") if os.path.exists(MetadataFile): meta = FreeCAD.Metadata(MetadataFile) + if not meta.supportsCurrentFreeCAD(): + Msg(f'NOTICE: {meta.Name} does not support this version of FreeCAD, so is being skipped\n') + continue content = meta.Content if "workbench" in content: workbenches = content["workbench"] for workbench in workbenches: + if not workbench.supportsCurrentFreeCAD(): + Msg(f'NOTICE: {meta.Name} content item {workbench.Name} does not support this version of FreeCAD, so is being skipped\n') + continue subdirectory = workbench.Name if not workbench.Subdirectory else workbench.Subdirectory subdirectory = subdirectory.replace("/",os.path.sep) subdirectory = os.path.join(Dir, subdirectory) @@ -226,12 +232,20 @@ def InitApplications(): if freecad_module_ispkg: Log('Init: Initializing ' + freecad_module_name + '\n') try: - - stopFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", freecad_module_name, "ADDON_DISABLED") + # Check for a stopfile + stopFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", freecad_module_name[8:], "ADDON_DISABLED") if os.path.exists(stopFile): Msg(f'NOTICE: Addon "{freecad_module_name}" disabled by presence of ADDON_DISABLED stopfile\n') continue + # Make sure that package.xml (if present) does not exclude this version of FreeCAD + MetadataFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", freecad_module_name[8:], "package.xml") + if os.path.exists(MetadataFile): + meta = FreeCAD.Metadata(MetadataFile) + if not meta.supportsCurrentFreeCAD(): + Msg(f'NOTICE: Addon "{freecad_module_name}" does not support this version of FreeCAD, so is being skipped\n') + continue + freecad_module = importlib.import_module(freecad_module_name) extension_modules += [freecad_module_name] if any (module_name == 'init' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)): diff --git a/src/App/Metadata.cpp b/src/App/Metadata.cpp index dc0e4973ec..7be5c24a53 100644 --- a/src/App/Metadata.cpp +++ b/src/App/Metadata.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (c) 2021 Chris Hennes * + * Copyright (c) 2022 FreeCAD Project Association * * * * This file is part of the FreeCAD CAx development system. * * * @@ -456,6 +456,23 @@ bool Metadata::satisfies(const Meta::Dependency& dep) return true; } +bool App::Metadata::supportsCurrentFreeCAD() const +{ + static auto fcVersion = Meta::Version(); + if (fcVersion == Meta::Version()) { + std::map& config = App::Application::Config(); + std::stringstream ss; + ss << config["BuildVersionMajor"] << "." << config["BuildVersionMinor"] << "." << config["BuildRevision"].empty() ? "0" : config["BuildRevision"]; + fcVersion = Meta::Version(ss.str()); + } + + if (_freecadmin != Meta::Version() && _freecadmin > fcVersion) + return false; + else if (_freecadmax != Meta::Version() && _freecadmax < fcVersion) + return false; + return true; +} + void Metadata::appendToElement(DOMElement* root) const { appendSimpleXMLNode(root, "name", _name); diff --git a/src/App/Metadata.h b/src/App/Metadata.h index 67b1f76a25..968d91b51d 100644 --- a/src/App/Metadata.h +++ b/src/App/Metadata.h @@ -268,6 +268,12 @@ namespace App { */ bool satisfies(const Meta::Dependency&); + /** + * Determine whether the current metadata specifies support for the currently-running version of FreeCAD. + * Does not interrogate content items, which must be querried individually. + */ + bool supportsCurrentFreeCAD() const; + private: std::string _name; diff --git a/src/App/MetadataPy.xml b/src/App/MetadataPy.xml index 7736ee5d3c..c5202c56e0 100644 --- a/src/App/MetadataPy.xml +++ b/src/App/MetadataPy.xml @@ -174,6 +174,16 @@ limited to 0.20 as the lowest known version since the metadata standard was adde + + + supportsCurrentFreeCAD() +Returns false if this metadata object directly indicates that it does not support the current +version of FreeCAD, or true if it makes no indication, or specifically indicates that it does +support the current version. Does not recurse into Content items. + + + + getGenericMetadata(name) diff --git a/src/App/MetadataPyImp.cpp b/src/App/MetadataPyImp.cpp index e88e8d7f3c..5c0785f985 100644 --- a/src/App/MetadataPyImp.cpp +++ b/src/App/MetadataPyImp.cpp @@ -318,8 +318,11 @@ void MetadataPy::setFreeCADMax(Py::Object args) getMetadataPtr()->setFreeCADMax(App::Meta::Version(version)); } -PyObject* MetadataPy::getFirstSupportedFreeCADVersion(PyObject*) +PyObject* MetadataPy::getFirstSupportedFreeCADVersion(PyObject* p) { + if (!PyArg_ParseTuple(p, "")) + return nullptr; + // Short-circuit: if the toplevel sets a version, then the lower-levels are overridden if (getMetadataPtr()->freecadmin() != App::Meta::Version()) return Py::new_reference_to(Py::String(getMetadataPtr()->freecadmin().str())); @@ -341,8 +344,11 @@ PyObject* MetadataPy::getFirstSupportedFreeCADVersion(PyObject*) } } -PyObject* MetadataPy::getLastSupportedFreeCADVersion(PyObject*) +PyObject* MetadataPy::getLastSupportedFreeCADVersion(PyObject* p) { + if (!PyArg_ParseTuple(p, "")) + return nullptr; + // Short-circuit: if the toplevel sets a version, then the lower-levels are overridden if (getMetadataPtr()->freecadmax() != App::Meta::Version()) return Py::new_reference_to(Py::String(getMetadataPtr()->freecadmax().str())); @@ -364,6 +370,15 @@ PyObject* MetadataPy::getLastSupportedFreeCADVersion(PyObject*) } } +PyObject* MetadataPy::supportsCurrentFreeCAD(PyObject* p) +{ + if (!PyArg_ParseTuple(p, "")) + return nullptr; + + bool result = getMetadataPtr()->supportsCurrentFreeCAD(); + return Py::new_reference_to(Py::Boolean(result)); +} + PyObject* MetadataPy::getCustomAttributes(const char* /*attr*/) const { return 0; diff --git a/src/Gui/FreeCADGuiInit.py b/src/Gui/FreeCADGuiInit.py index b42db6e412..4e03b4fce5 100644 --- a/src/Gui/FreeCADGuiInit.py +++ b/src/Gui/FreeCADGuiInit.py @@ -150,11 +150,15 @@ def InitApplications(): MetadataFile = os.path.join(Dir, "package.xml") if os.path.exists(MetadataFile): meta = FreeCAD.Metadata(MetadataFile) + if not meta.supportsCurrentFreeCAD(): + continue content = meta.Content if "workbench" in content: FreeCAD.Gui.addIconPath(Dir) workbenches = content["workbench"] for workbench_metadata in workbenches: + if not workbench_metadata.supportsCurrentFreeCAD(): + continue subdirectory = workbench_metadata.Name if not workbench_metadata.Subdirectory else workbench_metadata.Subdirectory subdirectory = subdirectory.replace("/",os.path.sep) subdirectory = os.path.join(Dir, subdirectory) @@ -183,9 +187,18 @@ def InitApplications(): import freecad freecad.gui = FreeCADGui for _, freecad_module_name, freecad_module_ispkg in pkgutil.iter_modules(freecad.__path__, "freecad."): - stopFile = os.path.join(Dir, "ADDON_DISABLED") + # Check for a stopfile + stopFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", freecad_module_name[8:], "ADDON_DISABLED") if os.path.exists(stopFile): continue + + # Make sure that package.xml (if present) does not exclude this version of FreeCAD + MetadataFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", freecad_module_name[8:], "package.xml") + if os.path.exists(MetadataFile): + meta = FreeCAD.Metadata(MetadataFile) + if not meta.supportsCurrentFreeCAD(): + continue + if freecad_module_ispkg: Log('Init: Initializing ' + freecad_module_name + '\n') try: