class BuildSteps(object): ''' Enumeration factory for build steps ''' FETCH = (N_('Fetch'), 'fetch') EXTRACT = (N_('Extract'), 'extract') CONFIGURE = (N_('Configure'), 'configure') COMPILE = (N_('Compile'), 'compile') INSTALL = (N_('Install'), 'install') POST_INSTALL = (N_('Post Install'), 'post_install') # Not added by default CHECK = (N_('Check'), 'check') GEN_LIBFILES = (N_('Gen Library File'), 'gen_library_file') MERGE = (N_('Merge universal binaries'), 'merge') RELOCATE_OSX_LIBRARIES = (N_('Relocate OSX libraries'), 'relocate_osx_libraries') def __new__(cls): return [ BuildSteps.FETCH, BuildSteps.EXTRACT, BuildSteps.CONFIGURE, BuildSteps.COMPILE, BuildSteps.INSTALL, BuildSteps.POST_INSTALL ] @classmethod def all_names(cls): members = inspect.getmembers(cls, lambda x: isinstance(x, tuple)) return tuple(e[1][1] for e in members)
class RecoveryActions(object): ''' Enumeration factory for recovery actions after an error ''' SHELL = N_("Enter the shell") RETRY_ALL = N_("Rebuild the recipe from scratch") RETRY_STEP = N_("Rebuild starting from the failed step") SKIP = N_("Skip recipe") ABORT = N_("Abort") def __new__(klass): return [RecoveryActions.SHELL, RecoveryActions.RETRY_ALL, RecoveryActions.RETRY_STEP, RecoveryActions.SKIP, RecoveryActions.ABORT]
class TarballBuildTools(Command): doc = N_('Generate abstract for package') name = 'tar-build-tools' def __init__(self, force=None, no_deps=None): args = [ ArgparseArgument('--output-dir', type=str, default='.', help=_('directory of package stored')) ] Command.__init__(self, args) def run(self, config, args): name = 'build-tools' abst = Abstract(config) filename = abst.fullname(name) + '.tar.bz2' path = os.path.join(args.output_dir, filename) import tarfile tar = tarfile.open(path, "w:bz2") for name in os.listdir(config.build_tools_prefix): tar.add(os.path.join(config.build_tools_prefix, name), name) tar.close()
class FetchRecipes(Fetch): doc = N_('Fetch the recipes sources') name = 'fetch' def __init__(self): args = [ ArgparseArgument( 'recipes', nargs='*', help=_('list of the recipes to fetch (fetch all if none ' 'is passed)')), ArgparseArgument('--no-deps', action='store_true', default=False, help=_('do not fetch dependencies')), ] Fetch.__init__(self, args) def run(self, config, args): cookbook = CookBook(config) recipes = [] for recipe in args.recipes: found = cookbook.get_closest_recipe(recipe) if found: recipes.append(found) else: recipes.append(recipe) return self.fetch(cookbook, recipes, args.no_deps, args.reset_rdeps, args.full_reset, args.print_only, args.jobs)
class Deps(Command): doc = N_('List the dependencies of a recipe') name = 'deps' def __init__(self): Command.__init__(self, [ ArgparseArgument('recipe', nargs=1, help=_('name of the recipe')), ArgparseArgument('--all', action='store_true', default=False, help=_('list all dependencies, including the ' 'build ones')), ]) def run(self, config, args): cookbook = CookBook(config) recipe_name = args.recipe[0] all_deps = args.all if all_deps: recipes = cookbook.list_recipe_deps(recipe_name) else: recipes = [ cookbook.get_recipe(x) for x in cookbook.get_recipe(recipe_name).list_deps() ] if len(recipes) == 0: m.message(_('%s has 0 dependencies') % recipe_name) return for recipe in recipes: # Don't print the recipe we asked for if recipe.name == recipe_name: continue m.message(recipe.name)
class FetchBootstrap(Command): doc = N_('Fetch the sources required for bootstrap') name = 'fetch-bootstrap' def __init__(self): args = [ ArgparseArgument('--build-tools-only', action='store_true', default=False, help=_('only fetch the build tools')), ArgparseArgument('--jobs', '-j', action='store', nargs='?', type=int, const=NUMBER_OF_JOBS_IF_USED, default=NUMBER_OF_JOBS_IF_UNUSED, help=_('number of async jobs')) ] Command.__init__(self, args) def run(self, config, args): bootstrappers = Bootstrapper(config, args.build_tools_only, offline=False, assume_yes=False, system_only=False) tasks = [] for bootstrapper in bootstrappers: bootstrapper.fetch_recipes(args.jobs) tasks.append(bootstrapper.fetch()) run_until_complete(tasks)
class Bootstrap(Command): doc = N_('Bootstrap the build system installing all the dependencies') name = 'bootstrap' def __init__(self): args = [ ArgparseArgument('--build-tools-only', action='store_true', default=False, help=_('only bootstrap the build tools')), ArgparseArgument('--system-only', action='store_true', default=False, help=('only boostrap the system')), ArgparseArgument('--offline', action='store_true', default=False, help=_('Use only the source cache, no network')), ArgparseArgument('-y', '--assume-yes', action='store_true', default=False, help=('Automatically say yes to prompts and run non-interactively'))] Command.__init__(self, args) def run(self, config, args): bootstrappers = Bootstrapper(config, args.build_tools_only, args.offline, args.assume_yes, args.system_only) tasks = [] for bootstrapper in bootstrappers: tasks.append(bootstrapper.fetch()) run_until_complete(tasks) for bootstraper in bootstrappers: bootstraper.extract() bootstrapper.start()
class Shell(Command): doc = N_('Starts a shell with the build environment') name = 'shell' def __init__(self): args = [ ArgparseArgument('--use-system-libs', action='store_true', default=False, help=_('add system paths to PKG_CONFIG_PATH')), ] Command.__init__(self, args) def run(self, config, args): # Load the cookbook which will parse all recipes and update config.bash_completions # We don't care about errors while loading recipes, which can happen # just because of the current configuration not matching what the # recipe supports cookbook = CookBook(config, skip_errors=True) env = config.env.copy() if args.use_system_libs: add_system_libs(config, env, config.env) shell.enter_build_environment(config.target_platform, config.target_arch, sourcedir=None, env=env, bash_completions=config.bash_completions)
class Tar(Command): doc = N_('Tar and pckage specifal item (build-tools, prefix )') name = 'tar' def __init__(self, force=None, no_deps=None): args = [ ArgparseArgument('name', nargs='*', help=_('name of the package')), ArgparseArgument('--output-dir', type=str, default='.', help=_('directory of package stored')) ] Command.__init__(self, args) def run(self, config, args): import tarfile for name in args.name: if name == 'build-tools': profile = BuildTools(config).get() filename = profile['tarball']['runtime']['filename'] path = os.path.join(args.output_dir, filename) srcd = config.build_tools_prefix else: print "invalid tar object %s, (build-tools)." % name raise FatalError( _("invalid tar object %s, (build-tools)." % name)) tar = tarfile.open(path, "w:bz2") for name in os.listdir(srcd): tar.add(os.path.join(srcd, name), name) tar.close()
class CleanOne(Command): doc = N_('Clean a single recipe without its dependencies') name = 'cleanone' def __init__(self): Command.__init__(self, [ ArgparseArgument( 'recipe', nargs=1, help=_('name of the recipe to clean')), ]) def run(self, config, args): cookbook = CookBook(config) recipe_name = args.recipe[0] recipe = cookbook.get_recipe(recipe_name) # call step function stepfunc = None try: stepfunc = getattr(recipe, 'clean') except: print('%s has no clean step, skipped' % recipe.name) if stepfunc: try: stepfunc() except FatalError as e: raise e except Exception as ex: raise FatalError( _("Error running %s checks: %s") % (recipe.name, ex))
class Clear(Command): doc = N_('Clear install install files.') name = 'clear' def __init__(self, force=None, no_deps=None): args = [ ArgparseArgument('name', nargs='*', help=_('name of the package to clear')) ] Command.__init__(self, args) def run(self, config, args): for name in args.name: path = None if name == 'build_tools_prefix': path = config.build_tools_prefix elif name == 'prefix': path = config.prefix elif name == 'cache_file': path = os.path.join(config.home_dir, config.cache_file) elif name == 'sources': path = config.sources print 'remove %s at %s'%(name,path) if os.path.isdir(path): shutil.rmtree(path) elif os.path.isfile(path): os.remove( path )
class AbstractCommand(Command): doc = N_('Generate abstract for package') name = 'abstract' def __init__(self, force=None, no_deps=None): args = [ ArgparseArgument( 'name', nargs='*', help=_('name of the package to generate abstract')), ArgparseArgument('--output-dir', type=str, default='.', help=_('directory of package stored')) ] Command.__init__(self, args) def run(self, config, args): import datetime start = datetime.datetime.now() abst = Abstract(config) for name in args.name: desc = abst.dump(name, args.output_dir) filename = abst.fullname(name) + '.yaml' path = os.path.join(args.output_dir, filename) import yaml f = open(path, 'w') yaml.dump(desc, f, default_flow_style=False) f.close()
class GenLibraryFiles(Command): doc = N_('Generate MSVC compatible library files (.lib)') name = 'genlibfiles' def __init__(self): Command.__init__(self, [ ArgparseArgument( '-o', '--output_dir', default=None, help=_('output directory where .lib files will be saved')), ]) def run(self, config, args): if config.target_platform != Platform.WINDOWS: raise UsageError( _('%s command can only be used targetting ' 'Windows platforms') % self.name) if args.output_dir is not None and not os.path.exists(args.output_dir): os.makedirs(args.output_dir) cookbook = CookBook(config) recipes = cookbook.get_recipes_list() for recipe in recipes: try: recipe.gen_library_file(args.output_dir) except Exception, e: m.message( _("Error generaring library files for %s:\n %s") % (recipe.name, e))
class GenXCodeConfig(Command): doc = N_('Generate XCode config files to use the SDK from VS') name = 'genxcconfig' def __init__(self): Command.__init__(self, [ArgparseArgument('-o', '--output_dir', default='.', help=_('output directory where .xcconfig files will be saved')), ArgparseArgument('-f', '--filename', default=None, help=_('filename of the .xcconfig file')), ArgparseArgument('libraries', nargs='*', help=_('List of libraries to include')), ]) def run(self, config, args): self.runargs(config, args.output_dir, args.filename, args.libraries) def runargs(self, config, output_dir, filename, libraries): if not os.path.exists(output_dir): os.makedirs(output_dir) if len(libraries) == 0: raise UsageError("You need to specify at least one library name") filename = filename or libraries[0] filepath = os.path.join(output_dir, '%s.xcconfig' % filename) xcconfig = XCConfig(libraries, env=config.env) xcconfig.create(filepath) m.action('Created %s.xcconfig' % filename) m.message('XCode config file were sucessfully created in %s' % os.path.abspath(filepath))
class GenVSProps(Command): doc = N_('Generate Visual Studio property sheets to use the SDK from VS') name = 'genvsprops' def __init__(self): Command.__init__(self, [ArgparseArgument('-o', '--output_dir', default='.', help=_('output directory where .vsprops files will be saved')), ArgparseArgument('-p', '--prefix', default=DEFAULT_PREFIX_MACRO, help=_('name of the prefix environment variable ' '(eg:CERBERO_SDK_ROOT_X86)')), ]) def run(self, config, args): self.runargs(config, args.output_dir, args.prefix) def runargs(self, config, output_dir, prefix=DEFAULT_PREFIX_MACRO): if not os.path.exists(output_dir): os.makedirs(output_dir) for pc in PkgConfig.list_all(env=config.env): p2v = PkgConfig2VSProps(pc, prefix=config.prefix, inherit_common=True, prefix_replacement='$(%s)' % prefix, env=config.env) p2v.create(output_dir) m.action('Created %s.props' % pc) common = CommonProps(prefix) common.create(output_dir) m.message('Property sheets files were sucessfully created in %s' % os.path.abspath(output_dir))
class Build(Command): doc = N_('Build a recipe') name = 'build' def __init__(self, force=None, no_deps=None): args = [ ArgparseArgument('recipe', nargs='*', help=_('name of the recipe to build')), ArgparseArgument('--missing-files', action='store_true', default=False, help=_( 'prints a list of files installed that are ' 'listed in the recipe')) ] if force is None: args.append( ArgparseArgument('--force', action='store_true', default=False, help=_( 'force the build of the recipe ingoring ' 'its cached state'))) if no_deps is None: args.append( ArgparseArgument('--no-deps', action='store_true', default=False, help=_('do not build dependencies'))) self.force = force self.no_deps = no_deps Command.__init__(self, args) def run(self, config, args): if self.force is None: self.force = args.force if self.no_deps is None: self.no_deps = args.no_deps self.runargs(config, args.recipe, args.missing_files, self.force, self.no_deps) def runargs(self, config, recipes, missing_files=False, force=False, no_deps=False, cookbook=None): if cookbook is None: cookbook = CookBook(config) oven = Oven(recipes, cookbook, force=self.force, no_deps=self.no_deps, missing_files=missing_files) oven.start_cooking()
class Wipe(Command): doc = N_('Wipes everything to restore the build system') name = 'wipe' def __init__(self): Command.__init__(self, [ ArgparseArgument( '--force', action='store_true', default=False, help=_('force the deletion of everything without user ' 'input')), ArgparseArgument('--build-tools', action='store_true', default=False, help=_('wipe the build tools too')) ]) def run(self, config, args): to_remove = [os.path.join(CONFIG_DIR, config.cache_file)] to_remove.append(config.prefix) to_remove.append(config.sources) if (args.build_tools): to_remove.append(os.path.join(CONFIG_DIR, config.build_tools_cache)) to_remove.append(config.build_tools_prefix) to_remove.append(config.build_tools_sources) if args.force: self.wipe(to_remove) return options = ['yes', 'no'] msg = _("WARNING!!!\n" "This command will delete cerbero's build cache, " "the sources directory and the builds directory " "to reset the build system to its initial state.\n" "The following paths will be removed:\n%s\n" "Do you want to continue?" % '\n'.join(to_remove)) # Ask once if shell.prompt(msg, options) == options[0]: msg = _("Are you sure?") # Ask twice if shell.prompt(msg, options) == options[0]: # Start with the Apocalypse self.wipe(to_remove) def wipe(self, paths): for path in paths: m.action(_("Removing path: %s") % path) if not os.path.exists(path): continue if os.path.isfile(path): if not os.access(path, os.W_OK): os.chmod(path, stat.S_IWUSR) os.remove(path) else: shutil.rmtree(path, onerror=_onerror)
class Shell(Command): doc = N_('Starts a shell with the build environment') name = 'shell' def __init__(self): Command.__init__(self, []) def run(self, config, args): shell.enter_build_environment(config.target_platform, config.target_arch)
class Bootstrap(Command): doc = N_('Bootstrap the build system installing all the dependencies') name = 'bootstrap' def __init__(self): Command.__init__(self, []) def run(self, config, args): bootstrapers = Bootstraper(config) for bootstraper in bootstrapers: bootstraper.start()
class Tag(Command): doc = N_('Tag a git recipe or all git recipes using their ' 'sdk-$version branch') name = 'tag' def __init__(self): args = [ ArgparseArgument('recipe', help=_('name of the recipe to tag or "all" to ' 'tag all recipes')), ArgparseArgument('tagname', help=_('name of the tag to use')), ArgparseArgument('tagdescription', help=_('description of the tag')), ArgparseArgument('-f', '--force', action='store_true', default=False, help=_('Replace tag if existing')) ] Command.__init__(self, args) def run(self, config, args): cookbook = CookBook(config) if args.recipe == 'all': recipes = cookbook.get_recipes_list() else: recipes = [cookbook.get_recipe(args.recipe)] if len(recipes) == 0: m.message(_("No recipes found")) tagname = args.tagname tagdescription = args.tagdescription force = args.force for recipe in recipes: if recipe.stype != SourceType.GIT and \ recipe.stype != SourceType.GIT_TARBALL: m.message( _("Recipe '%s' has a custom source repository, " "skipping") % recipe.name) continue recipe.fetch(checkout=False) tags = git.list_tags(recipe.repo_dir) exists = (tagname in tags) if exists: if not force: m.warning( _("Recipe '%s' tag '%s' already exists, " "not updating" % (recipe.name, tagname))) continue git.delete_tag(recipe.repo_dir, tagname) commit = 'origin/sdk-%s' % recipe.version git.create_tag(recipe.repo_dir, tagname, tagdescription, commit)
class Run(Command): doc = N_('Runs a command in the cerbero shell') name = 'run' def __init__(self): Command.__init__(self, [ ArgparseArgument('cmd', nargs='*', help=_('command to run')), ]) def run(self, config, args): command = ' '.join(args.cmd) shell.call(command)
class Run(Command): doc = N_('Runs a command in the cerbero shell') name = 'run' def __init__(self): Command.__init__(self, [ ArgparseArgument( 'cmd', nargs=argparse.REMAINDER, help=_('command to run')), ]) def run(self, config, args): sys.exit(shell.new_call(args.cmd, fail=False, env=config.env))
class Deps(Command): doc = N_('List the dependencies of a recipe') name = 'deps' def __init__(self): Command.__init__(self, [ ArgparseArgument('recipe', nargs=1, help=_('name of the recipe')), ArgparseArgument('--all', action='store_true', default=False, help=_('list all dependencies, including the ' 'build ones')), ArgparseArgument('--graph', action='store_true', default=False, help=_('show the depencies as a graph')), ]) def run(self, config, args): cookbook = CookBook(config) recipe_name = args.recipe[0] all_deps = args.all graph = args.graph if all_deps: recipes = cookbook.list_recipe_deps(recipe_name) else: recipes = [ cookbook.get_recipe(x) for x in cookbook.get_recipe(recipe_name).list_deps() ] if len(recipes) == 0: m.message(_('%s has 0 dependencies') % recipe_name) return if not graph: for recipe in recipes: # Don't print the recipe we asked for if recipe.name == recipe_name: continue m.message(recipe.name) else: def print_dep(cookbook, recipe, level=0, already_shown=[]): m.message("%s%s" % (" " * 3 * level, recipe.name)) already_shown.append(recipe) for r in [cookbook.get_recipe(x) for x in recipe.list_deps()]: if not r in already_shown: print_dep(cookbook, r, level + 1, already_shown) elif not r.name == recipe.name: m.message("%s(%s)" % (" " * 3 * (level + 1), r.name)) print_dep(cookbook, cookbook.get_recipe(recipe_name))
class Bootstrap(Command): doc = N_('Bootstrap the build system installing all the dependencies') name = 'bootstrap' def __init__(self): args = [ ArgparseArgument('--build-tools-only', action='store_true', default=False, help=_('only bootstrap the build tools')), ArgparseArgument('--system-only', action='store_true', default=False, help=('only boostrap the system')), ArgparseArgument('--offline', action='store_true', default=False, help=_('Use only the source cache, no network')), ArgparseArgument( '-y', '--assume-yes', action='store_true', default=False, help=( 'Automatically say yes to prompts and run non-interactively' )), ArgparseArgument('--jobs', '-j', action='store', type=int, default=0, help=_('How many recipes to build concurrently. ' '0 = number of CPUs.')) ] Command.__init__(self, args) def run(self, config, args): bootstrappers = Bootstrapper(config, args.build_tools_only, args.offline, args.assume_yes, args.system_only) tasks = [] async def bootstrap_fetch_extract(bs): await bs.fetch() await bs.extract() for bootstrapper in bootstrappers: tasks.append(bootstrap_fetch_extract(bootstrapper)) run_until_complete(tasks) for bootstrapper in bootstrappers: bootstrapper.start(jobs=args.jobs)
class BuildSteps(object): ''' Enumeration factory for build steps ''' FETCH = (N_('Fetch'), 'fetch') EXTRACT = (N_('Extract'), 'extract') CONFIGURE = (N_('Configure'), 'configure') COMPILE = (N_('Compile'), 'compile') INSTALL = (N_('Install'), 'install') POST_INSTALL = (N_('Post Install'), 'post_install') # Not added by default CHECK = (N_('Check'), 'check') GEN_LIBFILES = (N_('Gen Library File'), 'gen_library_file') MERGE = (N_('Merge universal binaries'), 'merge') RELOCATE_OSX_LIBRARIES = (N_('Relocate OSX libraries'), 'relocate_osx_libraries') def __new__(klass): return [BuildSteps.FETCH, BuildSteps.EXTRACT, BuildSteps.CONFIGURE, BuildSteps.COMPILE, BuildSteps.INSTALL, BuildSteps.POST_INSTALL]
class Check(Command): doc = N_('Run checks on a given recipe') name = 'check' def __init__(self): Command.__init__(self, [ ArgparseArgument('recipe', nargs=1, help=_('name of the recipe to run checks on')), ArgparseArgument('--recursive', action='store_true', default=False, help=_('Recursively run checks on dependencies')), ]) def run(self, config, args): cookbook = CookBook(config) recipe_name = args.recipe[0] recursive = args.recursive recipe = cookbook.get_recipe(recipe_name) if recursive: ordered_recipes = cookbook.list_recipe_deps(recipe_name) else: ordered_recipes = [recipe] for recipe in ordered_recipes: if cookbook.recipe_needs_build(recipe.name): raise FatalError(_("Recipe %s is not built yet" % recipe.name)) for recipe in ordered_recipes: # call step function stepfunc = None try: stepfunc = getattr(recipe, 'check') except: m.message('%s has no check step, skipped' % recipe.name) if stepfunc: try: if asyncio.iscoroutinefunction(stepfunc): loop = asyncio.get_event_loop() loop.run_until_complete(stepfunc(recipe)) else: stepfunc() except FatalError as e: raise e except Exception as ex: raise FatalError( _("Error running %s checks: %s") % (recipe.name, ex))
class FetchBootstrap(Command): doc = N_('Fetch the sources required for bootstrap') name = 'fetch-bootstrap' def __init__(self): args = [ ArgparseArgument('--build-tools-only', action='store_true', default=False, help=argparse.SUPPRESS), ArgparseArgument( '--toolchains', action=StoreBool, default=True, nargs='?', choices=('yes', 'no'), help='Setup any toolchains needed by the target platform'), ArgparseArgument( '--build-tools', action=StoreBool, default=True, nargs='?', choices=('yes', 'no'), help='Compile the build tools needed while building'), ArgparseArgument('--jobs', '-j', action='store', nargs='?', type=int, const=NUMBER_OF_JOBS_IF_USED, default=NUMBER_OF_JOBS_IF_UNUSED, help=_('number of async jobs')) ] Command.__init__(self, args) def run(self, config, args): if args.build_tools_only: # --build-tools-only meant '--toolchains=no --build-tools=yes' args.toolchains = False m.deprecation('Replace --build-tools-only with --toolchains=no') bootstrappers = Bootstrapper(config, False, args.toolchains, args.build_tools, offline=False, assume_yes=False) tasks = [] for bootstrapper in bootstrappers: bootstrapper.fetch_recipes(args.jobs) tasks.append(bootstrapper.fetch()) run_until_complete(tasks)
class Bootstrap(Command): doc = N_('Bootstrap the build system installing all the dependencies') name = 'bootstrap' def __init__(self): args = [ ArgparseArgument('--build-tools-only', action='store_true', default=False, help=_('only bootstrap the build tools'))] Command.__init__(self, args) def run(self, config, args): bootstrappers = Bootstrapper(config, args.build_tools_only) for bootstrapper in bootstrappers: bootstrapper.start()
class ListPackages(Command): doc = N_('List all the available packages') name = 'list-packages' def __init__(self): Command.__init__(self, []) def run(self, config, args): store = PackagesStore(config) packages = store.get_packages_list() if len(packages) == 0: m.message(_("No packages found")) for p in packages: m.message("%s - %s" % (p.name, p.version))
class List(Command): doc = N_('List all the available recipes') name = 'list' def __init__(self): Command.__init__(self, []) def run(self, config, args): cookbook = CookBook(config) recipes = cookbook.get_recipes_list() if len(recipes) == 0: m.message(_("No recipes found")) for recipe in recipes: m.message("%s - %s" % (recipe.name, recipe.version))