diff --git a/README.md b/README.md index 63c51e4..3d2146a 100644 --- a/README.md +++ b/README.md @@ -947,26 +947,33 @@ using os specific configurations. ### Defining Distros -The `distos` key in distro and config definitions is used to define the distro version +The `distos` key in distro and config definitions are used to define the distro version requirements. When a config is processed the distro requirements are evaluated recursively to include the requirements of the latest DistroVersion matching the specifier. -This uses the python packaging module to resolve version specifiers so you can use the -same configuration syntax you would use in a pip requirements file. +This uses the python [packaging module](https://packaging.pypa.io/en/stable/requirements.html) +to resolve version specifiers so you can use the same configuration syntax you +would use in a pip requirements file. -```json +```json5 "distros": [ "maya2020", "maya2022", "houdini18.5", "hsite", "animBot<=1.4", - "studiolibrary==2.5.7.post1" + "studiolibrary==2.5.7.post1", + // Use markers to only include 3ds max if hab is currently running on + // windows. It can't be run on linux. + "3dsmax2019;platform_system=='Windows'" ] - ``` The resolved versions matching the requested distros are shown in the `versions` property. +It also supports [markers](https://packaging.pypa.io/en/stable/markers.html). Hab +should support all of the [officially supported markers](https://peps.python.org/pep-0508/), +but the most common marker likely to be used with hab is `platform_system`. + ### Platform specific code Hab works on windows, linux and osx(needs tested). To make it easier to handle diff --git a/hab/solvers.py b/hab/solvers.py index 4d8e2bc..10c7d6c 100644 --- a/hab/solvers.py +++ b/hab/solvers.py @@ -100,6 +100,19 @@ def _resolve( for req in reqs: name = req.name + + marker = req.marker + if marker and not marker.evaluate(): + # If a marker is specified and its not valid for the current + # system, skip this requirement. + # https://packaging.pypa.io/en/stable/markers.html + msg = f"Requirement ignored due to marker: {req}" + if name in self.forced: + logger.critical(f"Forced {msg}") # pragma: no cover + else: + logger.warning(f"{msg}") + continue + if name in self.forced: if name in reported: # Once we have processed this requirement, there is no need to diff --git a/tests/test_resolver.py b/tests/test_resolver.py index 9c445a9..39b7593 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -467,6 +467,45 @@ def test_resolve_requirements_recalculate(resolver): ) +@pytest.mark.parametrize( + "platform,marker", + ( + ("windows", "Windows"), + ("linux", "Linux"), + ("osx", "Darwin"), + ), +) +def test_resolve_requirements_markers(resolver, platform, marker): + """Test that platform_system for current host is included or excluded correctly. + + The packaging marker library isn't setup to allow for testing on other + platforms, so these tests need to pass on all platforms, if running the test + on this platform, the dependency is included, otherwise it should be ignored. + """ + check = [ + "the_dcc", + "the_dcc_plugin_a", + "the_dcc_plugin_b", + "the_dcc_plugin_d", + "the_dcc_plugin_e", + ] + # This requirement is only included if running on the target platform + if utils.Platform.name() == platform: + check.append("the_dcc_plugin_c") + + # Build requirements utilizing the platform marker. + requirements = { + "the_dcc": Requirement("the_dcc"), + # the_dcc_plugin_c is only included if the current platform matches that. + "the_dcc_plugin_c": Requirement( + f"the_dcc_plugin_c;platform_system=='{marker}'" + ), + } + + ret = resolver.resolve_requirements(requirements) + assert set(ret.keys()) == set(check) + + def test_resolve_requirements_errors(resolver): # This requirement is not possible because the_dcc_plugin_b requires the_dcc<1.2 requirements = {