def init_loader(self, recursive=False, _break_recursion=None): """ Check all available loaders as defined in the :attr:`manifest` until the first loads successfully. :param recursive: Initialize the loaders of all dependencies as well. :raise RuntimeError: If there is no current session context. :raise LoaderInitializationError: If none of the loaders matched. """ if not session: raise RuntimeError('no current session') if not self.manifest.loaders: return if _break_recursion is self: return if recursive: for name, version in self.manifest.dependencies.items(): module = session.find_module(name, version) module.init_loader(True, _break_recursion=self) self.init_options() if self.loader is not None: return logger.info('running loaders for {}'.format(self.ident)) with logger.indent(): # Read the cached loader data and create the context. installdir = path.join(session.builddir, self.ident, 'src') cache = session.cache['loaders'].get(self.ident) context = LoaderContext(self.directory, self.manifest, self.options, installdir = installdir) context.get_temporary_directory = session.get_temporary_directory # Check all loaders in-order. errors = [] for loader in self.manifest.loaders: logger.info('[+]', loader.name) with logger.indent(): try: if cache and loader.name == cache['name']: new_data = loader.load(context, cache['data']) else: new_data = loader.load(context, None) except manifest.LoaderError as exc: errors.append(exc) else: self.loader = loader session.cache['loaders'][self.ident] = { 'name': loader.name, 'data': new_data} break else: raise LoaderInitializationError(self, errors)
def _create_lockfile(self): if not read_cache(True): sys.exit(1) modules = unserialise_loaded_module_info( session.cache['build']['modules']) filename = session.cache['build']['dependency_lock_filename'] deps = {} for module, version_info in modules.items(): entries = {} for version, module_data in version_info.items(): if module_data['dependencies']: entries[str(version)] = module_data['dependencies'] if entries: deps[module] = entries with open(filename, 'w') as fp: cson.dump(deps, fp, indent=2, sort_keys=True) logger.info('Dependency lockfile created at "{}"'.format(filename))
def _build_or_clean(self, args): """ Will be called for the 'build' and 'clean' modes. Loads the Craftr cache and invokes Ninja. """ # Read the cache and parse command-line options. if not read_cache(True): sys.exit(1) parse_cmdline_options(session.cache['build']['options']) main = session.cache['build']['main'] available_targets = frozenset(session.cache['build']['targets']) available_modules = unserialise_loaded_module_info( session.cache['build']['modules']) logger.debug('build main module:', main) session.expand_relative_options(get_volatile_module_version(main)[0]) # Check if any of the modules changed, so we can let the user know he # might have to re-export the build files. changed_modules = [] for name, versions in available_modules.items(): for version, info in versions.items(): if info['changed']: changed_modules.append('{}-{}'.format(name, version)) if changed_modules: if len(changed_modules) == 1: logger.info( 'note: module "{}" has changed, maybe you should re-export' .format(changed_modules[0])) else: logger.info( 'note: some modules have changed, maybe you should re-export' ) for name in changed_modules: logger.info(' -', name) # Check the targets and if they exist. targets = [] for target_name in args.targets: if '.' not in target_name: target_name = main + '.' + target_name elif target_name.startswith('.'): target_name = main + target_name module_name, target_name = target_name.rpartition('.')[::2] module_name, version = get_volatile_module_version(module_name) if module_name not in available_modules: error('no such module:', module_name) if not version: version = max(available_modules[module_name].keys()) target_name = craftr.targetbuilder.get_full_name( target_name, module_name=module_name, version=version) if target_name not in available_targets: logger.error('no such target: {}'.format(target_name)) return 1 targets.append(target_name) # Make sure we get all the output before running the subcommand. logger.flush() # Execute the ninja build. cmd = [self.ninja_bin] if args.verbose: cmd += ['-v'] if self.mode == 'clean': cmd += ['-t', 'clean'] if not args.recursive: cmd += ['-r'] cmd += targets return shell.run(cmd).returncode
def _export_run_or_help(self, args, module): """ Called when the mode is 'export' or 'run'. Will execute the specified *module* and eventually export a Ninja manifest and Cache. """ read_cache(False) session.expand_relative_options() session.cache['build'] = {} # Load the dependency lock information if it exists. deplock_fn = path.join(path.dirname(module.manifest.filename), '.dependency-lock') if os.path.isfile(deplock_fn): with open(deplock_fn) as fp: session.preferred_versions = cson.load(fp) logger.debug('note: dependency lock file "{}" loaded'.format( deplock_fn)) try: module.run() except Module.InvalidOption as exc: for error in exc.format_errors(): logger.error(error) return 1 except craftr.defaults.ModuleError as exc: logger.error('error:', exc) return 1 finally: if sys.exc_info() and self.mode == 'export': # We still want to write the cache, especially so that data already # loaded with loaders doesn't need to be re-loaded. They'll find out # when the cached information was not valid. write_cache(self.cachefile) # Fill the cache. session.cache['build']['targets'] = list(session.graph.targets.keys()) session.cache['build']['modules'] = serialise_loaded_module_info() session.cache['build']['main'] = module.ident session.cache['build']['options'] = args.options session.cache['build']['dependency_lock_filename'] = deplock_fn if self.mode == 'export': # Add the Craftr_run_command variable which is necessary for tasks # to properly executed. run_command = ['craftr', '-q', '-P', path.rel(session.maindir)] if args.no_config: run_command += ['-C'] run_command += ['-c' + x for x in args.config] run_command += ['run'] if args.module: run_command += ['-m', args.module] run_command += ['-i' + x for x in args.include_path] run_command += ['-b', path.rel(session.builddir)] session.graph.vars['Craftr_run_command'] = shell.join(run_command) write_cache(self.cachefile) # Write the Ninja manifest. with open("build.ninja", 'w') as fp: platform = core.build.get_platform_helper() context = core.build.ExportContext(self.ninja_version) writer = core.build.NinjaWriter(fp) session.graph.export(writer, context, platform) logger.info('exported "build.ninja"') return 0 elif self.mode == 'run': if args.task: if args.task not in session.graph.tasks: logger.error('no such task exists: "{}"'.format(args.task)) return 1 task = session.graph.tasks[args.task] return task.invoke(args.task_args) return 0 elif self.mode == 'help': if args.name not in vars(module.namespace): logger.error('symbol not found: "{}:{}"'.format( module.manifest.name, args.name)) return 1 help(getattr(module.namespace, args.name)) return 0 assert False, "unhandled mode: {}".format(self.mode)
def load(self, context, cache): if cache is not None and path.isdir(cache.get("directory", "")): # Check if the requested version changes. url_template = context.expand_variables(cache.get("url_template", "")) if url_template == cache.get("url"): self.directory = cache["directory"] logger.info("Reusing cached directory: {}".format(path.rel(self.directory, nopar=True))) return cache else: logger.info("Cached URL is outdated:", cache.get("url")) directory = None archive = None delete_after_extract = True for url_template in self.urls: url = context.expand_variables(url_template) if not url: continue if url.startswith("file://"): name = url[7:] if path.isdir(name): logger.info("Using directory", url) directory = name break elif path.isfile(name): logger.info("Using archive", url) archive = name delete_after_extract = False break error = None else: error = None try: progress = lambda d: self._download_progress(url, context, d) archive, reused = httputils.download_file( url, directory=context.get_temporary_directory(), on_exists="skip", progress=progress ) except (httputils.URLError, httputils.HTTPError) as exc: error = exc except self.DownloadAlreadyExists as exc: directory = exc.directory logger.info("Reusing existing directory", directory) else: if reused: logger.info("Reusing cached download", path.basename(archive)) break if error: logger.info("Error reading", url, ":", error) if directory or archive: logger.debug("URL applies: {}".format(url)) if not directory and archive: suffix, directory = self._get_archive_unpack_info(context, archive) logger.info( 'Unpacking "{}" to "{}" ...'.format(path.rel(archive, nopar=True), path.rel(directory, nopar=True)) ) nr.misc.archive.extract( archive, directory, suffix=suffix, unpack_single_dir=True, check_extract_file=self._check_extract_file, progress_callback=self._extract_progress, ) elif not directory: raise LoaderError(self, "no URL matched") self.directory = directory with open(path.join(self.directory, ".craftr_downloadurl"), "w") as fp: fp.write(url) return {"directory": directory, "url_template": url_template, "url": url}