Core: Support disabling Addon by FreeCAD version

If package.xml metadata file exists, it is scanned for FreeCAD version
compatibility before the Addon is loaded. If the Addon specifies that it is
explicitly not compatible with the current version of FreeCAD, the Addon is
not loaded.
This commit is contained in:
Chris Hennes
2022-03-08 23:17:03 -06:00
parent 907ef395f4
commit f122061424
6 changed files with 81 additions and 6 deletions

View File

@@ -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__)):

View File

@@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
* 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<std::string, std::string>& 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);

View File

@@ -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;

View File

@@ -174,6 +174,16 @@ limited to 0.20 as the lowest known version since the metadata standard was adde
</Documentation>
</Methode>
<Methode Name="supportsCurrentFreeCAD">
<Documentation>
<UserDocu>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.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getGenericMetadata">
<Documentation>
<UserDocu>getGenericMetadata(name)

View File

@@ -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;

View File

@@ -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: