From 4b9905d5ae7ef3ae999da8a179432e66652390e9 Mon Sep 17 00:00:00 2001 From: Mathew Robinson Date: Fri, 24 Jan 2020 16:27:58 -0500 Subject: [PATCH 01/12] [WIP] make install a command generator --- src/engine/SCons/Tool/install.py | 64 ++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index dcb3581a47..eb10ed659a 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -177,9 +177,9 @@ def installFunc(target, source, env): """Install a source file into a target using the function specified as the INSTALL construction variable.""" try: - install = env['INSTALL'] + install = env['COPYFUNC'] except KeyError: - raise SCons.Errors.UserError('Missing INSTALL construction variable.') + raise SCons.Errors.UserError('Missing COPYFUNC construction variable.') assert len(target)==len(source), \ "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) @@ -267,11 +267,34 @@ def Dir(self, name): name = SCons.Util.make_path_relative(name) return self.dir.Dir(name) + +def install_command_generator(env, target, source, for_signature=False): + if not SCons.Util.is_List(target): + target = [target] + + if not SCons.Util.is_List(source): + source = [source] + + if not for_signature: + for t in target: + if t.isfile(): + t.remove() + + if len(target) == 1: + copyingdirs = [s for s in source if s.isdir()] + if copyingdirs: + return '$DIRCOPY $DIRCOPYFLAGS $SOURCES.abspath $TARGET' + else: + return '$COPY $COPYFLAGS $SOURCES.abspath $TARGET' + else: + return '$INSTALLFUNC' + + # # The Builder Definition # -install_action = SCons.Action.Action(installFunc, stringFunc) -installas_action = SCons.Action.Action(installFunc, stringFunc) +install_action = SCons.Action.Action(install_command_generator, stringFunc, generator=True) +installas_action = SCons.Action.Action(install_command_generator, stringFunc, generator=True) installVerLib_action = SCons.Action.Action(installFuncVersionedLib, stringFunc) BaseInstallBuilder = None @@ -408,10 +431,35 @@ def generate(env): #except KeyError: # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' - try: - env['INSTALL'] - except KeyError: - env['INSTALL'] = copyFunc + if 'COPYFUNC' not in env: + env['COPYFUNC'] = copyFunc + + if 'COPY' not in env: + if env['PLATFORM'] == 'win32': + # Xcopy is a builtin windows tool that has more power than + # the regular copy command such as supporting directories. + env['COPY'] = 'Xcopy' + env['COPYFLAGS'] = [ + '/o', # Copies file ownership and discretionary access control list information. + '/x', # Copies file audit settings and system access control list information. + ] + else: + env['COPY'] = 'cp' + env['COPYFLAGS'] = [ + '--preserve=all', + ] + + if 'DIRCOPY' not in env: + env['DIRCOPYFLAGS'] = env['COPYFLAGS'][:] + if env['PLATFORM'] == 'win32': + env['DIRCOPY'] = 'Xcopy' + env['DIRCOPYFLAGS'].extend([ + '/s', + '/e', + ]) + else: + env['DIRCOPY'] = 'cp' + env['DIRCOPYFLAGS'].append('--recursive') try: env['INSTALLVERSIONEDLIB'] From 9dd6f7dde7346222040b7d863e2d2ee8094808f9 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 24 Feb 2020 15:12:21 -0500 Subject: [PATCH 02/12] Add INSTALLFILECOPY, INSTALLFILECOPYFLAGS, INSTALLDIRCOPYFLAGS, INSTALLDIRCOPY for use by Install() and InstallAs() --- src/engine/SCons/Platform/__init__.py | 4 ++- src/engine/SCons/Platform/linux.py | 50 +++++++++++++++++++++++++++ src/engine/SCons/Platform/posix.py | 14 ++++++-- src/engine/SCons/Platform/win32.py | 14 ++++++++ 4 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 src/engine/SCons/Platform/linux.py diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py index 5e9a3584d9..7f12454e9e 100644 --- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -68,7 +68,9 @@ def platform_default(): if osname == 'java': osname = os._osType if osname == 'posix': - if sys.platform == 'cygwin': + if sys.platform == 'linux': + return 'linux' + elif sys.platform == 'cygwin': return 'cygwin' elif sys.platform.find('irix') != -1: return 'irix' diff --git a/src/engine/SCons/Platform/linux.py b/src/engine/SCons/Platform/linux.py new file mode 100644 index 0000000000..1e392c583b --- /dev/null +++ b/src/engine/SCons/Platform/linux.py @@ -0,0 +1,50 @@ +"""engine.SCons.Platform.linux + +Platform-specific initialization for Linux systems. + +There normally shouldn't be any need to import this module directly. It +will usually be imported through the generic SCons.Platform.Platform() +selection method. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from . import posix + + +def generate(env): + posix.generate(env) + + # Flags for INSTALL + env['INSTALLFILECOPYFLAGS'] = ['--preserve=all'] + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] + env['INSTALLDIRCOPYFLAGS'].append('--recursive') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py index ad4e85911a..438a8ceedd 100644 --- a/src/engine/SCons/Platform/posix.py +++ b/src/engine/SCons/Platform/posix.py @@ -110,8 +110,9 @@ def generate(env): env['ESCAPE'] = escape env['TEMPFILE'] = TempFileMunge env['TEMPFILEPREFIX'] = '@' - #Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion - #Note: specific platforms might rise or lower this value + + # Based on LINUX: ARG_MAX=ARG_MAX=131072 - 3000 for environment expansion + # Note: specific platforms might increase or decrease this value env['MAXLINELENGTH'] = 128072 # This platform supports RPATH specifications. @@ -121,6 +122,15 @@ def generate(env): # Must be able to have GCC and DMD work in the same build, so: env['__DRPATH'] = '$_DRPATH' + # Flags for INSTALL + env['INSTALLFILECOPY'] = 'cp' + env['INSTALLFILECOPYFLAGS'] = ['-p'] + + env['INSTALLDIRCOPY'] = 'cp' + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] + env['INSTALLDIRCOPYFLAGS'].append('-r') + + if enable_virtualenv and not ignore_virtualenv: ImportVirtualenv(env) diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index bb1b46df64..d810293437 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -484,6 +484,20 @@ def generate(env): if enable_virtualenv and not ignore_virtualenv: ImportVirtualenv(env) + # Flags for INSTALL + env['INSTALLFILECOPY'] = 'Xcopy' + env['INSTALLFILECOPYFLAGS'] =[ + '/o', # Copies file ownership and discretionary access control list information. + '/x', # Copies file audit settings and system access control list information. + ] + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPY'][:] + env['INSTALLDIRCOPYFLAGS'].extend([ + '/s', + '/e', + ]) + env['INSTALLDIRCOPY'] = 'Xcopy' + + # Local Variables: # tab-width:4 From 94a7e9a859846c85e626ee7e9b11caee2ccc4688 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 24 Feb 2020 15:13:09 -0500 Subject: [PATCH 03/12] Add INSTALLFILECOPY, INSTALLFILECOPYFLAGS, INSTALLDIRCOPYFLAGS, INSTALLDIRCOPY for use by Install() and InstallAs() and purged platform specific settings from install.py --- src/engine/SCons/Tool/install.py | 33 +++----------------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index eb10ed659a..23ceb4d50e 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -283,9 +283,9 @@ def install_command_generator(env, target, source, for_signature=False): if len(target) == 1: copyingdirs = [s for s in source if s.isdir()] if copyingdirs: - return '$DIRCOPY $DIRCOPYFLAGS $SOURCES.abspath $TARGET' + return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES.abspath $TARGET' else: - return '$COPY $COPYFLAGS $SOURCES.abspath $TARGET' + return '$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES.abspath $TARGET' else: return '$INSTALLFUNC' @@ -293,7 +293,7 @@ def install_command_generator(env, target, source, for_signature=False): # # The Builder Definition # -install_action = SCons.Action.Action(install_command_generator, stringFunc, generator=True) +install_action = SCons.Action.Action(install_command_generator, generator=True) installas_action = SCons.Action.Action(install_command_generator, stringFunc, generator=True) installVerLib_action = SCons.Action.Action(installFuncVersionedLib, stringFunc) @@ -434,33 +434,6 @@ def generate(env): if 'COPYFUNC' not in env: env['COPYFUNC'] = copyFunc - if 'COPY' not in env: - if env['PLATFORM'] == 'win32': - # Xcopy is a builtin windows tool that has more power than - # the regular copy command such as supporting directories. - env['COPY'] = 'Xcopy' - env['COPYFLAGS'] = [ - '/o', # Copies file ownership and discretionary access control list information. - '/x', # Copies file audit settings and system access control list information. - ] - else: - env['COPY'] = 'cp' - env['COPYFLAGS'] = [ - '--preserve=all', - ] - - if 'DIRCOPY' not in env: - env['DIRCOPYFLAGS'] = env['COPYFLAGS'][:] - if env['PLATFORM'] == 'win32': - env['DIRCOPY'] = 'Xcopy' - env['DIRCOPYFLAGS'].extend([ - '/s', - '/e', - ]) - else: - env['DIRCOPY'] = 'cp' - env['DIRCOPYFLAGS'].append('--recursive') - try: env['INSTALLVERSIONEDLIB'] except KeyError: From d010847073ac8e8fad202e7ae7ccaf8ad351694a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 2 Mar 2020 07:34:33 -0800 Subject: [PATCH 04/12] Add ${NODETYPE.type} which will return directory, file, or other --- src/engine/SCons/Node/FS.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 6f16256324..e0fdbc0122 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -508,6 +508,16 @@ def __get_rsrcdir(self): def __get_dir(self): return EntryProxy(self.get().dir) + def __get_node_type(self): + actual = self.get() + if isinstance(actual, Dir): + return "directory" + elif isinstance(actual, File): + return "file" + else: + return "other" + + dictSpecialAttrs = { "base" : __get_base_path, "posix" : __get_posix_path, "windows" : __get_windows_path, @@ -521,6 +531,7 @@ def __get_dir(self): "file" : __get_file, "rsrcpath" : __get_rsrcnode, "rsrcdir" : __get_rsrcdir, + "type" : __get_node_type, } def __getattr__(self, name): From 7df78c9dcb674f22c14e6416ff9955640771a879 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 2 Mar 2020 07:36:39 -0800 Subject: [PATCH 05/12] Continued work on GitHub PR #3535. Change Install() and InstallAs() logic to use a generator to create the command. Break out variables for INSTALL{FILE,DIR}COPY[FLAGS] as a different command is run by default for copying files and directories. Some doc updates. --- src/engine/SCons/Platform/posix.py | 9 - src/engine/SCons/Platform/win32.py | 15 -- src/engine/SCons/Tool/install.py | 195 ++++++++++++------ src/engine/SCons/Tool/install.xml | 305 ++++++++++++++++++----------- test/Install/directories.py | 47 ++--- 5 files changed, 346 insertions(+), 225 deletions(-) diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py index 438a8ceedd..2181a33e0c 100644 --- a/src/engine/SCons/Platform/posix.py +++ b/src/engine/SCons/Platform/posix.py @@ -122,15 +122,6 @@ def generate(env): # Must be able to have GCC and DMD work in the same build, so: env['__DRPATH'] = '$_DRPATH' - # Flags for INSTALL - env['INSTALLFILECOPY'] = 'cp' - env['INSTALLFILECOPYFLAGS'] = ['-p'] - - env['INSTALLDIRCOPY'] = 'cp' - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] - env['INSTALLDIRCOPYFLAGS'].append('-r') - - if enable_virtualenv and not ignore_virtualenv: ImportVirtualenv(env) diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index d810293437..922bcf96b6 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -484,21 +484,6 @@ def generate(env): if enable_virtualenv and not ignore_virtualenv: ImportVirtualenv(env) - # Flags for INSTALL - env['INSTALLFILECOPY'] = 'Xcopy' - env['INSTALLFILECOPYFLAGS'] =[ - '/o', # Copies file ownership and discretionary access control list information. - '/x', # Copies file audit settings and system access control list information. - ] - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPY'][:] - env['INSTALLDIRCOPYFLAGS'].extend([ - '/s', - '/e', - ]) - env['INSTALLDIRCOPY'] = 'Xcopy' - - - # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 23ceb4d50e..a4c7c5a0dd 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -47,9 +47,11 @@ _INSTALLED_FILES = [] _UNIQUE_INSTALLED_FILES = None + class CopytreeError(EnvironmentError): pass + # This is a patched version of shutil.copytree from python 2.5. It # doesn't fail if the dir exists, which regular copytree does # (annoyingly). Note the XXX comment in the docstring. @@ -111,7 +113,8 @@ def copyFunc(dest, source, env): if os.path.isdir(source): if os.path.exists(dest): if not os.path.isdir(dest): - raise SCons.Errors.UserError("cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) + raise SCons.Errors.UserError( + "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) else: parent = os.path.split(dest)[0] if not os.path.exists(parent): @@ -124,6 +127,7 @@ def copyFunc(dest, source, env): return 0 + # # Functions doing the actual work of the InstallVersionedLib Builder. # @@ -133,7 +137,7 @@ def copyFuncVersionedLib(dest, source, env): required symlinks.""" if os.path.isdir(source): - raise SCons.Errors.UserError("cannot install directory `%s' as a version library" % str(source) ) + raise SCons.Errors.UserError("cannot install directory `%s' as a version library" % str(source)) else: # remove the link if it is already there try: @@ -147,22 +151,24 @@ def copyFuncVersionedLib(dest, source, env): return 0 + def listShlibLinksToInstall(dest, source, env): install_links = [] source = env.arg2nodes(source) dest = env.fs.File(dest) install_dir = dest.get_dir() for src in source: - symlinks = getattr(getattr(src,'attributes',None), 'shliblinks', None) + symlinks = getattr(getattr(src, 'attributes', None), 'shliblinks', None) if symlinks: for link, linktgt in symlinks: link_base = os.path.basename(link.get_path()) - linktgt_base = os.path.basename(linktgt.get_path()) + linktgt_base = os.path.basename(linktgt.get_path()) install_link = env.fs.File(link_base, install_dir) install_linktgt = env.fs.File(linktgt_base, install_dir) install_links.append((install_link, install_linktgt)) return install_links + def installShlibLinks(dest, source, env): """If we are installing a versioned shared library create the required links.""" Verbose = False @@ -173,22 +179,25 @@ def installShlibLinks(dest, source, env): SCons.Tool.CreateLibSymlinks(env, symlinks) return -def installFunc(target, source, env): + +def legacy_install_function(target, source, env): """Install a source file into a target using the function specified as the INSTALL construction variable.""" try: - install = env['COPYFUNC'] + install = env['INSTALL'] except KeyError: raise SCons.Errors.UserError('Missing COPYFUNC construction variable.') - assert len(target)==len(source), \ - "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) - for t,s in zip(target,source): - if install(t.get_path(),s.get_path(),env): + assert len(target) == len(source), \ + "Installing source %s into target %s: target and source lists must have same length." % ( + list(map(str, source)), list(map(str, target))) + for t, s in zip(target, source): + if install(t.get_path(), s.get_path(), env): return 1 return 0 + def installFuncVersionedLib(target, source, env): """Install a versioned library into a target using the function specified as the INSTALLVERSIONEDLIB construction variable.""" @@ -197,29 +206,32 @@ def installFuncVersionedLib(target, source, env): except KeyError: raise SCons.Errors.UserError('Missing INSTALLVERSIONEDLIB construction variable.') - assert len(target)==len(source), \ - "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) - for t,s in zip(target,source): + assert len(target) == len(source), \ + "Installing source %s into target %s: target and source lists must have same length." % ( + list(map(str, source)), list(map(str, target))) + for t, s in zip(target, source): if hasattr(t.attributes, 'shlibname'): tpath = os.path.join(t.get_dir(), t.attributes.shlibname) else: tpath = t.get_path() - if install(tpath,s.get_path(),env): + if install(tpath, s.get_path(), env): return 1 return 0 -def stringFunc(target, source, env): - installstr = env.get('INSTALLSTR') - if installstr: - return env.subst_target_source(installstr, 0, target, source) - target = str(target[0]) - source = str(source[0]) - if os.path.isdir(source): - type = 'directory' - else: - type = 'file' - return 'Install %s: "%s" as "%s"' % (type, source, target) + +# def stringFunc(target, source, env): +# installstr = env.get('INSTALLSTR') +# if installstr: +# return env.subst_target_source(installstr, 0, target, source) +# target = str(target[0]) +# source = str(source[0]) +# if os.path.isdir(source): +# type = 'directory' +# else: +# type = 'file' +# return 'Install %s: "%s" as "%s"' % (type, source, target) + # # Emitter functions @@ -235,6 +247,7 @@ def add_targets_to_INSTALLED_FILES(target, source, env): _UNIQUE_INSTALLED_FILES = None return (target, source) + def add_versioned_targets_to_INSTALLED_FILES(target, source, env): """ An emitter that adds all target files to the list stored in the _INSTALLED_FILES global variable. This way all installed files of one @@ -251,13 +264,15 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env): _UNIQUE_INSTALLED_FILES = None return (target, source) + class DESTDIR_factory(object): """ A node factory, where all files will be relative to the dir supplied in the constructor. """ + def __init__(self, env, dir): self.env = env - self.dir = env.arg2nodes( dir, env.fs.Dir )[0] + self.dir = env.arg2nodes(dir, env.fs.Dir)[0] def Entry(self, name): name = SCons.Util.make_path_relative(name) @@ -269,6 +284,9 @@ def Dir(self, name): def install_command_generator(env, target, source, for_signature=False): + if env.get('INSTALL', False): + return "$INSTALLFUNC" + if not SCons.Util.is_List(target): target = [target] @@ -283,28 +301,30 @@ def install_command_generator(env, target, source, for_signature=False): if len(target) == 1: copyingdirs = [s for s in source if s.isdir()] if copyingdirs: - return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES.abspath $TARGET' + return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES/ $TARGET' else: - return '$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES.abspath $TARGET' + return '$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET' else: return '$INSTALLFUNC' - + # # The Builder Definition # -install_action = SCons.Action.Action(install_command_generator, generator=True) -installas_action = SCons.Action.Action(install_command_generator, stringFunc, generator=True) -installVerLib_action = SCons.Action.Action(installFuncVersionedLib, stringFunc) +# stringFunc +install_action = SCons.Action.Action(install_command_generator, "$INSTALLSTR", generator=True) +installas_action = SCons.Action.Action(install_command_generator, "$INSTALLSTR", generator=True) +installVerLib_action = SCons.Action.Action(installFuncVersionedLib, "$INSTALLSTR") + +BaseInstallBuilder = None -BaseInstallBuilder = None def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): if target and dir: import SCons.Errors raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") if not dir: - dir=target + dir = target import SCons.Script install_sandbox = SCons.Script.GetOption('install_sandbox') @@ -316,7 +336,9 @@ def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): try: dnodes = env.arg2nodes(dir, target_factory.Dir) except TypeError: - raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) + raise SCons.Errors.UserError( + "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str( + dir)) sources = env.arg2nodes(source, env.fs.Entry) tgt = [] for dnode in dnodes: @@ -324,7 +346,7 @@ def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): # Prepend './' so the lookup doesn't interpret an initial # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. - target = env.fs.Entry('.'+os.sep+src.name, dnode) + target = env.fs.Entry('.' + os.sep + src.name, dnode) tgt.extend(BaseInstallBuilder(env, target, src, **kw)) return tgt @@ -335,6 +357,7 @@ def InstallAsBuilderWrapper(env, target=None, source=None, **kw): result.extend(BaseInstallBuilder(env, tgt, src, **kw)) return result + BaseVersionedInstallBuilder = None @@ -343,7 +366,7 @@ def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw import SCons.Errors raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") if not dir: - dir=target + dir = target import SCons.Script install_sandbox = SCons.Script.GetOption('install_sandbox') @@ -355,7 +378,9 @@ def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw try: dnodes = env.arg2nodes(dir, target_factory.Dir) except TypeError: - raise SCons.Errors.UserError("Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir)) + raise SCons.Errors.UserError( + "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str( + dir)) sources = env.arg2nodes(source, env.fs.Entry) tgt = [] for dnode in dnodes: @@ -363,15 +388,58 @@ def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw # Prepend './' so the lookup doesn't interpret an initial # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. - target = env.fs.Entry('.'+os.sep+src.name, dnode) + target = env.fs.Entry('.' + os.sep + src.name, dnode) tgt.extend(BaseVersionedInstallBuilder(env, target, src, **kw)) return tgt + added = None -def generate(env): +def set_posix_env_vars(env): + """ + Posix OS version + Setup env variables needed for file and dir copying used by Install and InstallAs + :param env: + :return: + """ + env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'cp') + env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', + ['-p']) # for verbose debugging , '-v'] + env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'cp') + + if not env.get('INSTALLDIRCOPYFLAGS', False): + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] + env['INSTALLDIRCOPYFLAGS'].append('-r') + + +def set_win32_env_vars(env): + """ + Win32 version + Setup env variables needed for file and dir copying used by Install and InstallAs + :param env: + :return: + """ + # Flags for INSTALL + env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'Xcopy') + env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', [ + '/o', # Copies file ownership and discretionary access control list information. + '/x', # Copies file audit settings and system access control list information. + ]) + + if not env.get('INSTALLDIRCOPYFLAGS', False): + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPY'][:] + env['INSTALLDIRCOPYFLAGS'].extend([ + '/s', # Copies directories and subdirectories, unless they are empty. + # If you omit /s, xcopy works within a single directory. + '/e', # Copies all subdirectories, even if they are empty. + # Use /e with the /s and /t command-line options. + ]) + env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'Xcopy') + + +def generate(env): from SCons.Script import AddOption, GetOption global added if not added: @@ -391,13 +459,13 @@ def generate(env): target_factory = env.fs BaseInstallBuilder = SCons.Builder.Builder( - action = install_action, - target_factory = target_factory.Entry, - source_factory = env.fs.Entry, - multi = 1, - emitter = [ add_targets_to_INSTALLED_FILES, ], - source_scanner = SCons.Scanner.Base( {}, name = 'Install', recursive = False ), - name = 'InstallBuilder') + action=install_action, + target_factory=target_factory.Entry, + source_factory=env.fs.Entry, + multi=1, + emitter=[add_targets_to_INSTALLED_FILES, ], + source_scanner=SCons.Scanner.Base({}, name='Install', recursive=False), + name='InstallBuilder') global BaseVersionedInstallBuilder if BaseVersionedInstallBuilder is None: @@ -408,12 +476,12 @@ def generate(env): target_factory = env.fs BaseVersionedInstallBuilder = SCons.Builder.Builder( - action = installVerLib_action, - target_factory = target_factory.Entry, - source_factory = env.fs.Entry, - multi = 1, - emitter = [ add_versioned_targets_to_INSTALLED_FILES, ], - name = 'InstallVersionedBuilder') + action=installVerLib_action, + target_factory=target_factory.Entry, + source_factory=env.fs.Entry, + multi=1, + emitter=[add_versioned_targets_to_INSTALLED_FILES, ], + name='InstallVersionedBuilder') env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper @@ -426,18 +494,25 @@ def generate(env): # the stringFunc() that we put in the action fall back to the # hand-crafted default string if it's not set. # - #try: - # env['INSTALLSTR'] - #except KeyError: - # env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' + try: + env['INSTALLSTR'] + except KeyError: + env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' - if 'COPYFUNC' not in env: - env['COPYFUNC'] = copyFunc + env['INSTALLFUNC'] = legacy_install_function + + env['INSTALL'] = env.get('INSTALL', None) + + if env['PLATFORM'] == 'win32': + set_win32_env_vars(env) + else: + set_posix_env_vars(env) try: env['INSTALLVERSIONEDLIB'] except KeyError: - env['INSTALLVERSIONEDLIB'] = copyFuncVersionedLib + env['INSTALLVERSIONEDLIB'] = copyFuncVersionedLib + def exists(env): return 1 diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml index ce70d91139..3113a7aa71 100644 --- a/src/engine/SCons/Tool/install.xml +++ b/src/engine/SCons/Tool/install.xml @@ -7,129 +7,198 @@ See its __doc__ string for a discussion of the format. --> -%scons; - -%builders-mod; - -%functions-mod; - -%tools-mod; - -%variables-mod; -]> + + %scons; + + %builders-mod; + + %functions-mod; + + %tools-mod; + + %variables-mod; + ]> - - - -Sets construction variables for file -and directory installation. - - - -INSTALL -INSTALLSTR - - - - - - -Installs one or more source files or directories -in the specified target, -which must be a directory. -The names of the specified source files or directories -remain the same within the destination directory. The -sources may be given as a string or as a node returned by -a builder. - - - -env.Install(target='/usr/local/bin', source=['foo', 'bar']) - - - -Note that if target paths chosen for the -&Install; builder (and the related &InstallAs; and -&InstallVersionedLib; builders) are outside the -project tree, such as in the example above, -they may not be selected for "building" by default, -since in the absence of other instructions -&scons; builds targets that are underneath the top directory -(the directory that contains the &SConstruct; file, -usually the current directory). -Use command line targets or the &Default; function -in this case. - - - -If the command line -option is given, the target directory will be prefixed -by the directory path specified. -This is useful to test installs without installing to -a "live" location in the system. - - - -See also &FindInstalledFiles;. -For more thoughts on installation, see the User Guide -(particularly the section on Command-Line Targets -and the chapters on Installing Files and on Alias Targets). - - - - - - - - -Installs one or more source files or directories -to specific names, -allowing changing a file or directory name -as part of the installation. -It is an error if the -target -and -source -arguments list different numbers of files or directories. - - - -env.InstallAs(target='/usr/local/bin/foo', - source='foo_debug') -env.InstallAs(target=['../lib/libfoo.a', '../lib/libbar.a'], - source=['libFOO.a', 'libBAR.a']) - - - -See the note under &Install;. - - - - - - - - -Installs a versioned shared library. The symlinks appropriate to the -architecture will be generated based on symlinks of the source library. - - - -env.InstallVersionedLib(target='/usr/local/bin/foo', - source='libxyz.1.5.2.so') - - - -See the note under &Install;. - - - - + + + + Sets construction variables for file + and directory installation. + + + + INSTALL + INSTALLSTR + INSTALLFILECOPY + INSTALLFILECOPYFLAGS + INSTALLDIRCOPY + INSTALLDIRCOPYFLAGS + + + + + + + The command to call to copy a file as part of the Install() and InstallAs() builders. + + + For Posix systems defaults to + cp + + + For Win32 systems defaults to + Xcopy + + + + + + + + Flags to add to the command line for copying a file as part of the Install() and InstallAs() builders. + + + For Posix systems defaults to + -p + + + For Win32 systems defaults to + /o /x + + + + + + + + The command to call to copy a directory tree as part of the Install() and InstallAs() builders. + + + For Posix systems defaults to + cp + + + For Win32 systems defaults to + Xcopy + + + + + + + + Flags to add to the command line for copying a directory tree as part of the Install() and InstallAs() + builders. + + + For Posix systems defaults to + -p -r + + + For Win32 systems defaults to + /o /x /s /e + + + + + + + + Installs one or more source files or directories + in the specified target, + which must be a directory. + The names of the specified source files or directories + remain the same within the destination directory. The + sources may be given as a string or as a node returned by + a builder. + + + + env.Install(target='/usr/local/bin', source=['foo', 'bar']) + + + + Note that if target paths chosen for the + &Install; builder (and the related &InstallAs; and + &InstallVersionedLib; builders) are outside the + project tree, such as in the example above, + they may not be selected for "building" by default, + since in the absence of other instructions + &scons; builds targets that are underneath the top directory + (the directory that contains the &SConstruct; file, + usually the current directory). + Use command line targets or the &Default; function + in this case. + + + + If the command line + option is given, the target directory will be prefixed + by the directory path specified. + This is useful to test installs without installing to + a "live" location in the system. + + + + See also &FindInstalledFiles;. + For more thoughts on installation, see the User Guide + (particularly the section on Command-Line Targets + and the chapters on Installing Files and on Alias Targets). + + + + + + + + + Installs one or more source files or directories + to specific names, + allowing changing a file or directory name + as part of the installation. + It is an error if the + target + and + source + arguments list different numbers of files or directories. + + + + env.InstallAs(target='/usr/local/bin/foo', + source='foo_debug') + env.InstallAs(target=['../lib/libfoo.a', '../lib/libbar.a'], + source=['libFOO.a', 'libBAR.a']) + + + + See the note under &Install;. + + + + + + + + + Installs a versioned shared library. The symlinks appropriate to the + architecture will be generated based on symlinks of the source library. + + + + env.InstallVersionedLib(target='/usr/local/bin/foo', + source='libxyz.1.5.2.so') + + + + See the note under &Install;. + + + + diff --git a/test/Install/directories.py b/test/Install/directories.py index 3ebc7139eb..d628166f49 100644 --- a/test/Install/directories.py +++ b/test/Install/directories.py @@ -56,41 +56,42 @@ env.InstallAs('../outside/d4', 'dir4') """) -test.write(['work', 'f1'], "work/f1\n") -test.write(['work', 'dir1', 'f2'], "work/dir1/f2\n") -test.write(['work', 'dir1', 'sub', 'f3'], "work/dir1/sub/f3\n") -test.write(['work', 'dir2', 'f4'], "work/dir2/f4\n") -test.write(['work', 'dir2', 'sub', 'f5'], "work/dir2/sub/f5\n") -test.write(['work', 'dir3', 'f6'], "work/dir3/f6\n") -test.write(['work', 'dir3', 'sub', 'f7'], "work/dir3/sub/f7\n") -test.write(['work', 'dir4', 'f8'], "work/dir4/f8\n") -test.write(['work', 'dir4', 'sub', 'f9'], "work/dir4/sub/f9\n") - +test.write(['work', 'f1'], "work/f1\n") +test.write(['work', 'dir1', 'f2'], "work/dir1/f2\n") +test.write(['work', 'dir1', 'sub', 'f3'], "work/dir1/sub/f3\n") +test.write(['work', 'dir2', 'f4'], "work/dir2/f4\n") +test.write(['work', 'dir2', 'sub', 'f5'], "work/dir2/sub/f5\n") +test.write(['work', 'dir3', 'f6'], "work/dir3/f6\n") +test.write(['work', 'dir3', 'sub', 'f7'], "work/dir3/sub/f7\n") +test.write(['work', 'dir4', 'f8'], "work/dir4/f8\n") +test.write(['work', 'dir4', 'sub', 'f9'], "work/dir4/sub/f9\n") arguments = [ - test.workpath('outside', 'dir1'), test.workpath('outside', 'd2'), - test.workpath('outside', 'dir3'), test.workpath('outside', 'd4'), + test.workpath('outside', 'dir1'), + test.workpath('outside', 'dir3'), ] expect = test.wrap_stdout("""\ -Install directory: "dir1" as "%s" Install directory: "dir2" as "%s" -Install directory: "dir3" as "%s" Install directory: "dir4" as "%s" +Install directory: "dir1" as "%s" +Install directory: "dir3" as "%s" """ % tuple(arguments)) -test.run(chdir = 'work', arguments = arguments, stdout = expect) -test.must_match(test.workpath('outside', 'dir1', 'f2'), "work/dir1/f2\n") -test.must_match(test.workpath('outside', 'dir1', 'sub', 'f3'), "work/dir1/sub/f3\n") -test.must_match(test.workpath('outside', 'd2', 'f4'), "work/dir2/f4\n") -test.must_match(test.workpath('outside', 'd2', 'sub', 'f5'), "work/dir2/sub/f5\n") -test.must_match(test.workpath('outside', 'dir3', 'f6'), "work/dir3/f6\n") -test.must_match(test.workpath('outside', 'dir3', 'sub', 'f7'), "work/dir3/sub/f7\n") -test.must_match(test.workpath('outside', 'd4', 'f8'), "work/dir4/f8\n") -test.must_match(test.workpath('outside', 'd4', 'sub', 'f9'), "work/dir4/sub/f9\n") + +test.run(chdir='work', arguments=arguments, stdout=expect) + +test.must_match(test.workpath('outside', 'dir1', 'f2'), "work/dir1/f2\n") +test.must_match(test.workpath('outside', 'dir1', 'sub', 'f3'), "work/dir1/sub/f3\n") +test.must_match(test.workpath('outside', 'd2', 'f4'), "work/dir2/f4\n") +test.must_match(test.workpath('outside', 'd2', 'sub', 'f5'), "work/dir2/sub/f5\n") +test.must_match(test.workpath('outside', 'dir3', 'f6'), "work/dir3/f6\n") +test.must_match(test.workpath('outside', 'dir3', 'sub', 'f7'), "work/dir3/sub/f7\n") +test.must_match(test.workpath('outside', 'd4', 'f8'), "work/dir4/f8\n") +test.must_match(test.workpath('outside', 'd4', 'sub', 'f9'), "work/dir4/sub/f9\n") test.pass_test() From e0b34b7b1a571765fb51d754d2c627d7b20a863c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 4 Mar 2020 17:21:07 -0800 Subject: [PATCH 06/12] Fix incorrect variable name and add note about the default flags current funtionality --- src/engine/SCons/Tool/install.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml index 3113a7aa71..96041aea6c 100644 --- a/src/engine/SCons/Tool/install.xml +++ b/src/engine/SCons/Tool/install.xml @@ -61,6 +61,7 @@ See its __doc__ string for a discussion of the format. Flags to add to the command line for copying a file as part of the Install() and InstallAs() builders. + The defaults below preserve ownership and file permissions. For Posix systems defaults to @@ -89,11 +90,12 @@ See its __doc__ string for a discussion of the format. - + Flags to add to the command line for copying a directory tree as part of the Install() and InstallAs() - builders. + builders. + The defaults below preserve ownership and file permissions. For Posix systems defaults to From 682a69eefaf89592b403ff4fc827ca9d0f4ab9ec Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 4 Mar 2020 17:21:45 -0800 Subject: [PATCH 07/12] fix dir copy flags to work for both Install and InstallAs. Move specialized linux copy flags from platform/linux to install.py --- src/engine/SCons/Platform/linux.py | 5 ----- src/engine/SCons/Tool/install.py | 27 +++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/engine/SCons/Platform/linux.py b/src/engine/SCons/Platform/linux.py index 1e392c583b..a4da3b569f 100644 --- a/src/engine/SCons/Platform/linux.py +++ b/src/engine/SCons/Platform/linux.py @@ -38,11 +38,6 @@ def generate(env): posix.generate(env) - # Flags for INSTALL - env['INSTALLFILECOPYFLAGS'] = ['--preserve=all'] - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] - env['INSTALLDIRCOPYFLAGS'].append('--recursive') - # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index a4c7c5a0dd..cefe44d09e 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -301,7 +301,7 @@ def install_command_generator(env, target, source, for_signature=False): if len(target) == 1: copyingdirs = [s for s in source if s.isdir()] if copyingdirs: - return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES/ $TARGET' + return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES/. $TARGET' else: return '$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET' else: @@ -405,7 +405,9 @@ def set_posix_env_vars(env): """ env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'cp') env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', - ['-p']) # for verbose debugging , '-v'] + ['-p', + # '-v', # for verbose debugging + ]) env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'cp') if not env.get('INSTALLDIRCOPYFLAGS', False): @@ -413,6 +415,25 @@ def set_posix_env_vars(env): env['INSTALLDIRCOPYFLAGS'].append('-r') +def set_linux_env_vars(env): + """ + Linux version + Setup env variables needed for file and dir copying used by Install and InstallAs + :param env: + :return: + """ + env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'cp') + env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', + ['--preserve=all', + # '--verbose', # for verbose debugging + ]) + env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'cp') + + if not env.get('INSTALLDIRCOPYFLAGS', False): + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] + env['INSTALLDIRCOPYFLAGS'].append('--recursive') + + def set_win32_env_vars(env): """ Win32 version @@ -505,6 +526,8 @@ def generate(env): if env['PLATFORM'] == 'win32': set_win32_env_vars(env) + if env['PLATFORM'] == 'linux': + set_linux_env_vars(env) else: set_posix_env_vars(env) From 9e8547be1d3451012e5f3798bced128224f16c03 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 4 Mar 2020 20:38:18 -0800 Subject: [PATCH 08/12] remove linux as separate platform. not needed and breaks tests. Fix a few tests where outputs changed --- src/engine/SCons/Platform/__init__.py | 4 +-- src/engine/SCons/Platform/linux.py | 45 --------------------------- src/engine/SCons/Tool/install.py | 11 ++++--- test/Errors/permission-denied.py | 10 ++++-- 4 files changed, 15 insertions(+), 55 deletions(-) delete mode 100644 src/engine/SCons/Platform/linux.py diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py index 7f12454e9e..5e9a3584d9 100644 --- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -68,9 +68,7 @@ def platform_default(): if osname == 'java': osname = os._osType if osname == 'posix': - if sys.platform == 'linux': - return 'linux' - elif sys.platform == 'cygwin': + if sys.platform == 'cygwin': return 'cygwin' elif sys.platform.find('irix') != -1: return 'irix' diff --git a/src/engine/SCons/Platform/linux.py b/src/engine/SCons/Platform/linux.py deleted file mode 100644 index a4da3b569f..0000000000 --- a/src/engine/SCons/Platform/linux.py +++ /dev/null @@ -1,45 +0,0 @@ -"""engine.SCons.Platform.linux - -Platform-specific initialization for Linux systems. - -There normally shouldn't be any need to import this module directly. It -will usually be imported through the generic SCons.Platform.Platform() -selection method. -""" - -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -from . import posix - - -def generate(env): - posix.generate(env) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index cefe44d09e..8e72604c1a 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -293,10 +293,11 @@ def install_command_generator(env, target, source, for_signature=False): if not SCons.Util.is_List(source): source = [source] - if not for_signature: - for t in target: - if t.isfile(): - t.remove() + # This get's run even when scons -n is run.. and deletes targets when it shouldn't.. + # if not for_signature: + # for t in target: + # if t.isfile(): + # t.remove() if len(target) == 1: copyingdirs = [s for s in source if s.isdir()] @@ -304,6 +305,8 @@ def install_command_generator(env, target, source, for_signature=False): return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES/. $TARGET' else: return '$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET' + # return 'rm -f $TARGET; $INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET' + else: return '$INSTALLFUNC' diff --git a/test/Errors/permission-denied.py b/test/Errors/permission-denied.py index 0134803286..13b1a35593 100644 --- a/test/Errors/permission-denied.py +++ b/test/Errors/permission-denied.py @@ -55,11 +55,15 @@ test.writable('test2.out', 1) test.description_set("Incorrect STDERR:\n%s" % test.stderr()) +stderr_lines = test.stderr().splitlines() +test.description_set("Incorrect STDERR:\n%s" % stderr_lines) + +#TODO need windows specific first line.. errs = [ - "scons: *** [test2.out] test2.out: Permission denied\n", - "scons: *** [test2.out] test2.out: permission denied\n", + "cp: cannot create regular file 'test2.out': Permission denied", + "scons: *** [test2.out] Error 1", ] -test.fail_test(test.stderr() not in errs) +test.fail_test(stderr_lines != errs) test.pass_test() From 633ebf5261d2e41b987e0d85f1a10a74b7676912 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 5 Mar 2020 09:36:53 -0800 Subject: [PATCH 09/12] removed unneeded imports/PEP8 --- test/Install/multi-dir/src/SConstruct | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Install/multi-dir/src/SConstruct b/test/Install/multi-dir/src/SConstruct index 05b46a96af..2b70d22cf4 100644 --- a/test/Install/multi-dir/src/SConstruct +++ b/test/Install/multi-dir/src/SConstruct @@ -1,9 +1,9 @@ # This tests for a bug where installing a sequence dirs and subdirs # outside the source tree can cause SCons to fail to create the dest # dir. -import os, os.path, shutil +import os.path DefaultEnvironment(tools=[]) env=Environment(tools=[]) dst='../build' -env.Install(os.path.join(dst,'__foo/bar/baz'), 'a') -env.Install(os.path.join(dst,'__foo/bar/baz/a/b'), 'x/y') +env.Install(os.path.join(dst, '__foo/bar/baz'), 'a') +env.Install(os.path.join(dst, '__foo/bar/baz/a/b'), 'x/y') From 82dcc501b4e164588d86547a3f5bc579e2b2581a Mon Sep 17 00:00:00 2001 From: William Deegan Date: Thu, 5 Mar 2020 09:39:46 -0800 Subject: [PATCH 10/12] [skip travis] Fix windows variable initializations. Incorrectly was copying INSTALLFILECOPY into INSTALLDIRCOPYFLAGS should have been INSTALLFILECOPYFLAGS --- src/engine/SCons/Tool/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 8e72604c1a..a6496b7916 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -452,7 +452,7 @@ def set_win32_env_vars(env): ]) if not env.get('INSTALLDIRCOPYFLAGS', False): - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPY'][:] + env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] env['INSTALLDIRCOPYFLAGS'].extend([ '/s', # Copies directories and subdirectories, unless they are empty. # If you omit /s, xcopy works within a single directory. From 9af8a3d210a2a600521d47bd2cdf6c1fec97b15e Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 8 Mar 2020 16:54:06 -0700 Subject: [PATCH 11/12] switch to copy from xcopy for file copies. TODO:Check if target file permissions/etc match source.. --- src/engine/SCons/Tool/install.py | 227 +++++++++++++++++++------------ 1 file changed, 138 insertions(+), 89 deletions(-) diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index a6496b7916..3a80cd785e 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -114,7 +114,9 @@ def copyFunc(dest, source, env): if os.path.exists(dest): if not os.path.isdir(dest): raise SCons.Errors.UserError( - "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))) + "cannot overwrite non-directory `%s' with a directory `%s'" + % (str(dest), str(source)) + ) else: parent = os.path.split(dest)[0] if not os.path.exists(parent): @@ -137,7 +139,9 @@ def copyFuncVersionedLib(dest, source, env): required symlinks.""" if os.path.isdir(source): - raise SCons.Errors.UserError("cannot install directory `%s' as a version library" % str(source)) + raise SCons.Errors.UserError( + "cannot install directory `%s' as a version library" % str(source) + ) else: # remove the link if it is already there try: @@ -158,7 +162,7 @@ def listShlibLinksToInstall(dest, source, env): dest = env.fs.File(dest) install_dir = dest.get_dir() for src in source: - symlinks = getattr(getattr(src, 'attributes', None), 'shliblinks', None) + symlinks = getattr(getattr(src, "attributes", None), "shliblinks", None) if symlinks: for link, linktgt in symlinks: link_base = os.path.basename(link.get_path()) @@ -174,7 +178,11 @@ def installShlibLinks(dest, source, env): Verbose = False symlinks = listShlibLinksToInstall(dest, source, env) if Verbose: - print('installShlibLinks: symlinks={!r}'.format(SCons.Tool.StringizeLibSymlinks(symlinks))) + print( + "installShlibLinks: symlinks={!r}".format( + SCons.Tool.StringizeLibSymlinks(symlinks) + ) + ) if symlinks: SCons.Tool.CreateLibSymlinks(env, symlinks) return @@ -184,13 +192,14 @@ def legacy_install_function(target, source, env): """Install a source file into a target using the function specified as the INSTALL construction variable.""" try: - install = env['INSTALL'] + install = env["INSTALL"] except KeyError: - raise SCons.Errors.UserError('Missing COPYFUNC construction variable.') + raise SCons.Errors.UserError("Missing COPYFUNC construction variable.") - assert len(target) == len(source), \ - "Installing source %s into target %s: target and source lists must have same length." % ( - list(map(str, source)), list(map(str, target))) + assert len(target) == len(source), ( + "Installing source %s into target %s: target and source lists must have same length." + % (list(map(str, source)), list(map(str, target))) + ) for t, s in zip(target, source): if install(t.get_path(), s.get_path(), env): return 1 @@ -202,15 +211,18 @@ def installFuncVersionedLib(target, source, env): """Install a versioned library into a target using the function specified as the INSTALLVERSIONEDLIB construction variable.""" try: - install = env['INSTALLVERSIONEDLIB'] + install = env["INSTALLVERSIONEDLIB"] except KeyError: - raise SCons.Errors.UserError('Missing INSTALLVERSIONEDLIB construction variable.') + raise SCons.Errors.UserError( + "Missing INSTALLVERSIONEDLIB construction variable." + ) - assert len(target) == len(source), \ - "Installing source %s into target %s: target and source lists must have same length." % ( - list(map(str, source)), list(map(str, target))) + assert len(target) == len(source), ( + "Installing source %s into target %s: target and source lists must have same length." + % (list(map(str, source)), list(map(str, target))) + ) for t, s in zip(target, source): - if hasattr(t.attributes, 'shlibname'): + if hasattr(t.attributes, "shlibname"): tpath = os.path.join(t.get_dir(), t.attributes.shlibname) else: tpath = t.get_path() @@ -257,7 +269,11 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env): Verbose = False _INSTALLED_FILES.extend(target) if Verbose: - print("add_versioned_targets_to_INSTALLED_FILES: target={!r}".format(list(map(str, target)))) + print( + "add_versioned_targets_to_INSTALLED_FILES: target={!r}".format( + list(map(str, target)) + ) + ) symlinks = listShlibLinksToInstall(target[0], source, env) if symlinks: SCons.Tool.EmitLibSymlinks(env, symlinks, target[0]) @@ -284,7 +300,7 @@ def Dir(self, name): def install_command_generator(env, target, source, for_signature=False): - if env.get('INSTALL', False): + if env.get("INSTALL", False): return "$INSTALLFUNC" if not SCons.Util.is_List(target): @@ -302,21 +318,25 @@ def install_command_generator(env, target, source, for_signature=False): if len(target) == 1: copyingdirs = [s for s in source if s.isdir()] if copyingdirs: - return '$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES/. $TARGET' + return "$INSTALLDIRCOPY $INSTALLDIRCOPYFLAGS $SOURCES/. $TARGET" else: - return '$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET' + return "$INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET" # return 'rm -f $TARGET; $INSTALLFILECOPY $INSTALLFILECOPYFLAGS $SOURCES $TARGET' else: - return '$INSTALLFUNC' + return "$INSTALLFUNC" # # The Builder Definition # # stringFunc -install_action = SCons.Action.Action(install_command_generator, "$INSTALLSTR", generator=True) -installas_action = SCons.Action.Action(install_command_generator, "$INSTALLSTR", generator=True) +install_action = SCons.Action.Action( + install_command_generator, "$INSTALLSTR", generator=True +) +installas_action = SCons.Action.Action( + install_command_generator, "$INSTALLSTR", generator=True +) installVerLib_action = SCons.Action.Action(installFuncVersionedLib, "$INSTALLSTR") BaseInstallBuilder = None @@ -325,12 +345,16 @@ def install_command_generator(env, target, source, for_signature=False): def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): if target and dir: import SCons.Errors - raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") + + raise SCons.Errors.UserError( + "Both target and dir defined for Install(), only one may be defined." + ) if not dir: dir = target import SCons.Script - install_sandbox = SCons.Script.GetOption('install_sandbox') + + install_sandbox = SCons.Script.GetOption("install_sandbox") if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: @@ -340,8 +364,9 @@ def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): dnodes = env.arg2nodes(dir, target_factory.Dir) except TypeError: raise SCons.Errors.UserError( - "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str( - dir)) + "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" + % str(dir) + ) sources = env.arg2nodes(source, env.fs.Entry) tgt = [] for dnode in dnodes: @@ -349,7 +374,7 @@ def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): # Prepend './' so the lookup doesn't interpret an initial # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. - target = env.fs.Entry('.' + os.sep + src.name, dnode) + target = env.fs.Entry("." + os.sep + src.name, dnode) tgt.extend(BaseInstallBuilder(env, target, src, **kw)) return tgt @@ -367,12 +392,16 @@ def InstallAsBuilderWrapper(env, target=None, source=None, **kw): def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw): if target and dir: import SCons.Errors - raise SCons.Errors.UserError("Both target and dir defined for Install(), only one may be defined.") + + raise SCons.Errors.UserError( + "Both target and dir defined for Install(), only one may be defined." + ) if not dir: dir = target import SCons.Script - install_sandbox = SCons.Script.GetOption('install_sandbox') + + install_sandbox = SCons.Script.GetOption("install_sandbox") if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: @@ -382,8 +411,9 @@ def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw dnodes = env.arg2nodes(dir, target_factory.Dir) except TypeError: raise SCons.Errors.UserError( - "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str( - dir)) + "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" + % str(dir) + ) sources = env.arg2nodes(source, env.fs.Entry) tgt = [] for dnode in dnodes: @@ -391,7 +421,7 @@ def InstallVersionedBuilderWrapper(env, target=None, source=None, dir=None, **kw # Prepend './' so the lookup doesn't interpret an initial # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. - target = env.fs.Entry('.' + os.sep + src.name, dnode) + target = env.fs.Entry("." + os.sep + src.name, dnode) tgt.extend(BaseVersionedInstallBuilder(env, target, src, **kw)) return tgt @@ -406,16 +436,19 @@ def set_posix_env_vars(env): :param env: :return: """ - env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'cp') - env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', - ['-p', - # '-v', # for verbose debugging - ]) - env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'cp') + env["INSTALLFILECOPY"] = env.get("INSTALLFILECOPY", "cp") + env["INSTALLFILECOPYFLAGS"] = env.get( + "INSTALLFILECOPYFLAGS", + [ + "-p", + # '-v', # for verbose debugging + ], + ) + env["INSTALLDIRCOPY"] = env.get("INSTALLDIRCOPY", "cp") - if not env.get('INSTALLDIRCOPYFLAGS', False): - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] - env['INSTALLDIRCOPYFLAGS'].append('-r') + if not env.get("INSTALLDIRCOPYFLAGS", False): + env["INSTALLDIRCOPYFLAGS"] = env["INSTALLFILECOPYFLAGS"][:] + env["INSTALLDIRCOPYFLAGS"].append("-r") def set_linux_env_vars(env): @@ -425,16 +458,19 @@ def set_linux_env_vars(env): :param env: :return: """ - env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'cp') - env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', - ['--preserve=all', - # '--verbose', # for verbose debugging - ]) - env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'cp') + env["INSTALLFILECOPY"] = env.get("INSTALLFILECOPY", "cp") + env["INSTALLFILECOPYFLAGS"] = env.get( + "INSTALLFILECOPYFLAGS", + [ + "--preserve=all", + # '--verbose', # for verbose debugging + ], + ) + env["INSTALLDIRCOPY"] = env.get("INSTALLDIRCOPY", "cp") - if not env.get('INSTALLDIRCOPYFLAGS', False): - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] - env['INSTALLDIRCOPYFLAGS'].append('--recursive') + if not env.get("INSTALLDIRCOPYFLAGS", False): + env["INSTALLDIRCOPYFLAGS"] = env["INSTALLFILECOPYFLAGS"][:] + env["INSTALLDIRCOPYFLAGS"].append("--recursive") def set_win32_env_vars(env): @@ -445,38 +481,48 @@ def set_win32_env_vars(env): :return: """ # Flags for INSTALL - env['INSTALLFILECOPY'] = env.get('INSTALLFILECOPY', 'Xcopy') - env['INSTALLFILECOPYFLAGS'] = env.get('INSTALLFILECOPYFLAGS', [ - '/o', # Copies file ownership and discretionary access control list information. - '/x', # Copies file audit settings and system access control list information. - ]) - - if not env.get('INSTALLDIRCOPYFLAGS', False): - env['INSTALLDIRCOPYFLAGS'] = env['INSTALLFILECOPYFLAGS'][:] - env['INSTALLDIRCOPYFLAGS'].extend([ - '/s', # Copies directories and subdirectories, unless they are empty. - # If you omit /s, xcopy works within a single directory. - '/e', # Copies all subdirectories, even if they are empty. - # Use /e with the /s and /t command-line options. - ]) - - env['INSTALLDIRCOPY'] = env.get('INSTALLDIRCOPY', 'Xcopy') + env["INSTALLFILECOPY"] = env.get("INSTALLFILECOPY", "copy") + env["INSTALLFILECOPYFLAGS"] = env.get( + "INSTALLFILECOPYFLAGS", + [ + "/Y", # Suppresses prompting to confirm you want to overwrite an existing destination file. + ], + ) + + if not env.get("INSTALLDIRCOPYFLAGS", False): + env["INSTALLDIRCOPYFLAGS"] = env["INSTALLFILECOPYFLAGS"][:] + + env["INSTALLDIRCOPYFLAGS"] = [ + "/o", # Copies file ownership and discretionary access control list information. + "/x", # Copies file audit settings and system access control list information. + "/i", # If destination does not exist and copying more than one file, + # assumes that destination must be a directory. + "/s", # Copies directories and subdirectories, unless they are empty. + # If you omit /s, xcopy works within a single directory. + "/e", # Copies all subdirectories, even if they are empty. + # Use /e with the /s and /t command-line options. + ] + + env["INSTALLDIRCOPY"] = env.get("INSTALLDIRCOPY", "Xcopy") def generate(env): from SCons.Script import AddOption, GetOption + global added if not added: added = 1 - AddOption('--install-sandbox', - dest='install_sandbox', - type="string", - action="store", - help='A directory under which all installed files will be placed.') + AddOption( + "--install-sandbox", + dest="install_sandbox", + type="string", + action="store", + help="A directory under which all installed files will be placed.", + ) global BaseInstallBuilder if BaseInstallBuilder is None: - install_sandbox = GetOption('install_sandbox') + install_sandbox = GetOption("install_sandbox") if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: @@ -487,13 +533,14 @@ def generate(env): target_factory=target_factory.Entry, source_factory=env.fs.Entry, multi=1, - emitter=[add_targets_to_INSTALLED_FILES, ], - source_scanner=SCons.Scanner.Base({}, name='Install', recursive=False), - name='InstallBuilder') + emitter=[add_targets_to_INSTALLED_FILES,], + source_scanner=SCons.Scanner.Base({}, name="Install", recursive=False), + name="InstallBuilder", + ) global BaseVersionedInstallBuilder if BaseVersionedInstallBuilder is None: - install_sandbox = GetOption('install_sandbox') + install_sandbox = GetOption("install_sandbox") if install_sandbox: target_factory = DESTDIR_factory(env, install_sandbox) else: @@ -504,12 +551,13 @@ def generate(env): target_factory=target_factory.Entry, source_factory=env.fs.Entry, multi=1, - emitter=[add_versioned_targets_to_INSTALLED_FILES, ], - name='InstallVersionedBuilder') + emitter=[add_versioned_targets_to_INSTALLED_FILES,], + name="InstallVersionedBuilder", + ) - env['BUILDERS']['_InternalInstall'] = InstallBuilderWrapper - env['BUILDERS']['_InternalInstallAs'] = InstallAsBuilderWrapper - env['BUILDERS']['_InternalInstallVersionedLib'] = InstallVersionedBuilderWrapper + env["BUILDERS"]["_InternalInstall"] = InstallBuilderWrapper + env["BUILDERS"]["_InternalInstallAs"] = InstallAsBuilderWrapper + env["BUILDERS"]["_InternalInstallVersionedLib"] = InstallVersionedBuilderWrapper # We'd like to initialize this doing something like the following, # but there isn't yet support for a ${SOURCE.type} expansion that @@ -519,30 +567,31 @@ def generate(env): # hand-crafted default string if it's not set. # try: - env['INSTALLSTR'] + env["INSTALLSTR"] except KeyError: - env['INSTALLSTR'] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' + env["INSTALLSTR"] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' - env['INSTALLFUNC'] = legacy_install_function + env["INSTALLFUNC"] = legacy_install_function - env['INSTALL'] = env.get('INSTALL', None) + env["INSTALL"] = env.get("INSTALL", None) - if env['PLATFORM'] == 'win32': + if env["PLATFORM"] == "win32": set_win32_env_vars(env) - if env['PLATFORM'] == 'linux': + if env["PLATFORM"] == "linux": set_linux_env_vars(env) else: set_posix_env_vars(env) try: - env['INSTALLVERSIONEDLIB'] + env["INSTALLVERSIONEDLIB"] except KeyError: - env['INSTALLVERSIONEDLIB'] = copyFuncVersionedLib + env["INSTALLVERSIONEDLIB"] = copyFuncVersionedLib def exists(env): return 1 + # Local Variables: # tab-width:4 # indent-tabs-mode:nil From 9e9936c2b922cf557c1071e5768699f4ff46006b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 8 Mar 2021 20:56:23 -0800 Subject: [PATCH 12/12] incremental --- src/engine/SCons/Tool/install.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 3a80cd785e..16d80fa1e5 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -332,7 +332,7 @@ def install_command_generator(env, target, source, for_signature=False): # # stringFunc install_action = SCons.Action.Action( - install_command_generator, "$INSTALLSTR", generator=True + install_command_generator, "$INSTALLSTR", generator=True, ) installas_action = SCons.Action.Action( install_command_generator, "$INSTALLSTR", generator=True @@ -489,6 +489,15 @@ def set_win32_env_vars(env): ], ) + # env["INSTALLFILECOPY"] = env.get("INSTALLFILECOPY", "mklink") + # env["INSTALLFILECOPYFLAGS"] = env.get( + # "INSTALLFILECOPYFLAGS", + # [ + # "/H", # Suppresses prompting to confirm you want to overwrite an existing destination file. + # ], + # ) + + if not env.get("INSTALLDIRCOPYFLAGS", False): env["INSTALLDIRCOPYFLAGS"] = env["INSTALLFILECOPYFLAGS"][:] @@ -570,6 +579,7 @@ def generate(env): env["INSTALLSTR"] except KeyError: env["INSTALLSTR"] = 'Install ${SOURCE.type}: "$SOURCES" as "$TARGETS"' + env["INSTALLSTR"] = None env["INSTALLFUNC"] = legacy_install_function