Skip to content

Commit

Permalink
managed layers
Browse files Browse the repository at this point in the history
  • Loading branch information
rhubert committed May 8, 2024
1 parent 421f7e7 commit 51aa0b4
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 13 deletions.
33 changes: 21 additions & 12 deletions pym/bob/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,13 @@ def getProvideDepsResolver(pattern):
else:
return VerbatimProvideDepsResolver(pattern)

def getLayerName(layerSpec):
""""Get the Name of a layer from it's config spec"""
if isinstance(layerSpec,str):
return layerSpec
else:
return list(layerSpec.keys())[0] if len(layerSpec) > 0 else ""

class Recipe(object):
"""Representation of a single recipe
Expand Down Expand Up @@ -1946,7 +1953,7 @@ def createVirtualRoot(recipeSet, roots, properties):
"buildScript" : "true",
"packageScript" : "true"
}
ret = Recipe(recipeSet, recipe, [], "", ".", "", "", properties)
ret = Recipe(recipeSet, recipe, {}, "", ".", "", "", properties)
ret.resolveClasses(Env())
return ret

Expand Down Expand Up @@ -2000,7 +2007,7 @@ def __init__(self, recipeSet, recipe, layer, sourceFile, baseDir, packageName, b
}
self.__corePackagesByMatch = []
self.__corePackagesById = {}
self.__layer = layer
self.__layer = getLayerName(layer)

sourceName = ("Recipe " if isRecipe else "Class ") + packageName + (
", layer "+"/".join(layer) if layer else "")
Expand Down Expand Up @@ -2883,6 +2890,14 @@ class RecipeSet:
schema.Optional('max_depth') : int,
})

SCM_SCHEMA = ScmValidator({
'git' : GitScm.SCHEMA,
'svn' : SvnScm.SCHEMA,
'cvs' : CvsScm.SCHEMA,
'url' : UrlScm.SCHEMA,
'import' : ImportScm.SCHEMA,
})

STATIC_CONFIG_SCHEMA = schema.Schema({
schema.Optional('bobMinimumVersion') : str, # validated separately in preValidate
schema.Optional('plugins') : [str],
Expand All @@ -2908,20 +2923,12 @@ class RecipeSet:
},
error="Invalid policy specified! Are you using an appropriate version of Bob?"
),
schema.Optional('layers') : [str],
schema.Optional('layers') : [schema.Or(str, {str : schema.Schema({"checkoutSCM" : SCM_SCHEMA})} )],
schema.Optional('scriptLanguage',
default=ScriptLanguage.BASH) : schema.And(schema.Or("bash", "PowerShell"),
schema.Use(ScriptLanguage)),
})

SCM_SCHEMA = ScmValidator({
'git' : GitScm.SCHEMA,
'svn' : SvnScm.SCHEMA,
'cvs' : CvsScm.SCHEMA,
'url' : UrlScm.SCHEMA,
'import' : ImportScm.SCHEMA,
})

MIRRORS_SCHEMA = ScmValidator({
'url' : UrlScm.MIRRORS_SCHEMA,
})
Expand Down Expand Up @@ -3437,7 +3444,9 @@ def __parse(self, envOverrides, platform, recipesRoot=""):
self.__rootRecipe = Recipe.createVirtualRoot(self, sorted(filteredRoots), self.__properties)
self.__addRecipe(self.__rootRecipe)

def __parseLayer(self, layer, maxVer, recipesRoot):
def __parseLayer(self, layerSpec, maxVer, recipesRoot):
layer = getLayerName(layerSpec)

if layer in self.__layers:
return
self.__layers.append(layer)
Expand Down
210 changes: 210 additions & 0 deletions pym/bob/layers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import argparse
import datetime
import hashlib
import os
from .errors import BuildError, ParseError
from .invoker import CmdFailedError, Invoker
from .scm import getScm
from .state import BobState
from .stringparser import Env
from .input import RecipeSet, Scm, YamlCache
from .utils import EventLoopWrapper, processDefines, INVALID_CHAR_TRANS
from .tty import DEBUG, EXECUTED, INFO, NORMAL, SKIPPED, WARNING, colorize, log, setVerbosity

class LayerStepSpec:
def __init__(self, path):
self.__path = path

@property
def workspaceWorkspacePath(self):
return self.__path

@property
def env(self):
return {}

@property
def envWhiteList(self):
return []

class Layer:
def __init__(self, name, root, recipes, yamlCache, scms=None):
self.__name = name
self.__root = root
self.__recipes = recipes
self.__yamlCache = yamlCache
self.__subLayers = []
self.__scms = scms
self.__layerDir = os.path.join(self.__root, "layers", self.__name) if len(self.__name) else self.__root

async def __checkoutTask(self, verbose):
if self.__scms is None:
return
for scm in self.__scms:
dir = scm.getProperties(False).get("dir")
layerSrcPath = os.path.join(self.__root, "layers",
self.__name)
if dir != '.':
layerSrcPath = os.path.join(layerSrcPath, dir)
invoker = Invoker(spec=LayerStepSpec(layerSrcPath),
preserveEnv=True,
noLogFiles = True,
showStdOut = verbose >= INFO,
showStdErr = verbose >= INFO,
trace = verbose >= DEBUG,
redirect=False, executor=None)
newState = {}
newState["digest"] = scm.asDigestScript(),
newState["prop"] = {k:v for k,v in scm.getProperties(False).items() if v is not None}

oldState = BobState().getDirectoryState(layerSrcPath, False)

created = False
if not os.path.isdir(layerSrcPath):
os.makedirs(layerSrcPath)
created = True

if not created \
and oldState is not None \
and oldState["digest"] == newState["digest"]:
log(colorize("CHECKOUT: Layer " +
"{} skipped (up to date)".format(layerSrcPath), SKIPPED), INFO)
continue

if not created and oldState is not None and \
newState["digest"] != oldState["digest"] and \
scm.canSwitch(Scm(oldState["prop"], Env(),
overrides=self.__recipes.scmOverrides(),
recipeSet=self.__recipes)):
ret = await invoker.executeScmSwitch(scm, oldState["prop"])

if ret == 0:
BobState().setDirectoryState(layerSrcPath, newState)
continue
ret = os.path.exists(layerSrcPath)
if os.path.exists(layerSrcPath) and not created:
atticName = datetime.datetime.now().isoformat().translate(INVALID_CHAR_TRANS) + "_" + \
self.__name
log(colorize("ATTIC: Layer " +
"{} (move to layers.attic/{})".format(layerSrcPath, atticName), WARNING), WARNING)
atticPath = os.path.join(self.__root, "layers.attic")
if not os.path.isdir(atticPath):
os.makedirs(atticPath)
atticPath = os.path.join(atticPath, atticName)
os.rename(layerSrcPath, atticPath)
BobState().setAtticDirectoryState(atticPath, layerSrcPath)

if not os.path.isdir(layerSrcPath):
os.makedirs(layerSrcPath)
await scm.invoke(invoker)
log(colorize("CHECKOUT: Layer " +
"{} .. ok".format(self.getName()), EXECUTED), INFO)
BobState().setDirectoryState(layerSrcPath, newState)

def checkout(self, verbose):
try:
with EventLoopWrapper() as (loop, executor):
j = loop.create_task(self.__checkoutTask(verbose))
loop.run_until_complete(j)
except CmdFailedError as e:
raise BuildError(f"Failed to checkoutout Layer {self.getName()}")

def getName(self):
return self.__name

def loadYaml(self, path, schema):
if os.path.exists(path):
return self.__yamlCache.loadYaml(path, schema, {}, preValidate=lambda x: None)
return {}

def parse(self):
configYaml = os.path.join(self.__layerDir, "config.yaml")
config = self.loadYaml(configYaml, (RecipeSet.STATIC_CONFIG_SCHEMA, b''))
for l in config.get('layers', []):
if not isinstance(l, dict): continue
layerScms = []
for name, scms in l.items():
for scm in scms.get("checkoutSCM"):
scm["recipe"] = configYaml
layerScms.append(Scm(scm, Env(),
overrides=self.__recipes.scmOverrides(),
recipeSet=self.__recipes))
self.__subLayers.append(Layer(name,
self.__root,
self.__recipes,
self.__yamlCache,
layerScms))

def getSubLayers(self):
return self.__subLayers

class Layers:
def __init__(self, recipes):
self.__layers = {}
self.__scmUpdate = False
self.__recipes = recipes
self.__yamlCache = YamlCache()

def __haveLayer(self, layer):
for depth,layers in self.__layers.items():
for l in layers:
if l.getName() == layer.getName():
return True
return False

def __collect(self, depth, verbose):
self.__layers[depth+1] = []
newLevel = False
for l in self.__layers[depth]:
if self.__scmUpdate:
l.checkout(verbose)
l.parse()
for subLayer in l.getSubLayers():
if not self.__haveLayer(subLayer):
self.__layers[depth+1].append(subLayer)
newLevel = True
if newLevel:
self.__collect(depth + 1, verbose)

def collect(self, verbose):
self.__yamlCache.open()
try:
rootLayers = Layer("", os.getcwd(), self.__recipes, self.__yamlCache)
rootLayers.parse()
self.__layers[0] = rootLayers.getSubLayers();
self.__collect(0, verbose)
finally:
self.__yamlCache.close()

def setUpdateMode(self, onoff):
self.__scmUpdate = onoff

def doLayers(argv, bobRoot):

parser = argparse.ArgumentParser(prog="bob layers", description='Handle layers')
parser.add_argument('action', type=str, choices=['update', 'status'], default="status",
help="Action: [update, status]")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
parser.add_argument('-v', '--verbose', default=NORMAL, action='count',
help="Increase verbosity (may be specified multiple times)")
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
args = parser.parse_args(argv)

setVerbosity(args.verbose)

defines = processDefines(args.defines)

recipes = RecipeSet()
recipes.setConfigFiles(args.configFile)
try:
recipes.parse(defines)
except ParseError:
pass

layers = Layers(recipes)
if args.action == "update":
layers.setUpdateMode(True)
layers.collect(args.verbose)

6 changes: 6 additions & 0 deletions pym/bob/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ def __ls(*args, **kwargs):
doLS(*args, **kwargs)
return 0

def __layers(*args, **kwargs):
from .layers import doLayers
doLayers(*args, **kwargs)
return 0

def __project(*args, **kwargs):
from .cmds.build.project import doProject
doProject(*args, **kwargs)
Expand Down Expand Up @@ -111,6 +116,7 @@ def __jenkinsExecute(*args, **kwargs):
"help" : ('hl', __help, "Display help information about command"),
"init" : ('hl', __init, "Initialize build tree"),
"jenkins" : ('hl', __jenkins, "Configure Jenkins server"),
"layers" : ('hl', __layers, "Handle layers"),
"ls" : ('hl', __ls, "List package hierarchy"),
"project" : ('hl', __project, "Create project files"),
"show" : ('hl', __show, "Show properties of a package"),
Expand Down
2 changes: 1 addition & 1 deletion test/black-box/commands/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ for cmd, (hl, func, help) in sorted(availableCommands.items()):
echo "$cmds"
for c in $cmds; do
case "$c" in
archive | init | jenkins | help | _*)
archive | init | jenkins | layers | help | _*)
;;
clean)
run_bob $c -DBAR=1 -c testconfig
Expand Down

0 comments on commit 51aa0b4

Please sign in to comment.