Example #1
0
        def expand_mixed_list(mixed, implicit_deps, mode):
            result = []
            for item in mixed:
                if isinstance(item, Target):
                    if mode == 'implicit':
                        names = [item.name]
                    elif mode == 'inputs':
                        names = item.outputs
                    elif mode == 'cmd':
                        names = [path.abs(x) for x in item.outputs]
                    else:
                        raise RuntimeError(mode)

                    if implicit_deps is not None:
                        implicit_deps += names
                    result += names

                elif isinstance(item, Tool):
                    result.append(str(item))

                elif isinstance(item, str):
                    if mode != 'cmd':
                        item = path.abs(item)
                    result.append(item)

                else:
                    raise RuntimeError

            return result
Example #2
0
  def write_command_file(self, filename, commands, inputs=None, outputs=None,
      cwd=None, environ=None, foreach=False, suffix='.cmd', dry=False,
      accept_additional_args=False):

    if suffix is not None:
      filename = path.addsuffix(filename, suffix)
    result = ['cmd', '/Q', '/c', filename]
    if foreach:
      result += ['$in', '$out']
      inputs, outputs = ['%1'], ['%2']

    commands = self.replace_commands_inout_vars(commands, inputs, outputs)
    if dry:
      return result, filename

    path.makedirs(path.dirname(path.abs(filename)))
    with open(filename, 'w') as fp:
      fp.write('REM This file is automatically generated with Craftr. It is \n')
      fp.write('REM not recommended to modify it manually.\n\n')
      if cwd is not None:
        fp.write('cd ' + shell.quote(cwd) + '\n\n')
      for key, value in environ.items():
        fp.write('set ' + shell.quote('{}={}'.format(key, value), for_ninja=True) + '\n')
      fp.write('\n')
      for index, command in enumerate(commands):
        if accept_additional_args and index == len(commands)-1:
          command.append(shell.safe('%*'))
        fp.write(shell.join(command) + '\n')
        fp.write('if %errorlevel% neq 0 exit %errorlevel%\n\n')

    return result, filename
Example #3
0
  def parse_manifest(self, filename):
    """
    Parse a manifest by filename and add register the module to the module
    cache. Returns the :class:`Module` object. If the manifest has already
    been parsed, it will not be re-parsed.

    :raise Manifest.Invalid: If the manifest is invalid.
    :return: :const:`None` if the manifest is a duplicate of an already
      parsed manifest (determined by name and version), otherwise the
      :class:`Module` object for the manifest's module.
    """

    filename = path.norm(path.abs(filename))
    if filename in self._manifest_cache:
      manifest = self._manifest_cache[filename]
      return self.find_module(manifest.name, manifest.version)

    manifest = Manifest.parse(filename)
    self._manifest_cache[filename] = manifest
    versions = self.modules.setdefault(manifest.name, {})
    if manifest.version in versions:
      logger.debug('multiple occurences of "{}-{}" found, '
          'one of which is located at "{}"'.format(manifest.name,
          manifest.version, filename))
      module = None
    else:
      logger.debug('parsed manifest: {}-{} ({})'.format(
          manifest.name, manifest.version, filename))
      module = Module(path.dirname(filename), manifest)
      versions[manifest.version] = module

    return module
Example #4
0
    def parse_manifest(self, filename):
        """
    Parse a manifest by filename and add register the module to the module
    cache. Returns the :class:`Module` object. If the manifest has already
    been parsed, it will not be re-parsed.

    :raise Manifest.Invalid: If the manifest is invalid.
    :return: :const:`None` if the manifest is a duplicate of an already
      parsed manifest (determined by name and version), otherwise the
      :class:`Module` object for the manifest's module.
    """

        filename = path.norm(path.abs(filename))
        if filename in self._manifest_cache:
            manifest = self._manifest_cache[filename]
            return self.find_module(manifest.name, manifest.version)

        manifest = Manifest.parse(filename)
        self._manifest_cache[filename] = manifest
        versions = self.modules.setdefault(manifest.name, {})
        if manifest.version in versions:
            other = versions[manifest.version].manifest.filename
            logger.debug('multiple occurences of "{}-{}" found\n'
                         '  - {}\n  - {}'.format(manifest.name,
                                                 manifest.version, filename,
                                                 other))
            module = None
        else:
            logger.debug('parsed manifest: {}-{} ({})'.format(
                manifest.name, manifest.version, filename))
            module = Module(path.dirname(filename), manifest)
            versions[manifest.version] = module

        return module
