@@ -2214,6 +2214,15 @@ def __resolveClassesOrder(self, cls, stack, visited, isRecipe=False):
22142214 return ret
22152215
22162216 def getLayer (self ):
2217+ """Get layer to which this recipe belongs.
2218+
2219+ Returns a list of the layer hierarchy. The root layer is represented
2220+ by an empty list. If the recipe belongs to a nested layer the layers
2221+ are named from top to bottom. Example:
2222+ ``layers/foo/layers/bar/recipes/baz.yaml`` -> ``['foo', 'bar']``.
2223+
2224+ :rtype: List[str]
2225+ """
22172226 return self .__layer
22182227
22192228 def resolveClasses (self , rootEnv ):
@@ -2359,6 +2368,7 @@ def coDet(r):
23592368 self .__root = rootEnv .evaluate (self .__root , "root" )
23602369
23612370 def getRecipeSet (self ):
2371+ """Get the :class:`RecipeSet` to which the recipe belongs"""
23622372 return self .__recipeSet
23632373
23642374 def getSources (self ):
@@ -2388,15 +2398,21 @@ def isRoot(self):
23882398 return self .__root == True
23892399
23902400 def isRelocatable (self ):
2391- """Returns True if the packages of this recipe are relocatable."""
2401+ """Returns True if the packages of this recipe are relocatable.
2402+
2403+ :meta private:
2404+ """
23922405 return self .__relocatable
23932406
23942407 def isShared (self ):
23952408 return self .__shared
23962409
23972410 def jobServer (self ):
2398- """Returns True if the jobserver should be used to schedule
2399- builds for this recipe."""
2411+ """Returns True if the jobserver should be used to schedule builds for
2412+ this recipe.
2413+
2414+ :meta private:
2415+ """
24002416 return self .__jobServer
24012417
24022418 def prepare (self , inputEnv , sandboxEnabled , inputStates , inputSandbox = None ,
@@ -2972,6 +2988,10 @@ def validate(self, data):
29722988 raise schema .SchemaError (None , "Mount entry must be a string or a two/three items list!" )
29732989
29742990class RecipeSet :
2991+ """The RecipeSet corresponds to the project root directory.
2992+
2993+ It holds global information about the project.
2994+ """
29752995
29762996 BUILD_DEV_SCHEMA = schema .Schema (
29772997 {
@@ -3402,12 +3422,20 @@ def getProjectGenerators(self):
34023422 return self .__projectGenerators
34033423
34043424 def envWhiteList (self ):
3425+ """The set of all white listed environment variables
3426+
3427+ :rtype: Set[str]
3428+ """
34053429 return set (self .__whiteList )
34063430
34073431 def archiveSpec (self ):
34083432 return self .__archive
34093433
34103434 def defaultEnv (self ):
3435+ """The default environment that each root recipe inherits
3436+
3437+ :rtype: Mapping[str, str]
3438+ """
34113439 return self .__defaultEnv
34123440
34133441 def scmDefaults (self ):
@@ -3457,15 +3485,24 @@ def loadYaml(self, path, schema, default={}, preValidate=lambda x: None):
34573485 return schema [0 ].validate (default )
34583486
34593487 def parse (self , envOverrides = {}, platform = getPlatformString ()):
3460- if not os .path .isdir ("recipes" ):
3461- raise ParseError ("No recipes directory found." )
3488+ recipesRoot = ""
3489+ if os .path .isfile (".bob-project" ):
3490+ try :
3491+ with open (".bob-project" ) as f :
3492+ recipesRoot = f .read ()
3493+ except OSError as e :
3494+ raise ParseError ("Broken project link: " + str (e ))
3495+ recipesDir = os .path .join (recipesRoot , "recipes" )
3496+ if not os .path .isdir (recipesDir ):
3497+ raise ParseError ("No recipes directory found in " + recipesDir )
34623498 self .__cache .open ()
34633499 try :
3464- self .__parse (envOverrides , platform )
3500+ self .__parse (envOverrides , platform , recipesRoot )
34653501 finally :
34663502 self .__cache .close ()
3503+ self .__projectRoot = recipesRoot or os .getcwd ()
34673504
3468- def __parse (self , envOverrides , platform ):
3505+ def __parse (self , envOverrides , platform , recipesRoot = "" ):
34693506 self .__pluginPropDeps = b''
34703507 self .__pluginSettingsDeps = b''
34713508 self .__createSchemas ()
@@ -3477,7 +3514,11 @@ def __parse(self, envOverrides, platform):
34773514 os .path .join (os .path .expanduser ("~" ), '.config' )), 'bob' , 'default.yaml' ), True )
34783515
34793516 # Begin with root layer
3480- self .__parseLayer ([], "9999" )
3517+ self .__parseLayer ([], "9999" , recipesRoot )
3518+
3519+ # Out-of-tree builds may have a dedicated default.yaml
3520+ if recipesRoot :
3521+ self .__parseUserConfig ("default.yaml" , True )
34813522
34823523 # config files overrule everything else
34833524 for c in self .__configFiles :
@@ -3520,8 +3561,8 @@ def __parse(self, envOverrides, platform):
35203561 self .__rootRecipe = Recipe .createVirtualRoot (self , sorted (filteredRoots ), self .__properties )
35213562 self .__addRecipe (self .__rootRecipe )
35223563
3523- def __parseLayer (self , layer , maxVer ):
3524- rootDir = os .path .join ("" , * (os .path .join ("layers" , l ) for l in layer ))
3564+ def __parseLayer (self , layer , maxVer , recipesRoot ):
3565+ rootDir = os .path .join (recipesRoot , * (os .path .join ("layers" , l ) for l in layer ))
35253566 if not os .path .isdir (rootDir or "." ):
35263567 raise ParseError ("Layer '{}' does not exist!" .format ("/" .join (layer )))
35273568
@@ -3562,7 +3603,7 @@ def preValidate(data):
35623603 # First parse any sub-layers. Their settings have a lower precedence
35633604 # and may be overwritten by higher layers.
35643605 for l in config .get ("layers" , []):
3565- self .__parseLayer (layer + [l ], maxVer )
3606+ self .__parseLayer (layer + [l ], maxVer , recipesRoot )
35663607
35673608 # Load plugins and re-create schemas as new keys may have been added
35683609 self .__loadPlugins (rootDir , layer , config .get ("plugins" , []))
@@ -3831,6 +3872,15 @@ def sandboxFingerprints(self):
38313872 self .__sandboxFingerprints = self .getPolicy ("sandboxFingerprints" )
38323873 return self .__sandboxFingerprints
38333874
3875+ def getProjectRoot (self ):
3876+ """Get project root directory.
3877+
3878+ The project root is where the recipes, classes and layers are located.
3879+ In case of out-of-tree builds it will be distinct from the build
3880+ directory.
3881+ """
3882+ return self .__projectRoot
3883+
38343884
38353885class YamlCache :
38363886 def __if_expression_constructor (loader , node ):
0 commit comments