def __call__(self, target, source, env, for_signature): if not source: return [] if self.source_ext_match: suffixes = self.src_suffixes() ext = None for src in map(str, source): my_ext = match_splitext(src, suffixes)[1] if ext and my_ext != ext: raise UserError( "While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext)) ext = my_ext else: ext = match_splitext(str(source[0]), self.src_suffixes())[1] if not ext: #return ext raise UserError( "While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source)))) try: ret = SCons.Util.Selector.__call__(self, env, source, ext) except KeyError as e: raise UserError( "Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2])) if ret is None: raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ (repr(map(str, target)), repr(map(str, source)), ext, repr(self.keys()))) return ret
def __call__(self, target, source, env, for_signature): if not source: return [] if self.source_ext_match: suffixes = self.src_suffixes() ext = None for src in map(str, source): my_ext = match_splitext(src, suffixes)[1] if ext and my_ext != ext: raise UserError( "While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(list(map(str, target))), src, ext, my_ext)) ext = my_ext else: ext = match_splitext(str(source[0]), self.src_suffixes())[1] if not ext: #return ext raise UserError( "While building `%s': " "Cannot deduce file extension from source files: %s" % (repr(list(map(str, target))), repr(list(map(str, source))))) try: ret = SCons.Util.Selector.__call__(self, env, source, ext) except KeyError, e: raise UserError( "Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2]))
def SymlinkAction(target, source, env): target = target if is_List(target) else [target] source = source if is_List(source) else [source] if len(target) != 1 or len(source) != 1: raise UserError("Symlink only takes a single target and source") abs_src = os.path.abspath(str(source[0])) abs_trg = os.path.abspath(str(target[0])) if not os.path.isdir(abs_src): raise UserError( "Only folder symlink are allowed due to Windows limitation") try: os.unlink(abs_trg) except Exception: pass if env["HOST_OS"] == "win32": try: import _winapi _winapi.CreateJunction(abs_src, abs_trg) except Exception as e: raise UserError( f"Can't do a NTFS junction as symlink fallback ({abs_src} -> {abs_trg})" ) from e else: try: os.symlink(abs_src, abs_trg) except Exception as e: raise UserError( f"Can't create symlink ({abs_src} -> {abs_trg})") from e
def __call__(self, target, source, env, for_signature): ext = None for src in map(str, source): my_ext = SCons.Util.splitext(src)[1] if ext and my_ext != ext: raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext)) ext = my_ext if not ext: raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source)))) try: ret = SCons.Util.Selector.__call__(self, env, source) except KeyError, e: raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
def _install_files(env, target, source, exclude=None, glob=None, recurse=True): """ Install the given source paths to the given target location(s). """ if exclude is None: exclude = [] exclude = Flatten([exclude]) exclude.extend(['.*', '*~', '*.pyc', '*.o', '*.os']) if glob is None: glob = [] glob = Flatten([glob]) target = Flatten([target]) source = Flatten([source]) if len(target) != len(source): if len(target) == 1: target = target * len(source) else: raise UserError('Export files mismatch') results = [] for (t, s) in zip(target, source): if not isinstance(t, SConsFSBase): t = env.Dir(t) if not isinstance(s, SConsFSBase): s = env.Entry(s) for (dest, src) in _get_files(t, s, exclude, glob, recurse): results.extend(env.InstallAs(dest, src)) return results
class DictCmdGenerator(SCons.Util.Selector): """This is a callable class that can be used as a command generator function. It holds on to a dictionary mapping file suffixes to Actions. It uses that dictionary to return the proper action based on the file suffix of the source file.""" def src_suffixes(self): return self.keys() def add_action(self, suffix, action): """Add a suffix-action pair to the mapping. """ self[suffix] = action def __call__(self, target, source, env, for_signature): ext = None for src in map(str, source): my_ext = SCons.Util.splitext(src)[1] if ext and my_ext != ext: raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext)) ext = my_ext if not ext: raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source)))) try: ret = SCons.Util.Selector.__call__(self, env, source) except KeyError, e: raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2])) if ret is None: raise UserError("While building `%s': Don't know how to build a file with suffix `%s'." % (repr(map(str, target)), ext)) return ret
def CythonModule(env, target, source=None): if not target: target = [] elif not is_List(target): target = [target] if not source: source = [] elif not is_List(source): source = [source] # mod_target is passed to the compile builder mod_target, *other_targets = target if not source: source.append(f"{mod_target}.pyx") pyx_mod, *too_much_mods = [x for x in source if str(x).endswith(".pyx")] if too_much_mods: raise UserError( f"Must have exactly one .pyx file in sources (got `{[mod, *too_much_mods]}`)" ) c_mod = pyx_mod.split(".", 1)[0] + ".c" # Useful to do `xxx.gen.pyx` ==> `xxx` CythonToCBuilder(env, target=[c_mod, *other_targets], source=source) c_compile_target = CythonCompile(env, target=mod_target, source=[c_mod]) return [*c_compile_target, *other_targets]
def load_profile(utils, env, profile_name): """Imports the variables defined by the profile.""" compiler, available_profile = list_profile(env) if profile_name not in available_profile: msg = "" if profile_name != '?': msg += "[ERROR] No '{0}' profile found for '{1}'\n"\ .format(profile_name, compiler) if not available_profile: msg += "[WARNING] No available profile.\n" else: msg += "List of available profiles for '{0}':\n".format(compiler) profile_list = available_profile.items() profile_list.sort() for p_name, p_func in profile_list: if p_func.__doc__: p_doc = p_func.__doc__ else: p_doc = "(no documentation yet.)" msg += """ '{0}' {1} {2}""".format(p_name, '-' * (len(p_name) + 2), p_doc) profile_dir = os.path.dirname(scons_ext.profile.__file__) msg += """ ------------------------------------------------------------------------ | IMPORTANT: Profiles can be added and enriched, just have a look in: | | "{0}" ----------------------------------------------------------------------/ """.format(profile_dir) raise UserError(msg) available_profile[profile_name](utils)
def package(env, target, source, PACKAGEROOT, NAME, VERSION, PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, **kw): # initialize the rpm tool SCons.Tool.Tool('rpm').generate(env) bld = env['BUILDERS']['Rpm'] # Generate a UserError whenever the target name has been set explicitly, # since rpm does not allow for controlling it. This is detected by # checking if the target has been set to the default by the Package() # Environment function. if str(target[0]) != "%s-%s" % (NAME, VERSION): raise UserError("Setting target is not supported for rpm.") else: # This should be overridable from the construction environment, # which it is by using ARCHITECTURE=. # Guessing based on what os.uname() returns at least allows it # to work for both i386 and x86_64 Linux systems. archmap = { 'i686': 'i386', 'i586': 'i386', 'i486': 'i386', } buildarchitecture = os.uname()[4] buildarchitecture = archmap.get(buildarchitecture, buildarchitecture) if kw.has_key('ARCHITECTURE'): buildarchitecture = kw['ARCHITECTURE'] fmt = '%s-%s-%s.%s.rpm' #srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) #target = [ srcrpm, binrpm ] target = [ binrpm, ] # get the correct arguments into the kw hash loc = locals() del loc['kw'] kw.update(loc) del kw['source'], kw['target'], kw['env'] # if no "SOURCE_URL" tag is given add a default one. if not kw.has_key('SOURCE_URL'): #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') kw['SOURCE_URL'] = string.replace( str(target[0]) + ".tar.gz", '.rpm', '') # mangle the source and target list for the rpmbuild env = OverrideEnvironment(env, kw) target, source = stripinstallbuilder(target, source, env) target, source = addspecfile(target, source, env) target, source = collectintargz(target, source, env) # now call the rpm builder to actually build the packet. return apply(bld, [env, target, source], kw)
def _rm(env, target, source): # assert len(target) == 1 try: os.unlink(abs_trg) except FileNotFoundError: pass except Exception as e: raise UserError(f"Can't remove NTFS junction {abs_trg}") from e
def Builder(**kw): """A factory for builder objects.""" composite = None if 'generator' in kw: if 'action' in kw: raise UserError( "You must not specify both an action and a generator.") kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {}) del kw['generator'] elif 'action' in kw: source_ext_match = kw.get('source_ext_match', 1) if 'source_ext_match' in kw: del kw['source_ext_match'] if SCons.Util.is_Dict(kw['action']): composite = DictCmdGenerator(kw['action'], source_ext_match) kw['action'] = SCons.Action.CommandGeneratorAction(composite, {}) kw['src_suffix'] = composite.src_suffixes() else: kw['action'] = SCons.Action.Action(kw['action']) if 'emitter' in kw: emitter = kw['emitter'] if SCons.Util.is_String(emitter): # This allows users to pass in an Environment # variable reference (like "$FOO") as an emitter. # We will look in that Environment variable for # a callable to use as the actual emitter. var = SCons.Util.get_environment_var(emitter) if not var: raise UserError( "Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter) kw['emitter'] = EmitterProxy(var) elif SCons.Util.is_Dict(emitter): kw['emitter'] = DictEmitter(emitter) elif SCons.Util.is_List(emitter): kw['emitter'] = ListEmitter(emitter) result = BuilderBase(**kw) if not composite is None: result = CompositeBuilder(result, composite) return result
def _soname(target, source, env, for_signature): if "SONAME" in env: # Now verify that SOVERSION is not also set as that is not allowed if "SOVERSION" in env: raise UserError( "Ambiguous library .so naming, both SONAME: %s and SOVERSION: %s are defined. " "Only one can be defined for a target library." % (env["SONAME"], env["SOVERSION"])) return "$SONAME" else: return "$SHLIBPREFIX$_get_shlib_stem${SHLIBSUFFIX}$_SHLIBSOVERSION"
def __call__(self, target, source, env, for_signature): ext = None for src in map(str, source): my_ext = SCons.Util.splitext(src)[1] if ext and my_ext != ext: raise UserError( "Cannot build multiple sources with different extensions: %s, %s" % (ext, my_ext)) ext = my_ext if ext is None: raise UserError( "Cannot deduce file extension from source files: %s" % repr(map(str, source))) try: # XXX Do we need to perform Environment substitution # on the keys of action_dict before looking it up? return self.action_dict[ext] except KeyError: raise UserError("Don't know how to build a file with suffix %s." % ext)
class DictCmdGenerator(SCons.Util.Selector): """This is a callable class that can be used as a command generator function. It holds on to a dictionary mapping file suffixes to Actions. It uses that dictionary to return the proper action based on the file suffix of the source file.""" def __init__(self, dict=None, source_ext_match=1): SCons.Util.Selector.__init__(self, dict) self.source_ext_match = source_ext_match def src_suffixes(self): return list(self.keys()) def add_action(self, suffix, action): """Add a suffix-action pair to the mapping. """ self[suffix] = action def __call__(self, target, source, env, for_signature): if not source: return [] if self.source_ext_match: suffixes = self.src_suffixes() ext = None for src in map(str, source): my_ext = match_splitext(src, suffixes)[1] if ext and my_ext != ext: raise UserError( "While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(list(map(str, target))), src, ext, my_ext)) ext = my_ext else: ext = match_splitext(str(source[0]), self.src_suffixes())[1] if not ext: #return ext raise UserError( "While building `%s': " "Cannot deduce file extension from source files: %s" % (repr(list(map(str, target))), repr(list(map(str, source))))) try: ret = SCons.Util.Selector.__call__(self, env, source, ext) except KeyError, e: raise UserError( "Ambiguous suffixes after environment substitution: %s == %s == %s" % (e.args[0], e.args[1], e.args[2])) if ret is None: raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \ (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(list(self.keys())))) return ret
def get_android_has_code(fname): parsed = minidom.parse(open(fname)) application = parsed.getElementsByTagName('application')[0] hasCode = application.getAttributeNS(NSURI, 'hasCode') if not hasCode: return True # Default value if hasCode.lower() in ("yes", "true", "1"): return True if hasCode.lower() in ("no", "false", "0"): return False raise UserError("Value of hasCode is unknown")
def package(env, target, source, PACKAGEROOT, NAME, VERSION, PACKAGEVERSION, DESCRIPTION, SUMMARY, X_RPM_GROUP, LICENSE, **kw): # initialize the rpm tool SCons.Tool.Tool('rpm').generate(env) bld = env['BUILDERS']['Rpm'] # Generate a UserError whenever the target name has been set explicitly, # since rpm does not allow for controlling it. This is detected by # checking if the target has been set to the default by the Package() # Environment function. if str(target[0])!="%s-%s"%(NAME, VERSION): raise UserError( "Setting target is not supported for rpm." ) else: # This should be overridable from the construction environment, # which it is by using ARCHITECTURE=. buildarchitecture = SCons.Tool.rpmutils.defaultMachine() if 'ARCHITECTURE' in kw: buildarchitecture = kw['ARCHITECTURE'] fmt = '%s-%s-%s.%s.rpm' srcrpm = fmt % (NAME, VERSION, PACKAGEVERSION, 'src') binrpm = fmt % (NAME, VERSION, PACKAGEVERSION, buildarchitecture) target = [ srcrpm, binrpm ] # get the correct arguments into the kw hash loc=locals() del loc['kw'] kw.update(loc) del kw['source'], kw['target'], kw['env'] # if no "SOURCE_URL" tag is given add a default one. if 'SOURCE_URL' not in kw: #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') # mangle the source and target list for the rpmbuild env = OverrideEnvironment(env, kw) target, source = stripinstallbuilder(target, source, env) target, source = addspecfile(target, source, env) target, source = collectintargz(target, source, env) # now call the rpm builder to actually build the packet. return bld(env, target, source, **kw)
def _node_errors(builder, env, tlist, slist): """Validate that the lists of target and source nodes are legal for this builder and environment. Raise errors or issue warnings as appropriate. """ # First, figure out if there are any errors in the way the targets # were specified. for t in tlist: if t.side_effect: raise UserError( "Multiple ways to build the same target were specified for: %s" % t) if t.has_explicit_builder(): if not t.env is None and not t.env is env: action = t.builder.action t_contents = t.builder.action.get_contents(tlist, slist, t.env) contents = builder.action.get_contents(tlist, slist, env) if t_contents == contents: msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % ( t, action.genstring(tlist, slist, t.env)) SCons.Warnings.warn( SCons.Warnings.DuplicateEnvironmentWarning, msg) else: msg = "Two environments with different actions were specified for the same target: %s\n(action 1: %s)\n(action 2: %s)" % ( t, t_contents.decode('utf-8'), contents.decode('utf-8')) raise UserError(msg) if builder.multi: if t.builder != builder: msg = "Two different builders (%s and %s) were specified for the same target: %s" % ( t.builder.get_name(env), builder.get_name(env), t) raise UserError(msg) # TODO(batch): list constructed each time! if t.get_executor().get_all_targets() != tlist: msg = "Two different target lists have a target in common: %s (from %s and from %s)" % ( t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist))) raise UserError(msg) elif t.sources != slist: msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % ( t, list(map(str, t.sources)), list(map(str, slist))) raise UserError(msg) if builder.single_source: if len(slist) > 1: raise UserError( "More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str, tlist)), list(map(str, slist))))
def virtual_target_command(env, marker, condition, source, action): if not isinstance(marker, File): raise UserError("`marker` must be a File") if not condition(env) and os.path.exists(marker.abspath): # Condition has changed in our back, force rebuild env.Execute(Delete(marker)) return env.Command( marker, source, [ *(action if is_List(action) else [action]), Action( lambda target, source, env: install_marker(target[0]), "Write $TARGET to mark task complete", ), ], )
def Tag(env, target, source, *more_tags, **kw_tags): """ Tag a file with the given arguments. Just sets the accordingly named attribute on the file object. TODO: FIXME """ if not target: target = source first_tag = None else: first_tag = source if first_tag: kw_tags[first_tag[0]] = '' if not kw_tags and not more_tags: raise UserError("No tags given.") # XXX: sanity checks for x in more_tags: kw_tags[x] = '' if not SCons.Util.is_List(target): target = [target] else: # hmm, sometimes the target list is a list of a list # make sure it is flattened prior to processing. # TODO: perhaps some bug ?!? target = env.Flatten(target) for t in target: for k, v in kw_tags.items(): # all file tags have to start with PACKAGING_, so we can later # differentiate between "normal" object attributes and the # packaging attributes. As the user should not be bothered with # that, the prefix will be added here if missing. if not k.startswith('PACKAGING_'): k = 'PACKAGING_' + k t.Tag(k, v)
def _execute(self, env, target, source, overwarn={}, executor_kw={}): # We now assume that target and source are lists or None. if self.src_builder: source = self.src_builder_sources(env, source, overwarn) if self.single_source and len(source) > 1 and target is None: result = [] if target is None: target = [None] * len(source) for tgt, src in zip(target, source): if not tgt is None: tgt = [tgt] if not src is None: src = [src] result.extend(self._execute(env, tgt, src, overwarn)) return SCons.Node.NodeList(result) overwarn.warn() tlist, slist = self._create_nodes(env, target, source) # Check for errors with the specified target/source lists. _node_errors(self, env, tlist, slist) # The targets are fine, so find or make the appropriate Executor to # build this particular list of targets from this particular list of # sources. executor = None key = None if self.multi: try: executor = tlist[0].get_executor(create=0) except (AttributeError, IndexError): pass else: executor.add_sources(slist) if executor is None: if not self.action: fmt = "Builder %s must have an action to build %s." raise UserError( fmt % (self.get_name(env or self.env), list(map(str, tlist)))) key = self.action.batch_key(env or self.env, tlist, slist) if key: try: executor = SCons.Executor.GetBatchExecutor(key) except KeyError: pass else: executor.add_batch(tlist, slist) if executor is None: executor = SCons.Executor.Executor(self.action, env, [], tlist, slist, executor_kw) if key: SCons.Executor.AddBatchExecutor(key, executor) # Now set up the relevant information in the target Nodes themselves. for t in tlist: t.cwd = env.fs.getcwd() t.builder_set(self) t.env_set(env) t.add_source(slist) t.set_executor(executor) t.set_explicit(self.is_explicit) return SCons.Node.NodeList(tlist)
def _create_nodes(self, env, target=None, source=None): """Create and return lists of target and source nodes. """ src_suf = self.get_src_suffix(env) target_factory = env.get_factory(self.target_factory) source_factory = env.get_factory(self.source_factory) source = self._adjustixes(source, None, src_suf) slist = env.arg2nodes(source, source_factory) pre = self.get_prefix(env, slist) suf = self.get_suffix(env, slist) if target is None: try: t_from_s = slist[0].target_from_source except AttributeError: raise UserError( "Do not know how to create a target from source `%s'" % slist[0]) except IndexError: tlist = [] else: splitext = lambda S: self.splitext(S, env) tlist = [t_from_s(pre, suf, splitext)] else: target = self._adjustixes(target, pre, suf, self.ensure_suffix) tlist = env.arg2nodes(target, target_factory, target=target, source=source) if self.emitter: # The emitter is going to do str(node), but because we're # being called *from* a builder invocation, the new targets # don't yet have a builder set on them and will look like # source files. Fool the emitter's str() calls by setting # up a temporary builder on the new targets. new_targets = [] for t in tlist: if not t.is_derived(): t.builder_set(self) new_targets.append(t) orig_tlist = tlist[:] orig_slist = slist[:] target, source = self.emitter(target=tlist, source=slist, env=env) # Now delete the temporary builders that we attached to any # new targets, so that _node_errors() doesn't do weird stuff # to them because it thinks they already have builders. for t in new_targets: if t.builder is self: # Only delete the temporary builder if the emitter # didn't change it on us. t.builder_set(None) # Have to call arg2nodes yet again, since it is legal for # emitters to spit out strings as well as Node instances. tlist = env.arg2nodes(target, target_factory, target=orig_tlist, source=orig_slist) slist = env.arg2nodes(source, source_factory, target=orig_tlist, source=orig_slist) return tlist, slist
def _create_nodes(self, env, overrides, target = None, source = None): """Create and return lists of target and source nodes. """ def _adjustixes(files, pre, suf): if not files: return [] result = [] if not SCons.Util.is_List(files): files = [files] for f in files: if SCons.Util.is_String(f): f = SCons.Util.adjustixes(f, pre, suf) result.append(f) return result env = env.Override(overrides) src_suf = self.get_src_suffix(env) source = _adjustixes(source, None, src_suf) slist = env.arg2nodes(source, self.source_factory) pre = self.get_prefix(env, slist) suf = self.get_suffix(env, slist) if target is None: try: t_from_s = slist[0].target_from_source except AttributeError: raise UserError("Do not know how to create a target from source `%s'" % slist[0]) tlist = [ t_from_s(pre, suf, self.splitext) ] else: target = _adjustixes(target, pre, suf) tlist = env.arg2nodes(target, self.target_factory) if self.emitter: # The emitter is going to do str(node), but because we're # being called *from* a builder invocation, the new targets # don't yet have a builder set on them and will look like # source files. Fool the emitter's str() calls by setting # up a temporary builder on the new targets. new_targets = [] for t in tlist: if not t.is_derived(): t.builder = self new_targets.append(t) target, source = self.emitter(target=tlist, source=slist, env=env) # Now delete the temporary builders that we attached to any # new targets, so that _init_nodes() doesn't do weird stuff # to them because it thinks they already have builders. for t in new_targets: if t.builder is self: # Only delete the temporary builder if the emitter # didn't change it on us. t.builder = None # Have to call arg2nodes yet again, since it is legal for # emitters to spit out strings as well as Node instances. slist = env.arg2nodes(source, self.source_factory) tlist = env.arg2nodes(target, self.target_factory) return tlist, slist
def Package(env, target=None, source=None, **kw): """ Entry point for the package tool. """ # check if we need to find the source files ourselves if not source: source = env.FindInstalledFiles() if not source: raise UserError("No source for Package() given") # decide which types of packages shall be built. Can be defined through # four mechanisms: command line argument, keyword argument, # environment argument and default selection (zip or tar.gz) in that # order. try: kw['PACKAGETYPE'] = env['PACKAGETYPE'] except KeyError: pass if not kw.get('PACKAGETYPE'): kw['PACKAGETYPE'] = GetOption('package_type') if kw['PACKAGETYPE'] is None: if 'Tar' in env['BUILDERS']: kw['PACKAGETYPE'] = 'targz' elif 'Zip' in env['BUILDERS']: kw['PACKAGETYPE'] = 'zip' else: raise UserError("No type for Package() given") packagetype = kw['PACKAGETYPE'] if not is_List(packagetype): packagetype = packagetype.split(',') # load the needed packagers. def load_packager(pkgtype): try: # the specific packager is a relative import return importlib.import_module("." + pkgtype, __name__) except ImportError as e: raise SConsEnvironmentError("packager %s not available: %s" % (pkgtype, str(e))) packagers = [load_packager(p) for p in packagetype] # set up targets and the PACKAGEROOT try: # fill up the target list with a default target name until the # PACKAGETYPE list is of the same size as the target list. if not target: target = [] size_diff = len(packagetype) - len(target) default_name = "%(NAME)s-%(VERSION)s" if size_diff > 0: default_target = default_name % kw target.extend([default_target] * size_diff) if 'PACKAGEROOT' not in kw: kw['PACKAGEROOT'] = default_name % kw except KeyError as e: raise UserError("Missing Packagetag '%s'" % e.args[0]) # setup the source files source = env.arg2nodes(source, env.fs.Entry) # call the packager to setup the dependencies. targets = [] try: for packager in packagers: t = [target.pop(0)] t = packager.package(env, t, source, **kw) targets.extend(t) assert len(target) == 0 except KeyError as e: raise UserError("Missing Packagetag '%s' for %s packager" % (e.args[0], packager.__name__)) except TypeError: # this exception means that a needed argument for the packager is # missing. As our packagers get their "tags" as named function # arguments we need to find out which one is missing. argspec = getfullargspec(packager.package) args = argspec.args if argspec.defaults: # throw away arguments with default values args = args[:-len(argspec.defaults)] args.remove('env') args.remove('target') args.remove('source') # now remove any args for which we have a value in kw. args = [x for x in args if x not in kw] if not args: raise # must be a different error, so re-raise elif len(args) == 1: raise UserError("Missing Packagetag '%s' for %s packager" % (args[0], packager.__name__)) raise UserError("Missing Packagetags '%s' for %s packager" % (", ".join(args), packager.__name__)) target = env.arg2nodes(target, env.fs.Entry) #XXX: target set above unused... what was the intent? targets.extend(env.Alias('package', targets)) return targets