Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
rhubert committed Apr 21, 2024
1 parent cda3539 commit 2651bcd
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 14 deletions.
35 changes: 21 additions & 14 deletions pym/bob/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -2883,6 +2883,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 +2916,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, schema.Schema({str : 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 @@ -2960,6 +2960,7 @@ def __init__(self):
self.__commandConfig = {}
self.__uiConfig = {}
self.__shareConfig = {}
self.__parseableLayers = []
self.__policies = {
'noUndefinedTools' : (
"0.17.3.dev57",
Expand Down Expand Up @@ -3393,7 +3394,7 @@ def __parse(self, envOverrides, platform, recipesRoot=""):
os.path.join(os.path.expanduser("~"), '.config')), 'bob', 'default.yaml'))

# Begin with root layer
self.__parseLayer([], "9999", recipesRoot)
self.__parseLayer("", "9999", recipesRoot)

# Out-of-tree builds may have a dedicated default.yaml
if recipesRoot:
Expand Down Expand Up @@ -3432,14 +3433,20 @@ def __parse(self, envOverrides, platform, recipesRoot=""):

filteredRoots = [ root for root in rootRecipes
if (len(self.__rootFilter) == 0) or checkGlobList(root, maybeGlob(self.__rootFilter)) ]

# create virtual root package
self.__rootRecipe = Recipe.createVirtualRoot(self, sorted(filteredRoots), self.__properties)
self.__addRecipe(self.__rootRecipe)

def addParseableLayer(self, layer):
self.__parseableLayers.append(layer)

def __parseLayer(self, layer, maxVer, recipesRoot):
rootDir = os.path.join(recipesRoot, *(os.path.join("layers", l) for l in layer))
if len(self.__parseableLayers) == 0 or not layer in self.__parseableLayers:
return
rootDir = os.path.join(recipesRoot, "layers", layer)
if not os.path.isdir(rootDir or "."):
raise ParseError("Layer '{}' does not exist!".format("/".join(layer)))
raise ParseError(f"Layer '{layer}' does not exist!")

configYaml = os.path.join(rootDir, "config.yaml")
def preValidate(data):
Expand All @@ -3457,7 +3464,7 @@ def preValidate(data):
preValidate=preValidate)
minVer = config.get("bobMinimumVersion", "0.16")
if compareVersion(maxVer, minVer) < 0:
raise ParseError("Layer '{}' reqires a higher Bob version than root project!"
raise ParseError("Layer '{}' requires a higher Bob version than root project!"
.format("/".join(layer)))
if compareVersion(minVer, "0.16") < 0:
raise ParseError("Projects before bobMinimumVersion 0.16 are not supported!")
Expand All @@ -3480,7 +3487,7 @@ def preValidate(data):
# First parse any sub-layers. Their settings have a lower precedence
# and may be overwritten by higher layers.
for l in config.get("layers", []):
self.__parseLayer(layer + [l], maxVer, recipesRoot)
self.__parseLayer(l, maxVer, recipesRoot)

# Load plugins and re-create schemas as new keys may have been added
self.__loadPlugins(rootDir, layer, config.get("plugins", []))
Expand Down
171 changes: 171 additions & 0 deletions pym/bob/layers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import argparse
import hashlib
import os
from .invoker import Invoker
from .scm import getScm
from .state import BobState
from .stringparser import Env
from .input import RecipeSet, Scm, YamlCache
from .utils import EventLoopWrapper, processDefines
from .tty import NORMAL, stepExec

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

async def __checkoutTask(self):
for scm in self.__scms:
layerSrcPath = os.path.join(self.__root, "layers",
self.__name, scm.getProperties(False).get("dir", ""))
invoker = Invoker(spec=LayerStepSpec(layerSrcPath),
preserveEnv=True,
noLogFiles=True,
showStdOut=True, showStdErr=True,
trace=False, redirect=False, executor=None)
created = False
if not os.path.isdir(layerSrcPath):
os.makedirs(layerSrcPath)
created = True

newState = {}
newState["digest"] = scm.asDigestScript(),
newState["prop"] = scm.getProperties(False)
oldState = BobState().getDirectoryState(layerSrcPath, False)
runInvoke=True
if not created and oldState is not None and \
newState != oldState and \
scm.canSwitch(Scm(oldState["prop"], Env(),
overrides=self.__recipes.scmOverrides(),
recipeSet=self.__recipes)):
ret = await invoker.executeScmSwitch(scm, oldState["prop"])

if ret == 0:
runInvoke = False
if runInvoke:
# inline switch failed
await scm.invoke(invoker)
BobState().setDirectoryState(layerSrcPath, newState)

def checkout(self):
with EventLoopWrapper() as (loop, executor):
j = loop.create_task(self.__checkoutTask())
loop.run_until_complete(j)

def getName(self):
return self.__name

def getScmSpec(self):
return self.__scmSpec

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.__root, "config.yaml")
config = self.loadYaml(configYaml, (RecipeSet.STATIC_CONFIG_SCHEMA, b''))
for l in config.get('layers', []):
layerScms = []
for name, scms in l.items():
for scm in scms:
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.__recipes = recipes
self.__yamlCache = YamlCache()

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

def __handleLayer(self, layer):
for l in layer.getSubLayers():
if not self.__haveLayer(layer):
self.__layers.append(layer)
l.checkout()
self.__recipes.addParseableLayer(l.getName())
l.parse()
for l1 in layer.getSubLayers():
self.__handleLayer(l1)

def __collect(self):
# parse config of bobRoot -> add Layers for each entry
recipesRoot = Layer("", os.getcwd(), self.__recipes, self.__yamlCache)

recipesRoot.parse()
for l in recipesRoot.getSubLayers():
self.__layers.append(l)
l.checkout()
self.__recipes.addParseableLayer(l.getName())
l.parse()
self.__handleLayer(l)

def collect(self):
self.__yamlCache.open()
try:
self.__collect()
finally:
self.__yamlCache.close()

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)

defines = processDefines(args.defines)

# parse without failing due to missing layers.
# this is neccessary to set the scm relevant policies.
recipes = RecipeSet()
recipes.addParseableLayer([])
recipes.setConfigFiles(args.configFile)
recipes.parse(defines)

layers = Layers(recipes)
layers.collect()

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

0 comments on commit 2651bcd

Please sign in to comment.