def addDefaultTarget(self, target): """Add a target to be built when the default target for this script is built. @param target: The target to be built. @type target: L{Target} or L{AsyncResult} that yields a L{Target} """ Script.getCurrent().getDefaultTarget().addTarget(target)
def execute(self): """Executing a script is shorthand for adding it's default targets to your default targets. This means it will only actually run if the current script's default target is built. """ Script.getCurrent().getDefaultTarget().addTarget( self._script.getDefaultTarget())
def _include(self, path): """Include another script for execution within this script's context. A script will only be included once within a given context. @param path: The path of the file to include. @type path: string """ path = os.path.normpath(path) normalisedPath = os.path.normcase(self.configuration.abspath(path)) if normalisedPath in self._included: return currentScript = Script.getCurrent() includedScript = Script( path=path, variant=currentScript.variant, engine=currentScript.engine, configuration=currentScript.configuration, task=currentScript.task, tools=currentScript.tools, parent=currentScript, ) self._included[normalisedPath] = includedScript try: includedScript.execute() except IOError, e: currentScript.engine.raiseError( ("Failed to include cake script %s: %s\n" % (path, str(e))) + "".join(" from " + s.path + "\n" for s in currentScript.getAncestors()))
def setResult(self, **kwargs): """Export a result from this script that other scripts can import. Other scripts can use getResult(script, name) to get the result exported by the other script calling setResult(name=result). """ Script.getCurrent().setResult(**kwargs)
def addDefaultTargets(self, targets): """Add a collection of targets to the default script target. The default script target is the target that is built when the user executes the current build script without specifying a particular target name. @param targets: A collection of targets to be built. @type targets: A list of L{Target} or L{AsyncResult} yielding a list of L{Target} """ Script.getCurrent().getDefaultTarget().addTargets(targets)
def addTarget(self, name, target): """Add a target to the named script target. The specified target will be built whenever the named script target is built. @param name: The name of the script-target to add the target to. @type name: C{str} @param target: A target to build when the named script-target is built. @type target: L{Target} or L{AsyncResult} yielding a L{Target} """ Script.getCurrent().getTarget(name).addTarget(target)
def _run(targets, sources, cwd): if self.enabled: task = engine.createTask(lambda t=targets, s=sources, c=cwd: spawnProcess(t, s, c)) task.lazyStartAfter(getTasks(sources)) else: task = None if targets: targets = [FileTarget(path=t, task=task) for t in targets] Script.getCurrent().getDefaultTarget().addTargets(targets) return targets else: target = Target(task) Script.getCurrent().getDefaultTarget().addTarget(target) return target
def __setattr__(self, key, value): from cake.script import Script script = Script.getCurrent() if script is None: raise AttributeError("No current script.") else: script.tools[key] = value
def getTarget(self, name): """Get the named ScriptTarget for this script. @param name: The name of the target to get. @type name: C{str} """ return Script.getCurrent().getTarget(name)
def _run(targets, sources, cwd): if self.enabled: task = engine.createTask( lambda t=targets, s=sources, c=cwd: spawnProcess(t, s, c)) task.lazyStartAfter(getTasks(sources)) else: task = None if targets: targets = [FileTarget(path=t, task=task) for t in targets] Script.getCurrent().getDefaultTarget().addTargets(targets) return targets else: target = Target(task) Script.getCurrent().getDefaultTarget().addTarget(target) return target
def execute(self, scripts, **keywords): """Execute another script as a background task. Executes the other script using the current script's configuration but potentially a different build variant. If you need to execute a script using a different configuration then use the 'executeNoContext' method instead. @param scripts: A path or sequence of paths of scripts to execute. @type scripts: string or sequence of string @return: A Script object or sequence of Script objects that can be used to determine what scripts will be executed. The script's task will complete when the script has finished executing. @rtype: L{Script} or C{list} of L{Script} """ basePath = self.configuration.basePath scripts = basePath(scripts) script = Script.getCurrent() configuration = script.configuration variant = configuration.findVariant(keywords, baseVariant=script.variant) execute = configuration.execute def _execute(path): script = execute(path, variant) self.addDefaultTarget(script.getDefaultTarget()) return ScriptProxy(script) if isinstance(scripts, basestring): return _execute(scripts) else: return [_execute(path) for path in scripts]
def createTask(self, func=None): """Construct a new task that will call the specified function. This function wraps the function in an exception handler that prints out the stacktrace and exception details if an exception is raised by the function. @param func: The function that will be called with no args by the task once the task has been started. @type func: any callable @return: The newly created Task. @rtype: L{Task} """ if func is None: return cake.task.Task() # Save the script that created the task so that the task # inherits that same script when executed. currentScript = _Script.getCurrent() def _wrapper(): if self.maximumErrorCount and self.errorCount >= self.maximumErrorCount: # TODO: Output some sort of message saying the build is being terminated # because of too many errors. But only output it once. Perhaps just set # a flag and check that in the runner. raise BuildError() try: # Restore the old script oldScript = _Script.getCurrent() _Script._current.value = currentScript try: return func() finally: _Script._current.value = oldScript except BuildError: # Assume build errors have already been reported raise except Exception, e: tbs = [traceback.extract_tb(sys.exc_info()[2])] t = task while t is not None: tb = getattr(t, "traceback", None) if tb is not None: tbs.append(t.traceback) t = t.parent tracebackString = ''.join( ''.join(traceback.format_list(tb)) for tb in reversed(tbs) ) exceptionString = ''.join(traceback.format_exception_only(e.__class__, e)) message = 'Unhandled Task Exception:\n%s%s' % (tracebackString, exceptionString) if not self.logger.debugEnabled("stack"): message += "Pass '--debug=stack' if you require a more complete stack trace.\n" self.logger.outputError(message) self.errors.append(message) raise
def _makeTarget(results): if self.enabled: task = self.engine.createTask( lambda: _start(program, results, self.dependencies)) task.lazyStart() else: task = None if results: target = FileTarget(path=results, task=task) else: target = Target(task=task) Script.getCurrent().getDefaultTarget().addTarget(target) Script.getCurrent().getTarget('testresults').addTarget(target) return target
def _getTaskFactory(): # If called from within a Script we use Engine.createTask # so that the Script.getCurrent() context is flowed across # sub-tasks from the creator of the task. from cake.script import Script currentScript = Script.getCurrent() if currentScript is not None: return currentScript.engine.createTask else: return Task
def __getattribute__(self, key): from cake.script import Script script = Script.getCurrent() if script is None: raise AttributeError("No current script.") else: try: return script.tools[key] except KeyError: raise AttributeError("No such tool '%s'" % key)
def run(sourceDir): sources = set( cake.filesys.walkTree( path=abspath(sourceDir), recursive=recursive, includeMatch=includeMatch, )) if removeStale: targets = set( cake.filesys.walkTree(path=abspath(targetDir), recursive=recursive)) oldFiles = targets.difference(sources) removeTask = self.engine.createTask( lambda f=oldFiles: doDelete(f)) removeTask.lazyStart() else: removeTask = None results = [] for source in sources: sourcePath = cake.path.join(sourceDir, source) targetPath = cake.path.join(targetDir, source) if cake.path.isDir(abspath(sourcePath)): if self.enabled: dirTask = self.engine.createTask( lambda t=targetPath: doMakeDir(t)) dirTask.completeAfter(removeTask) dirTask.lazyStart() else: dirTask = None results.append(DirectoryTarget(path=source, task=dirTask)) else: fileTarget = self._copyFile(source=sourcePath, target=targetPath, onlyNewer=onlyNewer) if fileTarget.task: fileTarget.task.completeAfter(removeTask) results.append(fileTarget) Script.getCurrent().getDefaultTarget().addTargets(results) return results
def get(self, script, keywords={}, useContext=None, configScript=None, configScriptName=None): """Get another script to use in referencing targets. @param script: Path of the script to load. @type script: string @param keywords: A set of keywords used to find the variant the script will be executed with. The variant is looked up in the script's configuration. @type keywords: dictionary of string -> string @param useContext: If False or if None and either configScript or configScriptName are not None then lookup the corresponding configuration script starting from the script's path, if True then use the current configuration/variant. @type useContext: bool or None @param configScript: The path of the configuration script to use to execute the script. Ignored if useContext is True. @type configScript: string or None @param configScriptName: If not None and configScript is None then find the configuration script with this name starting the search at the script's path. Ignored if useContext is True. @type configScriptName: string or None """ if not isinstance(script, basestring): raise ValueError("'script' must be a string") script = self.configuration.basePath(script) if useContext is None: useContext = configScript is None and configScriptName is None if useContext: # Use the current configuration and lookup the variant relative # to the current variant. baseVariant = Script.getCurrent().variant variant = self.configuration.findVariant(keywords, baseVariant=baseVariant) return ScriptProxy(self.configuration.execute(path=script, variant=variant)) else: # Re-evaluate the configuration to execute the script with. # Uses the keywords specified to find the variant in the variants # defined in that configuration. path = self.configuration.abspath(script) if configScript is None: configuration = self.engine.findConfiguration( path=path, configScriptName=configScriptName, ) else: configuration = self.engine.getConfiguration( path=self.configuration.abspath(configScript), ) variant = configuration.findVariant(keywords) return ScriptProxy(configuration.execute(path=path, variant=variant))
def run(source): if self.enabled: sourceTask = getTask(source) copyTask = self.engine.createTask(doCopy) copyTask.lazyStartAfter(sourceTask) else: copyTask = None fileTarget = FileTarget(path=target, task=copyTask) currentScript = Script.getCurrent() currentScript.getDefaultTarget().addTarget(fileTarget) return fileTarget
def __getattribute__(self, name): """Return a variant keywords current value given its name. @param name: The name of the keyword to query. @type name: string @return: The current value of the named keyword. @rtype: string """ try: return Tool.__getattribute__(self, name) except AttributeError: try: return Script.getCurrent().variant.keywords[name] except KeyError: raise AttributeError("Unknown attribute '%s'" % name)
def cwd(self, *args): """Return the path prefixed with the this script's directory. Examples:: env.cwd("a") -> "{cwd}/a" env.cwd(["a", "b", "c"]) -> ["{cwd}/a", "{cwd}/b", "{cwd}/c"] @param args: The arguments that need to have the prefix added. @type args: string or list(string) @return: The path prefixed with this script's directory. @rtype: string or list(string) """ script = Script.getCurrent() return script.cwd(*args)
def run(sourceDir): sources = set(cake.filesys.walkTree( path=abspath(sourceDir), recursive=recursive, includeMatch=includeMatch, )) if removeStale: targets = set(cake.filesys.walkTree(path=abspath(targetDir), recursive=recursive)) oldFiles = targets.difference(sources) removeTask = self.engine.createTask(lambda f=oldFiles: doDelete(f)) removeTask.lazyStart() else: removeTask = None results = [] for source in sources: sourcePath = cake.path.join(sourceDir, source) targetPath = cake.path.join(targetDir, source) if cake.path.isDir(abspath(sourcePath)): if self.enabled: dirTask = self.engine.createTask(lambda t=targetPath: doMakeDir(t)) dirTask.completeAfter(removeTask) dirTask.lazyStart() else: dirTask = None results.append(DirectoryTarget(path=source, task=dirTask)) else: fileTarget = self._copyFile(source=sourcePath, target=targetPath, onlyNewer=onlyNewer) if fileTarget.task: fileTarget.task.completeAfter(removeTask) results.append(fileTarget) Script.getCurrent().getDefaultTarget().addTargets(results) return results
def _wrapper(): if self.maximumErrorCount and self.errorCount >= self.maximumErrorCount: # TODO: Output some sort of message saying the build is being terminated # because of too many errors. But only output it once. Perhaps just set # a flag and check that in the runner. raise BuildError() try: # Restore the old script oldScript = _Script.getCurrent() _Script._current.value = currentScript try: return func() finally: _Script._current.value = oldScript except BuildError: # Assume build errors have already been reported raise except Exception, e: tbs = [traceback.extract_tb(sys.exc_info()[2])] t = task while t is not None: tb = getattr(t, "traceback", None) if tb is not None: tbs.append(t.traceback) t = t.parent tracebackString = ''.join( ''.join(traceback.format_list(tb)) for tb in reversed(tbs) ) exceptionString = ''.join(traceback.format_exception_only(e.__class__, e)) message = 'Unhandled Task Exception:\n%s%s' % (tracebackString, exceptionString) if not self.logger.debugEnabled("stack"): message += "Pass '--debug=stack' if you require a more complete stack trace.\n" self.logger.outputError(message) self.errors.append(message) raise
def getDefaultTarget(self): return Script.getCurrent().getDefaultTarget()
def execute(self, path, variant): """Execute a build script. Uses this configuration with specified build variant. @param path: Path of the build script. @param variant: The variant to execute the script with. @return: The Script object representing the script that will be executed. Use the returned script's .task to wait for the script to finish executing. """ absPath = self.abspath(path) if cake.filesys.isDir(absPath): absPath = cake.path.join(absPath, self.defaultBuildScriptName) absPath = os.path.normpath(absPath) path = cake.path.relativePath(absPath, self.baseDir) key = (os.path.normcase(path), variant) currentScript = _Script.getCurrent() if currentScript: currentVariant = currentScript.variant currentConfiguration = currentScript.configuration else: currentVariant = None currentConfiguration = None # Make sure the variant is constructed and ready for use. variant._construct(self) self._executedLock.acquire() try: script = self._executed.get(key, None) if script is None: tools = {} for name, tool in variant.tools.items(): tools[name] = tool.clone() def execute(): if self is not currentConfiguration: self.engine.logger.outputInfo("Building with %s - %s\n" % (self.path, variant)) elif variant is not currentVariant: self.engine.logger.outputInfo("Building with %s\n" % str(variant)) self.engine.logger.outputDebug( "script", "Executing %s\n" % script.path, ) script.execute() task = self.engine.createTask(execute) script = _Script( path=path, configuration=self, variant=variant, task=task, tools=tools, engine=self.engine, ) self._executed[key] = script task.addCallback( lambda: self.engine.logger.outputDebug( "script", "Finished %s\n" % script.path, ) ) task.lazyStart(threadPool=self.engine.scriptThreadPool) finally: self._executedLock.release() return script
from cake.library.env import EnvironmentTool from cake.library.filesys import FileSystemTool from cake.library.logging import LoggingTool from cake.library.project import ProjectTool from cake.library.script import ScriptTool from cake.library.shell import ShellTool from cake.library.variant import VariantTool from cake.library.zipping import ZipTool from cake.script import Script import cake.path import cake.system platform = cake.system.platform().lower() hostArchitecture = cake.system.architecture().lower() configuration = Script.getCurrent().configuration engine = Script.getCurrent().engine # Override the configuration basePath() function. def basePath(value): from cake.tools import script, env @waitForAsyncResult def _basePath(path): if isinstance(path, basestring): path = env.expand(path) if path.startswith("#"): if path[1] in '\\/': # Keep project paths relative but remove slashes. return path[2:] else: return path[1:]
def dir(self): """The path of the directory of the currently executing script. """ return Script.getCurrent().dir
def variant(self): """The Variant the currently executing script is being built with. """ return Script.getCurrent().variant
def run(self, func, args=None, targets=None, sources=[]): """Execute the specified python function as a task. Only executes the function after the sources have been built and only if the target exists, args is the same as last run and the sources haven't changed. @note: I couldn't think of a better class to put this function in so for now it's here although it doesn't really belong. """ engine = self.engine configuration = self.configuration basePath = configuration.basePath targets = basePath(targets) sources = basePath(sources) def _run(): sourcePaths = getPaths(sources) if targets: buildArgs = (args, sourcePaths) try: _, reason = configuration.checkDependencyInfo( targets[0], buildArgs, ) if reason is None: # Up to date return engine.logger.outputDebug( "reason", "Building '%s' because '%s'\n" % (targets[0], reason), ) except EnvironmentError: pass try: result = func() except Exception: if targets: append = engine.failedTargets.append for t in targets: append(t) raise if targets: newDependencyInfo = configuration.createDependencyInfo( targets=targets, args=buildArgs, dependencies=sourcePaths, ) configuration.storeDependencyInfo(newDependencyInfo) return result if self.enabled: task = engine.createTask(_run) task.lazyStartAfter(getTask(sources)) else: task = None currentScript = Script.getCurrent() if targets is not None: targets = [FileTarget(path=t, task=task) for t in targets] currentScript.getDefaultTarget().addTargets(targets) return targets else: target = Target(task) currentScript.getDefaultTarget().addTarget(target) return target
newDependencyInfo = configuration.createDependencyInfo( targets=[target], args=buildArgs, dependencies=[], ) configuration.storeDependencyInfo(newDependencyInfo) def _run(): try: return _compress() except BuildError: raise except Exception, e: msg = "cake: Error creating %s: %s\n" % (target, str(e)) engine.raiseError(msg, targets=[target]) if self.enabled: task = engine.createTask(_run) task.lazyStartAfter(getTask(source)) else: task = None fileTarget = FileTarget(path=target, task=task) currentScript = Script.getCurrent() currentScript.getDefaultTarget().addTarget(fileTarget) currentScript.getTarget( cake.path.baseName(target)).addTarget(fileTarget) return fileTarget
# Now that the zip has been written successfully, save the new dependency file newDependencyInfo = configuration.createDependencyInfo( targets=[target], args=buildArgs, dependencies=[], ) configuration.storeDependencyInfo(newDependencyInfo) def _run(): try: return _compress() except BuildError: raise except Exception, e: msg = "cake: Error creating %s: %s\n" % (target, str(e)) engine.raiseError(msg, targets=[target]) if self.enabled: task = engine.createTask(_run) task.lazyStartAfter(getTask(source)) else: task = None fileTarget = FileTarget(path=target, task=task) currentScript = Script.getCurrent() currentScript.getDefaultTarget().addTarget(fileTarget) currentScript.getTarget(cake.path.baseName(target)).addTarget(fileTarget) return fileTarget
def get(self, script, keywords={}, useContext=None, configScript=None, configScriptName=None): """Get another script to use in referencing targets. @param script: Path of the script to load. @type script: string @param keywords: A set of keywords used to find the variant the script will be executed with. The variant is looked up in the script's configuration. @type keywords: dictionary of string -> string @param useContext: If False or if None and either configScript or configScriptName are not None then lookup the corresponding configuration script starting from the script's path, if True then use the current configuration/variant. @type useContext: bool or None @param configScript: The path of the configuration script to use to execute the script. Ignored if useContext is True. @type configScript: string or None @param configScriptName: If not None and configScript is None then find the configuration script with this name starting the search at the script's path. Ignored if useContext is True. @type configScriptName: string or None """ if not isinstance(script, basestring): raise ValueError("'script' must be a string") script = self.configuration.basePath(script) if useContext is None: useContext = configScript is None and configScriptName is None if useContext: # Use the current configuration and lookup the variant relative # to the current variant. baseVariant = Script.getCurrent().variant variant = self.configuration.findVariant(keywords, baseVariant=baseVariant) return ScriptProxy( self.configuration.execute(path=script, variant=variant)) else: # Re-evaluate the configuration to execute the script with. # Uses the keywords specified to find the variant in the variants # defined in that configuration. path = self.configuration.abspath(script) if configScript is None: configuration = self.engine.findConfiguration( path=path, configScriptName=configScriptName, ) else: configuration = self.engine.getConfiguration( path=self.configuration.abspath(configScript), ) variant = configuration.findVariant(keywords) return ScriptProxy( configuration.execute(path=path, variant=variant))
def path(self): """The path of the currently executing script. """ return Script.getCurrent().path
from cake.library.env import EnvironmentTool from cake.library.filesys import FileSystemTool from cake.library.logging import LoggingTool from cake.library.project import ProjectTool from cake.library.script import ScriptTool from cake.library.shell import ShellTool from cake.library.variant import VariantTool from cake.library.zipping import ZipTool from cake.script import Script import cake.path import cake.system platform = cake.system.platform().lower() hostArchitecture = cake.system.architecture().lower() configuration = Script.getCurrent().configuration engine = Script.getCurrent().engine # Override the configuration basePath() function. def basePath(value): from cake.tools import script, env @waitForAsyncResult def _basePath(path): if isinstance(path, basestring): path = env.expand(path) if path.startswith("#"): if path[1] in '\\/': # Keep project paths relative but remove slashes. return path[2:] else:
class ZipTool(Tool): def extract( self, targetDir, source, onlyNewer=True, removeStale=False, includeMatch=None, ): """Extract all files in a Zip to the specified path. @param targetDir: The directory to extract files to. @type targetDir: string @param source: Path to the zip file to extract files from. @type source: string @param onlyNewer: Only extract files that are newer than those in the target directory. @type onlyNewer: bool @param removeStale: Remove files and directories in the target directory that no longer exist in the zip. @type removeStale: bool @param includeMatch: A callable used to decide whether to include certain files in the extraction. This could be a python callable that returns True to include the file or False to exclude it, or a regular expression function such as re.compile().match or re.match. @type includeMatch: any callable @return: A DirectoryTarget that will complete when the extraction has finished. @rtype: L{DirectoryTarget} """ if not isinstance(targetDir, basestring): raise TypeError("targetDir must be a string") engine = self.engine configuration = self.configuration basePath = configuration.basePath targetDir = basePath(targetDir) source = basePath(source) def _extract(): sourcePath = getPath(source) absTargetDir = configuration.abspath(targetDir) zipFile = zipfile.ZipFile(configuration.abspath(sourcePath), "r") try: zipInfos = zipFile.infolist() if includeMatch is not None: zipInfos = [ z for z in zipInfos if includeMatch(z.filename) ] if removeStale: filesInZip = set() for zipInfo in zipInfos: filesInZip.add( os.path.normcase(os.path.normpath( zipInfo.filename))) searchDir = os.path.normpath(absTargetDir) for path in cake.filesys.walkTree(searchDir): normPath = os.path.normcase(path) # Skip files that also exist in the zip. if normPath in filesInZip: continue if engine.dependencyInfoPath is None: # Skip .dep files that match a file in the zip. p, e = os.path.splitext(normPath) if e == ".dep" and p in filesInZip: continue absPath = os.path.join(searchDir, path) engine.logger.outputInfo( "Deleting %s\n" % os.path.join(targetDir, path), ) if os.path.isdir(absPath): cake.filesys.removeTree(absPath) else: cake.filesys.remove(absPath) for zipinfo in zipInfos: _extractFile(configuration, zipFile, sourcePath, zipinfo, targetDir, absTargetDir, onlyNewer) finally: zipFile.close() def _run(): try: _extract() except BuildError: raise except Exception, e: msg = "cake: Error extracting %s to %s: %s\n" % ( getPath(source), targetDir, str(e)) engine.raiseError(msg, targets=[targetDir]) if self.enabled: task = engine.createTask(_run) task.lazyStartAfter(getTask(source)) else: task = None directoryTarget = DirectoryTarget(path=targetDir, task=task) Script.getCurrent().getDefaultTarget().addTarget(directoryTarget) return directoryTarget