Example #5
0
    def expand_mixed_list(mixed, implicit_deps, mode):
      result = []
      for item in mixed:
        if isinstance(item, Target):
          if mode == 'implicit':
            names = [item.name]
          elif mode == 'inputs':
            names = item.outputs
          elif mode == 'cmd':
            names = [path.abs(x) for x in item.outputs]
          else:
            raise RuntimeError(mode)

          if implicit_deps is not None:
            implicit_deps += names
          result += names

        elif isinstance(item, str):
          if mode != 'cmd':
            item = path.abs(item)
          result.append(item)
        else: raise RuntimeError

      return result
Example #6
0
    def execute(self, parser, args):
        if hasattr(args, 'include_path'):
            session.path.extend(map(path.norm, args.include_path))

        # Help-command preprocessing. Check if we're to show the help on a builtin
        # object, otherwise extract the module name if applicable.
        if self.mode == 'help':
            if not args.name:
                help('craftr')
                return 0
            if args.name in vars(craftr.defaults):
                help(getattr(craftr.defaults, args.name))
                return 0
            # Check if we have an absolute symbol reference.
            if ':' in args.name:
                if args.module:
                    parser.error(
                        '-m/--module option conflicting with name argument: "{}"'
                        .format(args.name))
                args.module, args.name = args.name.split(':', 1)

        module = self._find_module(parser, args)
        session.main_module = module
        self.ninja_bin, self.ninja_version = get_ninja_info()

        # Create and switch to the build directory.
        session.builddir = path.abs(path.norm(args.build_dir, INIT_DIR))
        path.makedirs(session.builddir)
        os.chdir(session.builddir)
        self.cachefile = path.join(session.builddir, '.craftrcache')

        # Prepare options, loaders and execute.
        if self.mode in ('export', 'run', 'help'):
            return self._export_run_or_help(args, module)
        elif self.mode == 'dump-options':
            return self._dump_options(args, module)
        elif self.mode == 'dump-deptree':
            return self._dump_deptree(args, module)
        elif self.mode in ('build', 'clean'):
            return self._build_or_clean(args)
        elif self.mode == 'lock':
            self._create_lockfile()
        else:
            raise RuntimeError("mode: {}".format(self.mode))
Example #7
0
    def write_command_file(self,
                           filename,
                           commands,
                           inputs=None,
                           outputs=None,
                           cwd=None,
                           environ=None,
                           foreach=False,
                           suffix='.cmd',
                           dry=False,
                           accept_additional_args=False):

        if suffix is not None:
            filename = path.addsuffix(filename, suffix)
        result = ['cmd', '/Q', '/c', filename]
        if foreach:
            result += ['$in', '$out']
            inputs, outputs = ['%1'], ['%2']

        commands = self.replace_commands_inout_vars(commands, inputs, outputs)
        if dry:
            return result, filename

        path.makedirs(path.dirname(path.abs(filename)))
        with open(filename, 'w') as fp:
            fp.write(
                'REM This file is automatically generated with Craftr. It is \n'
            )
            fp.write('REM not recommended to modify it manually.\n\n')
            if cwd is not None:
                fp.write('cd ' + shell.quote(cwd) + '\n\n')
            for key, value in environ.items():
                fp.write(
                    'set ' +
                    shell.quote('{}={}'.format(key, value), for_ninja=True) +
                    '\n')
            fp.write('\n')
            for index, command in enumerate(commands):
                if accept_additional_args and index == len(commands) - 1:
                    command.append(shell.safe('%*'))
                fp.write(shell.join(command) + '\n')
                fp.write('if %errorlevel% neq 0 exit %errorlevel%\n\n')

        return result, filename
