Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

input: add 'inherit' dependency property #565

Merged
merged 1 commit into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/manual/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,16 @@ The following settings are supported:
| | | At the dependency both names will refer to the same |
| | | tool. |
+-------------+-----------------+-----------------------------------------------------+
| inherit | Boolean | Inherit current environment, tools and sandbox to |
| | | this dependency. When set to ``false``, all |
| | | environment variables are reset to their default |
| | | and no tools or sandbox are passed down to the |
| | | dependency. This is mostly useful to make an |
| | | existing root-package become a dependency of |
| | | another (root) package. |
| | | |
| | | Default: ``true`` |
+-------------+-----------------+-----------------------------------------------------+

.. _configuration-recipes-env:

Expand Down
50 changes: 34 additions & 16 deletions pym/bob/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -1855,10 +1855,11 @@ class Recipe(object):
"""

class Dependency(object):
def __init__(self, recipe, env, fwd, use, cond, tools, checkoutDep):
def __init__(self, recipe, env, fwd, use, cond, tools, checkoutDep, inherit):
self.recipe = recipe
self.envOverride = env
self.provideGlobal = fwd
self.inherit = inherit
self.use = use
self.useEnv = "environment" in self.use
self.useTools = "tools" in self.use
Expand All @@ -1870,9 +1871,9 @@ def __init__(self, recipe, env, fwd, use, cond, tools, checkoutDep):
self.checkoutDep = checkoutDep

@staticmethod
def __parseEntry(dep, env, fwd, use, cond, tools, checkoutDep):
def __parseEntry(dep, env, fwd, use, cond, tools, checkoutDep, inherit):
if isinstance(dep, str):
return [ Recipe.Dependency(dep, env, fwd, use, cond, tools, checkoutDep) ]
return [ Recipe.Dependency(dep, env, fwd, use, cond, tools, checkoutDep, inherit) ]
else:
envOverride = dep.get("environment")
if envOverride:
Expand All @@ -1884,6 +1885,7 @@ def __parseEntry(dep, env, fwd, use, cond, tools, checkoutDep):
tools.update(toolOverride)
fwd = dep.get("forward", fwd)
use = dep.get("use", use)
inherit = dep.get("inherit", inherit)
newCond = dep.get("if")
if newCond is not None:
cond = cond + [newCond] if cond is not None else [ newCond ]
Expand All @@ -1892,22 +1894,22 @@ def __parseEntry(dep, env, fwd, use, cond, tools, checkoutDep):
if name:
if "depends" in dep:
raise ParseError("A dependency must not use 'name' and 'depends' at the same time!")
return [ Recipe.Dependency(name, env, fwd, use, cond, tools, checkoutDep) ]
return [ Recipe.Dependency(name, env, fwd, use, cond, tools, checkoutDep, inherit) ]
dependencies = dep.get("depends")
if dependencies is None:
raise ParseError("Either 'name' or 'depends' required for dependencies!")
return Recipe.Dependency.parseEntries(dependencies, env, fwd,
use, cond, tools,
checkoutDep)
checkoutDep, inherit)

@staticmethod
def parseEntries(deps, env={}, fwd=False, use=["result", "deps"],
cond=None, tools={}, checkoutDep=False):
cond=None, tools={}, checkoutDep=False, inherit=True):
"""Returns an iterator yielding all dependencies as flat list"""
# return flattened list of dependencies
return chain.from_iterable(
Recipe.Dependency.__parseEntry(dep, env, fwd, use, cond, tools,
checkoutDep)
checkoutDep, inherit)
for dep in deps )

@staticmethod
Expand Down Expand Up @@ -2311,6 +2313,21 @@ def prepare(self, inputEnv, sandboxEnabled, inputStates, inputSandbox=None,
env.setFunArgs({ "recipe" : self, "sandbox" : bool(sandbox) and sandboxEnabled,
"__tools" : tools })

thisDepEnv = depEnv
thisDepTools = depTools
thisDepDiffTools = depDiffTools
thisDepSandbox = depSandbox
thisDepDiffSandbox = depDiffSandbox
if not dep.inherit:
thisDepEnv = self.getRecipeSet().getRootEnv()
thisDepTools = Env()
# Compute the diff to remove all tools that were passed to the
# package.
thisDepDiffTools = { n : None for n in inputTools.inspect().keys() }
thisDepSandbox = None
# Clear sandbox, if any
thisDepDiffSandbox = None

recipe = env.substitute(dep.recipe, "dependency::"+dep.recipe)
resolvedDeps.append(recipe)

Expand All @@ -2319,20 +2336,17 @@ def prepare(self, inputEnv, sandboxEnabled, inputStates, inputSandbox=None,

if dep.toolOverride:
try:
thisDepTools = depTools.derive({
thisDepTools = thisDepTools.derive({
k : depTools[v] for k,v in dep.toolOverride.items() })
except KeyError as e:
raise ParseError("Cannot remap unkown tool '{}' for dependency '{}'!"
.format(e.args[0], recipe))
thisDepDiffTools = depDiffTools.copy()
thisDepDiffTools = thisDepDiffTools.copy()
thisDepDiffTools.update({
k : depDiffTools.get(v, v)
for k, v in dep.toolOverride.items() })
else:
thisDepTools = depTools
thisDepDiffTools = depDiffTools

thisDepEnv = depEnv.derive(
thisDepEnv = thisDepEnv.derive(
{ key : env.substitute(value, "depends["+recipe+"].environment["+key+"]")
for key, value in dep.envOverride.items() })

Expand All @@ -2342,11 +2356,11 @@ def prepare(self, inputEnv, sandboxEnabled, inputStates, inputSandbox=None,
raise ParseError("Recipes are cyclic (1st package in cylce)")
depStack = stack + [r.__packageName]
p, s = r.prepare(thisDepEnv, sandboxEnabled, depStates,
depSandbox, thisDepTools, depStack)
thisDepSandbox, thisDepTools, depStack)
subTreePackages.add(p.getName())
subTreePackages.update(s)
depCoreStep = p.getCorePackageStep()
depRef = CoreRef(depCoreStep, [p.getName()], thisDepDiffTools, depDiffSandbox)
depRef = CoreRef(depCoreStep, [p.getName()], thisDepDiffTools, thisDepDiffSandbox)
except ParseError as e:
e.pushFrame(r.getPackageName())
raise e
Expand All @@ -2365,7 +2379,7 @@ def prepare(self, inputEnv, sandboxEnabled, inputStates, inputSandbox=None,

# Remember dependency diffs before changing them
origDepDiffTools = thisDepDiffTools
origDepDiffSandbox = depDiffSandbox
origDepDiffSandbox = thisDepDiffSandbox

# pick up various results of package
for (n, s) in states.items():
Expand Down Expand Up @@ -3340,6 +3354,9 @@ async def getScmStatus(self):
def getBuildHook(self, name):
return self.__buildHooks.get(name)

def getRootEnv(self):
return self.__rootEnv

def getSandboxMounts(self):
return self.__sandboxOpts.get("mount", [])

Expand Down Expand Up @@ -3564,6 +3581,7 @@ def __createSchemas(self):
schema.Optional('name') : str,
schema.Optional('use') : useClauses,
schema.Optional('forward') : bool,
schema.Optional('inherit') : bool,
schema.Optional('environment') : VarDefineValidator("depends::environment"),
schema.Optional('if') : schema.Or(str, IfExpression),
schema.Optional('tools') : { toolNameSchema : toolNameSchema },
Expand Down
88 changes: 88 additions & 0 deletions test/unit/test_input_recipeset.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,94 @@ def testCheckoutDepVariants(self):

self.assertNotEqual(paVId, pbVId, "checkout steps are different")

def testDepInherit(self):
""" Test the `inherit` property for dependencies """

self.writeDefault(
{"environment" : {"DEFAULT" : "42" }})

self.writeClass("foo", """\
root: True
depends:
- name: foo
use: [tools]
forward: True
packageTools: [foo]
""")

self.writeRecipe("a", """\
inherit: [foo]
depends:
- name: sandbox
use: [sandbox]
forward: True
- name: b-env
use: [environment]
forward: true
- name: b
inherit: False
- name: c
inherit: False
environment:
C: "1"
tools:
c: foo
- d
packageScript: "true"
""")

self.writeRecipe("b-env", """\
provideVars:
B: "2"
""")

self.writeRecipe("b", """\
inherit: [foo]
packageVars: [DEFAULT, B]
packageScript: "true"
""")

self.writeRecipe("c", """\
packageVars: [C]
packageTools: [c]
packageScript: "true"
""")

self.writeRecipe("d", """\
packageVars: [B]
packageTools: [foo]
packageScript: "true"
""")

self.writeRecipe("foo", """\
packageTools: [foo]
packageScript: "true"
provideTools:
foo: .
""")

self.writeRecipe("sandbox", """\
provideSandbox:
paths: ["."]
""")

recipes = RecipeSet()
recipes.parse()
packages = recipes.generatePackages(lambda s,m: "unused", True)

pa_b = packages.walkPackagePath("a/b")
pb = packages.walkPackagePath("b")
pb_e = packages.walkPackagePath("a/b-env")
pc = packages.walkPackagePath("a/c")
pd = packages.walkPackagePath("a/d")
self.assertEqual(pa_b.getPackageStep().getVariantId(),
pb.getPackageStep().getVariantId())
self.assertEqual({"DEFAULT" : "42"}, pb.getPackageStep().getEnv())
self.assertEqual({"C" : "1"}, pc.getPackageStep().getEnv())
self.assertEqual(list(pc.getPackageStep().getTools()), ["c"])
self.assertEqual(pc.getPackageStep().getSandbox(), None)
self.assertNotEqual(pb_e.getPackageStep().getSandbox(), None)
self.assertNotEqual(pd.getPackageStep().getSandbox(), None)

class TestDependencyEnv(RecipesTmp, TestCase):
"""Tests related to "environment" block in dependencies"""
Expand Down
Loading