Skip to content

Commit

Permalink
input: add 'inherit' dependency property
Browse files Browse the repository at this point in the history
This property controls the inheritance of environment, tools and sandbox
for a dependency. It defaults to true so everything is inherited.

If set to `false` environment, tools and sandbox are dropped. The
dependency starts with the default-environment, no tools and no sandbox.

This becomes particularly useful when an existing root-package should
become a dependency of another root-package, e.g. for building an
installer.
  • Loading branch information
rhubert committed May 24, 2024
1 parent cc29acc commit ff60e4e
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 16 deletions.
9 changes: 9 additions & 0 deletions doc/manual/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,15 @@ The following settings are supported:
| | | At the dependency both names will refer to the same |
| | | tool. |
+-------------+-----------------+-----------------------------------------------------+
| inherit | Boolean | Inherit environment and tools available for this |
| | | dependency. |
| | | When set to "false" all the environment and tools |
| | | are dropped. This is mostly useful to make |
| | | an existing root-package become a dependency |
| | | of another root package. |
| | | |
| | | Default: "true" |
+-------------+-----------------+-----------------------------------------------------+

.. _configuration-recipes-env:

Expand Down
52 changes: 36 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 @@ -2308,7 +2310,21 @@ def prepare(self, inputEnv, sandboxEnabled, inputStates, inputSandbox=None,
resolvedDeps = []

for dep in self.__deps:
env.setFunArgs({ "recipe" : self, "sandbox" : bool(sandbox) and sandboxEnabled,
thisDepEnv = depEnv
thisDepTools = depTools
thisDepDiffTools = depDiffTools
thisSandbox = sandbox
thisSandboxEnabled = sandboxEnabled
thisDepDiffSandbox = depDiffSandbox
if not dep.inherit:
thisDepEnv = self.getRecipeSet().getRootEnv()
thisDepTools = Env()
thisDepDiffTools = {}
thisDepDiffSandbox = ...
thisSandbox = None
thisSandboxEnabled = False

env.setFunArgs({ "recipe" : self, "sandbox" : bool(thisSandbox) and thisSandboxEnabled,
"__tools" : tools })

recipe = env.substitute(dep.recipe, "dependency::"+dep.recipe)
Expand All @@ -2319,20 +2335,20 @@ 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
thisDepTools = thisDepTools
thisDepDiffTools = thisDepDiffTools

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

Expand All @@ -2341,12 +2357,12 @@ def prepare(self, inputEnv, sandboxEnabled, inputStates, inputSandbox=None,
if r.__packageName in stack:
raise ParseError("Recipes are cyclic (1st package in cylce)")
depStack = stack + [r.__packageName]
p, s = r.prepare(thisDepEnv, sandboxEnabled, depStates,
p, s = r.prepare(thisDepEnv, thisSandboxEnabled, depStates,
depSandbox, 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 +2381,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 +3356,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 +3583,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

0 comments on commit ff60e4e

Please sign in to comment.