Example #8
0
  def __init__(self, name, commands, inputs, outputs, implicit_deps=(),
               order_only_deps=(), pool=None, deps=None, depfile=None,
               msvc_deps_prefix=None, explicit=False, foreach=False,
               description=None, metadata=None, cwd=None, environ=None,
               frameworks=()):
    argspec.validate('name', name, {'type': str})
    argspec.validate('commands', commands,
      {'type': list, 'allowEmpty': False, 'items':
        {'type': list, 'allowEmpty': False, 'items': {'type': [Tool, Target, str]}}})
    argspec.validate('inputs', inputs, {'type': [list, tuple], 'items': {'type': [Target, str]}})
    argspec.validate('outputs', outputs, {'type': [list, tuple], 'items': {'type': str}})
    argspec.validate('implicit_deps', implicit_deps, {'type': [list, tuple], 'items': {'type': [Target, str]}})
    argspec.validate('order_only_deps', order_only_deps, {'type': [list, tuple], 'items': {'type': [Target, str]}})
    argspec.validate('pool', pool, {'type': [None, str]})
    argspec.validate('deps', deps, {'type': [None, str], 'enum': ['msvc', 'gcc']})
    argspec.validate('depfile', depfile, {'type': [None, str]})
    argspec.validate('msvc_deps_prefix', msvc_deps_prefix, {'type': [None, str]})
    argspec.validate('explicit', explicit, {'type': bool})
    argspec.validate('foreach', foreach, {'type': bool})
    argspec.validate('description', description, {'type': [None, str]})
    argspec.validate('metadata', metadata, {'type': [None, dict]})
    argspec.validate('cwd', cwd, {'type': [None, str]})
    argspec.validate('environ', environ, {'type': [None, dict]})
    argspec.validate('frameworks', frameworks, {'type': [list, tuple], 'items': {'type': dict}})

    def expand_mixed_list(mixed, implicit_deps, mode):
      result = []
      for item in mixed:
        if isinstance(item, Target):
          if mode == 'implicit':
            names = [item.name]
          elif mode == 'inputs':
            names = item.outputs
          elif mode == 'cmd':
            names = [path.abs(x) for x in item.outputs]
          else:
            raise RuntimeError(mode)

          if implicit_deps is not None:
            implicit_deps += names
          result += names

        elif isinstance(item, str):
          if mode != 'cmd':
            item = path.abs(item)
          result.append(item)
        else: raise RuntimeError

      return result

    self.implicit_deps = []
    self.inputs = expand_mixed_list(inputs, None, 'inputs')
    self.outputs = [path.abs(x) for x in outputs]
    self.commands = [expand_mixed_list(cmd, self.implicit_deps, 'cmd') for cmd in commands]
    self.implicit_deps += expand_mixed_list(implicit_deps, None, 'implicit')
    self.order_only_deps = expand_mixed_list(order_only_deps, None, 'implicit')

    self.name = name
    self.pool = pool
    self.deps = deps
    self.depfile = depfile
    self.msvc_deps_prefix = msvc_deps_prefix
    self.explicit = explicit
    self.foreach = foreach
    self.description = description
    self.metadata = metadata or {}
    self.cwd = cwd
    self.environ = environ or {}
    self.frameworks = frameworks

    if self.foreach and len(self.inputs) != len(self.outputs):
      raise ValueError('foreach target must have the same number of output '
        'files ({}) as input files ({})'.format(len(self.outputs),
        len(self.inputs)))

    if self.deps == 'gcc' and not self.depfile:
      raise ValueError('require depfile with deps="gcc"')
