diff --git a/src/Mod/AddonManager/Addon.py b/src/Mod/AddonManager/Addon.py index 4da5d282f6..6fd25f6879 100644 --- a/src/Mod/AddonManager/Addon.py +++ b/src/Mod/AddonManager/Addon.py @@ -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] diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index 66eb18a7b7..f878d45f3b 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -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 = "
" + 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) + "
" + 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) + "