Example #1
0
  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)
Example #2
0
 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))
Example #3
0
    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
Example #4
0
    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)
Example #5
0
    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}