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
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
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
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
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
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))
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
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"')
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"')
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)
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