From 1a3cb9d9b0ab24d21d7e4edb70bb4a939f621082 Mon Sep 17 00:00:00 2001 From: Kenneth Heafield Date: Sat, 12 May 2012 14:01:52 -0400 Subject: Give in and copy bjam into cdec source code --- jam-files/boost-build/tools/common.py | 840 ++++++++++++++++++++++++++++++++++ 1 file changed, 840 insertions(+) create mode 100644 jam-files/boost-build/tools/common.py (limited to 'jam-files/boost-build/tools/common.py') diff --git a/jam-files/boost-build/tools/common.py b/jam-files/boost-build/tools/common.py new file mode 100644 index 00000000..612745b8 --- /dev/null +++ b/jam-files/boost-build/tools/common.py @@ -0,0 +1,840 @@ +# Status: being ported by Steven Watanabe +# Base revision: 47174 +# +# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears in +# all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. + +""" Provides actions common to all toolsets, such as creating directories and + removing files. +""" + +import re +import bjam +import os +import os.path +import sys + +from b2.build import feature +from b2.util.utility import * +from b2.util import path + +__re__before_first_dash = re.compile ('([^-]*)-') + +def reset (): + """ Clear the module state. This is mainly for testing purposes. + Note that this must be called _after_ resetting the module 'feature'. + """ + global __had_unspecified_value, __had_value, __declared_subfeature + global __init_loc + global __all_signatures, __debug_configuration, __show_configuration + + # Stores toolsets without specified initialization values. + __had_unspecified_value = {} + + # Stores toolsets with specified initialization values. + __had_value = {} + + # Stores toolsets with declared subfeatures. + __declared_subfeature = {} + + # Stores all signatures of the toolsets. + __all_signatures = {} + + # Stores the initialization locations of each toolset + __init_loc = {} + + __debug_configuration = '--debug-configuration' in bjam.variable('ARGV') + __show_configuration = '--show-configuration' in bjam.variable('ARGV') + + global __executable_path_variable + OS = bjam.call("peek", [], "OS")[0] + if OS == "NT": + # On Windows the case and capitalization of PATH is not always predictable, so + # let's find out what variable name was really set. + for n in sys.environ: + if n.lower() == "path": + __executable_path_variable = n + break + else: + __executable_path_variable = "PATH" + + m = {"NT": __executable_path_variable, + "CYGWIN": "PATH", + "MACOSX": "DYLD_LIBRARY_PATH", + "AIX": "LIBPATH"} + global __shared_library_path_variable + __shared_library_path_variable = m.get(OS, "LD_LIBRARY_PATH") + +reset() + +def shared_library_path_variable(): + return __shared_library_path_variable + +# ported from trunk@47174 +class Configurations(object): + """ + This class helps to manage toolset configurations. Each configuration + has a unique ID and one or more parameters. A typical example of a unique ID + is a condition generated by 'common.check-init-parameters' rule. Other kinds + of IDs can be used. Parameters may include any details about the configuration + like 'command', 'path', etc. + + A toolset configuration may be in one of the following states: + + - registered + Configuration has been registered (e.g. by autodetection code) but has + not yet been marked as used, i.e. 'toolset.using' rule has not yet been + called for it. + - used + Once called 'toolset.using' rule marks the configuration as 'used'. + + The main difference between the states above is that while a configuration is + 'registered' its options can be freely changed. This is useful in particular + for autodetection code - all detected configurations may be safely overwritten + by user code. + """ + + def __init__(self): + self.used_ = set() + self.all_ = set() + self.params = {} + + def register(self, id): + """ + Registers a configuration. + + Returns True if the configuration has been added and False if + it already exists. Reports an error if the configuration is 'used'. + """ + if id in self.used_: + #FIXME + errors.error("common: the configuration '$(id)' is in use") + + if id not in self.all_: + self.all_ += [id] + + # Indicate that a new configuration has been added. + return True + else: + return False + + def use(self, id): + """ + Mark a configuration as 'used'. + + Returns True if the state of the configuration has been changed to + 'used' and False if it the state wasn't changed. Reports an error + if the configuration isn't known. + """ + if id not in self.all_: + #FIXME: + errors.error("common: the configuration '$(id)' is not known") + + if id not in self.used_: + self.used_ += [id] + + # indicate that the configuration has been marked as 'used' + return True + else: + return False + + def all(self): + """ Return all registered configurations. """ + return self.all_ + + def used(self): + """ Return all used configurations. """ + return self.used_ + + def get(self, id, param): + """ Returns the value of a configuration parameter. """ + self.params_.getdefault(param, {}).getdefault(id, None) + + def set (self, id, param, value): + """ Sets the value of a configuration parameter. """ + self.params_.setdefault(param, {})[id] = value + +# Ported from trunk@47174 +def check_init_parameters(toolset, requirement, *args): + """ The rule for checking toolset parameters. Trailing parameters should all be + parameter name/value pairs. The rule will check that each parameter either has + a value in each invocation or has no value in each invocation. Also, the rule + will check that the combination of all parameter values is unique in all + invocations. + + Each parameter name corresponds to a subfeature. This rule will declare a + subfeature the first time a non-empty parameter value is passed and will + extend it with all the values. + + The return value from this rule is a condition to be used for flags settings. + """ + # The type checking here is my best guess about + # what the types should be. + assert(isinstance(toolset, str)) + assert(isinstance(requirement, str) or requirement is None) + sig = toolset + condition = replace_grist(toolset, '') + subcondition = [] + + for arg in args: + assert(isinstance(arg, tuple)) + assert(len(arg) == 2) + name = arg[0] + value = arg[1] + assert(isinstance(name, str)) + assert(isinstance(value, str) or value is None) + + str_toolset_name = str((toolset, name)) + + # FIXME: is this the correct translation? + ### if $(value)-is-not-empty + if value is not None: + condition = condition + '-' + value + if __had_unspecified_value.has_key(str_toolset_name): + raise BaseException("'%s' initialization: parameter '%s' inconsistent\n" \ + "no value was specified in earlier initialization\n" \ + "an explicit value is specified now" % (toolset, name)) + + # The logic below is for intel compiler. It calls this rule + # with 'intel-linux' and 'intel-win' as toolset, so we need to + # get the base part of toolset name. + # We can't pass 'intel' as toolset, because it that case it will + # be impossible to register versionles intel-linux and + # intel-win of specific version. + t = toolset + m = __re__before_first_dash.match(toolset) + if m: + t = m.group(1) + + if not __had_value.has_key(str_toolset_name): + if not __declared_subfeature.has_key(str((t, name))): + feature.subfeature('toolset', t, name, [], ['propagated']) + __declared_subfeature[str((t, name))] = True + + __had_value[str_toolset_name] = True + + feature.extend_subfeature('toolset', t, name, [value]) + subcondition += ['' + value ] + + else: + if __had_value.has_key(str_toolset_name): + raise BaseException ("'%s' initialization: parameter '%s' inconsistent\n" \ + "an explicit value was specified in an earlier initialization\n" \ + "no value is specified now" % (toolset, name)) + + __had_unspecified_value[str_toolset_name] = True + + if value == None: value = '' + + sig = sig + value + '-' + + if __all_signatures.has_key(sig): + message = "duplicate initialization of '%s' with the following parameters: " % toolset + + for arg in args: + name = arg[0] + value = arg[1] + if value == None: value = '' + + message += "'%s' = '%s'\n" % (name, value) + + raise BaseException(message) + + __all_signatures[sig] = True + # FIXME + __init_loc[sig] = "User location unknown" #[ errors.nearest-user-location ] ; + + # If we have a requirement, this version should only be applied under that + # condition. To accomplish this we add a toolset requirement that imposes + # the toolset subcondition, which encodes the version. + if requirement: + r = ['' + toolset, requirement] + r = ','.join(r) + toolset.add_requirements([r + ':' + c for c in subcondition]) + + # We add the requirements, if any, to the condition to scope the toolset + # variables and options to this specific version. + condition = [condition] + if requirement: + condition += [requirement] + + if __show_configuration: + print "notice:", condition + return ['/'.join(condition)] + +# Ported from trunk@47077 +def get_invocation_command_nodefault( + toolset, tool, user_provided_command=[], additional_paths=[], path_last=False): + """ + A helper rule to get the command to invoke some tool. If + 'user-provided-command' is not given, tries to find binary named 'tool' in + PATH and in the passed 'additional-path'. Otherwise, verifies that the first + element of 'user-provided-command' is an existing program. + + This rule returns the command to be used when invoking the tool. If we can't + find the tool, a warning is issued. If 'path-last' is specified, PATH is + checked after 'additional-paths' when searching for 'tool'. + """ + assert(isinstance(toolset, str)) + assert(isinstance(tool, str)) + assert(isinstance(user_provided_command, list)) + if additional_paths is not None: + assert(isinstance(additional_paths, list)) + assert(all([isinstance(path, str) for path in additional_paths])) + assert(all(isinstance(path, str) for path in additional_paths)) + assert(isinstance(path_last, bool)) + + if not user_provided_command: + command = find_tool(tool, additional_paths, path_last) + if not command and __debug_configuration: + print "warning: toolset", toolset, "initialization: can't find tool, tool" + #FIXME + #print "warning: initialized from" [ errors.nearest-user-location ] ; + else: + command = check_tool(user_provided_command) + if not command and __debug_configuration: + print "warning: toolset", toolset, "initialization:" + print "warning: can't find user-provided command", user_provided_command + #FIXME + #ECHO "warning: initialized from" [ errors.nearest-user-location ] + + assert(isinstance(command, str)) + + return command + +# ported from trunk@47174 +def get_invocation_command(toolset, tool, user_provided_command = [], + additional_paths = [], path_last = False): + """ Same as get_invocation_command_nodefault, except that if no tool is found, + returns either the user-provided-command, if present, or the 'tool' parameter. + """ + + assert(isinstance(toolset, str)) + assert(isinstance(tool, str)) + assert(isinstance(user_provided_command, list)) + if additional_paths is not None: + assert(isinstance(additional_paths, list)) + assert(all([isinstance(path, str) for path in additional_paths])) + assert(isinstance(path_last, bool)) + + result = get_invocation_command_nodefault(toolset, tool, + user_provided_command, + additional_paths, + path_last) + + if not result: + if user_provided_command: + result = user_provided_command[0] + else: + result = tool + + assert(isinstance(result, str)) + + return result + +# ported from trunk@47281 +def get_absolute_tool_path(command): + """ + Given an invocation command, + return the absolute path to the command. This works even if commnad + has not path element and is present in PATH. + """ + if os.path.dirname(command): + return os.path.dirname(command) + else: + programs = path.programs_path() + m = path.glob(programs, [command, command + '.exe' ]) + if not len(m): + print "Could not find:", command, "in", programs + return os.path.dirname(m[0]) + +# ported from trunk@47174 +def find_tool(name, additional_paths = [], path_last = False): + """ Attempts to find tool (binary) named 'name' in PATH and in + 'additional-paths'. If found in path, returns 'name'. If + found in additional paths, returns full name. If the tool + is found in several directories, returns the first path found. + Otherwise, returns the empty string. If 'path_last' is specified, + path is checked after 'additional_paths'. + """ + assert(isinstance(name, str)) + assert(isinstance(additional_paths, list)) + assert(isinstance(path_last, bool)) + + programs = path.programs_path() + match = path.glob(programs, [name, name + '.exe']) + additional_match = path.glob(additional_paths, [name, name + '.exe']) + + result = [] + if path_last: + result = additional_match + if not result and match: + result = match + + else: + if match: + result = match + + elif additional_match: + result = additional_match + + if result: + return path.native(result[0]) + else: + return '' + +#ported from trunk@47281 +def check_tool_aux(command): + """ Checks if 'command' can be found either in path + or is a full name to an existing file. + """ + assert(isinstance(command, str)) + dirname = os.path.dirname(command) + if dirname: + if os.path.exists(command): + return command + # Both NT and Cygwin will run .exe files by their unqualified names. + elif on_windows() and os.path.exists(command + '.exe'): + return command + # Only NT will run .bat files by their unqualified names. + elif os_name() == 'NT' and os.path.exists(command + '.bat'): + return command + else: + paths = path.programs_path() + if path.glob(paths, [command]): + return command + +# ported from trunk@47281 +def check_tool(command): + """ Checks that a tool can be invoked by 'command'. + If command is not an absolute path, checks if it can be found in 'path'. + If comand is absolute path, check that it exists. Returns 'command' + if ok and empty string otherwise. + """ + assert(isinstance(command, list)) + assert(all(isinstance(c, str) for c in command)) + #FIXME: why do we check the first and last elements???? + if check_tool_aux(command[0]) or check_tool_aux(command[-1]): + return command + +# ported from trunk@47281 +def handle_options(tool, condition, command, options): + """ Handle common options for toolset, specifically sets the following + flag variables: + - CONFIG_COMMAND to 'command' + - OPTIOns for compile to the value of in options + - OPTIONS for compile.c to the value of in options + - OPTIONS for compile.c++ to the value of in options + - OPTIONS for compile.fortran to the value of in options + - OPTIONs for link to the value of in options + """ + from b2.build import toolset + + assert(isinstance(tool, str)) + assert(isinstance(condition, list)) + assert(isinstance(command, str)) + assert(isinstance(options, list)) + assert(command) + toolset.flags(tool, 'CONFIG_COMMAND', condition, [command]) + toolset.flags(tool + '.compile', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.compile.c', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.compile.c++', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.compile.fortran', 'OPTIONS', condition, feature.get_values('', options)) + toolset.flags(tool + '.link', 'OPTIONS', condition, feature.get_values('', options)) + +# ported from trunk@47281 +def get_program_files_dir(): + """ returns the location of the "program files" directory on a windows + platform + """ + ProgramFiles = bjam.variable("ProgramFiles") + if ProgramFiles: + ProgramFiles = ' '.join(ProgramFiles) + else: + ProgramFiles = "c:\\Program Files" + return ProgramFiles + +# ported from trunk@47281 +def rm_command(): + return __RM + +# ported from trunk@47281 +def copy_command(): + return __CP + +# ported from trunk@47281 +def variable_setting_command(variable, value): + """ + Returns the command needed to set an environment variable on the current + platform. The variable setting persists through all following commands and is + visible in the environment seen by subsequently executed commands. In other + words, on Unix systems, the variable is exported, which is consistent with the + only possible behavior on Windows systems. + """ + assert(isinstance(variable, str)) + assert(isinstance(value, str)) + + if os_name() == 'NT': + return "set " + variable + "=" + value + os.linesep + else: + # (todo) + # The following does not work on CYGWIN and needs to be fixed. On + # CYGWIN the $(nl) variable holds a Windows new-line \r\n sequence that + # messes up the executed export command which then reports that the + # passed variable name is incorrect. This is most likely due to the + # extra \r character getting interpreted as a part of the variable name. + # + # Several ideas pop to mind on how to fix this: + # * One way would be to separate the commands using the ; shell + # command separator. This seems like the quickest possible + # solution but I do not know whether this would break code on any + # platforms I I have no access to. + # * Another would be to not use the terminating $(nl) but that would + # require updating all the using code so it does not simply + # prepend this variable to its own commands. + # * I guess the cleanest solution would be to update Boost Jam to + # allow explicitly specifying \n & \r characters in its scripts + # instead of always relying only on the 'current OS native newline + # sequence'. + # + # Some code found to depend on this behaviour: + # * This Boost Build module. + # * __test__ rule. + # * path-variable-setting-command rule. + # * python.jam toolset. + # * xsltproc.jam toolset. + # * fop.jam toolset. + # (todo) (07.07.2008.) (Jurko) + # + # I think that this works correctly in python -- Steven Watanabe + return variable + "=" + value + os.linesep + "export " + variable + os.linesep + +def path_variable_setting_command(variable, paths): + """ + Returns a command to sets a named shell path variable to the given NATIVE + paths on the current platform. + """ + assert(isinstance(variable, str)) + assert(isinstance(paths, list)) + sep = os.path.pathsep + return variable_setting_command(variable, sep.join(paths)) + +def prepend_path_variable_command(variable, paths): + """ + Returns a command that prepends the given paths to the named path variable on + the current platform. + """ + return path_variable_setting_command(variable, + paths + os.environ.get(variable, "").split(os.pathsep)) + +def file_creation_command(): + """ + Return a command which can create a file. If 'r' is result of invocation, then + 'r foobar' will create foobar with unspecified content. What happens if file + already exists is unspecified. + """ + if os_name() == 'NT': + return "echo. > " + else: + return "touch " + +#FIXME: global variable +__mkdir_set = set() +__re_windows_drive = re.compile(r'^.*:\$') + +def mkdir(engine, target): + # If dir exists, do not update it. Do this even for $(DOT). + bjam.call('NOUPDATE', target) + + global __mkdir_set + + # FIXME: Where is DOT defined? + #if $(<) != $(DOT) && ! $($(<)-mkdir): + if target != '.' and target not in __mkdir_set: + # Cheesy gate to prevent multiple invocations on same dir. + __mkdir_set.add(target) + + # Schedule the mkdir build action. + if os_name() == 'NT': + engine.set_update_action("common.MkDir1-quick-fix-for-windows", target, []) + else: + engine.set_update_action("common.MkDir1-quick-fix-for-unix", target, []) + + # Prepare a Jam 'dirs' target that can be used to make the build only + # construct all the target directories. + engine.add_dependency('dirs', target) + + # Recursively create parent directories. $(<:P) = $(<)'s parent & we + # recurse until root. + + s = os.path.dirname(target) + if os_name() == 'NT': + if(__re_windows_drive.match(s)): + s = '' + + if s: + if s != target: + engine.add_dependency(target, s) + mkdir(engine, s) + else: + bjam.call('NOTFILE', s) + +__re_version = re.compile(r'^([^.]+)[.]([^.]+)[.]?([^.]*)') + +def format_name(format, name, target_type, prop_set): + """ Given a target, as given to a custom tag rule, returns a string formatted + according to the passed format. Format is a list of properties that is + represented in the result. For each element of format the corresponding target + information is obtained and added to the result string. For all, but the + literal, the format value is taken as the as string to prepend to the output + to join the item to the rest of the result. If not given "-" is used as a + joiner. + + The format options can be: + + [joiner] + :: The basename of the target name. + [joiner] + :: The abbreviated toolset tag being used to build the target. + [joiner] + :: Indication of a multi-threaded build. + [joiner] + :: Collective tag of the build runtime. + [joiner] + :: Short version tag taken from the given "version-feature" + in the build properties. Or if not present, the literal + value as the version number. + [joiner] + :: Direct lookup of the given property-name value in the + build properties. /property-name/ is a regular expression. + e.g. will match every toolset. + /otherwise/ + :: The literal value of the format argument. + + For example this format: + + boost_ + + Might return: + + boost_thread-vc80-mt-gd-1_33.dll, or + boost_regex-vc80-gd-1_33.dll + + The returned name also has the target type specific prefix and suffix which + puts it in a ready form to use as the value from a custom tag rule. + """ + assert(isinstance(format, list)) + assert(isinstance(name, str)) + assert(isinstance(target_type, str) or not type) + # assert(isinstance(prop_set, property_set.PropertySet)) + if type.is_derived(target_type, 'LIB'): + result = "" ; + for f in format: + grist = get_grist(f) + if grist == '': + result += os.path.basename(name) + elif grist == '': + result += join_tag(ungrist(f), + toolset_tag(name, target_type, prop_set)) + elif grist == '': + result += join_tag(ungrist(f), + threading_tag(name, target_type, prop_set)) + elif grist == '': + result += join_tag(ungrist(f), + runtime_tag(name, target_type, prop_set)) + elif grist.startswith('') + if not version: + version = key + version = __re_version.match(version) + result += join_tag(ungrist(f), version[1] + '_' + version[2]) + elif grist.startswith('') + p0 = None + for prop in prop_set.raw(): + match = property_re.match(prop) + if match: + p0 = match[1] + break + if p0: + p = prop_set.get('<' + p0 + '>') + if p: + assert(len(p) == 1) + result += join_tag(ungrist(f), p) + else: + result += ungrist(f) + + result = virtual_target.add_prefix_and_suffix( + ''.join(result), target_type, prop_set) + return result + +def join_tag(joiner, tag): + if not joiner: joiner = '-' + return joiner + tag + +__re_toolset_version = re.compile(r"(\d+)[.](\d*)") + +def toolset_tag(name, target_type, prop_set): + tag = '' + + properties = prop_set.raw() + tools = prop_set.get('') + assert(len(tools) == 0) + tools = tools[0] + if tools.startswith('borland'): tag += 'bcb' + elif tools.startswith('como'): tag += 'como' + elif tools.startswith('cw'): tag += 'cw' + elif tools.startswith('darwin'): tag += 'xgcc' + elif tools.startswith('edg'): tag += edg + elif tools.startswith('gcc'): + flavor = prop_set.get('') + ''.find + if flavor.find('mingw') != -1: + tag += 'mgw' + else: + tag += 'gcc' + elif tools == 'intel': + if prop_set.get('') == ['win']: + tag += 'iw' + else: + tag += 'il' + elif tools.startswith('kcc'): tag += 'kcc' + elif tools.startswith('kylix'): tag += 'bck' + #case metrowerks* : tag += cw ; + #case mingw* : tag += mgw ; + elif tools.startswith('mipspro'): tag += 'mp' + elif tools.startswith('msvc'): tag += 'vc' + elif tools.startswith('sun'): tag += 'sw' + elif tools.startswith('tru64cxx'): tag += 'tru' + elif tools.startswith('vacpp'): tag += 'xlc' + + for prop in properties: + match = __re_toolset_version.match(prop) + if(match): + version = match + break + version_string = None + # For historical reasons, vc6.0 and vc7.0 use different naming. + if tag == 'vc': + if version.group(1) == '6': + # Cancel minor version. + version_string = '6' + elif version.group(1) == '7' and version.group(2) == '0': + version_string = '7' + + # On intel, version is not added, because it does not matter and it's the + # version of vc used as backend that matters. Ideally, we'd encode the + # backend version but that would break compatibility with V1. + elif tag == 'iw': + version_string = '' + + # On borland, version is not added for compatibility with V1. + elif tag == 'bcb': + version_string = '' + + if version_string is None: + version = version.group(1) + version.group(2) + + tag += version + + return tag + + +def threading_tag(name, target_type, prop_set): + tag = '' + properties = prop_set.raw() + if 'multi' in properties: tag = 'mt' + + return tag + + +def runtime_tag(name, target_type, prop_set ): + tag = '' + + properties = prop_set.raw() + if 'static' in properties: tag += 's' + + # This is an ugly thing. In V1, there's a code to automatically detect which + # properties affect a target. So, if does not affect gcc + # toolset, the tag rules won't even see . Similar + # functionality in V2 is not implemented yet, so we just check for toolsets + # which are known to care about runtime debug. + if 'msvc' in properties \ + or 'stlport' in properties \ + or 'win' in properties: + if 'on' in properties: tag += 'g' + + if 'on' in properties: tag += 'y' + if 'debug' in properties: tag += 'd' + if 'stlport' in properties: tag += 'p' + if 'hostios' in properties: tag += 'n' + + return tag + + +## TODO: +##rule __test__ ( ) +##{ +## import assert ; +## +## local nl = " +##" ; +## +## local save-os = [ modules.peek os : .name ] ; +## +## modules.poke os : .name : LINUX ; +## +## assert.result "PATH=foo:bar:baz$(nl)export PATH$(nl)" +## : path-variable-setting-command PATH : foo bar baz ; +## +## assert.result "PATH=foo:bar:$PATH$(nl)export PATH$(nl)" +## : prepend-path-variable-command PATH : foo bar ; +## +## modules.poke os : .name : NT ; +## +## assert.result "set PATH=foo;bar;baz$(nl)" +## : path-variable-setting-command PATH : foo bar baz ; +## +## assert.result "set PATH=foo;bar;%PATH%$(nl)" +## : prepend-path-variable-command PATH : foo bar ; +## +## modules.poke os : .name : $(save-os) ; +##} + +def init(manager): + engine = manager.engine() + + engine.register_action("common.MkDir1-quick-fix-for-unix", 'mkdir -p "$(<)"') + engine.register_action("common.MkDir1-quick-fix-for-windows", 'if not exist "$(<)\\" mkdir "$(<)"') + + import b2.tools.make + import b2.build.alias + + global __RM, __CP, __IGNORE, __LN + # ported from trunk@47281 + if os_name() == 'NT': + __RM = 'del /f /q' + __CP = 'copy' + __IGNORE = '2>nul >nul & setlocal' + __LN = __CP + #if not __LN: + # __LN = CP + else: + __RM = 'rm -f' + __CP = 'cp' + __IGNORE = '' + __LN = 'ln' + + engine.register_action("common.Clean", __RM + ' "$(>)"', + flags=['piecemeal', 'together', 'existing']) + engine.register_action("common.copy", __CP + ' "$(>)" "$(<)"') + engine.register_action("common.RmTemps", __RM + ' "$(>)" ' + __IGNORE, + flags=['quietly', 'updated', 'piecemeal', 'together']) + + engine.register_action("common.hard-link", + __RM + ' "$(<)" 2$(NULL_OUT) $(NULL_OUT)' + os.linesep + + __LN + ' "$(>)" "$(<)" $(NULL_OUT)') -- cgit v1.2.3