Example #9
0
    def __init__(self,
                 name,
                 commands,
                 inputs,
                 outputs,
                 implicit_deps=(),
                 order_only_deps=(),
                 pool=None,
                 deps=None,
                 depfile=None,
                 msvc_deps_prefix=None,
                 explicit=False,
                 foreach=False,
                 description=None,
                 metadata=None,
                 cwd=None,
                 environ=None,
                 frameworks=(),
                 task=None,
                 runprefix=None):
        argspec.validate('name', name, {'type': str})
        argspec.validate(
            'commands', commands, {
                'type': list,
                'allowEmpty': False,
                'items': {
                    'type': list,
                    'allowEmpty': False,
                    'items': {
                        'type': [Tool, Target, str]
                    }
                }
            })
        argspec.validate('inputs', inputs, {
            'type': [list, tuple],
            'items': {
                'type': [Target, str]
            }
        })
        argspec.validate('outputs', outputs, {
            'type': [list, tuple],
            'items': {
                'type': str
            }
        })
        argspec.validate('implicit_deps', implicit_deps, {
            'type': [list, tuple],
            'items': {
                'type': [Target, str]
            }
        })
        argspec.validate('order_only_deps', order_only_deps, {
            'type': [list, tuple],
            'items': {
                'type': [Target, str]
            }
        })
        argspec.validate('pool', pool, {'type': [None, str]})
        argspec.validate('deps', deps, {
            'type': [None, str],
            'enum': ['msvc', 'gcc']
        })
        argspec.validate('depfile', depfile, {'type': [None, str]})
        argspec.validate('msvc_deps_prefix', msvc_deps_prefix,
                         {'type': [None, str]})
        argspec.validate('explicit', explicit, {'type': bool})
        argspec.validate('foreach', foreach, {'type': bool})
        argspec.validate('description', description, {'type': [None, str]})
        argspec.validate('metadata', metadata, {'type': [None, dict]})
        argspec.validate('cwd', cwd, {'type': [None, str]})
        argspec.validate('environ', environ, {'type': [None, dict]})
        argspec.validate('frameworks', frameworks, {
            'type': [list, tuple],
            'items': {
                'type': dict
            }
        })
        argspec.validate('task', task, {'type': [None, Task]})
        argspec.validate('runprefix', runprefix, {
            'type': [None, list, str],
            'items': {
                'type': str
            }
        })

        if isinstance(runprefix, str):
            runprefix = shell.split(runprefix)
        elif runprefix is None:
            runprefix = []

        def expand_mixed_list(mixed, implicit_deps, mode):
            result = []
            for item in mixed:
                if isinstance(item, Target):
                    if mode == 'implicit':
                        names = [item.name]
                    elif mode == 'inputs':
                        names = item.outputs
                    elif mode == 'cmd':
                        names = [path.abs(x) for x in item.outputs]
                    else:
                        raise RuntimeError(mode)

                    if implicit_deps is not None:
                        implicit_deps += names
                    result += names

                elif isinstance(item, Tool):
                    result.append(str(item))

                elif isinstance(item, str):
                    if mode != 'cmd':
                        item = path.abs(item)
                    result.append(item)

                else:
                    raise RuntimeError

            return result

        self.implicit_deps = []
        self.inputs = expand_mixed_list(inputs, None, 'inputs')
        self.outputs = [path.abs(x) for x in outputs]
        self.commands = [
            expand_mixed_list(cmd, self.implicit_deps, 'cmd')
            for cmd in commands
        ]
        self.implicit_deps += expand_mixed_list(implicit_deps, None,
                                                'implicit')
        self.order_only_deps = expand_mixed_list(order_only_deps, None,
                                                 'implicit')

        self.name = name
        self.pool = pool
        self.deps = deps
        self.depfile = depfile
        self.msvc_deps_prefix = msvc_deps_prefix
        self.explicit = explicit
        self.foreach = foreach
        self.description = description
        self.metadata = metadata or {}
        self.cwd = cwd
        self.environ = environ or {}
        self.frameworks = frameworks
        self.task = task
        self.runprefix = runprefix

        if self.foreach and len(self.inputs) != len(self.outputs):
            raise ValueError(
                'foreach target must have the same number of output '
                'files ({}) as input files ({})'.format(
                    len(self.outputs), len(self.inputs)))

        if self.deps == 'gcc' and not self.depfile:
            raise ValueError('require depfile with deps="gcc"')
Example #10
0
  def execute(self, parser, args):
    session.path.extend(map(path.norm, args.include_path))

    if self.is_export:
      # Determine the module to execute, either from the current working
      # directory or find it by name if one is specified.
      if not args.module:
        for fn in [MANIFEST_FILENAME, path.join('craftr', MANIFEST_FILENAME)]:
          if path.isfile(fn):
            module = session.parse_manifest(fn)
            break
        else:
          parser.error('"{}" does not exist'.format(MANIFEST_FILENAME))
      else:
        # TODO: For some reason, prints to stdout are not visible here.
        # TODO: Prints to stderr however work fine.
        try:
          module_name, version = parse_module_spec(args.module)
        except ValueError as exc:
          parser.error('{} (note: you have to escape > and < characters)'.format(exc))
        try:
          module = session.find_module(module_name, version)
        except Module.NotFound as exc:
          parser.error('module not found: ' + str(exc))
    else:
      module = None

    ninja_bin, ninja_version = get_ninja_info()

    # Create and switch to the build directory.
    session.builddir = path.abs(args.build_dir)
    path.makedirs(session.builddir)
    os.chdir(session.builddir)

    # Read the cache and parse command-line options.
    cachefile = path.join(session.builddir, '.craftrcache')
    if not read_cache(cachefile) and not self.is_export:
      logger.error('Unable to load "{}", can not build'.format(cachefile))
      return 1

    # Prepare options, loaders and execute.
    if self.is_export:
      session.cache['build'] = {}
      try:
        write_cache(cachefile)
        module.run()
      except (Module.InvalidOption, Module.LoaderInitializationError) as exc:
        for error in exc.format_errors():
          logger.error(error)
        return 1
      except craftr.defaults.ModuleError as exc:
        logger.error(exc)
        return 1

      # Write the cache back.
      session.cache['build']['targets'] = list(session.graph.targets.keys())
      session.cache['build']['main'] = module.ident
      session.cache['build']['options'] = args.options
      write_cache(cachefile)

      # Write the Ninja manifest.
      with open("build.ninja", 'w') as fp:
        platform = core.build.get_platform_helper()
        context = core.build.ExportContext(ninja_version)
        writer = core.build.NinjaWriter(fp)
        session.graph.export(writer, context, platform)

    else:
      parse_cmdline_options(session.cache['build']['options'])
      main = session.cache['build']['main']
      available_targets = frozenset(session.cache['build']['targets'])

      # Check the targets and if they exist.
      targets = []
      for target in args.targets:
        if '.' not in target:
          target = main + '.' + target
        elif target.startswith('.'):
          target = main + target

        module_name, target = target.rpartition('.')[::2]
        module_name, version = get_volatile_module_version(module_name)
        ref_module = session.find_module(module_name, version or '*')
        target = craftr.targetbuilder.get_full_name(target, ref_module)
        if target not in available_targets:
          parser.error('no such target: {}'.format(target))
        targets.append(target)

      # Execute the ninja build.
      cmd = [ninja_bin]
      if args.verbose:
        cmd += ['-v']
      cmd += targets
      shell.run(cmd)
