Example #1
0
def include(file):
	""" Parse and register the targets and properties in in the specified 
	``XXX.xpybuild.py`` file. 
	
	Targets should only be defined in files included using this method, 
	not using python import statements. 
	
	@param file: a path relative to the directory containing this file. 
	"""

	from xpybuild.buildcontext import getBuildInitializationContext
	from xpybuild.utils.buildfilelocation import BuildFileLocation

	file = getBuildInitializationContext().expandPropertyValues(file)

	assert file.endswith('.xpybuild.py') # enforce recommended naming convention
	
	filepath = getBuildInitializationContext().getFullPath(file, os.path.dirname(BuildFileLocation._currentBuildFile[-1]))
	
	BuildFileLocation._currentBuildFile.append(filepath) # add to stack of files being parsed
	
	namespace = {}
	exec(compile(open(filepath, "rb").read(), filepath, 'exec'), namespace, namespace)
	
	del BuildFileLocation._currentBuildFile[-1]
	
	return namespace
Example #2
0
def registerBuildLoadPostProcessor(fn):
	""" Add a callback function that will be invoked just after the build files have been parsed and all targets added, 
	to allow for custom post processing logic to execute before the clean/build. 
	
	This is useful for performing global customizations of targets throughout the build, such as adding tags or 
	additional target options. For example, to override the default retry setting and add a tag to all defined targets 
	of a particular Python class::
	
		def demoPostProcessor(context):
			for target in context.targets().values():
				if isinstance(target, MyCustomTarget) or 'FooBar' in target.name: 
					# Override options for these targets
					target.option(BaseTarget.Options.failureRetries, 2)
					
					# Add additional tags for these targets
					target.tags('my-post-processed-tag')

		registerBuildLoadPostProcessor(demoPostProcessor)

	:param Callable[xpybuild.buildcontext.BuildInitializationContext] fn: A function that takes a context as its argument.
	You may wish to use ``BuildInitializationContext`` methods such as `xpybuild.buildcontext.BuildInitializationContext.targets` from your function. 
	"""
	from xpybuild.buildcontext import getBuildInitializationContext
	if not fn: return
	getBuildInitializationContext()._buildParsePostProcessors.append(fn)
Example #3
0
def defineAtomicTargetGroup(*targets):
	""" The given targets must all be built before anything which depends on any of those targets.
	
	Returns the flattened list of targets. 
	"""
	
	from xpybuild.buildcontext import getBuildInitializationContext
	targets = flatten(targets)
	getBuildInitializationContext().defineAtomicTargetGroup(targets)
	return targets
Example #4
0
def registerPreBuildCheck(fn):
	""" Defines a check which will be called after any clean but before any build actions take place, to provide fail-fast 
	behaviour if something is wrong.

	See also `registerBuildLoadPostProcessor`. 
	
	:param Callable[xpybuild.buildcontext.BuildInitializationContext] fn: A function that takes a context as its argument, 
	and raises a BuildException if the check fails.

	"""
	from xpybuild.buildcontext import getBuildInitializationContext
	getBuildInitializationContext().registerPreBuildCheck(fn)
Example #5
0
    def clearTags(self):
        """Called by build file authors to removes all tags other than ``all`` from this target.
		
		See `tag`. 

		:return: Returns the same target instance it was called on, to permit fluent calling. 
		"""
        init = getBuildInitializationContext()
        init.removeFromTags(self, self.__tags)
        self.__tags = ['full'] if 'full' in self.__tags else []
        init.registerTags(self, self.__tags)
        return self
Example #6
0
    def disableInFullBuild(self):
        """Called by build file authors to configure this target to not build in ``all`` mode, so that it will only 
		be built if the target name or tag is specified on the command line (or if pulled in by a dependency).
		
		This is useful for targets that perform operations such as configuring developer IDEs which would not be 
		needed in the main build, or for expensive parts of the build that are often not needed such as generation 
		of installers. 
		
		See also `tag`. 

		:return: Returns the same target instance it was called on, to permit fluent calling. 

		"""
        self.__tags = list(set(self.__tags) - {'full'})
        init = getBuildInitializationContext()
        init.removeFromTags(self, ['full'])
        return self
Example #7
0
    def tags(self, *tags: str):
        """Called by build file authors to append one or more tags to this target to make groups of related targets 
		easier to build (or just to provide a shorter alias for the target on the command line).

		@param tags: The tag, tags or list of tags to add to the target.
		
		:return: Returns the same target instance it was called on, to permit fluent calling. 

		>>> BaseTarget('a',[]).tags('abc').getTags()
		<using test initialization context> <using test initialization context> ['abc', 'full']
		>>> BaseTarget('a',[]).tags(['abc', 'def']).getTags()
		<using test initialization context> <using test initialization context> ['abc', 'def', 'full']
		>>> BaseTarget('a',[]).tags('abc', 'def').tags('ghi').getTags()
		<using test initialization context> <using test initialization context> <using test initialization context> ['ghi', 'abc', 'def', 'full']
		"""
        taglist = getStringList(list(tags))
        self.__tags = taglist + self.__tags
        assert sorted(list(set(self.__tags))) == sorted(list(
            self.__tags))  # check for duplicates
        init = getBuildInitializationContext()
        if init:
            init.registerTags(self,
                              taglist)  # init will be None during doctests
        return self
Example #8
0
    def __init__(self, name, dependencies):
        self.__getAttrImpl = {
            'path':
            lambda: self.__returnOrRaiseIfNone(
                self.__path,
                'Target path has not yet been resolved by this phase of the build process: %s'
                % self),
            'name':
            lambda: self.__name,
            'options':
            lambda: self.__returnOrRaiseIfNone(
                self.__optionsResolved,
                "Cannot read the value of basetarget.targetOptions during the initialization phase of the build as the resolved option values are not yet available"
            ),
            'workDir':
            lambda: self.__workDir,
            'type':
            lambda: self.__class__.__name__,
            'baseDir':
            lambda: self.location.buildDir,
        }

        self.__optionsTargetOverridesUnresolved = {
        }  # for target-specific option overrides. for internal use (by buildcontext), do not use
        self.__optionsResolved = None  # gets assigned during end of initialization phase

        if isinstance(name, str):
            if '//' in name:
                raise BuildException(
                    'Invalid target name: double slashes are not permitted: %s'
                    % name)
            if '\\' in name:
                raise BuildException(
                    'Invalid target name: backslashes are not permitted: %s' %
                    name)
        self.__name = BaseTarget._normalizeTargetName(str(name))
        self.__path_src = name
        self.__tags = ['full']
        self.__priority = 0.0  # default so we can go bigger or smaller
        self.log = logging.getLogger(self.__class__.__name__)

        # put the class first, since it results in better ordering (e.g. for errors)
        # use a space to delimit these to make it easier to copy to the clipboard by double-clicking
        self.__stringvalue = f'<{self.type}> {self.name}'

        init = getBuildInitializationContext()
        if not init:  # doc-test mode
            self.location = BuildFileLocation(raiseOnError=False)
        else:
            self.location = BuildFileLocation(raiseOnError=True)
            init.registerTarget(self)  # this can throw

        # should ensure changes to the build file cause a rebuild? probs no need
        # PathSet will perform all necessary flattening etc
        self.__dependencies = PathSet(dependencies)

        self.__path = None  # set by _resolveTargetPath
        self.__workDir = None

        self.__registeredImplicitInputs = []

        # aliases for pre-3.0
        self.addHashableImplicitInputOption = self.registerImplicitInputOption
        self.addHashableImplicitInput = self.registerImplicitInput