summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README58
-rw-r--r--config4
-rw-r--r--runtest.py15
-rw-r--r--src/CHANGES.txt5
-rw-r--r--src/README.txt55
-rw-r--r--src/script/scons.py63
-rw-r--r--src/setup.py173
-rw-r--r--src/setupTests.py137
8 files changed, 434 insertions, 76 deletions
diff --git a/README b/README
index b1bdc048..a0661dc7 100644
--- a/README
+++ b/README
@@ -104,24 +104,58 @@ script as follows:
# cd build/scons
# python setup.py install
-This will install the scons script in the default system script
-directory (/usr/bin or C:\Python*\Scripts, for example) and the build
-engine in an appropriate SCons library directory (/usr/lib/scons or
-C:\Python*\SCons, for example).
+If this is the first time you are installing SCons on your system,
+the above command will install the scons script in the default system
+script directory (/usr/bin or C:\Python*\Scripts, for example) and the
+build engine in an appropriate stand-alone SCons library directory
+(/usr/lib/scons or C:\Python*\scons, for example).
-You should have system installation privileges (that is, "root" on POSIX
-or "Administrator" on Windows) when running the setup.py script to
-install SCons in the default system directories.
+Note that, by default, SCons does not install its library in the
+standard Python library directories. If you want to be able to use the
+SCons library modules (the build engine) in other Python scripts, you
+can run the setup script as follows:
-If you don't have system installation privileges, you can use the
---prefix option to specify an alternate installation location, such as
-your home directory:
+ # cd build/scons
+ # python setup.py install --standard-lib
+
+This will install the build engine in the standard Python
+library directory (/usr/lib/python*/site-packages or
+C:\Python*\Lib\site-packages).
+
+Alternatively, you may want to install multiple versions of SCons
+side-by-side, which you can do as follows:
+
+ # cd build/scons
+ # python setup.py install --version-lib
+
+This will install the build engine in a version-specific library
+directory (/usr/lib/scons-__VERSION__ or C:\Python*\scons-__VERSION__).
+
+If this is not the first time you are installing SCons on your system,
+the setup script will, by default, search for where you have previously
+installed the SCons library, and install this version's library the
+same way--that is, if you previously installed the SCons library in
+the standard Python library, the setup script will install this one
+in the same location. You may, of course, specify one of the --*-lib
+options described above to select a specific library location, or use
+the following option to explicitly select installation into the default
+standalone library directory (/usr/lib/scons or C:\Python*\scons):
+
+ # cd build/scons
+ # python setup.py install --standalone-lib
+
+Note that, to install SCons in any of the above system directories,
+you should have system installation privileges (that is, "root" or
+"Administrator") when running the setup.py script. If you don't have
+system installation privileges, you can use the --prefix option to
+specify an alternate installation location, such as your home directory:
$ cd build/scons
$ python setup.py install --prefix=$HOME
-This will install the scons script itself in $HOME/bin and the
-associated library in $HOME/lib/scons
+This will install SCons in the appropriate locations relative to
+$HOME--that is, the scons script itself $HOME/bin and the associated
+library in $HOME/lib/scons, for example.
TESTING
diff --git a/config b/config
index f1b58ade..0769a2bc 100644
--- a/config
+++ b/config
@@ -250,9 +250,9 @@ diff_command =
* is set appropriately during a baseline test. So we just use the
* proper aesub variable to comment out the expanded $spe.
*/
-test_command = "python ${Source runtest.py Absolute} -p tar-gz -q ${File_Name}";
+test_command = "python ${Source runtest.py Absolute} -p tar-gz -v ${SUBSTitute '\\.[CD][0-9]+$' '' ${VERsion}} -q ${File_Name}";
-batch_test_command = "python ${Source runtest.py Absolute} -p tar-gz -o ${Output} ${File_Names} ${COMment $spe}";
+batch_test_command = "python ${Source runtest.py Absolute} -p tar-gz -v ${SUBSTitute '\\.[CD][0-9]+$' '' ${VERsion}} -o ${Output} ${File_Names} ${COMment $spe}";
new_test_filename = "test/CHANGETHIS.py";
diff --git a/runtest.py b/runtest.py
index 52e76d48..eff0a358 100644
--- a/runtest.py
+++ b/runtest.py
@@ -73,6 +73,7 @@ package = None
scons = None
scons_exec = None
output = None
+version = ''
if os.name == 'java':
python = os.path.join(sys.prefix, 'jython')
@@ -107,13 +108,15 @@ Options:
tar-gz .tar.gz distribution
zip .zip distribution
-q, --quiet Don't print the test being executed.
+ -v version Specify the SCons version.
-X Test script is executable, don't feed to Python.
-x SCRIPT, --exec SCRIPT Test SCRIPT.
"""
-opts, args = getopt.getopt(sys.argv[1:], "adho:P:p:qXx:",
+opts, args = getopt.getopt(sys.argv[1:], "adho:P:p:qv:Xx:",
['all', 'debug', 'help', 'output=',
- 'package=', 'python=', 'quiet', 'exec='])
+ 'package=', 'python=', 'quiet',
+ 'version=', 'exec='])
for o, a in opts:
if o == '-a' or o == '--all':
@@ -133,6 +136,8 @@ for o, a in opts:
package = a
elif o == '-q' or o == '--quiet':
printcmd = 0
+ elif o == '-v' or o == '--version':
+ version = a
elif o == '-X':
scons_exec = 1
elif o == '-x' or o == '--exec':
@@ -144,7 +149,7 @@ def whereis(file):
if os.path.isfile(f):
try:
st = os.stat(f)
- except:
+ except OSError:
continue
if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
return f
@@ -304,6 +309,10 @@ elif scons_lib_dir:
if scons_exec:
os.environ['SCONS_EXEC'] = '1'
+os.environ['SCONS_CWD'] = cwd
+
+os.environ['SCONS_VERSION'] = version
+
os.environ['PYTHONPATH'] = pythonpath_dir + \
os.pathsep + \
os.path.join(cwd, 'build', 'etc') + \
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 237aa60e..9f45dbd5 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -64,6 +64,11 @@ RELEASE 0.11 - XXX
everything in the current directory and below (like Make). This
can be disabled by specifying Default(None) in an SConscript.
+ - Revamp SCons installation to fix a case-sensitive installation
+ on Win32 systems, and to add SCons-specific --standard-lib,
+ --standalone-lib, and --version-lib options for easier user
+ control of where the libraries get installed.
+
From Steve Leblanc:
- Fix the output of -c -n when directories are involved, so it
diff --git a/src/README.txt b/src/README.txt
index ca73a556..e9c6455e 100644
--- a/src/README.txt
+++ b/src/README.txt
@@ -87,23 +87,54 @@ provided Python-standard setup script as follows:
# python setup.py install
-This will install the scons script in the default system script
-directory (/usr/bin or C:\Python*\Scripts, for example) and the build
-engine in an appropriate SCons library directory (/usr/lib/scons or
-C:\Python*\SCons, for example).
+If this is the first time you are installing SCons on your system,
+the above command will install the scons script in the default system
+script directory (/usr/bin or C:\Python*\Scripts, for example) and the
+build engine in an appropriate stand-alone SCons library directory
+(/usr/lib/scons or C:\Python*\scons, for example).
-You should have system installation privileges (that is, "root" or
-"Administrator") when running the setup.py script to install SCons in
-the default system directories.
+Note that, by default, SCons does not install its library in the
+standard Python library directories. If you want to be able to use the
+SCons library modules (the build engine) in other Python scripts, you
+can run the setup script as follows:
-If you don't have system installation privileges, you can use the
---prefix option to specify an alternate installation location, such as
-your home directory:
+ # python setup.py install --standard-lib
+
+This will install the build engine in the standard Python
+library directory (/usr/lib/python*/site-packages or
+C:\Python*\Lib\site-packages).
+
+Alternatively, you may want to install multiple versions of SCons
+side-by-side, which you can do as follows:
+
+ # python setup.py install --version-lib
+
+This will install the build engine in a version-specific library
+directory (/usr/lib/scons-__VERSION__ or C:\Python*\scons-__VERSION__).
+
+If this is not the first time you are installing SCons on your system,
+the setup script will, by default, search for where you have previously
+installed the SCons library, and install this version's library the
+same way--that is, if you previously installed the SCons library in
+the standard Python library, the setup script will install this one
+in the same location. You may, of course, specify one of the --*-lib
+options described above to select a specific library location, or use
+the following option to explicitly select installation into the default
+standalone library directory (/usr/lib/scons or C:\Python*\scons):
+
+ # python setup.py install --standalone-lib
+
+Note that, to install SCons in any of the above system directories,
+you should have system installation privileges (that is, "root" or
+"Administrator") when running the setup.py script. If you don't have
+system installation privileges, you can use the --prefix option to
+specify an alternate installation location, such as your home directory:
$ python setup.py install --prefix=$HOME
-This will install the scons script itself in $HOME/bin and the
-associated library in $HOME/lib/scons
+This will install SCons in the appropriate locations relative to
+$HOME--that is, the scons script itself $HOME/bin and the associated
+library in $HOME/lib/scons, for example.
DOCUMENTATION
diff --git a/src/script/scons.py b/src/script/scons.py
index f21a3eb5..f128d1d4 100644
--- a/src/script/scons.py
+++ b/src/script/scons.py
@@ -62,36 +62,61 @@ if script_dir:
local = os.path.join(script_dir, local)
libs.append(local)
-if sys.platform == 'win32':
- libs.extend([ os.path.join(sys.prefix, 'SCons-%s' % __version__),
- os.path.join(sys.prefix, 'SCons') ])
-else:
- prefs = []
+scons_version = 'scons-%s' % __version__
- _bin = os.path.join('', 'bin')
- _usr = os.path.join('', 'usr')
- _usr_local = os.path.join('', 'usr', 'local')
+prefs = []
+if sys.platform == 'win32':
+ # sys.prefix is (likely) C:\Python*;
+ # check only C:\Python*.
+ prefs.append(sys.prefix)
+else:
+ # On other (POSIX) platforms, things are more complicated due to
+ # the variety of path names and library locations. Try to be smart
+ # about it.
if script_dir == 'bin':
+ # script_dir is `pwd`/bin;
+ # check `pwd`/lib/scons*.
prefs.append(os.getcwd())
else:
if script_dir == '.' or script_dir == '':
script_dir = os.getcwd()
- if script_dir[-len(_bin):] == _bin:
- prefs.append(script_dir[:-len(_bin)])
-
- if sys.prefix[-len(_usr):] == _usr:
- prefs.append(sys.prefix)
- prefs.append(os.path.join(sys.prefix, "local"))
- elif sys.prefix[-len(_usr_local):] == _usr_local:
- _local = os.path.join('', 'local')
- prefs.append(sys.prefix[:-len(_local)])
+ head, tail = os.path.split(script_dir)
+ if tail == "bin":
+ # script_dir is /foo/bin;
+ # check /foo/lib/scons*.
+ prefs.append(head)
+
+ head, tail = os.path.split(sys.prefix)
+ if tail == "usr":
+ # sys.prefix is /foo/usr;
+ # check /foo/usr/lib/scons* first,
+ # then /foo/usr/local/lib/scons*.
prefs.append(sys.prefix)
+ prefs.append(os.path.join(sys.prefix, "local"))
+ elif tail == "local":
+ h, t = os.path.split(head)
+ if t == "usr":
+ # sys.prefix is /foo/usr/local;
+ # check /foo/usr/local/lib/scons* first,
+ # then /foo/usr/lib/scons*.
+ prefs.append(sys.prefix)
+ prefs.append(head)
+ else:
+ # sys.prefix is /foo/local;
+ # check only /foo/local/lib/scons*.
+ prefs.append(sys.prefix)
else:
+ # sys.prefix is /foo (ends in neither /usr or /local);
+ # check only /foo/lib/scons*.
prefs.append(sys.prefix)
- libs.extend(map(lambda x: os.path.join(x, 'lib', 'scons-%s' % __version__), prefs))
- libs.extend(map(lambda x: os.path.join(x, 'lib', 'scons'), prefs))
+ prefs = map(lambda x: os.path.join(x, 'lib'), prefs)
+
+# Look first for 'scons-__version__' in all of our preference libs,
+# then for 'scons'.
+libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
+libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
sys.path = libs + sys.path
diff --git a/src/setup.py b/src/setup.py
index 3c240a90..6aa673a8 100644
--- a/src/setup.py
+++ b/src/setup.py
@@ -25,6 +25,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
+import string
import sys
(head, tail) = os.path.split(sys.argv[0])
@@ -34,8 +35,10 @@ if head:
sys.argv[0] = tail
try:
- from distutils.core import setup
- from distutils.command.install_lib import install_lib
+ import distutils.core
+ import distutils.command.install
+ import distutils.command.install_lib
+ import distutils.command.install_scripts
except ImportError:
sys.stderr.write("""Could not import distutils.
@@ -46,34 +49,141 @@ your system, or on how to install SCons from a different package.
""")
sys.exit(1)
-class my_install_lib(install_lib):
+_install = distutils.command.install.install
+_install_lib = distutils.command.install_lib.install_lib
+_install_scripts = distutils.command.install_scripts.install_scripts
+
+standard_lib = 0
+standalone_lib = 0
+version_lib = 0
+
+installed_lib_dir = None
+installed_scripts_dir = None
+
+def set_explicitly(name, args):
+ """
+ Return if the installation directory was set explicitly by the
+ user on the command line. This is complicated by the fact that
+ "install --install-lib=/foo" gets turned into "install_lib
+ --install-dir=/foo" internally.
+ """
+ if args[0] == "install_" + name:
+ s = "--install-dir="
+ else:
+ # The command is something else (usually "install")
+ s = "--install-%s=" % name
+ set = 0
+ length = len(s)
+ for a in args[1:]:
+ if a[:length] == s:
+ set = 1
+ break
+ return set
+
+class install(_install):
+ user_options = _install.user_options + [
+ ('standard-lib', None,
+ "install SCons library in standard Python location"),
+ ('standalone-lib', None,
+ "install SCons library in separate standalone directory"),
+ ('version-lib', None,
+ "install SCons library in version-specific directory")
+ ]
+ boolean_options = _install.boolean_options + [
+ 'standard-lib',
+ 'standalone-lib',
+ 'version-lib'
+ ]
+
+ def initialize_options(self):
+ _install.initialize_options(self)
+ self.standard_lib = 0
+ self.standalone_lib = 0
+ self.version_lib = 0
+
def finalize_options(self):
- install_lib.finalize_options(self)
- head = self.install_dir
- drive, head = os.path.splitdrive(self.install_dir)
- while head:
- if head == os.sep:
- head = None
- break
- else:
- head, tail = os.path.split(head)
- if tail[:6] == "python":
- self.install_dir = os.path.join(drive + head, "scons")
- # Our original packaging scheme placed the build engine
- # in a private library directory that contained the SCons
- # version number in the directory name. Here's how this
- # was supported here. See the Construct file for details
- # on other files that would need to be changed to support
- # this as well.
- #self.install_dir = os.path.join(drive + head, "scons-__VERSION__")
- return
- elif tail[:6] == "Python":
- self.install_dir = os.path.join(drive + head, tail)
- return
+ _install.finalize_options(self)
+ global standard_lib, standalone_lib, version_lib
+ standard_lib = self.standard_lib
+ standalone_lib = self.standalone_lib
+ version_lib = self.version_lib
+
+def get_scons_prefix(libdir):
+ """
+ Return the right prefix for SCons library installation. Find
+ this by starting with the library installation directory
+ (.../site-packages, most likely) and crawling back up until we reach
+ a directory name beginning with "python" (or "Python").
+ """
+ drive, head = os.path.splitdrive(libdir)
+ while head:
+ if head == os.sep:
+ break
+ head, tail = os.path.split(head)
+ if string.lower(tail)[:6] == "python":
+ # Found the Python library directory...
+ if sys.platform == "win32":
+ # ...on Win32 systems, "scons" goes in the directory:
+ # C:\PythonXX => C:\PythonXX\scons
+ return os.path.join(drive + head, tail)
+ else:
+ # ...on other systems, "scons" goes above the directory:
+ # /usr/lib/pythonX.X => /usr/lib/scons
+ return os.path.join(drive + head)
+ return libdir
+
+class install_lib(_install_lib):
+ def initialize_options(self):
+ _install_lib.initialize_options(self)
+ global standard_lib, standalone_lib, version_lib
+ self.standard_lib = standard_lib
+ self.standalone_lib = standalone_lib
+ self.version_lib = version_lib
+
+ def finalize_options(self):
+ _install_lib.finalize_options(self)
+ if not set_explicitly("lib", self.distribution.script_args):
+ # They didn't explicitly specify the installation
+ # directory for libraries...
+ prefix = get_scons_prefix(self.install_dir)
+ standard_dir = os.path.join(self.install_dir, "SCons")
+ version_dir = os.path.join(prefix, "scons-0.11")
+ standalone_dir = os.path.join(prefix, "scons")
+ if self.version_lib:
+ # ...but they asked for a version-specific directory.
+ self.install_dir = version_dir
+ elif self.standalone_lib:
+ # ...but they asked for a standalone directory.
+ self.install_dir = standalone_dir
+ elif not self.standard_lib:
+ # ...and they didn't explicitly ask for the standard
+ # directory, so guess based on what's out there.
+ try:
+ e = filter(lambda x: x[:6] == "scons-", os.listdir(prefix))
+ except:
+ e = None
+ if e:
+ # We found a path name (e.g.) /usr/lib/scons-XXX,
+ # so pick the version-specific directory.
+ self.install_dir = version_dir
+ elif os.path.exists(standalone_dir) or \
+ not os.path.exists(standard_dir):
+ # There's already a standalone directory, or
+ # there's no SCons library in the standard
+ # directory, so go with the standalone.
+ self.install_dir = standalone_dir
+ global installed_lib_dir
+ installed_lib_dir = self.install_dir
+
+class install_scripts(_install_scripts):
+ def finalize_options(self):
+ _install_scripts.finalize_options(self)
+ global installed_scripts_dir
+ installed_scripts_dir = self.install_dir
arguments = {
'name' : "scons",
- 'version' : "__VERSION__",
+ 'version' : "0.11",
'packages' : ["SCons",
"SCons.Node",
"SCons.Optik",
@@ -84,7 +194,9 @@ arguments = {
"SCons.Tool"],
'package_dir' : {'' : 'engine'},
'scripts' : ["script/scons"],
- 'cmdclass' : {'install_lib' : my_install_lib}
+ 'cmdclass' : {'install' : install,
+ 'install_lib' : install_lib,
+ 'install_scripts' : install_scripts}
}
try:
@@ -93,4 +205,9 @@ try:
except IndexError:
pass
-apply(setup, (), arguments)
+apply(distutils.core.setup, (), arguments)
+
+if installed_lib_dir:
+ print "Installed SCons library modules into %s" % installed_lib_dir
+if installed_scripts_dir:
+ print "Installed SCons script into %s" % installed_scripts_dir
diff --git a/src/setupTests.py b/src/setupTests.py
new file mode 100644
index 00000000..6c151eb5
--- /dev/null
+++ b/src/setupTests.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+#
+# __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__"
+
+"""
+Test how the setup.py script installs SCons (specifically, its libraries).
+"""
+
+import os
+import os.path
+import shutil
+import string
+
+import TestSCons
+
+python = TestSCons.python
+
+class MyTestSCons(TestSCons.TestSCons):
+ def installed(self, lib):
+ lines = string.split(self.stdout(), '\n')
+ return lines[-3] == 'Installed SCons library modules into %s' % lib
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ cwd = os.getcwd()
+
+try:
+ version = os.environ['SCONS_VERSION']
+except KeyError:
+ version = '__VERSION__'
+
+scons_version = 'scons-%s' % version
+
+tar_gz = os.path.join(cwd, 'build', 'dist', '%s.tar.gz' % scons_version)
+
+test = MyTestSCons()
+
+if not os.path.isfile(tar_gz):
+ print "Did not find an SCons package `%s'." % tar_gz
+ print "Cannot test package installation."
+ test.no_result(1)
+
+test.subdir('root')
+
+root = test.workpath('root')
+
+standard_lib = '%s/usr/lib/python1.5/site-packages/' % root
+standalone_lib = '%s/usr/lib/scons' % root
+version_lib = '%s/usr/lib/%s' % (root, scons_version)
+
+def installed(lib):
+ return 'Installed SCons library modules into %s' % lib
+
+os.system("tar zxf %s" % tar_gz)
+
+# Verify that a virgin installation installs the standalone library.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s' % root,
+ stderr = None)
+test.fail_test(not test.installed(standalone_lib))
+
+# Verify that --standard-lib installs into the Python standard library.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s --standard-lib' % root,
+ stderr = None)
+lines = string.split(test.stdout(), '\n')
+test.fail_test(not test.installed(standard_lib))
+
+# Verify that --standalone-lib installs the standalone library.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s --standalone-lib' % root,
+ stderr = None)
+test.fail_test(not test.installed(standalone_lib))
+
+# Verify that --version-lib installs into a version-specific library directory.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s --version-lib' % root,
+ stderr = None)
+test.fail_test(not test.installed(version_lib))
+
+# Now that all of the libraries are in place,
+# verify that a default installation finds the version-specific library first.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s' % root,
+ stderr = None)
+test.fail_test(not test.installed(version_lib))
+
+shutil.rmtree(version_lib)
+
+# Now with only the standard and standalone libraries in place,
+# verify that a default installation finds the standalone library first.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s' % root,
+ stderr = None)
+test.fail_test(not test.installed(standalone_lib))
+
+shutil.rmtree(standalone_lib)
+
+# Now with only the standard libraries in place,
+# verify that a default installation installs the standard library.
+test.run(chdir = scons_version,
+ program = python,
+ arguments = 'setup.py install --root=%s' % root,
+ stderr = None)
+test.fail_test(not test.installed(standard_lib))
+
+# All done.
+test.pass_test()