Example #11
0
    def execute(self, parser, args):
        session.path.extend(map(path.norm, args.include_path))

        if self.mode == "export":
            # Determine the module to execute, either from the current working
            # directory or find it by name if one is specified.
            if not args.module:
                for fn in [MANIFEST_FILENAME, path.join("craftr", MANIFEST_FILENAME)]:
                    if path.isfile(fn):
                        module = session.parse_manifest(fn)
                        break
                else:
                    parser.error('"{}" does not exist'.format(MANIFEST_FILENAME))
            else:
                # TODO: For some reason, prints to stdout are not visible here.
                # TODO: Prints to stderr however work fine.
                try:
                    module_name, version = parse_module_spec(args.module)
                except ValueError as exc:
                    parser.error("{} (note: you have to escape > and < characters)".format(exc))
                try:
                    module = session.find_module(module_name, version)
                except Module.NotFound as exc:
                    parser.error("module not found: " + str(exc))
        else:
            module = None

        ninja_bin, ninja_version = get_ninja_info()

        # Create and switch to the build directory.
        session.builddir = path.abs(args.build_dir)
        path.makedirs(session.builddir)
        os.chdir(session.builddir)

        # Read the cache and parse command-line options.
        cachefile = path.join(session.builddir, ".craftrcache")
        if not read_cache(cachefile) and self.mode != "export":
            logger.error('Unable to load "{}", can not {}'.format(cachefile, self.mode))
            logger.error("Make sure to generate a build tree with 'craftr export'")
            return 1

        # Prepare options, loaders and execute.
        if self.mode == "export":
            session.expand_relative_options(module.manifest.name)
            session.cache["build"] = {}
            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():
                    # 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(cachefile)

            # Write the cache back.
            session.cache["build"]["targets"] = list(session.graph.targets.keys())
            session.cache["build"]["main"] = module.ident
            session.cache["build"]["options"] = args.options
            write_cache(cachefile)

            # Write the Ninja manifest.
            with open("build.ninja", "w") as fp:
                platform = core.build.get_platform_helper()
                context = core.build.ExportContext(ninja_version)
                writer = core.build.NinjaWriter(fp)
                session.graph.export(writer, context, platform)

        else:
            parse_cmdline_options(session.cache["build"]["options"])
            main = session.cache["build"]["main"]
            available_targets = frozenset(session.cache["build"]["targets"])

            logger.debug("build main module:", main)
            session.expand_relative_options(get_volatile_module_version(main)[0])

            # Check the targets and if they exist.
            targets = []
            for target in args.targets:
                if "." not in target:
                    target = main + "." + target
                elif target.startswith("."):
                    target = main + target

                module_name, target = target.rpartition(".")[::2]
                module_name, version = get_volatile_module_version(module_name)
                ref_module = session.find_module(module_name, version or "*")
                target = craftr.targetbuilder.get_full_name(target, ref_module)
                if target not in available_targets:
                    parser.error("no such target: {}".format(target))
                targets.append(target)

            # Execute the ninja build.
            cmd = [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