From d0a974d767a1bb4a3947020c019d625a11e0af19 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Tue, 18 Nov 2003 07:10:57 +0000 Subject: Ensure that the ENV values are all strings. (Anthony Roach) --- doc/man/scons.1 | 7 +- doc/scons.mod | 4 + doc/user/ENV.in | 168 ++++++++++++++++++++++++++++++++++++++++++ doc/user/ENV.sgml | 168 ++++++++++++++++++++++++++++++++++++++++++ doc/user/MANIFEST | 1 + doc/user/command-line.in | 34 ++++++++- doc/user/command-line.sgml | 34 ++++++++- doc/user/environments.in | 25 +++---- doc/user/environments.sgml | 25 +++---- doc/user/libraries.in | 8 +- doc/user/libraries.sgml | 8 +- doc/user/main.in | 6 ++ doc/user/main.sgml | 6 ++ src/CHANGES.txt | 16 ++++ src/engine/SCons/Action.py | 17 +++++ src/engine/SCons/Tool/msvc.py | 45 ++++++----- src/engine/SCons/Tool/msvs.py | 10 +++ test/ENV.py | 22 ++++++ 18 files changed, 548 insertions(+), 56 deletions(-) create mode 100644 doc/user/ENV.in create mode 100644 doc/user/ENV.sgml diff --git a/doc/man/scons.1 b/doc/man/scons.1 index cdf785a..67b1b9e 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -4107,7 +4107,9 @@ General options passed to the TeX DVI file to PostScript converter. .IP ENV A dictionary of environment variables -to use when invoking commands. +to use when invoking commands. When ENV is used in a command all list +values will be joined using the path separator and any other non-string +values will simply be coerced to a string. Note that, by default, .B scons does @@ -4744,7 +4746,8 @@ The qt tool tries to take this from os.environ. It also initializes all QT_* construction variables listed below. (Note that all paths are constructed -with python's os.path.join() method, but listed here with the '/' seperator +with python's os.path.join() method, +but are listed here with the '/' separator for easier reading.) In addition, the construction environment variables CPPPATH, LIBPATH, LIBS, PROGEMITTER, SHLIBEMITTER and LIBEMITTER diff --git a/doc/scons.mod b/doc/scons.mod index 443be67..c4d0e8e 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -18,7 +18,9 @@ Ant"> Autoconf"> Automake"> +cc"> Cons"> +csh"> gcc"> Jam"> Make"> @@ -194,6 +196,7 @@ COLORS"> CONFIG"> CPPDEFINES"> +ENV"> LIBDIRPREFIX"> LIBDIRSUFFIX"> LIBLINKPREFIX"> @@ -218,6 +221,7 @@ --> +PATH"> PYTHONPATH"> SCONSFLAGS"> diff --git a/doc/user/ENV.in b/doc/user/ENV.in new file mode 100644 index 0000000..3fc3a29 --- /dev/null +++ b/doc/user/ENV.in @@ -0,0 +1,168 @@ + + + + + + When &SCons; builds a target file, + it does not execute the commands with + the same external environment + that you used to execute &SCons;. + Instead, it uses the dictionary + stored in the &ENV; construction variable + as the external environment + for executing commands. + + + + + + The most important ramification of this behavior + is that the &PATH; environment variable, + which controls where the operating system + will look for commands and utilities, + is not the same as in the external environment + from which you called &SCons;. + This means that &SCons; will not, by default, + necessarily find all of the tools + that you can execute from the command line. + + + + + + The default value of the &PATH; environment variable + on a POSIX system + is /usr/local/bin:/bin:/usr/bin. + The default value of the &PATH; environment variable + on a Win32 system comes from the Windows registry + value for the command interpreter. + If you want to execute any commands--compilers, linkers, etc.--that + are not in these default locations, + you need to set the &PATH; value + in the &ENV; dictionary + in your construction environment. + + + + + + The simplest way to do this is to initialize explicitly + the value when you create the construction environment; + this is one way to do that: + + + + + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + + + + +
+ Propagating &PATH; From the External Environment + + + + You may want to propagate the external &PATH; + to the execution environment for commands. + You do this by initializing the &PATH; + variable with the &PATH; value from + the os.environ + dictionary, + which is Python's way of letting you + get at the external environment: + + + + + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + + + + + Alternatively, you may find it easier + to just propagate the entire external + environment to the execution environment + for commands. + This is simpler to code than explicity + selecting the &PATH; value: + + + + + import os + env = Environment(ENV = os.environ) + + + + + Either of these will guarantee that + &SCons; will be able to execute + any command that you can execute from the command line. + The drawback is that the build can behave + differently if it's run by people with + different &PATH; values in their environment--for example, + both the /bin and + /usr/local/bin directories + have different &cc; commands, + then which one will be used to compile programs + will depend on which directory is listed + first in the user's &PATH; variable. + + + +
diff --git a/doc/user/ENV.sgml b/doc/user/ENV.sgml new file mode 100644 index 0000000..d3078f1 --- /dev/null +++ b/doc/user/ENV.sgml @@ -0,0 +1,168 @@ + + + + + + When &SCons; builds a target file, + it does not execute the commands with + the same external environment + that you used to execute &SCons;. + Instead, it uses the dictionary + stored in the &ENV; construction variable + as the external environment + for executing commands. + + + + + + The most important ramification of this behavior + is that the &PATH; environment variable, + which controls where the operating system + will look for commands and utilities, + is not the same as in the external environment + from which you called &SCons;. + This means that &SCons; will not, by default, + necessarily find all of the tools + that you can execute from the command line. + + + + + + The default value of the &PATH; environment variable + on a POSIX system + is /usr/local/bin:/bin:/usr/bin. + The default value of the &PATH; environment variable + on a Win32 system comes from the Windows registry + value for the command interpreter. + If you want to execute any commands--compilers, linkers, etc.--that + are not in these default locations, + you need to set the &PATH; value + in the &ENV; dictionary + in your construction environment. + + + + + + The simplest way to do this is to initialize explicitly + the value when you create the construction environment; + this is one way to do that: + + + + + path = ['/usr/local/bin', '/bin', '/usr/bin'] + env = Environment(ENV = {'PATH' : path}) + + + + +
+ Propagating &PATH; From the External Environment + + + + You may want to propagate the external &PATH; + to the execution environment for commands. + You do this by initializing the &PATH; + variable with the &PATH; value from + the os.environ + dictionary, + which is Python's way of letting you + get at the external environment: + + + + + import os + env = Environment(ENV = {'PATH' : os.environ['PATH']}) + + + + + Alternatively, you may find it easier + to just propagate the entire external + environment to the execution environment + for commands. + This is simpler to code than explicity + selecting the &PATH; value: + + + + + import os + env = Environment(ENV = os.environ) + + + + + Either of these will guarantee that + &SCons; will be able to execute + any command that you can execute from the command line. + The drawback is that the build can behave + differently if it's run by people with + different &PATH; values in their environment--for example, + both the /bin and + /usr/local/bin directories + have different &cc; commands, + then which one will be used to compile programs + will depend on which directory is listed + first in the user's &PATH; variable. + + + +
diff --git a/doc/user/MANIFEST b/doc/user/MANIFEST index 337c8e0..c9af4a4 100644 --- a/doc/user/MANIFEST +++ b/doc/user/MANIFEST @@ -10,6 +10,7 @@ cons.pl cons.sgml copyright.sgml depends.sgml +ENV.sgml environments.sgml errors.sgml example.sgml diff --git a/doc/user/command-line.in b/doc/user/command-line.in index 9367c09..6f523a8 100644 --- a/doc/user/command-line.in +++ b/doc/user/command-line.in @@ -40,8 +40,25 @@ the same command-line options every time they run &SCons;. For example, a user might find that it saves time - to always specify a value of -j 2 + to specify a value of -j 2 to run the builds in parallel. + To avoid having to type -j 2 by hand + every time, + you can set the external environment variable + &SCONSFLAGS; to a string containing + command-line options that you want &SCons; to use. + + + + + + If, for example, + and you're using a POSIX shell that's + compatible with the Bourne shell, + and you always want &SCons; to use the + -Q option, + you can set the &SCONSFLAGS; + environment as follows: @@ -68,6 +85,21 @@ + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: + + + + + $ setenv SCONSFLAGS "-Q" + + + + + Windows users may typically want to set this + &SCONSFLAGS; in the appropriate tab of the + System Properties window. + diff --git a/doc/user/command-line.sgml b/doc/user/command-line.sgml index f6f219c..cf15b93 100644 --- a/doc/user/command-line.sgml +++ b/doc/user/command-line.sgml @@ -40,8 +40,25 @@ the same command-line options every time they run &SCons;. For example, a user might find that it saves time - to always specify a value of -j 2 + to specify a value of -j 2 to run the builds in parallel. + To avoid having to type -j 2 by hand + every time, + you can set the external environment variable + &SCONSFLAGS; to a string containing + command-line options that you want &SCons; to use. + + + + + + If, for example, + and you're using a POSIX shell that's + compatible with the Bourne shell, + and you always want &SCons; to use the + -Q option, + you can set the &SCONSFLAGS; + environment as follows: @@ -67,6 +84,21 @@ + Users of &csh;-style shells on POSIX systems + can set the &SCONSFLAGS; environment as follows: + + + + + $ setenv SCONSFLAGS "-Q" + + + + + Windows users may typically want to set this + &SCONSFLAGS; in the appropriate tab of the + System Properties window. + diff --git a/doc/user/environments.in b/doc/user/environments.in index 2083d4f..7e68b5e 100644 --- a/doc/user/environments.in +++ b/doc/user/environments.in @@ -402,7 +402,7 @@ environment undisturbed. Technically, a &consenv; is an object that has a number of associated &consvars;, each with a name and a value. - (A &consenv; also has an attached + (A construction environment also has an attached set of &Builder; methods, about which we'll learn more later.) @@ -411,7 +411,7 @@ environment undisturbed. A &consenv; is created by the &Environment; method. - When you initialize a &consenv;, + When you initialize a construction environment you can set the values of the environment's &consvars; to control how a program is built. @@ -453,8 +453,7 @@ environment undisturbed. The real advantage of construction environments - become apparent when you realize - that you can create as many different construction + is that you can create as many different construction environments as you need, each tailored to a different way to build some piece of software or other file. @@ -488,7 +487,7 @@ environment undisturbed. - We can even use multiple &consenvs; to build + We can even use multiple construction environments to build multiple versions of a single program. If you do this by simply trying to use the &Program; builder with both environments, though, @@ -587,24 +586,24 @@ environment undisturbed. - Sometimes you want more than one &consenv; + Sometimes you want more than one construction environment to share the same values for one or more variables. Rather than always having to repeat all of the common - variables when you create each &consenv;, + variables when you create each construction environment, you can use the &Copy; method - to create a copy of a &consenv;. + to create a copy of a construction environment. - Like the &Environment; call that creates a &consenv;, + Like the &Environment; call that creates a construction environment, the &Copy; method takes &consvar; assignments, - which will override the values in the copied &consenv;. + which will override the values in the copied construction environment. For example, suppose we want to use &gcc; to create three versions of a program, one optimized, one debug, and one with neither. - We could do this by creating a "base" &consenv; + We could do this by creating a "base" construction environment that sets &CC; to &gcc;, and then creating two copies, one which sets &CCFLAGS; for optimization @@ -675,7 +674,7 @@ environment undisturbed. - A &consenv;, however, + A construction environment, however, is actually a Python object with associated methods, etc. If you want to have direct access to only the @@ -723,7 +722,7 @@ environment undisturbed. &SCons; provides various methods that - support modifying existing values in a &consenv;. + support modifying existing values in a construction environment. diff --git a/doc/user/environments.sgml b/doc/user/environments.sgml index 2be8614..2b4fb5d 100644 --- a/doc/user/environments.sgml +++ b/doc/user/environments.sgml @@ -402,7 +402,7 @@ environment undisturbed. Technically, a &consenv; is an object that has a number of associated &consvars;, each with a name and a value. - (A &consenv; also has an attached + (A construction environment also has an attached set of &Builder; methods, about which we'll learn more later.) @@ -411,7 +411,7 @@ environment undisturbed. A &consenv; is created by the &Environment; method. - When you initialize a &consenv;, + When you initialize a construction environment you can set the values of the environment's &consvars; to control how a program is built. @@ -450,8 +450,7 @@ environment undisturbed. The real advantage of construction environments - become apparent when you realize - that you can create as many different construction + is that you can create as many different construction environments as you need, each tailored to a different way to build some piece of software or other file. @@ -481,7 +480,7 @@ environment undisturbed. - We can even use multiple &consenvs; to build + We can even use multiple construction environments to build multiple versions of a single program. If you do this by simply trying to use the &Program; builder with both environments, though, @@ -577,24 +576,24 @@ environment undisturbed. - Sometimes you want more than one &consenv; + Sometimes you want more than one construction environment to share the same values for one or more variables. Rather than always having to repeat all of the common - variables when you create each &consenv;, + variables when you create each construction environment, you can use the &Copy; method - to create a copy of a &consenv;. + to create a copy of a construction environment. - Like the &Environment; call that creates a &consenv;, + Like the &Environment; call that creates a construction environment, the &Copy; method takes &consvar; assignments, - which will override the values in the copied &consenv;. + which will override the values in the copied construction environment. For example, suppose we want to use &gcc; to create three versions of a program, one optimized, one debug, and one with neither. - We could do this by creating a "base" &consenv; + We could do this by creating a "base" construction environment that sets &CC; to &gcc;, and then creating two copies, one which sets &CCFLAGS; for optimization @@ -666,7 +665,7 @@ environment undisturbed. - A &consenv;, however, + A construction environment, however, is actually a Python object with associated methods, etc. If you want to have direct access to only the @@ -720,7 +719,7 @@ environment undisturbed. &SCons; provides various methods that - support modifying existing values in a &consenv;. + support modifying existing values in a construction environment. diff --git a/doc/user/libraries.in b/doc/user/libraries.in index a69bbee..a31b2b4 100644 --- a/doc/user/libraries.in +++ b/doc/user/libraries.in @@ -25,9 +25,10 @@ - One of the more useful ways in which you can use multiple - construction environments is to link programs - with different sets of libraries. + It's often useful to organize large software projects + by collecting parts of the software into one or more libraries. + &SCons; makes it easy to create libraries + and to use them in the programs. @@ -109,7 +110,6 @@ and by specifying the directory in which the library will be found in the &LIBPATH; construction variable: - env = Environment(LIBS = 'foo', LIBPATH = '.') diff --git a/doc/user/libraries.sgml b/doc/user/libraries.sgml index 118a560..fedeb7f 100644 --- a/doc/user/libraries.sgml +++ b/doc/user/libraries.sgml @@ -25,9 +25,10 @@ - One of the more useful ways in which you can use multiple - construction environments is to link programs - with different sets of libraries. + It's often useful to organize large software projects + by collecting parts of the software into one or more libraries. + &SCons; makes it easy to create libraries + and to use them in the programs. @@ -104,7 +105,6 @@ and by specifying the directory in which the library will be found in the &LIBPATH; construction variable: - env = Environment(LIBS = 'foo', LIBPATH = '.') diff --git a/doc/user/main.in b/doc/user/main.in index 4aa4d9c..1a017d3 100644 --- a/doc/user/main.in +++ b/doc/user/main.in @@ -43,6 +43,7 @@ + @@ -115,6 +116,11 @@ &environments; + + Controlling the Environment Used to Execute Build Commands + &ENV_file; + + Controlling a Build From the Command Line &command-line; diff --git a/doc/user/main.sgml b/doc/user/main.sgml index 4aa4d9c..1a017d3 100644 --- a/doc/user/main.sgml +++ b/doc/user/main.sgml @@ -43,6 +43,7 @@ + @@ -115,6 +116,11 @@ &environments; + + Controlling the Environment Used to Execute Build Commands + &ENV_file; + + Controlling a Build From the Command Line &command-line; diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a36b2ab..a6965c6 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -20,6 +20,20 @@ RELEASE 0.95 - XXX - Accomodate the fact that Cygwin's os.path.normcase() lies about the underlying system being case-sensitive. + From Charles Crain: + + - If no version of MSVC is detected but the tool is specified, + use the MSVC 6.0 paths by default. + + - Ignore any "6.1" version of MSVC found in the registry; this is a + phony version number (created by later service packs?) and would + throw off the logic if the user had any non-default paths configure. + + - Correctly detect if the user has independently configured the MSVC + "include," "lib" or "path" in the registry and use the appropriate + values. Previously, SCons would only use the values if all three + were set in the registry. + From Steven Knight: - Fix EnsureSConsVersion() so it checks against the SCons version, @@ -46,6 +60,8 @@ RELEASE 0.95 - XXX - Add -H help text listing the legal --debug values. + - Don't choke if a construction variable is a non-string value. + RELEASE 0.94 - Fri, 07 Nov 2003 05:29:48 -0600 diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 93580cc..1791532 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -237,6 +237,23 @@ class CommandAction(ActionBase): import SCons.Environment default_ENV = SCons.Environment.Environment()['ENV'] ENV = default_ENV + + # ensure that the ENV values are all strings: + for key, value in ENV.items(): + if SCons.Util.is_List(value): + # If the value is a list, then we assume + # it is a path list, because that's a pretty + # common list like value to stick in an environment + # variable: + ENV[key] = string.join(map(str, value), os.pathsep) + elif not SCons.Util.is_String(value): + # If it isn't a string or a list, then + # we just coerce it to a string, which + # is proper way to handle Dir and File instances + # and will produce something reasonable for + # just about everything else: + ENV[key] = str(value) + # Escape the command line for the command # interpreter we are using map(lambda x, e=escape: x.escape(e), cmd_line) diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 2a067b9..f7ec5e4 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -261,29 +261,38 @@ def get_msvc_paths(version=None): lib_path = '' include_path = '' - if not version and not SCons.Util.can_read_reg: - version = '6.0' - - try: - if not version: - version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version + if not version: + versions = SCons.Tool.msvs.get_visualstudio_versions() + if versions: + version = versions[0] #use highest version by default + else: + version = '6.0' + # Some of the configured directories only + # appear if the user changes them from the default. + # Therefore, we'll see if we can get the path to the MSDev + # base installation from the registry and deduce the default + # directories. + if float(version) >= 7.0: + defpaths = _get_msvc7_default_paths(version) + else: + defpaths = _get_msvc6_default_paths(version) + + try: include_path = get_msvc_path("include", version) + except (SCons.Util.RegError, SCons.Errors.InternalError): + include_path = defpaths[0] + + try: lib_path = get_msvc_path("lib", version) - exe_path = get_msvc_path("path", version) + except (SCons.Util.RegError, SCons.Errors.InternalError): + lib_path = defpaths[1] + try: + exe_path = get_msvc_path("path", version) except (SCons.Util.RegError, SCons.Errors.InternalError): - # Could not get all the configured directories from the - # registry. However, some of the configured directories only - # appear if the user changes them from the default. - # Therefore, we'll see if we can get the path to the MSDev - # base installation from the registry and deduce the default - # directories. - if float(version) >= 7.0: - return _get_msvc7_default_paths(version) - else: - return _get_msvc6_default_paths(version) - + exe_path = defpaths[2] + return (include_path, lib_path, exe_path) def get_msvc_default_paths(version = None): diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index 949793a..ee860e3 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -787,6 +787,16 @@ def get_visualstudio_versions(): if not L: return [] + # This is a hack to get around the fact that certain Visual Studio + # patches place a "6.1" version in the registry, which does not have + # any of the keys we need to find include paths, install directories, + # etc. Therefore we ignore it if it is there, since it throws all + # other logic off. + try: + L.remove("6.1") + except ValueError: + pass + L.sort() L.reverse() diff --git a/test/ENV.py b/test/ENV.py index 31ad970..98d04f9 100644 --- a/test/ENV.py +++ b/test/ENV.py @@ -24,6 +24,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import os +import string import sys import TestSCons @@ -60,4 +62,24 @@ test.run(arguments = '.') test.fail_test(test.read('env1.out') != "build.py env1\ninput file\n") test.fail_test(test.read('env2.out') != "build.py env2\ninput file\n") + +test.write('SConstruct', """ +env = Environment() +foo = env.Command('foo', [], r'%s build.py $TARGET') +env['ENV']['LIST'] = [foo, 'bar'] +env['ENV']['FOO'] = foo +"""%python) + +test.write('build.py', +r""" +import os +print 'LIST:', os.environ['LIST'] +print 'FOO:', os.environ['FOO'] +""") + +test.run() + +test.fail_test(string.find(test.stdout(), "LIST: foo%sbar"%os.pathsep) == -1) +test.fail_test(string.find(test.stdout(), "FOO: foo") == -1) + test.pass_test() -- cgit v0.12