Addon Manager: Improve dependency handling
This commit is contained in:
@@ -32,6 +32,22 @@ import addonmanager_utilities as utils
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
INTERNAL_WORKBENCHES = {}
|
||||
INTERNAL_WORKBENCHES["arch"] = "Arch"
|
||||
INTERNAL_WORKBENCHES["draft"] = "Draft"
|
||||
INTERNAL_WORKBENCHES["fem"] = "FEM"
|
||||
INTERNAL_WORKBENCHES["mesh"] = "Mesh"
|
||||
INTERNAL_WORKBENCHES["openscad"] = "OpenSCAD"
|
||||
INTERNAL_WORKBENCHES["part"] = "Part"
|
||||
INTERNAL_WORKBENCHES["partdesign"] = "PartDesign"
|
||||
INTERNAL_WORKBENCHES["path"] = "Path"
|
||||
INTERNAL_WORKBENCHES["plot"] = "Plot"
|
||||
INTERNAL_WORKBENCHES["points"] = "Points"
|
||||
INTERNAL_WORKBENCHES["raytracing"] = "Raytracing"
|
||||
INTERNAL_WORKBENCHES["robot"] = "Robot"
|
||||
INTERNAL_WORKBENCHES["sketcher"] = "Sketcher"
|
||||
INTERNAL_WORKBENCHES["spreadsheet"] = "Spreadsheet"
|
||||
INTERNAL_WORKBENCHES["techdraw"] = "TechDraw"
|
||||
|
||||
class Addon:
|
||||
"Encapsulate information about a FreeCAD addon"
|
||||
@@ -83,7 +99,7 @@ class Addon:
|
||||
self.required_external_addons = [] # A list of Addons
|
||||
self.blockers = [] # A list of Addons
|
||||
self.replaces = [] # A list of Addons
|
||||
self.unrecognized_addons: Set[str] = set()
|
||||
self.internal_workbenches: Set[str] = set() # Required internal workbenches
|
||||
self.python_required: Set[str] = set()
|
||||
self.python_optional: Set[str] = set()
|
||||
|
||||
@@ -242,6 +258,57 @@ class Addon:
|
||||
else:
|
||||
self.branch = "master"
|
||||
self.extract_tags(self.metadata)
|
||||
self.extract_metadata_dependencies(self.metadata)
|
||||
|
||||
def version_is_ok(self, metadata) -> bool:
|
||||
dep_fc_min = metadata.FreeCADMin
|
||||
dep_fc_max = metadata.FreeCADMax
|
||||
|
||||
fc_major = int(FreeCAD.Version()[0])
|
||||
fc_minor = int(FreeCAD.Version()[1])
|
||||
|
||||
try:
|
||||
if dep_fc_min and dep_fc_min != "0.0.0":
|
||||
required_version = dep_fc_min.split(".")
|
||||
if fc_major < int(required_version[0]):
|
||||
return False # Major version is too low
|
||||
elif fc_major == int(required_version[0]):
|
||||
if len(required_version) > 1 and fc_minor < int(required_version[1]):
|
||||
return False # Same major, and minor is too low
|
||||
except ValueError:
|
||||
FreeCAD.Console.PrintMessage(f"Metadata file for {self.name} has invalid FreeCADMin version info\n")
|
||||
|
||||
try:
|
||||
if dep_fc_max and dep_fc_max != "0.0.0":
|
||||
required_version = dep_fc_max.split(".")
|
||||
if fc_major > int(required_version[0]):
|
||||
return False # Major version is too high
|
||||
elif fc_major == int(required_version[0]):
|
||||
if len(required_version) > 1 and fc_minor > int(required_version[1]):
|
||||
return False # Same major, and minor is too high
|
||||
except ValueError:
|
||||
FreeCAD.Console.PrintMessage(f"Metadata file for {self.name} has invalid FreeCADMax version info\n")
|
||||
|
||||
return True
|
||||
|
||||
def extract_metadata_dependencies(self, metadata):
|
||||
|
||||
# Version check: if this piece of metadata doesn't apply to this version of
|
||||
# FreeCAD, just skip it.
|
||||
if not self.version_is_ok(metadata):
|
||||
return
|
||||
|
||||
for dep in metadata.Depend:
|
||||
# Simple version for now: eventually support all of the version params...
|
||||
self.requires.add(dep["package"])
|
||||
for dep in metadata.Conflict:
|
||||
self.blocks.add(dep["package"])
|
||||
|
||||
# Recurse
|
||||
content = metadata.Content
|
||||
for _, value in content.items():
|
||||
for item in value:
|
||||
self.extract_metadata_dependencies(item)
|
||||
|
||||
def verify_url_and_branch(self, url: str, branch: str) -> None:
|
||||
"""Print diagnostic information for Addon Developers if their metadata is
|
||||
@@ -266,6 +333,12 @@ class Addon:
|
||||
)
|
||||
|
||||
def extract_tags(self, metadata: FreeCAD.Metadata) -> None:
|
||||
|
||||
# Version check: if this piece of metadata doesn't apply to this version of
|
||||
# FreeCAD, just skip it.
|
||||
if not self.version_is_ok(metadata):
|
||||
return
|
||||
|
||||
for new_tag in metadata.Tag:
|
||||
self.tags.add(new_tag)
|
||||
|
||||
@@ -377,8 +450,20 @@ class Addon:
|
||||
deps.required_external_addons.append(all_repos[dep])
|
||||
all_repos[dep].walk_dependency_tree(all_repos, deps)
|
||||
else:
|
||||
# Maybe this is an internal workbench, just store its name
|
||||
deps.unrecognized_addons.add(dep)
|
||||
# See if this is an internal workbench:
|
||||
if dep.upper().endswith("WB"):
|
||||
real_name = dep[:-2].strip().lower()
|
||||
elif dep.upper().endswith("WORKBENCH"):
|
||||
real_name = dep[:-9].strip().lower()
|
||||
else:
|
||||
real_name = dep.strip().lower()
|
||||
|
||||
if real_name in INTERNAL_WORKBENCHES:
|
||||
deps.internal_workbenches.add(INTERNAL_WORKBENCHES[real_name])
|
||||
else:
|
||||
# Assume it's a Python requirement of some kind:
|
||||
deps.python_required.add(dep)
|
||||
|
||||
for dep in self.blocks:
|
||||
if dep in all_repos:
|
||||
deps.blockers[dep] = all_repos[dep]
|
||||
|
||||
@@ -929,7 +929,7 @@ class CommandAddonManager:
|
||||
repo.walk_dependency_tree(repo_name_dict, deps)
|
||||
|
||||
FreeCAD.Console.PrintLog("The following Workbenches are required:\n")
|
||||
for addon in deps.unrecognized_addons:
|
||||
for addon in deps.internal_workbenches:
|
||||
FreeCAD.Console.PrintLog(addon + "\n")
|
||||
|
||||
FreeCAD.Console.PrintLog("The following addons are required:\n")
|
||||
@@ -950,11 +950,20 @@ class CommandAddonManager:
|
||||
missing_external_addons.append(dep)
|
||||
|
||||
# Now check the loaded addons to see if we are missing an internal workbench:
|
||||
wbs = FreeCADGui.listWorkbenches()
|
||||
wbs = [wb.lower() for wb in FreeCADGui.listWorkbenches()]
|
||||
|
||||
missing_wbs = []
|
||||
for dep in deps.unrecognized_addons:
|
||||
if dep not in wbs and dep + "Workbench" not in wbs:
|
||||
missing_wbs.append(dep)
|
||||
for dep in deps.internal_workbenches:
|
||||
if dep.lower() + "workbench" not in wbs:
|
||||
if dep == "Plot":
|
||||
# Special case for plot, which is no longer a full workbench:
|
||||
try:
|
||||
__import__("Plot")
|
||||
except ImportError:
|
||||
# Plot might fail for a number of reasons
|
||||
missing_wbs.append(dep)
|
||||
else:
|
||||
missing_wbs.append(dep)
|
||||
|
||||
# Check the Python dependencies:
|
||||
missing_python_requirements = []
|
||||
@@ -971,19 +980,20 @@ class CommandAddonManager:
|
||||
bad_packages.append(dep)
|
||||
|
||||
if bad_packages:
|
||||
message = translate(
|
||||
message = "<p>" + translate(
|
||||
"AddonsInstaller",
|
||||
"The Addon {} requires Python packages that are not installed, and cannot be installed automatically. To use this workbench you must install the following Python packages manually:",
|
||||
).format(repo.name)
|
||||
).format(repo.name) + "</p><ul>"
|
||||
if len(bad_packages) < 15:
|
||||
for dep in bad_packages:
|
||||
message += f"\n * {dep}"
|
||||
message += f"<li>{dep}</li>"
|
||||
else:
|
||||
message += (
|
||||
"\n * (" + translate("AddonsInstaller", "Too many to list") + ")"
|
||||
"<li>(" + translate("AddonsInstaller", "Too many to list") + ")</li>"
|
||||
)
|
||||
message += "</ul>"
|
||||
QtWidgets.QMessageBox.critical(
|
||||
None, translate("AddonsInstaller", "Connection failed"), message
|
||||
None, translate("AddonsInstaller", "Missing Requirement"), message
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
translate(
|
||||
@@ -1017,15 +1027,16 @@ class CommandAddonManager:
|
||||
name = missing_wbs[0]
|
||||
message = translate(
|
||||
"AddonsInstaller",
|
||||
"Installing {} requires '{}', which is not installed in your copy of FreeCAD.",
|
||||
"Addon '{}' requires '{}', which is not available in your copy of FreeCAD.",
|
||||
).format(addon, name)
|
||||
else:
|
||||
message = translate(
|
||||
message = "<p>" + translate(
|
||||
"AddonsInstaller",
|
||||
"Installing {} requires the following workbenches, which are not installed in your copy of FreeCAD:\n",
|
||||
).format(addon)
|
||||
"Addon '{}' requires the following workbenches, which are not available in your copy of FreeCAD:",
|
||||
).format(addon) + "</p><ul>"
|
||||
for wb in missing_wbs:
|
||||
message += " - " + wb + "\n"
|
||||
message += "<li>" + wb + "</li>"
|
||||
message += "</ul>"
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self.dialog,
|
||||
translate("AddonsInstaller", "Missing Requirement"),
|
||||
|
||||
Reference in New Issue
Block a user