summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.hgignore1
-rw-r--r--QMTest/TestSCons.py2
-rw-r--r--QMTest/TestSConsMSVS.py108
-rw-r--r--README.rst43
-rw-r--r--ReleaseConfig4
-rw-r--r--SConstruct6
-rwxr-xr-xbin/ae-cvs-ci204
-rwxr-xr-xbin/ae-svn-ci240
-rw-r--r--bin/ae2cvs579
-rw-r--r--bin/docs-create-example-outputs.py2
-rw-r--r--bin/docs-update-generated.py5
-rw-r--r--bin/docs-validate.py1
-rwxr-xr-xbin/scp-sourceforge8
-rw-r--r--bin/update-release-info.py1
-rwxr-xr-xbin/upload-release-files.sh14
-rw-r--r--debian/changelog18
-rw-r--r--doc/generated/builders.gen35
-rw-r--r--doc/generated/examples/caching_ex-random_1.xml6
-rw-r--r--doc/generated/examples/commandline_Variables_Help_1.xml2
-rw-r--r--doc/generated/examples/output_ex1_1.xml2
-rw-r--r--doc/generated/examples/output_ex2_1.xml2
-rw-r--r--doc/generated/examples/output_ex2_2.xml2
-rw-r--r--doc/generated/examples/troubleshoot_Dump_1.xml4
-rw-r--r--doc/generated/examples/troubleshoot_Dump_2.xml4
-rw-r--r--doc/generated/examples/troubleshoot_explain1_3.xml2
-rw-r--r--doc/generated/examples/troubleshoot_stacktrace_2.xml2
-rw-r--r--doc/generated/functions.gen18
-rw-r--r--doc/generated/tools.gen16
-rw-r--r--doc/generated/tools.mod2
-rw-r--r--doc/generated/variables.gen238
-rw-r--r--doc/generated/variables.mod36
-rw-r--r--doc/man/scons.xml2
-rw-r--r--doc/user/build-install.xml2
-rw-r--r--doc/user/main.xml4
-rw-r--r--doc/user/output.xml22
-rw-r--r--doc/user/repositories.xml2
-rw-r--r--doc/user/sconf.xml6
-rw-r--r--doc/version.xml4
-rw-r--r--src/Announce.txt50
-rw-r--r--src/CHANGES.txt58
-rw-r--r--src/README.txt43
-rw-r--r--src/RELEASE.txt2
-rw-r--r--src/engine/SCons/Action.py2
-rw-r--r--src/engine/SCons/Builder.py11
-rw-r--r--src/engine/SCons/BuilderTests.py69
-rw-r--r--src/engine/SCons/CacheDir.py16
-rw-r--r--src/engine/SCons/Debug.py13
-rw-r--r--src/engine/SCons/Defaults.py15
-rw-r--r--src/engine/SCons/Environment.py16
-rw-r--r--src/engine/SCons/EnvironmentTests.py92
-rw-r--r--src/engine/SCons/Executor.py133
-rw-r--r--src/engine/SCons/Memoize.py159
-rw-r--r--src/engine/SCons/MemoizeTests.py18
-rw-r--r--src/engine/SCons/Node/Alias.py49
-rw-r--r--src/engine/SCons/Node/AliasTests.py4
-rw-r--r--src/engine/SCons/Node/FS.py597
-rw-r--r--src/engine/SCons/Node/FSTests.py272
-rw-r--r--src/engine/SCons/Node/NodeTests.py129
-rw-r--r--src/engine/SCons/Node/Python.py40
-rw-r--r--src/engine/SCons/Node/PythonTests.py4
-rw-r--r--src/engine/SCons/Node/__init__.py472
-rw-r--r--src/engine/SCons/PathList.py8
-rw-r--r--src/engine/SCons/Platform/cygwin.py4
-rw-r--r--src/engine/SCons/SConf.py24
-rw-r--r--src/engine/SCons/SConfTests.py2
-rw-r--r--src/engine/SCons/SConsign.py38
-rw-r--r--src/engine/SCons/SConsignTests.py4
-rw-r--r--src/engine/SCons/Scanner/CTests.py13
-rw-r--r--src/engine/SCons/Scanner/Dir.py2
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py13
-rw-r--r--src/engine/SCons/Scanner/IDLTests.py13
-rw-r--r--src/engine/SCons/Script/Main.py14
-rw-r--r--src/engine/SCons/Script/SConsOptions.py2
-rw-r--r--src/engine/SCons/Script/SConscript.py8
-rw-r--r--src/engine/SCons/Script/SConscript.xml16
-rw-r--r--src/engine/SCons/Script/__init__.py26
-rw-r--r--src/engine/SCons/Taskmaster.py2
-rw-r--r--src/engine/SCons/Tool/MSCommon/arch.py6
-rw-r--r--src/engine/SCons/Tool/MSCommon/vc.py6
-rw-r--r--src/engine/SCons/Tool/MSCommon/vs.py22
-rw-r--r--src/engine/SCons/Tool/__init__.py562
-rw-r--r--src/engine/SCons/Tool/__init__.xml51
-rw-r--r--src/engine/SCons/Tool/cyglink.py158
-rw-r--r--src/engine/SCons/Tool/cyglink.xml49
-rw-r--r--src/engine/SCons/Tool/dmd.py13
-rw-r--r--src/engine/SCons/Tool/docbook/__init__.py6
-rw-r--r--src/engine/SCons/Tool/gdc.py13
-rw-r--r--src/engine/SCons/Tool/gettext.xml2
-rw-r--r--src/engine/SCons/Tool/gnulink.py13
-rw-r--r--src/engine/SCons/Tool/gnulink.xml4
-rw-r--r--src/engine/SCons/Tool/install.py131
-rw-r--r--src/engine/SCons/Tool/install.xml11
-rw-r--r--src/engine/SCons/Tool/ldc.py13
-rw-r--r--src/engine/SCons/Tool/link.py287
-rw-r--r--src/engine/SCons/Tool/link.xml154
-rw-r--r--src/engine/SCons/Tool/linkloc.py1
-rw-r--r--src/engine/SCons/Tool/midl.xml2
-rw-r--r--src/engine/SCons/Tool/mingw.py1
-rw-r--r--src/engine/SCons/Tool/msgmerge.xml2
-rw-r--r--src/engine/SCons/Tool/mslink.py5
-rw-r--r--src/engine/SCons/Tool/msvc.xml2
-rw-r--r--src/engine/SCons/Tool/msvs.py6
-rw-r--r--src/engine/SCons/Tool/mwld.py1
-rw-r--r--src/engine/SCons/Tool/packaging/__init__.py16
-rw-r--r--src/engine/SCons/Tool/packaging/ipk.py2
-rw-r--r--src/engine/SCons/Tool/packaging/msi.py2
-rw-r--r--src/engine/SCons/Tool/packaging/rpm.py11
-rw-r--r--src/engine/SCons/Tool/qt.py1
-rw-r--r--src/engine/SCons/Tool/rpm.py6
-rw-r--r--src/engine/SCons/Tool/sunar.py3
-rw-r--r--src/engine/SCons/Tool/sunar.xml4
-rw-r--r--src/engine/SCons/Tool/sunlink.py4
-rw-r--r--src/engine/SCons/Tool/swig.py9
-rw-r--r--src/engine/SCons/Tool/swig.xml6
-rw-r--r--src/engine/SCons/Tool/xgettext.xml2
-rw-r--r--src/engine/SCons/Util.py6
-rw-r--r--src/engine/SCons/UtilTests.py3
-rw-r--r--src/script/scons.py2
-rw-r--r--src/script/sconsign.py2
-rw-r--r--test/Actions/addpost-link.py2
-rw-r--r--test/CacheDir/NoCache.py2
-rw-r--r--test/Glob/Repository.py2
-rw-r--r--test/Glob/VariantDir.py34
-rw-r--r--test/Glob/exclude.py4
-rw-r--r--test/Help.py63
-rw-r--r--test/Install/wrap-by-attribute.py4
-rw-r--r--test/Interactive/variant_dir.py4
-rw-r--r--test/LINK/SHLIBVERSIONFLAGS.py59
-rw-r--r--test/LINK/VersionedLib-VariantDir.py168
-rw-r--r--test/LINK/VersionedLib-j2.py146
-rw-r--r--test/LINK/VersionedLib-subdir.py160
-rw-r--r--test/LINK/VersionedLib.py326
-rw-r--r--test/MSVS/vs-14.0-exec.py109
-rw-r--r--test/MSVS/vs-14.0-files.py110
-rw-r--r--test/MSVS/vs-14.0-scc-files.py115
-rw-r--r--test/MSVS/vs-14.0-scc-legacy-files.py98
-rw-r--r--test/MSVS/vs-14.0Exp-exec.py109
-rw-r--r--test/NoClean.py6
-rw-r--r--test/Scanner/exception.py1
-rw-r--r--test/TEX/variant_dir_newglossary.py2
-rw-r--r--test/explain/function-actions.py4
-rw-r--r--test/implicit-cache/DualTargets.py4
-rw-r--r--test/option--tree.py15
-rw-r--r--test/option/md5-chunksize.py4
-rw-r--r--test/packaging/guess-package-name.py2
-rw-r--r--test/packaging/msi/explicit-target.py2
-rw-r--r--test/sconsign/script/SConsignFile.py42
-rw-r--r--test/sconsign/script/no-SConsignFile.py14
-rw-r--r--test/site_scons/sysdirs.py2
149 files changed, 4791 insertions, 2524 deletions
diff --git a/.hgignore b/.hgignore
index 27c728a..dcc9505 100644
--- a/.hgignore
+++ b/.hgignore
@@ -9,6 +9,7 @@ syntax:glob
*~
*.xcodeproj
*.orig
+*.DS_Store
doc/user/scons-user
doc/user/scons_db.xml
diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py
index 923e319..06fa575 100644
--- a/QMTest/TestSCons.py
+++ b/QMTest/TestSCons.py
@@ -34,7 +34,7 @@ from TestCmd import PIPE
# here provides some independent verification that what we packaged
# conforms to what we expect.
-default_version = '2.3.4'
+default_version = '2.4.0'
python_version_unsupported = (2, 3, 0)
python_version_deprecated = (2, 7, 0)
diff --git a/QMTest/TestSConsMSVS.py b/QMTest/TestSConsMSVS.py
index 540d24d..65a09eb 100644
--- a/QMTest/TestSConsMSVS.py
+++ b/QMTest/TestSConsMSVS.py
@@ -514,6 +514,26 @@ Global
EndGlobal
"""
+expected_slnfile_14_0 = """\
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}"
+EndProject
+Global
+<SCC_SLN_INFO>
+\tGlobalSection(SolutionConfigurationPlatforms) = preSolution
+\t\tRelease|Win32 = Release|Win32
+\tEndGlobalSection
+\tGlobalSection(ProjectConfigurationPlatforms) = postSolution
+\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32
+\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32
+\tEndGlobalSection
+\tGlobalSection(SolutionProperties) = preSolution
+\t\tHideSolutionNode = FALSE
+\tEndGlobalSection
+EndGlobal
+"""
+
expected_vcprojfile_8_0 = """\
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
@@ -820,6 +840,71 @@ expected_vcprojfile_11_0 = """\
</Project>
"""
+expected_vcprojfile_14_0 = """\
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+\t<ItemGroup Label="ProjectConfigurations">
+\t\t<ProjectConfiguration Include="Release|Win32">
+\t\t\t<Configuration>Release</Configuration>
+\t\t\t<Platform>Win32</Platform>
+\t\t</ProjectConfiguration>
+\t</ItemGroup>
+\t<PropertyGroup Label="Globals">
+\t\t<ProjectGuid>{39A97E1F-1A52-8954-A0B1-A10A8487545E}</ProjectGuid>
+<SCC_VCPROJ_INFO>
+\t\t<RootNamespace>Test</RootNamespace>
+\t\t<Keyword>MakeFileProj</Keyword>
+\t</PropertyGroup>
+\t<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
+\t<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+\t\t<ConfigurationType>Makefile</ConfigurationType>
+\t\t<UseOfMfc>false</UseOfMfc>
+\t\t<PlatformToolset>v140</PlatformToolset>
+\t</PropertyGroup>
+\t<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" />
+\t<ImportGroup Label="ExtensionSettings">
+\t</ImportGroup>
+\t<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+\t\t<Import Project="$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+\t</ImportGroup>
+\t<PropertyGroup Label="UserMacros" />
+\t<PropertyGroup>
+\t<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+\t\t<NMakeBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo Starting SCons &amp;&amp; &quot;<PYTHON>&quot; -c &quot;<SCONS_SCRIPT_MAIN_XML>&quot; -C &quot;<WORKPATH>&quot; -f SConstruct &quot;Test.exe&quot;</NMakeBuildCommandLine>
+\t\t<NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo Starting SCons &amp;&amp; &quot;<PYTHON>&quot; -c &quot;<SCONS_SCRIPT_MAIN_XML>&quot; -C &quot;<WORKPATH>&quot; -f SConstruct &quot;Test.exe&quot;</NMakeReBuildCommandLine>
+\t\t<NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo Starting SCons &amp;&amp; &quot;<PYTHON>&quot; -c &quot;<SCONS_SCRIPT_MAIN_XML>&quot; -C &quot;<WORKPATH>&quot; -f SConstruct -c &quot;Test.exe&quot;</NMakeCleanCommandLine>
+\t\t<NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Test.exe</NMakeOutput>
+\t\t<NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">DEF1;DEF2;DEF3=1234</NMakePreprocessorDefinitions>
+\t\t<NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">inc1;inc2</NMakeIncludeSearchPath>
+\t\t<NMakeForcedIncludes Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeForcedIncludes)</NMakeForcedIncludes>
+\t\t<NMakeAssemblySearchPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeAssemblySearchPath)</NMakeAssemblySearchPath>
+\t\t<NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies>
+\t</PropertyGroup>
+\t<ItemGroup>
+\t\t<ClInclude Include="sdk_dir\sdk.h" />
+\t</ItemGroup>
+\t<ItemGroup>
+\t\t<ClInclude Include="test.h" />
+\t</ItemGroup>
+\t<ItemGroup>
+\t\t<None Include="readme.txt" />
+\t</ItemGroup>
+\t<ItemGroup>
+\t\t<None Include="test.rc" />
+\t</ItemGroup>
+\t<ItemGroup>
+\t\t<ClCompile Include="test1.cpp" />
+\t\t<ClCompile Include="test2.cpp" />
+\t</ItemGroup>
+\t<ItemGroup>
+\t\t<None Include="SConstruct" />
+\t</ItemGroup>
+\t<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.targets" />
+\t<ImportGroup Label="ExtensionTargets">
+\t</ImportGroup>
+</Project>
+"""
+
SConscript_contents_8_0 = """\
env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0',
CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')],
@@ -912,6 +997,29 @@ env.MSVSProject(target = 'Test.vcxproj',
variant = 'Release')
"""
+SConscript_contents_14_0 = """\
+env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0',
+ CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')],
+ CPPPATH=['inc1', 'inc2'],
+ HOST_ARCH='%(HOST_ARCH)s')
+
+testsrc = ['test1.cpp', 'test2.cpp']
+testincs = ['sdk_dir\sdk.h']
+testlocalincs = ['test.h']
+testresources = ['test.rc']
+testmisc = ['readme.txt']
+
+env.MSVSProject(target = 'Test.vcxproj',
+ slnguid = '{SLNGUID}',
+ srcs = testsrc,
+ incs = testincs,
+ localincs = testlocalincs,
+ resources = testresources,
+ misc = testmisc,
+ buildtarget = 'Test.exe',
+ variant = 'Release')
+"""
+
class TestSConsMSVS(TestSCons):
"""Subclass for testing MSVS-specific portions of SCons."""
diff --git a/README.rst b/README.rst
index 599abaf..05be712 100644
--- a/README.rst
+++ b/README.rst
@@ -47,7 +47,7 @@ version at the SCons download page:
Execution Requirements
======================
-Running SCons requires Python version 2.6 or later (Python 3 is not
+Running SCons requires Python version 2.7 or later (Python 3 is not
yet supported). There should be no other dependencies or requirements
to run SCons.
@@ -156,7 +156,7 @@ Or on Windows::
By default, the above commands will do the following:
-- Install the version-numbered "scons-2.3.3" and "sconsign-2.3.3" scripts in
+- Install the version-numbered "scons-2.4.0" and "sconsign-2.4.0" scripts in
the default system script directory (/usr/bin or C:\\Python\*\\Scripts, for
example). This can be disabled by specifying the "--no-version-script"
option on the command line.
@@ -168,23 +168,23 @@ By default, the above commands will do the following:
before making it the default on your system.
On UNIX or Linux systems, you can have the "scons" and "sconsign" scripts be
- hard links or symbolic links to the "scons-2.3.3" and "sconsign-2.3.3"
+ hard links or symbolic links to the "scons-2.4.0" and "sconsign-2.4.0"
scripts by specifying the "--hardlink-scons" or "--symlink-scons" options on
the command line.
-- Install "scons-2.3.3.bat" and "scons.bat" wrapper scripts in the Python
+- Install "scons-2.4.0.bat" and "scons.bat" wrapper scripts in the Python
prefix directory on Windows (C:\\Python\*, for example). This can be disabled
by specifying the "--no-install-bat" option on the command line.
On UNIX or Linux systems, the "--install-bat" option may be specified to
- have "scons-2.3.3.bat" and "scons.bat" files installed in the default system
+ have "scons-2.4.0.bat" and "scons.bat" files installed in the default system
script directory, which is useful if you want to install SCons in a shared
file system directory that can be used to execute SCons from both UNIX/Linux
and Windows systems.
- Install the SCons build engine (a Python module) in an appropriate
- version-numbered SCons library directory (/usr/lib/scons-2.3.3 or
- C:\\Python\*\\scons-2.3.3, for example). See below for more options related to
+ version-numbered SCons library directory (/usr/lib/scons-2.4.0 or
+ C:\\Python\*\\scons-2.4.0, for example). See below for more options related to
installing the build engine library.
- Install the troff-format man pages in an appropriate directory on UNIX or
@@ -462,7 +462,7 @@ running all of "runtest.py -a".
Building Packages
=================
-We use SCons (version 2.3.3 or later) to build its own packages. If you
+We use SCons (version 2.4.0 or later) to build its own packages. If you
already have an appropriate version of SCons installed on your system, you can
build everything by simply running it::
@@ -477,18 +477,18 @@ about `Executing SCons Without Installing`_)::
Depending on the utilities installed on your system, any or all of the
following packages will be built::
- build/dist/scons-2.3.3-1.noarch.rpm
- build/dist/scons-2.3.3-1.src.rpm
- build/dist/scons-2.3.3.linux-i686.tar.gz
- build/dist/scons-2.3.4.tar.gz
- build/dist/scons-2.3.4.win32.exe
- build/dist/scons-2.3.4.zip
- build/dist/scons-doc-2.3.4.tar.gz
- build/dist/scons-local-2.3.4.tar.gz
- build/dist/scons-local-2.3.4.zip
- build/dist/scons-src-2.3.4.tar.gz
- build/dist/scons-src-2.3.4.zip
- build/dist/scons_2.3.3-1_all.deb
+ build/dist/scons-2.4.0-1.noarch.rpm
+ build/dist/scons-2.4.0-1.src.rpm
+ build/dist/scons-2.4.0.linux-i686.tar.gz
+ build/dist/scons-2.4.0.tar.gz
+ build/dist/scons-2.4.0.win32.exe
+ build/dist/scons-2.4.0.zip
+ build/dist/scons-doc-2.4.0.tar.gz
+ build/dist/scons-local-2.4.0.tar.gz
+ build/dist/scons-local-2.4.0.zip
+ build/dist/scons-src-2.4.0.tar.gz
+ build/dist/scons-src-2.4.0.zip
+ build/dist/scons_2.4.0-1_all.deb
The SConstruct file is supposed to be smart enough to avoid trying to build
packages for which you don't have the proper utilities installed. For
@@ -748,6 +748,5 @@ many contributors, including but not at all limited to:
\... and many others.
-__COPYRIGHT__
-
+Copyright (c) 2001 - 2015 The SCons Foundation
diff --git a/ReleaseConfig b/ReleaseConfig
index 0735012..6194438 100644
--- a/ReleaseConfig
+++ b/ReleaseConfig
@@ -32,7 +32,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
# 'final', the patchlevel is set to the release date. This value is
# mandatory and must be present in this file.
#version_tuple = (2, 2, 0, 'final', 0)
-version_tuple = (2, 3, 5, 'alpha', 0)
+version_tuple = (2, 4, 1, 'alpha', 0)
# Python versions prior to unsupported_python_version cause a fatal error
# when that version is used. Python versions prior to deprecate_python_version
@@ -51,7 +51,7 @@ deprecated_python_version = (2, 7, 0)
#month_year = 'December 2012'
# If copyright years is not given, the release year is used as the end.
-copyright_years = '2001 - 2014'
+copyright_years = '2001 - 2015'
# Local Variables:
# tab-width:4
diff --git a/SConstruct b/SConstruct
index abf5fa8..4bff134 100644
--- a/SConstruct
+++ b/SConstruct
@@ -3,10 +3,10 @@
#
# See the README.rst file for an overview of how SCons is built and tested.
-copyright_years = '2001 - 2014'
+copyright_years = '2001 - 2015'
# This gets inserted into the man pages to reflect the month of release.
-month_year = 'September 2014'
+month_year = 'September 2015'
#
# __COPYRIGHT__
@@ -43,7 +43,7 @@ import tempfile
import bootstrap
project = 'scons'
-default_version = '2.3.4'
+default_version = '2.4.0'
copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
platform = distutils.util.get_platform()
diff --git a/bin/ae-cvs-ci b/bin/ae-cvs-ci
deleted file mode 100755
index 47c8073..0000000
--- a/bin/ae-cvs-ci
+++ /dev/null
@@ -1,204 +0,0 @@
-#
-# aegis - project change supervisor
-# Copyright (C) 2004 Peter Miller;
-# All rights reserved.
-#
-# As a specific exception to the GPL, you are allowed to copy
-# this source file into your own project and modify it, without
-# releasing your project under the GPL, unless there is some other
-# file or condition which would require it.
-#
-# MANIFEST: shell script to commit changes to CVS
-#
-# It is assumed that your CVSROOT and CVS_RSH environment variables have
-# already been set appropriately.
-#
-# This script is expected to be run as by integrate_pass_notify_command
-# and as such the baseline has already assumed the shape asked for by
-# the change.
-#
-# integrate_pass_notify_command =
-# "$bin/ae-cvs-ci $project $change";
-#
-# Alternatively, you may wish to tailor this script to the individual
-# needs of your project. Make it a source file, e.g. "etc/ae-cvs-ci.sh"
-# and then use the following:
-#
-# integrate_pass_notify_command =
-# "$sh ${s etc/ae-cvs-ci} $project $change";
-#
-
-USAGE="Usage: $0 <project> <change>"
-
-PRINT="echo"
-EXECUTE="eval"
-
-while getopts "hnq" FLAG
-do
- case ${FLAG} in
- h )
- echo "${USAGE}"
- exit 0
- ;;
- n )
- EXECUTE=":"
- ;;
- q )
- PRINT=":"
- ;;
- * )
- echo "$0: unknown option ${FLAG}" >&2
- exit 1
- ;;
- esac
-done
-
-shift `expr ${OPTIND} - 1`
-
-case $# in
-2)
- project=$1
- change=$2
- ;;
-*)
- echo "${USAGE}" 1>&2
- exit 1
- ;;
-esac
-
-here=`pwd`
-
-AEGIS_PROJECT=$project
-export AEGIS_PROJECT
-AEGIS_CHANGE=$change
-export AEGIS_CHANGE
-
-module=`echo $project | sed 's|[.].*||'`
-
-baseline=`aegis -cd -bl`
-
-if test X${TMPDIR} = X; then TMPDIR=/var/tmp; fi
-
-TMP=${TMPDIR}/ae-cvs-ci.$$
-mkdir ${TMP}
-cd ${TMP}
-
-PWD=`pwd`
-if test X${PWD} != X${TMP}; then
- echo "$0: ended up in ${PWD}, not ${TMP}" >&2
- exit 1
-fi
-
-fail()
-{
- set +x
- cd $here
- rm -rf ${TMP}
- echo "FAILED" 1>&2
- exit 1
-}
-trap "fail" 1 2 3 15
-
-Command()
-{
- ${PRINT} "$*"
- ${EXECUTE} "$*"
-}
-
-#
-# Create a new CVS work area.
-#
-# Note: this assumes the module is checked-out into a directory of the
-# same name. Is there a way to ask CVS where is is going to put a
-# modules, so we can always get the "cd" right?
-#
-${PRINT} cvs co $module
-${EXECUTE} cvs co $module > LOG 2>&1
-if test $? -ne 0; then cat LOG; fail; fi
-${EXECUTE} cd $module
-
-#
-# Now we need to extract the sources from Aegis and drop them into the
-# CVS work area. There are two ways to do this.
-#
-# The first way is to use the generated tarball.
-# This has the advantage that it has the Makefile.in file in it, and
-# will work immediately.
-#
-# The second way is to use aetar, which will give exact sources, and
-# omit all derived files. This will *not* include the Makefile.in,
-# and so will not be readily compilable.
-#
-# gunzip < $baseline/export/${project}.tar.gz | tardy -rp ${project} | tar xf -
-aetar -send -comp-alg=gzip -o - | tar xzf -
-
-#
-# If any new directories have been created we will need to add them
-# to CVS before we can add the new files which we know are in them,
-# or they would not have been created. Do this only if the -n option
-# isn't used, because if it is, we won't have actually checked out the
-# source and we'd erroneously report that all of them need to be added.
-#
-if test "X${EXECUTE}" != "X:"
-then
- find . \( -name CVS -o -name Attic \) -prune -o -type d -print |
- xargs --max-args=1 |
- while read dir
- do
- if [ ! -d "$dir/CVS" ]
- then
- Command cvs add "$dir"
- fi
- done
-fi
-
-#
-# Use the Aegis meta-data to perform some CVS commands that CVS can't
-# figure out for itself.
-#
-aegis -l cf -unf | sed 's| -> [0-9][0-9.]*||' |
-while read usage action rev filename
-do
- if test "x$filename" = "x"
- then
- filename="$rev"
- fi
- case $action in
- create)
- Command cvs add $filename
- ;;
- remove)
- Command rm -f $filename
- Command cvs remove $filename
- ;;
- *)
- ;;
- esac
-done
-
-#
-# Extract the brief description. We'd like to do this using aesub
-# or something, like so:
-#
-# message=`aesub '${version} - ${change description}'`
-#
-# but the expansion of ${change description} has a lame hard-coded max of
-# 80 characters, so we have to do this by hand. (This has the slight
-# benefit of preserving backslashes in front of any double-quotes in
-# the text; that will have to be handled if we go back to using aesub.)
-#
-description=`aegis -ca -l | sed -n 's/brief_description = "\(.*\)";$/\1/p'`
-version=`aesub '${version}'`
-message="$version - $description"
-
-#
-# Now commit all the changes.
-#
-Command cvs -q commit -m \"$message\"
-
-#
-# All done. Clean up and go home.
-#
-cd $here
-rm -rf ${TMP}
-exit 0
diff --git a/bin/ae-svn-ci b/bin/ae-svn-ci
deleted file mode 100755
index 301d890..0000000
--- a/bin/ae-svn-ci
+++ /dev/null
@@ -1,240 +0,0 @@
-#
-# aegis - project change supervisor
-# Copyright (C) 2004 Peter Miller;
-# All rights reserved.
-#
-# As a specific exception to the GPL, you are allowed to copy
-# this source file into your own project and modify it, without
-# releasing your project under the GPL, unless there is some other
-# file or condition which would require it.
-#
-# MANIFEST: shell script to commit changes to Subversion
-#
-# This script is expected to be run by the integrate_pass_notify_command
-# and as such the baseline has already assumed the shape asked for by
-# the change.
-#
-# integrate_pass_notify_command =
-# "$bin/ae-svn-ci $project $change http://svn.site.com/svn/trunk --username svn_user";
-#
-# Alternatively, you may wish to tailor this script to the individual
-# needs of your project. Make it a source file, e.g. "etc/ae-svn-ci.sh"
-# and then use the following:
-#
-# integrate_pass_notify_command =
-# "$sh ${s etc/ae-svn-ci} $project $change http://svn.site.com/svn/trunk --username svn_user";
-#
-
-USAGE="Usage: $0 [-hnq] <project> <change> <url> [<co_options>]"
-
-PRINT="echo"
-EXECUTE="eval"
-
-while getopts "hnq" FLAG
-do
- case ${FLAG} in
- h )
- echo "${USAGE}"
- exit 0
- ;;
- n )
- EXECUTE=":"
- ;;
- q )
- PRINT=":"
- ;;
- * )
- echo "$0: unknown option ${FLAG}" >&2
- exit 1
- ;;
- esac
-done
-
-shift `expr ${OPTIND} - 1`
-
-case $# in
-[012])
- echo "${USAGE}" 1>&2
- exit 1
- ;;
-*)
- project=$1
- change=$2
- svn_url=$3
- shift 3
- svn_co_flags=$*
- ;;
-esac
-
-here=`pwd`
-
-AEGIS_PROJECT=$project
-export AEGIS_PROJECT
-AEGIS_CHANGE=$change
-export AEGIS_CHANGE
-
-module=`echo $project | sed 's|[.].*||'`
-
-baseline=`aegis -cd -bl`
-
-if test X${TMPDIR} = X; then TMPDIR=/var/tmp; fi
-
-TMP=${TMPDIR}/ae-svn-ci.$$
-mkdir ${TMP}
-cd ${TMP}
-
-PWD=`pwd`
-if test X${PWD} != X${TMP}; then
- echo "$0: ended up in ${PWD}, not ${TMP}" >&2
- exit 1
-fi
-
-fail()
-{
- set +x
- cd $here
- rm -rf ${TMP}
- echo "FAILED" 1>&2
- exit 1
-}
-trap "fail" 1 2 3 15
-
-Command()
-{
- ${PRINT} "$*"
- ${EXECUTE} "$*"
-}
-
-#
-# Create a new Subversion work area.
-#
-# Note: this assumes the module is checked-out into a directory of the
-# same name. Is there a way to ask Subversion where it is going to put a
-# module, so we can always get the "cd" right?
-#
-${PRINT} svn co $svn_url $module $svn_co_flags
-${EXECUTE} svn co $svn_url $module $svn_co_flags > LOG 2>&1
-if test $? -ne 0; then cat LOG; fail; fi
-${EXECUTE} cd $module
-
-#
-# Now we need to extract the sources from Aegis and drop them into the
-# Subversion work area. There are two ways to do this.
-#
-# The first way is to use the generated tarball.
-# This has the advantage that it has the Makefile.in file in it, and
-# will work immediately.
-#
-# The second way is to use aetar, which will give exact sources, and
-# omit all derived files. This will *not* include the Makefile.in,
-# and so will not be readily compilable.
-#
-# gunzip < $baseline/export/${project}.tar.gz | tardy -rp ${project} | tar xf -
-aetar -send -comp-alg=gzip -o - | tar xzf -
-
-#
-# If any new directories have been created we will need to add them
-# to Subversion before we can add the new files which we know are in them,
-# or they would not have been created. Do this only if the -n option
-# isn't used, because if it is, we won't have actually checked out the
-# source and we'd erroneously report that all of them need to be added.
-#
-if test "X${EXECUTE}" != "X:"
-then
- find . -name .svn -prune -o -type d -print |
- xargs --max-args=1 |
- while read dir
- do
- if [ ! -d "$dir/.svn" ]
- then
- Command svn add -N "$dir"
- fi
- done
-fi
-
-#
-# Use the Aegis meta-data to perform some commands that Subversion can't
-# figure out for itself. We use an inline "aer" report script to identify
-# when a remove-create pair are actually due to a move.
-#
-aegis -rpt -nph -f - <<_EOF_ |
-auto cs;
-cs = project[project_name()].state.branch.change[change_number()];
-
-columns({width = 1000;});
-
-auto file, moved;
-for (file in cs.src)
-{
- if (file.move != "")
- moved[file.move] = 1;
-}
-
-auto action;
-for (file in cs.src)
-{
- if (file.action == "remove" && file.move != "")
- action = "move";
- else
- action = file.action;
- /*
- * Suppress printing of any files created as the result of a move.
- * These are printed as the destination when printing the line for
- * the file that was *removed* as a result of the move.
- */
- if (action != "create" || ! moved[file.file_name])
- print(sprintf("%s %s \\"%s\\" \\"%s\\"", file.usage, action, file.file_name, file.move));
-}
-_EOF_
-while read line
-do
- eval set -- "$line"
- usage="$1"
- action="$2"
- srcfile="$3"
- dstfile="$4"
- case $action in
- create)
- Command svn add $srcfile
- ;;
- remove)
- Command rm -f $srcfile
- Command svn remove $srcfile
- ;;
- move)
- Command mv $dstfile $dstfile.move
- Command svn move $srcfile $dstfile
- Command cp $dstfile.move $dstfile
- Command rm -f $dstfile.move
- ;;
- *)
- ;;
- esac
-done
-
-#
-# Extract the brief description. We'd like to do this using aesub
-# or something, like so:
-#
-# message=`aesub '${version} - ${change description}'`
-#
-# but the expansion of ${change description} has a lame hard-coded max of
-# 80 characters, so we have to do this by hand. (This has the slight
-# benefit of preserving backslashes in front of any double-quotes in
-# the text; that will have to be handled if we go back to using aesub.)
-#
-description=`aegis -ca -l | sed -n 's/brief_description = "\(.*\)";$/\1/p'`
-version=`aesub '${version}'`
-message="$version - $description"
-
-#
-# Now commit all the changes.
-#
-Command svn commit -m \"$message\"
-
-#
-# All done. Clean up and go home.
-#
-cd $here
-rm -rf ${TMP}
-exit 0
diff --git a/bin/ae2cvs b/bin/ae2cvs
deleted file mode 100644
index e7cb22b..0000000
--- a/bin/ae2cvs
+++ /dev/null
@@ -1,579 +0,0 @@
-#! /usr/bin/env perl
-
-$revision = "src/ae2cvs.pl 0.04.D001 2005/08/14 15:13:36 knight";
-
-$copyright = "Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight.";
-
-#
-# All rights reserved. This program is free software; you can
-# redistribute and/or modify under the same terms as Perl itself.
-#
-
-use strict;
-use File::Find;
-use File::Spec;
-use Pod::Usage ();
-
-use vars qw( @add_list @args @cleanup @copy_list @libraries
- @mkdir_list @remove_list
- %seen_dir
- $ae_copy $aedir $aedist
- $cnum $comment $commit $common $copyright
- $cvs_command $cvsmod $cvsroot
- $delta $description $exec $help $indent $infile
- $proj $pwd $quiet $revision
- $summary $usedir $usepath );
-
-$aedist = 1;
-$cvsroot = undef;
-$exec = undef;
-$indent = "";
-
-sub version {
- print "ae2cvs: $revision\n";
- print "$copyright\n";
- exit 0;
-}
-
-{
- use Getopt::Long;
-
- Getopt::Long::Configure('no_ignore_case');
-
- my $ret = GetOptions (
- "aedist" => sub { $aedist = 1 },
- "aegis" => sub { $aedist = 0 },
- "change=i" => \$cnum,
- "d=s" => \$cvsroot,
- "file=s" => \$infile,
- "help|?" => \$help,
- "library=s" => \@libraries,
- "module=s" => \$cvsmod,
- "noexecute" => sub { $exec = 0 },
- "project=s" => \$proj,
- "quiet" => \$quiet,
- "usedir=s" => \$usedir,
- "v|version" => \&version,
- "x|execute" => sub { $exec++ if ! defined $exec || $exec != 0 },
- "X|EXECUTE" => sub { $exec = 2 if ! defined $exec || $exec != 0 },
- );
-
- Pod::Usage::pod2usage(-verbose => 0) if $help || ! $ret;
-
- $exec = 0 if ! defined $exec;
-}
-
-$cvs_command = $cvsroot ? "cvs -d $cvsroot -Q" : "cvs -Q";
-
-#
-# Wrap up the $quiet logic in one place.
-#
-sub printit {
- return if $quiet;
- my $string = join('', @_);
- $string =~ s/^/$indent/msg if $indent;
- print $string;
-}
-
-#
-# Wrappers for executing various builtin Perl functions in
-# accordance with the -n, -q and -x options.
-#
-sub execute {
- my $cmd = shift;
- printit "$cmd\n";
- if (! $exec) {
- return 1;
- }
- ! system($cmd);
-}
-
-sub _copy {
- my ($source, $dest) = @_;
- printit "cp $source $dest\n";
- if ($exec) {
- use File::Copy;
- copy($source, $dest);
- }
-}
-
-sub _chdir {
- my $dir = shift;
- printit "cd $dir\n";
- if ($exec) {
- chdir($dir) || die "ae2cvs: could not chdir($dir): $!";
- }
-}
-
-sub _mkdir {
- my $dir = shift;
- printit "mkdir $dir\n";
- if ($exec) {
- mkdir($dir);
- }
-}
-
-#
-# Put some input data through an external filter and capture the output.
-#
-sub filter {
- my ($cmd, $input) = @_;
-
- use FileHandle;
- use IPC::Open2;
-
- my $pid = open2(*READ, *WRITE, $cmd) || die "Cannot exec '$cmd': $!\n";
- print WRITE $input;
- close(WRITE);
- my $output = join('', <READ>);
- close(READ);
- return $output;
-}
-
-#
-# Parse a change description, in both 'aegis -l cd" and "aedist" formats.
-#
-# Returns an array containing the project name, the change number
-# (if any), the delta number (if any), the SUMMARY, the DESCRIPTION
-# and the lines describing the files in the change.
-#
-sub parse_change {
- my $output = shift;
-
- my ($p, $c, $d, $c_or_d, $sum, $desc, $filesection, @flines);
-
- # The project name line comes after NAME in "aegis -l cd" format,
- # and PROJECT in "aedist" format. In both cases, the project name
- # and the change/delta name are separated a comma.
- ($p = $output) =~ s/(?:NAME|PROJECT)\n([^\n]*)\n.*/$1/ms;
- ($p, $c_or_d) = (split(/,/, $p));
-
- # In "aegis -l cd" format, the project name actually comes after
- # the string "Project" and is itself enclosed in double quotes.
- $p =~ s/Project "([^"]*)"/$1/;
-
- # The change or delta string was the right-hand side of the comma.
- # "aegis -l cd" format spells it "Change 123." or "Delta 123." while
- # "aedist" format spells it "change 123."
- if ($c_or_d =~ /\s*[Cc]hange (\d+).*/) { $c = $1 };
- if ($c_or_d =~ /\s*[Dd]elta (\d+).*/) { $d = $1 };
-
- # The SUMMARY line is always followed the DESCRIPTION section.
- # It seems to always be a single line, but we grab everything in
- # between just in case.
- ($sum = $output) =~ s/.*\nSUMMARY\n//ms;
- $sum =~ s/\nDESCRIPTION\n.*//ms;
-
- # The DESCRIPTION section is followed ARCHITECTURE in "aegis -l cd"
- # format and by CAUSE in "aedist" format. Explicitly under it if the
- # string is only "none," which means they didn't supply a description.
- ($desc = $output) =~ s/.*\nDESCRIPTION\n//ms;
- $desc =~ s/\n(ARCHITECTURE|CAUSE)\n.*//ms;
- chomp($desc);
- if ($desc eq "none" || $desc eq "none\n") { $desc = undef }
-
- # The FILES section is followed by HISTORY in "aegis -l cd" format.
- # It seems to be the last section in "aedist" format, but stripping
- # a non-existent HISTORY section doesn't hurt.
- ($filesection = $output) =~ s/.*\nFILES\n//ms;
- $filesection =~ s/\nHISTORY\n.*//ms;
-
- @flines = split(/\n/, $filesection);
-
- ($p, $c, $d, $sum, $desc, \@flines)
-}
-
-#
-#
-#
-$pwd = Cwd::cwd();
-
-#
-# Fetch the file list either from our aedist input
-# or directly from the project itself.
-#
-my @filelines;
-if ($aedist) {
- local ($/);
- undef $/;
- my $infile_redir = "";
- my $contents;
- if (! $infile || $infile eq "-") {
- $contents = join('', <STDIN>);
- } else {
- open(FILE, "<$infile") || die "Cannot open '$infile': $!\n";
- binmode(FILE);
- $contents = join('', <FILE>);
- close(FILE);
- if (! File::Spec->file_name_is_absolute($infile)) {
- $infile = File::Spec->catfile($pwd, $infile);
- }
- $infile_redir = " < $infile";
- }
-
- my $output = filter("aedist -l -unf", $contents);
- my ($p, $c, $d, $s, $desc, $fl) = parse_change($output);
-
- $proj = $p if ! defined $proj;
- $summary = $s;
- $description = $desc;
- @filelines = @$fl;
-
- if (! $exec) {
- printit qq(MYTMP="/tmp/ae2cvs-ae.\$\$"\n),
- qq(mkdir \$MYTMP\n),
- qq(cd \$MYTMP\n);
- printit q(perl -MMIME::Base64 -e 'undef $/; ($c = <>) =~ s/.*\n\n//ms; print decode_base64($c)'),
- $infile_redir,
- qq( | zcat),
- qq( | cpio -i -d --quiet\n);
- $aedir = '$MYTMP';
- push(@cleanup, $aedir);
- } else {
- $aedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs-ae.$$");
- _mkdir($aedir);
- push(@cleanup, $aedir);
- _chdir($aedir);
-
- use MIME::Base64;
-
- $contents =~ s/.*\n\n//ms;
- $contents = filter("zcat", decode_base64($contents));
-
- open(CPIO, "|cpio -i -d --quiet");
- print CPIO $contents;
- close(CPIO);
- }
-
- $ae_copy = sub {
- foreach my $dest (@_) {
- my $source = File::Spec->catfile($aedir, "src", $dest);
- execute(qq(cp $source $dest));
- }
- }
-} else {
- $cnum = $ENV{AEGIS_CHANGE} if ! defined $cnum;
- $proj = $ENV{AEGIS_PROJECT} if ! defined $proj;
-
- $common = "-lib " . join(" -lib ", @libraries) if @libraries;
- $common = "$common -proj $proj" if $proj;
-
- my $output = `aegis -l cd $cnum -unf $common`;
- my ($p, $c, $d, $s, $desc, $fl) = parse_change($output);
-
- $delta = $d;
- $summary = $s;
- $description = $desc;
- @filelines = @$fl;
-
- if (! $delta) {
- print STDERR "ae2cvs: No delta number, exiting.\n";
- exit 1;
- }
-
- $ae_copy = sub {
- execute(qq(aegis -cp -ind -delta $delta $common @_));
- }
-}
-
-if (! $usedir) {
- $usedir = File::Spec->catfile(File::Spec->tmpdir, "ae2cvs.$$");
- _mkdir($usedir);
- push(@cleanup, $usedir);
-}
-
-_chdir($usedir);
-
-$usepath = $usedir;
-if (! File::Spec->file_name_is_absolute($usepath)) {
- $usepath = File::Spec->catfile($pwd, $usepath);
-}
-
-if (! -d File::Spec->catfile($usedir, "CVS")) {
- $cvsmod = (split(/\./, $proj))[0] if ! defined $cvsmod;
-
- execute(qq($cvs_command co $cvsmod));
-
- _chdir($cvsmod);
-
- $usepath = File::Spec->catfile($usepath, $cvsmod);
-}
-
-#
-# Figure out what we have to do to accomplish everything.
-#
-foreach (@filelines) {
- my @arr = split(/\s+/, $_);
- my $type = shift @arr; # source / test
- my $act = shift @arr; # modify / create
- my $file = pop @arr;
-
- if ($act eq "create" or $act eq "modify") {
- # XXX Do we really only need to do this for
- # ($act eq "create") files?
- my (undef, $dirs, undef) = File::Spec->splitpath($file);
- my $absdir = $usepath;
- my $reldir;
- my $d;
- foreach $d (File::Spec->splitdir($dirs)) {
- next if ! $d;
- $absdir = File::Spec->catdir($absdir, $d);
- $reldir = $reldir ? File::Spec->catdir($reldir, $d) : $d;
- if (! -d $absdir && ! $seen_dir{$reldir}) {
- $seen_dir{$reldir} = 1;
- push(@mkdir_list, $reldir);
- }
- }
-
- push(@copy_list, $file);
-
- if ($act eq "create") {
- push(@add_list, $file);
- }
- } elsif ($act eq "remove") {
- push(@remove_list, $file);
- } else {
- print STDERR "Unsure how to '$act' the '$file' file.\n";
- }
-}
-
-# Now go through and mkdir() the directories,
-# adding them to the CVS tree as we do.
-if (@mkdir_list) {
- if (! $exec) {
- printit qq(# The following "mkdir" and "cvs -Q add" calls are not\n),
- qq(# necessary for any directories that already exist in the\n),
- qq(# CVS tree but which aren't present locally.\n);
- }
- foreach (@mkdir_list) {
- if (! $exec) {
- printit qq(if test ! -d $_; then\n);
- $indent = " ";
- }
- _mkdir($_);
- execute(qq($cvs_command add $_));
- if (! $exec) {
- $indent = "";
- printit qq(fi\n);
- }
- }
- if (! $exec) {
- printit qq(# End of directory creation.\n);
- }
-}
-
-# Copy in any files in the change, before we try to "cvs add" them.
-$ae_copy->(@copy_list) if @copy_list;
-
-if (@add_list) {
- execute(qq($cvs_command add @add_list));
-}
-
-if (@remove_list) {
- execute(qq(rm -f @remove_list));
- execute(qq($cvs_command remove @remove_list));
-}
-
-# Last, commit the whole bunch.
-$comment = $summary;
-$comment .= "\n" . $description if $description;
-$commit = qq($cvs_command commit -m '$comment' .);
-if ($exec == 1) {
- printit qq(# Execute the following to commit the changes:\n),
- qq(# $commit\n);
-} else {
- execute($commit);
-}
-
-_chdir($pwd);
-
-#
-# Directory cleanup.
-#
-sub END {
- my $dir;
- foreach $dir (@cleanup) {
- printit "rm -rf $dir\n";
- if ($exec) {
- finddepth(sub {
- # print STDERR "unlink($_)\n" if (!-d $_);
- # print STDERR "rmdir($_)\n" if (-d $_ && $_ ne ".");
- unlink($_) if (!-d $_);
- rmdir($_) if (-d $_ && $_ ne ".");
- 1;
- }, $dir);
- rmdir($dir) || print STDERR "Could not remove $dir: $!\n";
- }
- }
-}
-
-__END__;
-
-=head1 NAME
-
-ae2cvs - convert an Aegis change set to CVS commands
-
-=head1 SYNOPSIS
-
-ae2cvs [-aedist|-aegis] [-c change] [-d cvs_root] [-f file] [-l lib]
- [-m module] [-n] [-p proj] [-q] [-u dir] [-v] [-x] [-X]
-
- -aedist use aedist format from input (default)
- -aegis query aegis repository directly
- -c change change number
- -d cvs_root CVS root directory
- -f file read aedist from file ('-' == stdin)
- -l lib Aegis library directory
- -m module CVS module
- -n no execute
- -p proj project name
- -q quiet, don't print commands
- -u dir use dir for CVS checkin
- -v print version string and exit
- -x execute the commands, but don't commit;
- two or more -x options commit changes
- -X execute the commands and commit changes
-
-=head1 DESCRIPTION
-
-The C<ae2cvs> utility can convert an Aegis change into a set of CVS (and
-other) commands to make the corresponding change(s) to a carbon-copy CVS
-repository. This can be used to keep a front-end CVS repository in sync
-with changes made to an Aegis project, either manually or automatically
-using the C<integrate_pass_notify_command> attribute of the Aegis
-project.
-
-By default, C<ae2cvs> makes no changes to any software, and only prints
-out the necessary commands. These commands can be examined first for
-safety, and then fed to any Bourne shell variant (sh, ksh, or bash) to
-make the actual CVS changes.
-
-An option exists to have C<ae2cvs> execute the commands directly.
-
-=head1 OPTIONS
-
-The C<ae2cvs> utility supports the following options:
-
-=over 4
-
-=item -aedist
-
-Reads an aedist change set.
-By default, the change set is read from standard input,
-or a file specified with the C<-f> option.
-
-=item -aegis
-
-Reads the change directly from the Aegis repository
-by executing the proper C<aegis> commands.
-
-=item -c change
-
-Specify the Aegis change number to be used.
-The value of the C<AEGIS_CHANGE> environment variable
-is used by default.
-
-=item -d cvsroot
-
-Specify the CVS root directory to be used.
-This option is passed explicitly to each executed C<cvs> command.
-The default behavior is to omit any C<-d> options
-and let the executed C<cvs> commands use the
-C<CVSROOT> environment variable as they normally would.
-
-=item -f file
-
-Reads the aedist change set from the specified C<file>,
-or from standard input if C<file> is C<'-'>.
-
-=item -l lib
-
-Specifies an Aegis library directory to be searched for global states
-files and user state files.
-
-=item -m module
-
-Specifies the name of the CVS module to be brought up-to-date.
-The default is to use the Aegis project name,
-minus any branch numbers;
-for example, given an Aegis project name of C<foo-cmd.0.1>,
-the default CVS module name is C<foo-cmd>.
-
-=item -n
-
-No execute. Commands are printed (including a command for a final
-commit of changes), but not executed. This is the default.
-
-=item -p proj
-
-Specifies the name of the Aegis project from which this change is taken.
-The value of the C<AEGIS_PROJECT> environment variable
-is used by default.
-
-=item -q
-
-Quiet. Commands are not printed.
-
-=item -u dir
-
-Use the already checked-out CVS tree that exists at C<dir>
-for the checkins and commits.
-The default is to use a separately-created temporary directory.
-
-=item -v
-
-Print the version string and exit.
-
-=item -x
-
-Execute the commands to bring the CVS repository up to date,
-except for the final commit of the changes. Two or more
-C<-x> options will cause the change to be committed.
-
-=item -X
-
-Execute the commands to bring the CVS repository up to date,
-including the final commit of the changes.
-
-=back
-
-=head1 ENVIRONMENT VARIABLES
-
-=over 4
-
-=item AE2CVS_FLAGS
-
-Specifies any options to be used to initialize
-the C<ae2cvs> utility.
-Options on the command line override these values.
-
-=back
-
-=head1 AUTHOR
-
-Steven Knight (knight at baldmt dot com)
-
-=head1 BUGS
-
-If errors occur during the execution of the Aegis or CVS commands, and
-the -X option is used, a partial change (consisting of those files for
-which the command(s) succeeded) will be committed. It would be safer to
-generate code to detect the error and print a warning.
-
-When a file has been deleted in Aegis, the standard whiteout file can
-cause a regex failure in this script. It doesn't necessarily happen all
-the time, though, so this needs more investigation.
-
-=head1 TODO
-
-Add an explicit test for using ae2cvs in the Aegis
-integrate_pass_notify_command field to support fully keeping a
-repository in sync automatically.
-
-=head1 COPYRIGHT
-
-Copyright 2001, 2002, 2003, 2004, 2005 Steven Knight.
-
-=head1 SEE ALSO
-
-aegis(1), cvs(1)
diff --git a/bin/docs-create-example-outputs.py b/bin/docs-create-example-outputs.py
index 30dc0ee..6e59d9f 100644
--- a/bin/docs-create-example-outputs.py
+++ b/bin/docs-create-example-outputs.py
@@ -14,6 +14,6 @@ if __name__ == "__main__":
print "OK"
else:
print "Not all example names and suffixes are unique! Please correct the errors listed above and try again."
- sys.exit(0)
+ sys.exit(1)
SConsExamples.createAllExampleOutputs(os.path.join('doc','user'))
diff --git a/bin/docs-update-generated.py b/bin/docs-update-generated.py
index 66b22c0..55f0035 100644
--- a/bin/docs-update-generated.py
+++ b/bin/docs-update-generated.py
@@ -8,6 +8,7 @@
#
import os
+import sys
import SConsDoc
# Directory where all generated files are stored
@@ -41,8 +42,8 @@ def generate_all():
print "Couldn't create destination folder %s! Exiting..." % gen_folder
return
# Call scons-proc.py
- os.system('python %s -b %s -f %s -t %s -v %s %s' %
- (os.path.join('bin','scons-proc.py'),
+ os.system('%s %s -b %s -f %s -t %s -v %s %s' %
+ (sys.executable, os.path.join('bin','scons-proc.py'),
argpair('builders'), argpair('functions'),
argpair('tools'), argpair('variables'), ' '.join(flist)))
diff --git a/bin/docs-validate.py b/bin/docs-validate.py
index c445c3f..f888c21 100644
--- a/bin/docs-validate.py
+++ b/bin/docs-validate.py
@@ -25,3 +25,4 @@ if __name__ == "__main__":
print "OK"
else:
print "Validation failed! Please correct the errors above and try again."
+ sys.exit(1)
diff --git a/bin/scp-sourceforge b/bin/scp-sourceforge
index ad761d1..07df3ea 100755
--- a/bin/scp-sourceforge
+++ b/bin/scp-sourceforge
@@ -19,10 +19,14 @@ do
sf/$p/$VERSION
done
+cp -p build/scons/README.txt \
+ sf/.
+
cp -p build/dist/scons-$VERSION-1.noarch.rpm \
build/dist/scons-$VERSION-1.src.rpm \
build/dist/scons-$VERSION.tar.gz \
build/dist/scons-$VERSION.win32.exe \
+ build/dist/scons-$VERSION.win-amd64.exe \
build/dist/scons-$VERSION.zip \
sf/scons/$VERSION
cp -p build/dist/scons-local-$VERSION.tar.gz \
@@ -35,4 +39,8 @@ cp -p build/dist/scons-src-$VERSION.tar.gz \
# Transmit them in this order, since the most-recent is displayed at the top
scp -r sf/scons-local/ sf/scons-src/ sf/scons/ \
$SF_USER,scons@frs.sourceforge.net:/home/pfs/project/s/sc/scons
+
+scp sf/README.txt \
+ $SF_USER,scons@frs.sourceforge.net:/home/pfs/project/s/sc/scons
+
rm -rf sf
diff --git a/bin/update-release-info.py b/bin/update-release-info.py
index 59b3e84..896f54a 100644
--- a/bin/update-release-info.py
+++ b/bin/update-release-info.py
@@ -295,6 +295,7 @@ t = UpdateFile(os.path.join('src', 'Announce.txt'))
if DEBUG: t.file = '/tmp/Announce.txt'
t.sub('\nRELEASE .*', '\nRELEASE ' + version_string + ' - ' + t.new_date)
+
# Update SConstruct
t = UpdateFile('SConstruct')
diff --git a/bin/upload-release-files.sh b/bin/upload-release-files.sh
index bf09751..c853bda 100755
--- a/bin/upload-release-files.sh
+++ b/bin/upload-release-files.sh
@@ -1,4 +1,6 @@
#!/bin/sh
+set -e
+set -x
if [ $# -lt 2 ]; then
echo Usage: $0 VERSION SF_USERNAME
@@ -15,7 +17,10 @@ SF_TOPDIR='/home/frs/project/scons'
# the build products are here:
cd build/dist
-cp -f ../../src/CHANGES.txt ../../src/RELEASE.txt ../../src/Announce.txt .
+cp -f ../../src/CHANGES.txt ../../src/RELEASE.txt ../../src/Announce.txt ../../src/README.txt .
+
+cp scons-$VERSION.win32.exe scons-$VERSION-setup.exe
+cp scons-$VERSION.win-amd64.exe scons-$VERSION-amd64-setup.exe
set -x
@@ -24,6 +29,7 @@ $RSYNC $RSYNCOPTS \
scons-$VERSION-1.noarch.rpm \
scons-$VERSION-1.src.rpm \
scons-$VERSION-setup.exe \
+ scons-$VERSION-amd64-setup.exe \
scons-$VERSION.tar.gz \
scons-$VERSION.zip \
Announce.txt CHANGES.txt RELEASE.txt \
@@ -43,6 +49,12 @@ $RSYNC $RSYNCOPTS \
Announce.txt CHANGES.txt RELEASE.txt \
$SF_USER@$SF_MACHINE:$SF_TOPDIR/scons-src/$VERSION/
+# Readme
+$RSYNC $RSYNCOPTS \
+ README.txt \
+ $SF_USER@$SF_MACHINE:$SF_TOPDIR/
+
+
#
# scons.org stuff:
diff --git a/debian/changelog b/debian/changelog
index a39a73b..7311f78 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,21 @@
+scons (2.4.0) unstable; urgency=low
+
+ * Maintenance release.
+
+ -- William Deegan <bill@baddogconsulting.com> Mon, 21 Sep 2015 08:56:00 -0700
+
+scons (2.3.6) unstable; urgency=low
+
+ * Maintenance release.
+
+ -- William Deegan <bill@baddogconsulting.com> Mon, 17 Jun 2015 21:07:32 -0700
+
+scons (2.3.5) unstable; urgency=low
+
+ * Maintenance release.
+
+ -- William Deegan <bill@baddogconsulting.com> Mon, 17 Jun 2015 21:07:32 -0700
+
scons (2.3.4) unstable; urgency=low
* Maintenance release.
diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen
index 02dc04b..d49156f 100644
--- a/doc/generated/builders.gen
+++ b/doc/generated/builders.gen
@@ -505,20 +505,13 @@ env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'],
</term>
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
-Installs a versioned shared library. The <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link>
-construction variable should be defined in the environment
-to confirm the version number in the library name.
-If <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> is not defined a warning will be issued
-and the name of the library will be parsed to derive the version.
-The symlinks appropriate to the architecture will be generated.
+Installs a versioned shared library. The symlinks appropriate to the
+architecture will be generated based on symlinks of the source library.
</para>
<example_commands xmlns="http://www.scons.org/dbxsd/v1.0">
env.InstallVersionedLib(target = '/usr/local/bin/foo',
source = 'libxyz.1.5.2.so')
-env.InstallVersionedLib(target = '/usr/local/bin/foo',
- source = 'libxyz.1.5.2.so',
- SHLIBVERSION='1.5.2')
</example_commands>
</listitem>
</varlistentry>
@@ -1257,7 +1250,7 @@ file as the second element. Normally the object file is ignored.
This builder method is only
provided when Microsoft Visual C++ is being used as the compiler.
The PCH builder method is generally used in
-conjuction with the PCH construction variable to force object files to use
+conjunction with the PCH construction variable to force object files to use
the precompiled header:
</para>
@@ -1490,7 +1483,7 @@ the results shall be as the comments above say.
<emphasis>Example 2.</emphasis>
The <function xmlns="http://www.scons.org/dbxsd/v1.0">POTUpdate</function> builder may be used with no target specified, in which
case default target <filename>messages.pot</filename> will be used. The
-default target may also be overriden by setting <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-POTDOMAIN"><envar>$POTDOMAIN</envar></link> construction
+default target may also be overridden by setting <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-POTDOMAIN"><envar>$POTDOMAIN</envar></link> construction
variable or providing it as an override to <function xmlns="http://www.scons.org/dbxsd/v1.0">POTUpdate</function> builder:
</para>
<example_commands xmlns="http://www.scons.org/dbxsd/v1.0">
@@ -1623,7 +1616,7 @@ Target nodes defined through <function xmlns="http://www.scons.org/dbxsd/v1.0">P
(they're <literal>Ignore</literal>d from <literal>'.'</literal> node). Instead,
they are added automatically to special <literal>Alias</literal>
(<literal>'po-update'</literal> by default). The alias name may be changed
-through the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-POUPDATE_ALIAS"><envar>$POUPDATE_ALIAS</envar></link> construction variable. You can easilly
+through the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-POUPDATE_ALIAS"><envar>$POUPDATE_ALIAS</envar></link> construction variable. You can easily
update <literal>PO</literal> files in your project by <command>scons
po-update</command>.
</para>
@@ -1995,22 +1988,20 @@ For maximum portability, use the <function xmlns="http://www.scons.org/dbxsd/v1.
When the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> construction variable is defined a versioned
shared library is created. This modifies the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLINKFLAGS"><envar>$SHLINKFLAGS</envar></link> as required,
adds the version number to the library name, and creates the symlinks that
-are needed. <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> needs to be of the form X.Y.Z, where X
-and Y are numbers, and Z is a number but can also contain letters to designate
-alpha, beta, or release candidate patch levels.
+are needed.
</para>
<example_commands xmlns="http://www.scons.org/dbxsd/v1.0">
env.SharedLibrary(target = 'bar', source = ['bar.c', 'foo.o'], SHLIBVERSION='1.5.2')
</example_commands>
-
<para xmlns="http://www.scons.org/dbxsd/v1.0">
-This builder may create multiple links to the library. On a POSIX system,
-for the shared library libbar.so.2.3.1, the links created would be
-libbar.so and libbar.so.2; on a Darwin (OSX) system
-the library would be libbar.2.3.1.dylib and the link would be
-libbar.dylib.
+On a POSIX system, versions with a single token create exactly one symlink:
+libbar.so.6 would have symlinks libbar.so only.
+On a POSIX system, versions with two or more
+tokens create exactly two symlinks: libbar.so.2.3.1 would have symlinks
+libbar.so and libbar.so.2; on a Darwin (OSX) system the library would be
+libbar.2.3.1.dylib and the link would be libbar.dylib.
</para>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
@@ -2564,7 +2555,7 @@ running <command>scons '.'</command>.</para></note>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
Builds a Windows type library (<filename>.tlb</filename>)
file from an input IDL file (<filename>.idl</filename>).
-In addition, it will build the associated inteface stub and
+In addition, it will build the associated interface stub and
proxy source files,
naming them according to the base name of the <filename>.idl</filename> file.
For example,
diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml
index b864ef4..904e1dd 100644
--- a/doc/generated/examples/caching_ex-random_1.xml
+++ b/doc/generated/examples/caching_ex-random_1.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<screen xmlns="http://www.scons.org/dbxsd/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">% <userinput>scons -Q</userinput>
-cc -o f1.o -c f1.c
-cc -o f3.o -c f3.c
-cc -o f4.o -c f4.c
cc -o f2.o -c f2.c
cc -o f5.o -c f5.c
+cc -o f3.o -c f3.c
+cc -o f4.o -c f4.c
+cc -o f1.o -c f1.c
cc -o prog f1.o f2.o f3.o f4.o f5.o
</screen>
diff --git a/doc/generated/examples/commandline_Variables_Help_1.xml b/doc/generated/examples/commandline_Variables_Help_1.xml
index db171eb..76f13c5 100644
--- a/doc/generated/examples/commandline_Variables_Help_1.xml
+++ b/doc/generated/examples/commandline_Variables_Help_1.xml
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<screen xmlns="http://www.scons.org/dbxsd/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">% <userinput>scons -Q -h</userinput>
+Local Build Variables:
+
RELEASE: Set to 1 to build for release
default: 0
actual: 0
diff --git a/doc/generated/examples/output_ex1_1.xml b/doc/generated/examples/output_ex1_1.xml
index 535960b..2607243 100644
--- a/doc/generated/examples/output_ex1_1.xml
+++ b/doc/generated/examples/output_ex1_1.xml
@@ -3,6 +3,8 @@
scons: Reading SConscript files ...
scons: done reading SConscript files.
+Local Build Variables:
+
Type: 'scons program' to build the production program,
'scons debug' to build the debug version.
diff --git a/doc/generated/examples/output_ex2_1.xml b/doc/generated/examples/output_ex2_1.xml
index e951c3a..74d7192 100644
--- a/doc/generated/examples/output_ex2_1.xml
+++ b/doc/generated/examples/output_ex2_1.xml
@@ -3,6 +3,8 @@
scons: Reading SConscript files ...
scons: done reading SConscript files.
+Local Build Variables:
+
Type: 'scons program' to build the production program.
Type: 'scons windebug' to build the Windows debug version.
diff --git a/doc/generated/examples/output_ex2_2.xml b/doc/generated/examples/output_ex2_2.xml
index 8dca6c3..9130d5a 100644
--- a/doc/generated/examples/output_ex2_2.xml
+++ b/doc/generated/examples/output_ex2_2.xml
@@ -3,6 +3,8 @@
scons: Reading SConscript files ...
scons: done reading SConscript files.
+Local Build Variables:
+
Type: 'scons program' to build the production program.
Use scons -H for help about command-line options.
diff --git a/doc/generated/examples/troubleshoot_Dump_1.xml b/doc/generated/examples/troubleshoot_Dump_1.xml
index 248e85c..bf141a0 100644
--- a/doc/generated/examples/troubleshoot_Dump_1.xml
+++ b/doc/generated/examples/troubleshoot_Dump_1.xml
@@ -64,7 +64,11 @@ scons: Reading SConscript files ...
'_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
'_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
'__DRPATH': '$_DRPATH',
+ '__DSHLIBVERSIONFLAGS': '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}',
+ '__LDMODULEVERSIONFLAGS': '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}',
'__RPATH': '$_RPATH',
+ '__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',
+ '__libversionflags': &lt;function __libversionflags at 0x700000&amp;gt;,
'_concat': &lt;function _concat at 0x700000&amp;gt;,
'_defines': &lt;function _defines at 0x700000&amp;gt;,
'_stripixes': &lt;function _stripixes at 0x700000&amp;gt;}
diff --git a/doc/generated/examples/troubleshoot_Dump_2.xml b/doc/generated/examples/troubleshoot_Dump_2.xml
index d6fa404..0ae8fe1 100644
--- a/doc/generated/examples/troubleshoot_Dump_2.xml
+++ b/doc/generated/examples/troubleshoot_Dump_2.xml
@@ -97,6 +97,10 @@ scons: Reading SConscript files ...
'_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
'_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
'_MSVC_OUTPUT_FLAG': &lt;function msvc_output_flag at 0x700000&amp;gt;,
+ '__DSHLIBVERSIONFLAGS': '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}',
+ '__LDMODULEVERSIONFLAGS': '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}',
+ '__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',
+ '__libversionflags': &lt;function __libversionflags at 0x700000&amp;gt;,
'_concat': &lt;function _concat at 0x700000&amp;gt;,
'_defines': &lt;function _defines at 0x700000&amp;gt;,
'_stripixes': &lt;function _stripixes at 0x700000&amp;gt;}
diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml
index 0a5299b..9763efe 100644
--- a/doc/generated/examples/troubleshoot_explain1_3.xml
+++ b/doc/generated/examples/troubleshoot_explain1_3.xml
@@ -3,5 +3,5 @@
cp file.in file.oout
scons: warning: Cannot find target file.out after building
-File "/home/garyo/src/scons-scons/src/script/scons.py", line 199, in &lt;module&gt;
+File "/home/bdbaddog/scons/scons/bootstrap/src/script/scons.py", line 199, in &lt;module&gt;
</screen>
diff --git a/doc/generated/examples/troubleshoot_stacktrace_2.xml b/doc/generated/examples/troubleshoot_stacktrace_2.xml
index 1ab65ee..add59ff 100644
--- a/doc/generated/examples/troubleshoot_stacktrace_2.xml
+++ b/doc/generated/examples/troubleshoot_stacktrace_2.xml
@@ -8,6 +8,6 @@ scons: internal stack trace:
return SCons.Taskmaster.OutOfDateTask.prepare(self)
File "bootstrap/src/engine/SCons/Taskmaster.py", line 191, in prepare
executor.prepare()
- File "bootstrap/src/engine/SCons/Executor.py", line 396, in prepare
+ File "bootstrap/src/engine/SCons/Executor.py", line 430, in prepare
raise SCons.Errors.StopError(msg % (s, self.batches[0].targets[0]))
</screen>
diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen
index 3ad36e5..e80c1c9 100644
--- a/doc/generated/functions.gen
+++ b/doc/generated/functions.gen
@@ -2565,10 +2565,10 @@ sources = Glob('*.cpp', exclude=['os_*_specific_*.cpp']) + Glob('os_%s_specific_
</varlistentry>
<varlistentry id="f-Help">
<term>
- <literal>Help(text)</literal>
+ <literal>Help(text, append=False)</literal>
</term>
<term>
- <literal>env.Help(text)</literal>
+ <literal>env.Help(text, append=False)</literal>
</term>
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
@@ -2576,12 +2576,18 @@ This specifies help text to be printed if the
<option>-h</option>
argument is given to
<filename xmlns="http://www.scons.org/dbxsd/v1.0">scons</filename>.
-If
+If
<function xmlns="http://www.scons.org/dbxsd/v1.0">Help</function>
-is called multiple times, the text is appended together in the order
-that
+is called multiple times, the text is appended together in the order that
<function xmlns="http://www.scons.org/dbxsd/v1.0">Help</function>
-is called.
+is called. With append set to False, any
+<function xmlns="http://www.scons.org/dbxsd/v1.0">Help</function>
+text generated with
+<function xmlns="http://www.scons.org/dbxsd/v1.0">AddOption</function>
+is clobbered. If append is True, the AddOption help is prepended to the help
+string, thus preserving the
+<option>-h</option>
+message.
</para>
</listitem>
</varlistentry>
diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen
index db89475..fcb7587 100644
--- a/doc/generated/tools.gen
+++ b/doc/generated/tools.gen
@@ -129,6 +129,14 @@ Sets construction variables for generic POSIX C++ compilers.
</para>
<para>Sets: &cv-link-CPPDEFPREFIX;, &cv-link-CPPDEFSUFFIX;, &cv-link-CXX;, &cv-link-CXXCOM;, &cv-link-CXXFILESUFFIX;, &cv-link-CXXFLAGS;, &cv-link-INCPREFIX;, &cv-link-INCSUFFIX;, &cv-link-OBJSUFFIX;, &cv-link-SHCXX;, &cv-link-SHCXXCOM;, &cv-link-SHCXXFLAGS;, &cv-link-SHOBJSUFFIX;.</para><para>Uses: &cv-link-CXXCOMSTR;.</para></listitem>
</varlistentry>
+ <varlistentry id="t-cyglink">
+ <term>cyglink</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Set construction variables for cygwin linker/loader.
+</para>
+<para>Sets: &cv-link-IMPLIBPREFIX;, &cv-link-IMPLIBSUFFIX;, &cv-link-LDMODULEVERSIONFLAGS;, &cv-link-LINKFLAGS;, &cv-link-RPATHPREFIX;, &cv-link-RPATHSUFFIX;, &cv-link-SHLIBPREFIX;, &cv-link-SHLIBSUFFIX;, &cv-link-SHLIBVERSIONFLAGS;, &cv-link-SHLINKCOM;, &cv-link-SHLINKFLAGS;, &cv-link-_LDMODULEVERSIONFLAGS;, &cv-link-_SHLIBVERSIONFLAGS;.</para></listitem>
+ </varlistentry>
<varlistentry id="t-default">
<term>default</term>
<listitem>
@@ -395,7 +403,7 @@ Sets construction variables for the D language compiler GDC.
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
This is actually a toolset, which supports internationalization and
-localization of sofware being constructed with SCons. The toolset loads
+localization of software being constructed with SCons. The toolset loads
following tools:
</para>
@@ -454,7 +462,7 @@ Sets construction variables for the GNU F95/F2003 GNU compiler.
<para xmlns="http://www.scons.org/dbxsd/v1.0">
Set construction variables for GNU linker/loader.
</para>
-<para>Sets: &cv-link-RPATHPREFIX;, &cv-link-RPATHSUFFIX;, &cv-link-SHLINKFLAGS;.</para></listitem>
+<para>Sets: &cv-link-LDMODULEVERSIONFLAGS;, &cv-link-RPATHPREFIX;, &cv-link-RPATHSUFFIX;, &cv-link-SHLIBVERSIONFLAGS;, &cv-link-SHLINKFLAGS;, &cv-link-_LDMODULESONAME;, &cv-link-_SHLIBSONAME;.</para></listitem>
</varlistentry>
<varlistentry id="t-gs">
<term>gs</term>
@@ -623,7 +631,7 @@ Sets construction variables for the <application xmlns="http://www.scons.org/dbx
<para xmlns="http://www.scons.org/dbxsd/v1.0">
Sets construction variables for generic POSIX linkers.
</para>
-<para>Sets: &cv-link-LDMODULE;, &cv-link-LDMODULECOM;, &cv-link-LDMODULEFLAGS;, &cv-link-LDMODULEPREFIX;, &cv-link-LDMODULESUFFIX;, &cv-link-LIBDIRPREFIX;, &cv-link-LIBDIRSUFFIX;, &cv-link-LIBLINKPREFIX;, &cv-link-LIBLINKSUFFIX;, &cv-link-LINK;, &cv-link-LINKCOM;, &cv-link-LINKFLAGS;, &cv-link-SHLIBSUFFIX;, &cv-link-SHLINK;, &cv-link-SHLINKCOM;, &cv-link-SHLINKFLAGS;.</para><para>Uses: &cv-link-LDMODULECOMSTR;, &cv-link-LINKCOMSTR;, &cv-link-SHLINKCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-LDMODULE;, &cv-link-LDMODULECOM;, &cv-link-LDMODULEFLAGS;, &cv-link-LDMODULENOVERSIONSYMLINKS;, &cv-link-LDMODULEPREFIX;, &cv-link-LDMODULESUFFIX;, &cv-link-LDMODULEVERSION;, &cv-link-LDMODULEVERSIONFLAGS;, &cv-link-LIBDIRPREFIX;, &cv-link-LIBDIRSUFFIX;, &cv-link-LIBLINKPREFIX;, &cv-link-LIBLINKSUFFIX;, &cv-link-LINK;, &cv-link-LINKCOM;, &cv-link-LINKFLAGS;, &cv-link-SHLIBSUFFIX;, &cv-link-SHLINK;, &cv-link-SHLINKCOM;, &cv-link-SHLINKFLAGS;, &cv-link-__LDMODULEVERSIONFLAGS;, &cv-link-__SHLIBVERSIONFLAGS;.</para><para>Uses: &cv-link-LDMODULECOMSTR;, &cv-link-LINKCOMSTR;, &cv-link-SHLINKCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-linkloc">
<term>linkloc</term>
@@ -901,7 +909,7 @@ Sets construction variables for the SGI linker.
<para xmlns="http://www.scons.org/dbxsd/v1.0">
Sets construction variables for the Sun library archiver.
</para>
-<para>Sets: &cv-link-AR;, &cv-link-ARCOM;, &cv-link-ARFLAGS;, &cv-link-LIBPREFIX;, &cv-link-LIBSUFFIX;, &cv-link-SHLINK;, &cv-link-SHLINKCOM;, &cv-link-SHLINKFLAGS;.</para><para>Uses: &cv-link-ARCOMSTR;, &cv-link-SHLINKCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-AR;, &cv-link-ARCOM;, &cv-link-ARFLAGS;, &cv-link-LIBPREFIX;, &cv-link-LIBSUFFIX;.</para><para>Uses: &cv-link-ARCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-suncXX">
<term>sunc++</term>
diff --git a/doc/generated/tools.mod b/doc/generated/tools.mod
index 5a5795a..1191e6a 100644
--- a/doc/generated/tools.mod
+++ b/doc/generated/tools.mod
@@ -22,6 +22,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY t-cvf "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>cvf</literal>">
<!ENTITY t-CVS "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>CVS</literal>">
<!ENTITY t-cXX "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>cXX</literal>">
+<!ENTITY t-cyglink "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>cyglink</literal>">
<!ENTITY t-default "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>default</literal>">
<!ENTITY t-dmd "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>dmd</literal>">
<!ENTITY t-docbook "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>docbook</literal>">
@@ -132,6 +133,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY t-link-cvf "<link linkend='t-cvf' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>cvf</literal></link>">
<!ENTITY t-link-CVS "<link linkend='t-CVS' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>CVS</literal></link>">
<!ENTITY t-link-cXX "<link linkend='t-cXX' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>cXX</literal></link>">
+<!ENTITY t-link-cyglink "<link linkend='t-cyglink' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>cyglink</literal></link>">
<!ENTITY t-link-default "<link linkend='t-default' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>default</literal></link>">
<!ENTITY t-link-dmd "<link linkend='t-dmd' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>dmd</literal></link>">
<!ENTITY t-link-docbook "<link linkend='t-docbook' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>docbook</literal></link>">
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index 120b4a3..3d8a87a 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -13,6 +13,24 @@
]>
<variablelist xmlns="http://www.scons.org/dbxsd/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">
+ <varlistentry id="cv-__LDMODULEVERSIONFLAGS">
+ <term>__LDMODULEVERSIONFLAGS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+This construction variable automatically introduces <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_LDMODULEVERSIONFLAGS"><envar>$_LDMODULEVERSIONFLAGS</envar></link>
+if <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULEVERSION"><envar>$LDMODULEVERSION</envar></link> is set. Othervise it evaluates to an empty string.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-__SHLIBVERSIONFLAGS">
+ <term>__SHLIBVERSIONFLAGS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+This construction variable automatically introduces <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_SHLIBVERSIONFLAGS"><envar>$_SHLIBVERSIONFLAGS</envar></link>
+if <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> is set. Othervise it evaluates to an empty string.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-AR">
<term>AR</term>
<listitem>
@@ -950,19 +968,19 @@ DFLAGPREFIX.
</para>
</listitem>
</varlistentry>
- <varlistentry id="cv-DFLAGS">
- <term>DFLAGS</term>
+ <varlistentry id="cv-_DFLAGS">
+ <term>_DFLAGS</term>
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
-DFLAGS.
+_DFLAGS.
</para>
</listitem>
</varlistentry>
- <varlistentry id="cv-_DFLAGS">
- <term>_DFLAGS</term>
+ <varlistentry id="cv-DFLAGS">
+ <term>DFLAGS</term>
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
-_DFLAGS.
+DFLAGS.
</para>
</listitem>
</varlistentry>
@@ -2820,6 +2838,15 @@ is <quote><literal>-dNOPAUSE -dBATCH -sDEVICE=pdfwrite</literal></quote>
<term>HOST_ARCH</term>
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
+ The name of the host hardware architecture used to create the Environment.
+ If a platform is specified when creating the Environment, then
+ that Platform's logic will handle setting this value.
+ This value is immutable, and should not be changed by the user after
+ the Environment is initialized.
+ Currently only set for Win32.
+</para>
+
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
Sets the host architecture for Visual Studio compiler. If not set,
default to the detected host architecture: note that this may depend
on the python you are using.
@@ -2835,16 +2862,7 @@ Valid values are the same as for <envar xmlns="http://www.scons.org/dbxsd/v1.0">
This is currently only used on Windows, but in the future it will be
used on other OSes as well.
</para>
-
-<para xmlns="http://www.scons.org/dbxsd/v1.0">
- The name of the host hardware architecture used to create the Environment.
- If a platform is specified when creating the Environment, then
- that Platform's logic will handle setting this value.
- This value is immutable, and should not be changed by the user after
- the Environment is initialized.
- Currently only set for Win32.
-</para>
- </listitem>
+</listitem>
</varlistentry>
<varlistentry id="cv-HOST_OS">
<term>HOST_OS</term>
@@ -2874,6 +2892,52 @@ The default list is:
</example_commands>
</listitem>
</varlistentry>
+ <varlistentry id="cv-IMPLIBNOVERSIONSYMLINKS">
+ <term>IMPLIBNOVERSIONSYMLINKS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Used to override <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBNOVERSIONSYMLINKS"><envar>$SHLIBNOVERSIONSYMLINKS</envar></link>/<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULENOVERSIONSYMLINKS"><envar>$LDMODULENOVERSIONSYMLINKS</envar></link> when
+creating versioned import library for a shared library/loadable module. If not defined,
+then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBNOVERSIONSYMLINKS"><envar>$SHLIBNOVERSIONSYMLINKS</envar></link>/<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULENOVERSIONSYMLINKS"><envar>$LDMODULENOVERSIONSYMLINKS</envar></link> is used to determine
+whether to disable symlink generation or not.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-IMPLIBPREFIX">
+ <term>IMPLIBPREFIX</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+The prefix used for import library names. For example, cygwin uses import
+libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries
+(<literal>cygfoo.dll</literal>). The <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="t-cyglink"><literal>cyglink</literal></link> linker sets
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-IMPLIBPREFIX"><envar>$IMPLIBPREFIX</envar></link> to <literal>'lib'</literal> and <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBPREFIX"><envar>$SHLIBPREFIX</envar></link>
+to <literal>'cyg'</literal>.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-IMPLIBSUFFIX">
+ <term>IMPLIBSUFFIX</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+The suffix used for import library names. For example, cygwin uses import
+libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries
+(<literal>cygfoo.dll</literal>). The <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="t-cyglink"><literal>cyglink</literal></link> linker sets
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-IMPLIBSUFFIX"><envar>$IMPLIBSUFFIX</envar></link> to <literal>'.dll.a'</literal> and <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBSUFFIX"><envar>$SHLIBSUFFIX</envar></link>
+to <literal>'.dll'</literal>.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-IMPLIBVERSION">
+ <term>IMPLIBVERSION</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Used to override <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link>/<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULEVERSION"><envar>$LDMODULEVERSION</envar></link> when
+generating versioned import library for a shared library/loadable module. If
+undefined, the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link>/<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULEVERSION"><envar>$LDMODULEVERSION</envar></link> is used to
+determine the version of versioned import library.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-IMPLICIT_COMMAND_DEPENDENCIES">
<term>IMPLICIT_COMMAND_DEPENDENCIES</term>
<listitem>
@@ -3408,6 +3472,15 @@ General user options passed to the linker for building loadable modules.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-LDMODULENOVERSIONSYMLINKS">
+ <term>LDMODULENOVERSIONSYMLINKS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Instructs the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-LoadableModule"><function>LoadableModule</function></link> builder to not automatically create symlinks
+for versioned modules. Defaults to <literal>$SHLIBNOVERSIONSYMLINKS</literal>
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-LDMODULEPREFIX">
<term>LDMODULEPREFIX</term>
<listitem>
@@ -3419,6 +3492,16 @@ the same as <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBPREFI
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-_LDMODULESONAME">
+ <term>_LDMODULESONAME</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+A macro that automatically generates loadable module's SONAME based on $TARGET,
+$LDMODULEVERSION and $LDMODULESUFFIX. Used by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-LoadableModule"><function>LoadableModule</function></link> builder
+when the linker tool supports SONAME (e.g. <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="t-gnulink"><literal>gnulink</literal></link>).
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-LDMODULESUFFIX">
<term>LDMODULESUFFIX</term>
<listitem>
@@ -3430,6 +3513,42 @@ the same as $SHLIBSUFFIX.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-LDMODULEVERSION">
+ <term>LDMODULEVERSION</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+When this construction variable is defined, a versioned loadable module
+is created by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-LoadableModule"><function>LoadableModule</function></link> builder. This activates the
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_LDMODULEVERSIONFLAGS"><envar>$_LDMODULEVERSIONFLAGS</envar></link> and thus modifies the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULECOM"><envar>$LDMODULECOM</envar></link> as
+required, adds the version number to the library name, and creates the symlinks
+that are needed. <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULEVERSION"><envar>$LDMODULEVERSION</envar></link> versions should exist in the same
+format as <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link>.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-LDMODULEVERSIONFLAGS">
+ <term>LDMODULEVERSIONFLAGS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Extra flags added to <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULECOM"><envar>$LDMODULECOM</envar></link> when building versioned
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-LoadableModule"><function>LoadableModule</function></link>. These flags are only used when <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULEVERSION"><envar>$LDMODULEVERSION</envar></link> is
+set.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-_LDMODULEVERSIONFLAGS">
+ <term>_LDMODULEVERSIONFLAGS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+This macro automatically introduces extra flags to <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULECOM"><envar>$LDMODULECOM</envar></link> when
+building versioned <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-LoadableModule"><function>LoadableModule</function></link> (that is when
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-LDMODULEVERSION"><envar>$LDMODULEVERSION</envar></link> is set). <literal>_LDMODULEVERSIONFLAGS</literal>
+usually adds <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSIONFLAGS"><envar>$SHLIBVERSIONFLAGS</envar></link> and some extra dynamically generated
+options (such as <literal>-Wl,-soname=$_LDMODULESONAME</literal>). It is unused
+by plain (unversioned) loadable modules.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-LEX">
<term>LEX</term>
<listitem>
@@ -6385,6 +6504,15 @@ TODO
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-SHLIBNOVERSIONSYMLINKS">
+ <term>SHLIBNOVERSIONSYMLINKS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Instructs the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-SharedLibrary"><function>SharedLibrary</function></link> builder to not create symlinks for versioned
+shared libraries.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-SHLIBPREFIX">
<term>SHLIBPREFIX</term>
<listitem>
@@ -6393,6 +6521,16 @@ The prefix used for shared library file names.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-_SHLIBSONAME">
+ <term>_SHLIBSONAME</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+A macro that automatically generates shared library's SONAME based on $TARGET,
+$SHLIBVERSION and $SHLIBSUFFIX. Used by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-SharedLibrary"><function>SharedLibrary</function></link> builder when
+the linker tool supports SONAME (e.g. <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="t-gnulink"><literal>gnulink</literal></link>).
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-SHLIBSUFFIX">
<term>SHLIBSUFFIX</term>
<listitem>
@@ -6406,11 +6544,35 @@ The suffix used for shared library file names.
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
When this construction variable is defined, a versioned shared library
-is created. This modifies the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLINKFLAGS"><envar>$SHLINKFLAGS</envar></link> as required, adds
-the version number to the library name, and creates the symlinks that
-are needed. <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> needs to be of the form X.Y.Z,
-where X and Y are numbers, and Z is a number but can also contain
-letters to designate alpha, beta, or release candidate patch levels.
+is created by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-SharedLibrary"><function>SharedLibrary</function></link> builder. This activates the
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_SHLIBVERSIONFLAGS"><envar>$_SHLIBVERSIONFLAGS</envar></link> and thus modifies the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLINKCOM"><envar>$SHLINKCOM</envar></link> as
+required, adds the version number to the library name, and creates the symlinks
+that are needed. <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> versions should exist as alpha-numeric,
+decimal-delimited values as defined by the regular expression "\w+[\.\w+]*".
+Example <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> values include '1', '1.2.3', and '1.2.gitaa412c8b'.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-SHLIBVERSIONFLAGS">
+ <term>SHLIBVERSIONFLAGS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Extra flags added to <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLINKCOM"><envar>$SHLINKCOM</envar></link> when building versioned
+<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-SharedLibrary"><function>SharedLibrary</function></link>. These flags are only used when <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link> is
+set.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-_SHLIBVERSIONFLAGS">
+ <term>_SHLIBVERSIONFLAGS</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+This macro automatically introduces extra flags to <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLINKCOM"><envar>$SHLINKCOM</envar></link> when
+building versioned <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="b-SharedLibrary"><function>SharedLibrary</function></link> (that is when <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSION"><envar>$SHLIBVERSION</envar></link>
+is set). <literal>_SHLIBVERSIONFLAGS</literal> usually adds <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHLIBVERSIONFLAGS"><envar>$SHLIBVERSIONFLAGS</envar></link>
+and some extra dynamically generated options (such as
+<literal>-Wl,-soname=$_SHLIBSONAME</literal>. It is unused by "plain"
+(unversioned) shared libraries.
</para>
</listitem>
</varlistentry>
@@ -6484,6 +6646,18 @@ The suffix used for shared object file names.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-SONAME">
+ <term>SONAME</term>
+ <listitem>
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
+Variable used to hard-code SONAME for versioned shared library/loadable module.
+<example_commands>
+env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SONAME='libtest.so.2')
+</example_commands>
+The variable is used, for example, by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="t-gnulink"><literal>gnulink</literal></link> linker tool.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-SOURCE">
<term>SOURCE</term>
<listitem>
@@ -6753,9 +6927,7 @@ and translated into the
The list of directories that the scripting language wrapper
and interface generate will search for included files.
The SWIG implicit dependency scanner will search these
-directories for include files.
-The default is to use the same path
-specified as <envar xmlns="http://www.scons.org/dbxsd/v1.0">$CPPPATH</envar>.
+directories for include files. The default value is an empty list.
</para>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
@@ -6802,7 +6974,7 @@ include <envar xmlns="http://www.scons.org/dbxsd/v1.0">$_SWIGINCFLAGS</envar>:
</para>
<example_commands xmlns="http://www.scons.org/dbxsd/v1.0">
-env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SORUCES")
+env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SOURCES")
</example_commands>
</listitem>
</varlistentry>
@@ -6866,6 +7038,13 @@ that may not be set or used in a construction environment.
<term>TARGET_ARCH</term>
<listitem>
<para xmlns="http://www.scons.org/dbxsd/v1.0">
+ The name of the target hardware architecture for the compiled objects
+ created by this Environment.
+ This defaults to the value of HOST_ARCH, and the user can override it.
+ Currently only set for Win32.
+</para>
+
+<para xmlns="http://www.scons.org/dbxsd/v1.0">
Sets the target architecture for Visual Studio compiler (i.e. the arch
of the binaries generated by the compiler). If not set, default to
<envar xmlns="http://www.scons.org/dbxsd/v1.0">$HOST_ARCH</envar>, or, if that is unset, to the architecture of the
@@ -6890,14 +7069,7 @@ and <literal>ia64</literal> (Itanium).
For example, if you want to compile 64-bit binaries, you would set
<literal>TARGET_ARCH='x86_64'</literal> in your SCons environment.
</para>
-
-<para xmlns="http://www.scons.org/dbxsd/v1.0">
- The name of the target hardware architecture for the compiled objects
- created by this Environment.
- This defaults to the value of HOST_ARCH, and the user can override it.
- Currently only set for Win32.
-</para>
- </listitem>
+</listitem>
</varlistentry>
<varlistentry id="cv-TARGET_OS">
<term>TARGET_OS</term>
diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod
index 3f254a5..f7303f2 100644
--- a/doc/generated/variables.mod
+++ b/doc/generated/variables.mod
@@ -8,6 +8,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
-->
+<!ENTITY cv-__LDMODULEVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$__LDMODULEVERSIONFLAGS</envar>">
+<!ENTITY cv-__SHLIBVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$__SHLIBVERSIONFLAGS</envar>">
<!ENTITY cv-AR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$AR</envar>">
<!ENTITY cv-ARCHITECTURE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$ARCHITECTURE</envar>">
<!ENTITY cv-ARCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$ARCOM</envar>">
@@ -76,8 +78,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-DESCRIPTION_lang "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DESCRIPTION_lang</envar>">
<!ENTITY cv-DFILESUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DFILESUFFIX</envar>">
<!ENTITY cv-DFLAGPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DFLAGPREFIX</envar>">
-<!ENTITY cv-DFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DFLAGS</envar>">
<!ENTITY cv-_DFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_DFLAGS</envar>">
+<!ENTITY cv-DFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DFLAGS</envar>">
<!ENTITY cv-DFLAGSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DFLAGSUFFIX</envar>">
<!ENTITY cv-_DINCFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_DINCFLAGS</envar>">
<!ENTITY cv-DINCPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$DINCPREFIX</envar>">
@@ -216,6 +218,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-HOST_ARCH "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$HOST_ARCH</envar>">
<!ENTITY cv-HOST_OS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$HOST_OS</envar>">
<!ENTITY cv-IDLSUFFIXES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$IDLSUFFIXES</envar>">
+<!ENTITY cv-IMPLIBNOVERSIONSYMLINKS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$IMPLIBNOVERSIONSYMLINKS</envar>">
+<!ENTITY cv-IMPLIBPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$IMPLIBPREFIX</envar>">
+<!ENTITY cv-IMPLIBSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$IMPLIBSUFFIX</envar>">
+<!ENTITY cv-IMPLIBVERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$IMPLIBVERSION</envar>">
<!ENTITY cv-IMPLICIT_COMMAND_DEPENDENCIES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$IMPLICIT_COMMAND_DEPENDENCIES</envar>">
<!ENTITY cv-INCPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$INCPREFIX</envar>">
<!ENTITY cv-INCSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$INCSUFFIX</envar>">
@@ -253,8 +259,13 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-LDMODULECOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULECOM</envar>">
<!ENTITY cv-LDMODULECOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULECOMSTR</envar>">
<!ENTITY cv-LDMODULEFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULEFLAGS</envar>">
+<!ENTITY cv-LDMODULENOVERSIONSYMLINKS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULENOVERSIONSYMLINKS</envar>">
<!ENTITY cv-LDMODULEPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULEPREFIX</envar>">
+<!ENTITY cv-_LDMODULESONAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_LDMODULESONAME</envar>">
<!ENTITY cv-LDMODULESUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULESUFFIX</envar>">
+<!ENTITY cv-LDMODULEVERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULEVERSION</envar>">
+<!ENTITY cv-LDMODULEVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LDMODULEVERSIONFLAGS</envar>">
+<!ENTITY cv-_LDMODULEVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_LDMODULEVERSIONFLAGS</envar>">
<!ENTITY cv-LEX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LEX</envar>">
<!ENTITY cv-LEXCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LEXCOM</envar>">
<!ENTITY cv-LEXCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LEXCOMSTR</envar>">
@@ -505,15 +516,20 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-SHFORTRANPPCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHFORTRANPPCOM</envar>">
<!ENTITY cv-SHFORTRANPPCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHFORTRANPPCOMSTR</envar>">
<!ENTITY cv-SHLIBEMITTER "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLIBEMITTER</envar>">
+<!ENTITY cv-SHLIBNOVERSIONSYMLINKS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLIBNOVERSIONSYMLINKS</envar>">
<!ENTITY cv-SHLIBPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLIBPREFIX</envar>">
+<!ENTITY cv-_SHLIBSONAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_SHLIBSONAME</envar>">
<!ENTITY cv-SHLIBSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLIBSUFFIX</envar>">
<!ENTITY cv-SHLIBVERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLIBVERSION</envar>">
+<!ENTITY cv-SHLIBVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLIBVERSIONFLAGS</envar>">
+<!ENTITY cv-_SHLIBVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_SHLIBVERSIONFLAGS</envar>">
<!ENTITY cv-SHLINK "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLINK</envar>">
<!ENTITY cv-SHLINKCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLINKCOM</envar>">
<!ENTITY cv-SHLINKCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLINKCOMSTR</envar>">
<!ENTITY cv-SHLINKFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHLINKFLAGS</envar>">
<!ENTITY cv-SHOBJPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHOBJPREFIX</envar>">
<!ENTITY cv-SHOBJSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHOBJSUFFIX</envar>">
+<!ENTITY cv-SONAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SONAME</envar>">
<!ENTITY cv-SOURCE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SOURCE</envar>">
<!ENTITY cv-SOURCE_URL "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SOURCE_URL</envar>">
<!ENTITY cv-SOURCES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SOURCES</envar>">
@@ -646,6 +662,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
-->
+<!ENTITY cv-link-__LDMODULEVERSIONFLAGS "<link linkend='cv-__LDMODULEVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$__LDMODULEVERSIONFLAGS</envar></link>">
+<!ENTITY cv-link-__SHLIBVERSIONFLAGS "<link linkend='cv-__SHLIBVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$__SHLIBVERSIONFLAGS</envar></link>">
<!ENTITY cv-link-AR "<link linkend='cv-AR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$AR</envar></link>">
<!ENTITY cv-link-ARCHITECTURE "<link linkend='cv-ARCHITECTURE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$ARCHITECTURE</envar></link>">
<!ENTITY cv-link-ARCOM "<link linkend='cv-ARCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$ARCOM</envar></link>">
@@ -714,8 +732,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-DESCRIPTION_lang "<link linkend='cv-DESCRIPTION_lang' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DESCRIPTION_lang</envar></link>">
<!ENTITY cv-link-DFILESUFFIX "<link linkend='cv-DFILESUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DFILESUFFIX</envar></link>">
<!ENTITY cv-link-DFLAGPREFIX "<link linkend='cv-DFLAGPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DFLAGPREFIX</envar></link>">
-<!ENTITY cv-link-DFLAGS "<link linkend='cv-DFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DFLAGS</envar></link>">
<!ENTITY cv-link-_DFLAGS "<link linkend='cv-_DFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_DFLAGS</envar></link>">
+<!ENTITY cv-link-DFLAGS "<link linkend='cv-DFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DFLAGS</envar></link>">
<!ENTITY cv-link-DFLAGSUFFIX "<link linkend='cv-DFLAGSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DFLAGSUFFIX</envar></link>">
<!ENTITY cv-link-_DINCFLAGS "<link linkend='cv-_DINCFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_DINCFLAGS</envar></link>">
<!ENTITY cv-link-DINCPREFIX "<link linkend='cv-DINCPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$DINCPREFIX</envar></link>">
@@ -854,6 +872,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-HOST_ARCH "<link linkend='cv-HOST_ARCH' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$HOST_ARCH</envar></link>">
<!ENTITY cv-link-HOST_OS "<link linkend='cv-HOST_OS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$HOST_OS</envar></link>">
<!ENTITY cv-link-IDLSUFFIXES "<link linkend='cv-IDLSUFFIXES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$IDLSUFFIXES</envar></link>">
+<!ENTITY cv-link-IMPLIBNOVERSIONSYMLINKS "<link linkend='cv-IMPLIBNOVERSIONSYMLINKS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$IMPLIBNOVERSIONSYMLINKS</envar></link>">
+<!ENTITY cv-link-IMPLIBPREFIX "<link linkend='cv-IMPLIBPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$IMPLIBPREFIX</envar></link>">
+<!ENTITY cv-link-IMPLIBSUFFIX "<link linkend='cv-IMPLIBSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$IMPLIBSUFFIX</envar></link>">
+<!ENTITY cv-link-IMPLIBVERSION "<link linkend='cv-IMPLIBVERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$IMPLIBVERSION</envar></link>">
<!ENTITY cv-link-IMPLICIT_COMMAND_DEPENDENCIES "<link linkend='cv-IMPLICIT_COMMAND_DEPENDENCIES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$IMPLICIT_COMMAND_DEPENDENCIES</envar></link>">
<!ENTITY cv-link-INCPREFIX "<link linkend='cv-INCPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$INCPREFIX</envar></link>">
<!ENTITY cv-link-INCSUFFIX "<link linkend='cv-INCSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$INCSUFFIX</envar></link>">
@@ -891,8 +913,13 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-LDMODULECOM "<link linkend='cv-LDMODULECOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULECOM</envar></link>">
<!ENTITY cv-link-LDMODULECOMSTR "<link linkend='cv-LDMODULECOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULECOMSTR</envar></link>">
<!ENTITY cv-link-LDMODULEFLAGS "<link linkend='cv-LDMODULEFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULEFLAGS</envar></link>">
+<!ENTITY cv-link-LDMODULENOVERSIONSYMLINKS "<link linkend='cv-LDMODULENOVERSIONSYMLINKS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULENOVERSIONSYMLINKS</envar></link>">
<!ENTITY cv-link-LDMODULEPREFIX "<link linkend='cv-LDMODULEPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULEPREFIX</envar></link>">
+<!ENTITY cv-link-_LDMODULESONAME "<link linkend='cv-_LDMODULESONAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_LDMODULESONAME</envar></link>">
<!ENTITY cv-link-LDMODULESUFFIX "<link linkend='cv-LDMODULESUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULESUFFIX</envar></link>">
+<!ENTITY cv-link-LDMODULEVERSION "<link linkend='cv-LDMODULEVERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULEVERSION</envar></link>">
+<!ENTITY cv-link-LDMODULEVERSIONFLAGS "<link linkend='cv-LDMODULEVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LDMODULEVERSIONFLAGS</envar></link>">
+<!ENTITY cv-link-_LDMODULEVERSIONFLAGS "<link linkend='cv-_LDMODULEVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_LDMODULEVERSIONFLAGS</envar></link>">
<!ENTITY cv-link-LEX "<link linkend='cv-LEX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LEX</envar></link>">
<!ENTITY cv-link-LEXCOM "<link linkend='cv-LEXCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LEXCOM</envar></link>">
<!ENTITY cv-link-LEXCOMSTR "<link linkend='cv-LEXCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LEXCOMSTR</envar></link>">
@@ -1143,15 +1170,20 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-SHFORTRANPPCOM "<link linkend='cv-SHFORTRANPPCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHFORTRANPPCOM</envar></link>">
<!ENTITY cv-link-SHFORTRANPPCOMSTR "<link linkend='cv-SHFORTRANPPCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHFORTRANPPCOMSTR</envar></link>">
<!ENTITY cv-link-SHLIBEMITTER "<link linkend='cv-SHLIBEMITTER' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLIBEMITTER</envar></link>">
+<!ENTITY cv-link-SHLIBNOVERSIONSYMLINKS "<link linkend='cv-SHLIBNOVERSIONSYMLINKS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLIBNOVERSIONSYMLINKS</envar></link>">
<!ENTITY cv-link-SHLIBPREFIX "<link linkend='cv-SHLIBPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLIBPREFIX</envar></link>">
+<!ENTITY cv-link-_SHLIBSONAME "<link linkend='cv-_SHLIBSONAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_SHLIBSONAME</envar></link>">
<!ENTITY cv-link-SHLIBSUFFIX "<link linkend='cv-SHLIBSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLIBSUFFIX</envar></link>">
<!ENTITY cv-link-SHLIBVERSION "<link linkend='cv-SHLIBVERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLIBVERSION</envar></link>">
+<!ENTITY cv-link-SHLIBVERSIONFLAGS "<link linkend='cv-SHLIBVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLIBVERSIONFLAGS</envar></link>">
+<!ENTITY cv-link-_SHLIBVERSIONFLAGS "<link linkend='cv-_SHLIBVERSIONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_SHLIBVERSIONFLAGS</envar></link>">
<!ENTITY cv-link-SHLINK "<link linkend='cv-SHLINK' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLINK</envar></link>">
<!ENTITY cv-link-SHLINKCOM "<link linkend='cv-SHLINKCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLINKCOM</envar></link>">
<!ENTITY cv-link-SHLINKCOMSTR "<link linkend='cv-SHLINKCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLINKCOMSTR</envar></link>">
<!ENTITY cv-link-SHLINKFLAGS "<link linkend='cv-SHLINKFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHLINKFLAGS</envar></link>">
<!ENTITY cv-link-SHOBJPREFIX "<link linkend='cv-SHOBJPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHOBJPREFIX</envar></link>">
<!ENTITY cv-link-SHOBJSUFFIX "<link linkend='cv-SHOBJSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHOBJSUFFIX</envar></link>">
+<!ENTITY cv-link-SONAME "<link linkend='cv-SONAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SONAME</envar></link>">
<!ENTITY cv-link-SOURCE "<link linkend='cv-SOURCE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SOURCE</envar></link>">
<!ENTITY cv-link-SOURCE_URL "<link linkend='cv-SOURCE_URL' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SOURCE_URL</envar></link>">
<!ENTITY cv-link-SOURCES "<link linkend='cv-SOURCES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SOURCES</envar></link>">
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index c3abb64..5c832c2 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -7040,7 +7040,7 @@ env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
<para>For more information see the document for the PCH builder, and the PCH and
PCHSTOP construction variables. To learn about the details of precompiled
-headers consult the MSDN documention for /Yc, /Yu, and /Yp.</para>
+headers consult the MSDN documentation for /Yc, /Yu, and /Yp.</para>
</refsect2>
diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml
index d107b4a..3e66172 100644
--- a/doc/user/build-install.xml
+++ b/doc/user/build-install.xml
@@ -231,7 +231,7 @@ Python 2.5.1
<para>
Or, you can use a graphical RPM package manager.
- See your package manager application's documention
+ See your package manager application's documentation
for specific instructions about
how to use it to install a downloaded RPM.
diff --git a/doc/user/main.xml b/doc/user/main.xml
index a165777..d2c4c18 100644
--- a/doc/user/main.xml
+++ b/doc/user/main.xml
@@ -74,10 +74,10 @@
<corpauthor>Steven Knight and the SCons Development Team</corpauthor>
- <pubdate>2004 - 2014</pubdate>
+ <pubdate>2004 - 2015</pubdate>
<copyright>
- <year>2004 - 2014</year>
+ <year>2004 - 2015</year>
<holder>The SCons Foundation</holder>
</copyright>
diff --git a/doc/user/output.xml b/doc/user/output.xml
index cdb28d7..78dcca4 100644
--- a/doc/user/output.xml
+++ b/doc/user/output.xml
@@ -83,6 +83,21 @@ Type: 'scons program' to build the production program,
<para>
+ Optionally, one can specify the append flag:
+
+ </para>
+
+ <scons_example name="output_ex1_a">
+ <file name="SConstruct" printme="1">
+Help("""
+Type: 'scons program' to build the production program,
+ 'scons debug' to build the debug version.
+""", append=True)
+ </file>
+ </scons_example>
+
+ <para>
+
(Note the above use of the Python triple-quote syntax,
which comes in very handy for
specifying multi-line strings like help text.)
@@ -120,6 +135,13 @@ Type: 'scons program' to build the production program,
<para>
+ When used with &AddOption; Help("text", append=False) will clobber any help output associated with AddOption().
+ To preserve the help output from AddOption(), set append=True.
+
+ </para>
+
+ <para>
+
Another use would be to make the help text conditional
on some variable.
For example, suppose you only want to display
diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml
index 32a0b5b..c91188e 100644
--- a/doc/user/repositories.xml
+++ b/doc/user/repositories.xml
@@ -423,7 +423,7 @@ int main() { printf("Hello, world!\n"); }
<para>
Change all occurrences of <literal>#include "file.h"</literal>
- to <literal>#include &amp;lt;file.h&amp;gt;</literal>.
+ to <literal>#include &lt;file.h&gt;</literal>.
Use of <literal>#include</literal> with angle brackets
does not have the same behavior--the <literal>-I</literal>
directories are searched first
diff --git a/doc/user/sconf.xml b/doc/user/sconf.xml
index 214569d..569ab1a 100644
--- a/doc/user/sconf.xml
+++ b/doc/user/sconf.xml
@@ -275,7 +275,7 @@ env = conf.Finish()
<sconstruct>
env = Environment()
conf = Configure(env)
-if not conf.CheckType('off_t', '#include &amp;lt;sys/types.h&amp;gt;\n'):
+if not conf.CheckType('off_t', '#include &lt;sys/types.h&gt;\n'):
print 'Did not find off_t typedef, assuming int'
conf.env.Append(CCFLAGS = '-Doff_t=int')
env = conf.Finish()
@@ -324,7 +324,7 @@ scons: `.' is up to date.
<sconstruct>
mylib_test_source_file = """
-#include &amp;lt;mylib.h&amp;gt;
+#include &lt;mylib.h&gt;
int main(int argc, char **argv)
{
MyLibrary mylib(argc, argv);
@@ -401,7 +401,7 @@ conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary})
<sconstruct>
mylib_test_source_file = """
-#include &amp;lt;mylib.h&amp;gt;
+#include &lt;mylib.h&gt;
int main(int argc, char **argv)
{
MyLibrary mylib(argc, argv);
diff --git a/doc/version.xml b/doc/version.xml
index f6eba1f..172a9ec 100644
--- a/doc/version.xml
+++ b/doc/version.xml
@@ -2,6 +2,6 @@
<!--
THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
-->
-<!ENTITY builddate "2013-03-23">
-<!ENTITY buildversion "2.3.0">
+<!ENTITY builddate "2015-06-17">
+<!ENTITY buildversion "2.3.5">
<!ENTITY buildrevision "1">
diff --git a/src/Announce.txt b/src/Announce.txt
index 7c6fdd5..7a13b81 100644
--- a/src/Announce.txt
+++ b/src/Announce.txt
@@ -18,6 +18,23 @@ So that everyone using SCons can help each other learn how to use it more
effectively, please go to http://scons.org/lists.php#users to sign up for
the scons-users mailing list.
+==============IMPORTANT NOTICE===========
+
+As has been pre-announced in SCons's mailing lists:
+
+* https://pairlist4.pair.net/pipermail/scons-users/2014-July/002734.html ,
+* https://pairlist2.pair.net/pipermail/scons-dev/2014-December/002107.html
+* https://pairlist4.pair.net/pipermail/scons-users/2015-February/003454.html
+
+We're planning to switch the Node class to using "slots" in the core sources,
+mainly to reduce memory consumption by up to 35% in large build projects.
+
+This feature has been tested extensively and we don't expect any problems for you.
+However as with all major changes it would be wise to test V2.4.0 when it is
+released. Especially if you are directly using the Node class.
+
+=================================================================
+
RELEASE VERSION/DATE TO BE FILLED IN LATER
@@ -26,6 +43,39 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
since last release. This announcement highlights only the important
changes.
+ Please note the following important changes since release 2.3.6:
+ - Switch several core classes to use "slots" to reduce memory
+ usage. (PR #2180, #2178, #2198)
+
+ Please note the following important changes since release 2.3.5:
+ - Support for Visual Studio 2015
+
+ Please note the following important changes since release 2.3.4:
+ - Documentation fixes for libraries.xml and
+ builders-writing.xml (#2989 and #2990)
+ - Extended docs for InstallVersionedLib/SharedLibrary,
+ and added SKIP_WIN_PACKAGES argument to build script
+ bootstrap.py (PR #230, #3002).
+ - Fixed symlink support (PR #227, #2395).
+ - Updated debug-count test case (PR #229).
+ - Fixed incomplete LIBS flattening and substitution in
+ Program scanner(PR #205, #2954).
+ - Added new method rentry_exists_on_disk to Node.FS (PR #193).
+ - Fixed several D tests under the different OS.
+ - Add support for f08 file extensions for Fortran 2008 code.
+ - Show --config choices if no argument is specified (PR #202).
+ - Fixed build crash when XML toolchain isn't installed, and
+ activated compression for ZIP archives.
+ - Fix for VersionedSharedLibrary under 'sunos' platform.
+ - Fixed dll link with precompiled headers on MSVC 2012
+ - Added an 'exclude' parameter to Glob()
+ - Support for multiple cmdargs (one per variant) in VS project files.
+ - Various improvements for TempFileMunge class.
+ - Added an implementation for Visual Studio users files (PR #209).
+ - Added support for the 'PlatformToolset' tag in VS project files (#2978).
+ - Added support for '-isystem' to ParseFlags.
+
+
Please note the following important changes since release 2.3.3:
-- Fix for EnsureSConsVersion regression in 2.3.3.
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 2a2c21f..11fdeba 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -6,6 +6,62 @@
RELEASE VERSION/DATE TO BE FILLED IN LATER
+ From Florian Miedniak:
+ - Fixed tigris issue #3011: Glob() excludes didn't work when used with VariantDir(duplicate=0)
+
+ From William Blevins:
+ - InstallVersionedLib now available in the DefaultEnvironment context.
+ - Improves orthogonality of use cases between different Install functions.
+
+ From William Roberts:
+ - Fix bug 2831 and allow Help() text to be appended to AddOption() help.
+
+ From Paweł Tomulik:
+ - Reimplemented versioning for shared libraries, with the following effects
+ - Fixed tigris issues #3001, #3006.
+ - Fixed several other issues not reported to tigris, including:
+ issues with versioned libraries in subdirectories with tricky names,
+ issues with versioned libraries and variant directories,
+ issue with soname not being injected to library when using D linkers,
+ - Switched to direct symlinks instead of daisy-chained ones -- soname and
+ development symlinks point directly to the versioned shared library now),
+ for rationale see:
+ https://www.debian.org/doc/debian-policy/ch-sharedlibs.html
+ https://fedoraproject.org/wiki/Packaging:Guidelines#Devel_Packages
+ https://bitbucket.org/scons/scons/pull-requests/247/new-versioned-libraries-gnulink-cyglink/diff#comment-10063929
+ - New construction variables to allow override default behavior: SONAME,
+ SHLIBVERSIONFLAGS, _SHLIBVERSIONFLAGS, SHLIBNOVERSIONSYMLINKS,
+ LDMODULEVERSION, LDMODULEVERSIONFLAGS, _LDMODULEVERSIONFLAGS,
+ LDMODULENOVERSIONSYMLINKS.
+ - Changed logic used to configure the versioning machinery from
+ platform-centric to linker-oriented.
+ - The SHLIBVERSION/LDMODULEVERSION variables are no longer validated by
+ SCons (more freedom to users).
+ - InstallVersionedLib() doesn't use SHLIBVERSION anymore.
+ - Enchanced docs for the library versioning stuff.
+ - New tests for versioned libraries.
+ - Library versioning is currently implemented for the following linker
+ tools: 'cyglink', 'gnulink', 'sunlink'.
+
+RELEASE 2.4.0 - Mon, 21 Sep 2015 08:56:00 -0700
+
+ From Dirk Baechle:
+ - Switched several core classes to use "slots", to
+ reduce the overall memory consumption in large
+ projects (fixes #2180, #2178, #2198)
+ - Memoizer counting uses decorators now, instead of
+ the old metaclasses approach.
+
+ From Andrew Featherstone
+ - Fixed typo in SWIGPATH description
+
+RELEASE 2.3.6 - Mon, 31 Jul 2015 14:35:03 -0700
+
+ From Rob Smith:
+ - Added support for Visual Studio 2015
+
+RELEASE 2.3.5 - Mon, 17 Jun 2015 21:07:32 -0700
+
From Stephen Pollard:
- Documentation fixes for libraries.xml and
builders-writing.xml (#2989 and #2990)
@@ -36,8 +92,6 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Show --config choices if no argument is specified (PR #202).
- Fixed build crash when XML toolchain isn't installed, and
activated compression for ZIP archives.
- - Fixed --tree=all print when build tree contains non-ascii
- Node representation (PR #235).
From Alexandre Feblot:
- Fix for VersionedSharedLibrary under 'sunos' platform.
diff --git a/src/README.txt b/src/README.txt
index 5d880c2..d80460a 100644
--- a/src/README.txt
+++ b/src/README.txt
@@ -28,7 +28,8 @@ the latest version by checking the SCons download page at:
EXECUTION REQUIREMENTS
======================
-Running SCons requires Python version 2.4 or later. There should be
+Running SCons requires Python version 2.7.*. Currently it does not
+run on the Python 3.x release. There should be
no other dependencies or requirements to run SCons. (There is, however,
an additional requirement to *install* SCons from this particular
package; see the next section.)
@@ -224,20 +225,26 @@ Check the SCons web site at:
AUTHOR INFO
===========
-
-Steven Knight
-knight at baldmt dot com
-http://www.baldmt.com/~knight/
-
-With plenty of help from the SCons Development team:
- Chad Austin
- Charles Crain
- Steve Leblanc
- Greg Noel
- Gary Oberbrunner
- Anthony Roach
- Greg Spencer
- Christoph Wiedemann
-
-__COPYRIGHT__
-__FILE__ __REVISION__ __DATE__ __DEVELOPER__
+SCons was originally written by Steven Knight, knight at baldmt dot com.
+Since around 2010 it has been maintained by the SCons
+development team, co-managed by Bill Deegan and Gary Oberbrunner, with
+many contributors, including but not at all limited to:
+
+- Chad Austin
+- Dirk Baechle
+- Charles Crain
+- William Deegan
+- Steve Leblanc
+- Rob Managan
+- Greg Noel
+- Gary Oberbrunner
+- Anthony Roach
+- Greg Spencer
+- Tom Tanner
+- Anatoly Techtonik
+- Christoph Wiedemann
+- Russel Winder
+
+\... and many others.
+
+Copyright (c) 2001 - 2015 The SCons Foundation
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
index 1fa033b..1bf2eb1 100644
--- a/src/RELEASE.txt
+++ b/src/RELEASE.txt
@@ -1,4 +1,4 @@
- A new SCons checkpoint release, 2.3.5.alpha.yyyymmdd, is now available
+ A new SCons checkpoint release, 2.3.13.alpha.yyyymmdd, is now available
on the SCons download page:
http://www.scons.org/download.php
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index eecea11..5a34825 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -540,7 +540,7 @@ class _ActionAction(ActionBase):
if chdir:
save_cwd = os.getcwd()
try:
- chdir = str(chdir.abspath)
+ chdir = str(chdir.get_abspath())
except AttributeError:
if not is_String(chdir):
if executor:
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index c82f6ac..769b15d 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -352,11 +352,6 @@ class BuilderBase(object):
nodes (files) from input nodes (files).
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
-
def __init__(self, action = None,
prefix = '',
suffix = '',
@@ -758,8 +753,7 @@ class BuilderBase(object):
def _get_src_builders_key(self, env):
return id(env)
- memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
-
+ @SCons.Memoize.CountDictCall(_get_src_builders_key)
def get_src_builders(self, env):
"""
Returns the list of source Builders for this Builder.
@@ -795,8 +789,7 @@ class BuilderBase(object):
def _subst_src_suffixes_key(self, env):
return id(env)
- memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
-
+ @SCons.Memoize.CountDictCall(_subst_src_suffixes_key)
def subst_src_suffixes(self, env):
"""
The suffix list may contain construction variable expansions,
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 70a7a3f..3eca588 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -163,7 +163,8 @@ class MyNode_without_target_from_source(object):
self.builder = None
self.is_explicit = None
self.side_effect = 0
- self.suffix = os.path.splitext(name)[1]
+ def get_suffix(self):
+ return os.path.splitext(self.name)[1]
def disambiguate(self):
return self
def __str__(self):
@@ -349,7 +350,7 @@ class BuilderTestCase(unittest.TestCase):
builder = SCons.Builder.Builder(action="foo")
target = builder(env, None, source='n22', srcdir='src_dir')[0]
- p = target.sources[0].path
+ p = target.sources[0].get_internal_path()
assert p == os.path.join('src_dir', 'n22'), p
def test_mistaken_variables(self):
@@ -487,20 +488,20 @@ class BuilderTestCase(unittest.TestCase):
builder = SCons.Builder.Builder(prefix = 'lib', action='')
assert builder.get_prefix(env) == 'lib'
tgt = builder(env, target = 'tgt1', source = 'src1')[0]
- assert tgt.path == 'libtgt1', \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == 'libtgt1', \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
- assert tgt.path == 'libtgt2a tgt2b', \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == 'libtgt2a tgt2b', \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
tgt = builder(env, target = None, source = 'src3')[0]
- assert tgt.path == 'libsrc3', \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == 'libsrc3', \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
tgt = builder(env, target = None, source = 'lib/src4')[0]
- assert tgt.path == os.path.join('lib', 'libsrc4'), \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == os.path.join('lib', 'libsrc4'), \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
- assert tgt.path == os.path.join('lib', 'libtgt5'), \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == os.path.join('lib', 'libtgt5'), \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
def gen_prefix(env, sources):
return "gen_prefix() says " + env['FOO']
@@ -520,17 +521,17 @@ class BuilderTestCase(unittest.TestCase):
'.zzz' : my_emit},
action = '')
tgt = builder(my_env, target = None, source = 'f1')[0]
- assert tgt.path == 'default-f1', tgt.path
+ assert tgt.get_internal_path() == 'default-f1', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f2.c')[0]
- assert tgt.path == 'default-f2', tgt.path
+ assert tgt.get_internal_path() == 'default-f2', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f3.in')[0]
- assert tgt.path == 'out-f3', tgt.path
+ assert tgt.get_internal_path() == 'out-f3', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f4.x')[0]
- assert tgt.path == 'y-f4', tgt.path
+ assert tgt.get_internal_path() == 'y-f4', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f5.foo')[0]
- assert tgt.path == 'foo-f5', tgt.path
+ assert tgt.get_internal_path() == 'foo-f5', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
- assert tgt.path == 'emit-f6', tgt.path
+ assert tgt.get_internal_path() == 'emit-f6', tgt.get_internal_path()
def test_set_suffix(self):
"""Test the set_suffix() method"""
@@ -560,13 +561,13 @@ class BuilderTestCase(unittest.TestCase):
assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
tgt = b1(env, target = 'tgt2', source = 'src2')[0]
- assert tgt.sources[0].path == 'src2.c', \
- "Source has unexpected name: %s" % tgt.sources[0].path
+ assert tgt.sources[0].get_internal_path() == 'src2.c', \
+ "Source has unexpected name: %s" % tgt.sources[0].get_internal_path()
tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0]
assert len(tgt.sources) == 1
- assert tgt.sources[0].path == 'src3a src3b.c', \
- "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
+ assert tgt.sources[0].get_internal_path() == 'src3a src3b.c', \
+ "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].get_internal_path()
b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1)
r = sorted(b2.src_suffixes(env))
@@ -636,14 +637,14 @@ class BuilderTestCase(unittest.TestCase):
builder = SCons.Builder.Builder(suffix = 'o', action='')
assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
tgt = builder(env, target = 'tgt3', source = 'src3')[0]
- assert tgt.path == 'tgt3.o', \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == 'tgt3.o', \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
- assert tgt.path == 'tgt4a tgt4b.o', \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == 'tgt4a tgt4b.o', \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
tgt = builder(env, target = None, source = 'src5')[0]
- assert tgt.path == 'src5.o', \
- "Target has unexpected name: %s" % tgt.path
+ assert tgt.get_internal_path() == 'src5.o', \
+ "Target has unexpected name: %s" % tgt.get_internal_path()
def gen_suffix(env, sources):
return "gen_suffix() says " + env['BAR']
@@ -663,17 +664,17 @@ class BuilderTestCase(unittest.TestCase):
'.zzz' : my_emit},
action='')
tgt = builder(my_env, target = None, source = 'f1')[0]
- assert tgt.path == 'f1.default', tgt.path
+ assert tgt.get_internal_path() == 'f1.default', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f2.c')[0]
- assert tgt.path == 'f2.default', tgt.path
+ assert tgt.get_internal_path() == 'f2.default', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f3.in')[0]
- assert tgt.path == 'f3.out', tgt.path
+ assert tgt.get_internal_path() == 'f3.out', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f4.x')[0]
- assert tgt.path == 'f4.y', tgt.path
+ assert tgt.get_internal_path() == 'f4.y', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f5.bar')[0]
- assert tgt.path == 'f5.new', tgt.path
+ assert tgt.get_internal_path() == 'f5.new', tgt.get_internal_path()
tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
- assert tgt.path == 'f6.emit', tgt.path
+ assert tgt.get_internal_path() == 'f6.emit', tgt.get_internal_path()
def test_single_source(self):
"""Test Builder with single_source flag set"""
diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py
index 9dd18e5..f32b326 100644
--- a/src/engine/SCons/CacheDir.py
+++ b/src/engine/SCons/CacheDir.py
@@ -50,11 +50,11 @@ def CacheRetrieveFunc(target, source, env):
cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile)
if SCons.Action.execute_actions:
if fs.islink(cachefile):
- fs.symlink(fs.readlink(cachefile), t.path)
+ fs.symlink(fs.readlink(cachefile), t.get_internal_path())
else:
- env.copy_from_cache(cachefile, t.path)
+ env.copy_from_cache(cachefile, t.get_internal_path())
st = fs.stat(cachefile)
- fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ fs.chmod(t.get_internal_path(), stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
return 0
def CacheRetrieveString(target, source, env):
@@ -63,7 +63,7 @@ def CacheRetrieveString(target, source, env):
cd = env.get_CacheDir()
cachedir, cachefile = cd.cachepath(t)
if t.fs.exists(cachefile):
- return "Retrieved `%s' from cache" % t.path
+ return "Retrieved `%s' from cache" % t.get_internal_path()
return None
CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString)
@@ -106,12 +106,12 @@ def CachePushFunc(target, source, env):
raise SCons.Errors.EnvironmentError(msg)
try:
- if fs.islink(t.path):
- fs.symlink(fs.readlink(t.path), tempfile)
+ if fs.islink(t.get_internal_path()):
+ fs.symlink(fs.readlink(t.get_internal_path()), tempfile)
else:
- fs.copy2(t.path, tempfile)
+ fs.copy2(t.get_internal_path(), tempfile)
fs.rename(tempfile, cachefile)
- st = fs.stat(t.path)
+ st = fs.stat(t.get_internal_path())
fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
except EnvironmentError:
# It's possible someone else tried writing the file at the
diff --git a/src/engine/SCons/Debug.py b/src/engine/SCons/Debug.py
index 9974039..b47c24c 100644
--- a/src/engine/SCons/Debug.py
+++ b/src/engine/SCons/Debug.py
@@ -34,6 +34,7 @@ import os
import sys
import time
import weakref
+import inspect
# Global variable that gets set to 'True' by the Main script,
# when the creation of class instances should get tracked.
@@ -46,7 +47,12 @@ def logInstanceCreation(instance, name=None):
name = instance.__class__.__name__
if name not in tracked_classes:
tracked_classes[name] = []
- tracked_classes[name].append(weakref.ref(instance))
+ if hasattr(instance, '__dict__'):
+ tracked_classes[name].append(weakref.ref(instance))
+ else:
+ # weakref doesn't seem to work when the instance
+ # contains only slots...
+ tracked_classes[name].append(instance)
def string_to_classes(s):
if s == '*':
@@ -66,7 +72,10 @@ def listLoggedInstances(classes, file=sys.stdout):
for classname in string_to_classes(classes):
file.write('\n%s:\n' % classname)
for ref in tracked_classes[classname]:
- obj = ref()
+ if inspect.isclass(ref):
+ obj = ref()
+ else:
+ obj = ref
if obj is not None:
file.write(' %s\n' % repr(obj))
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 6500443..744da5f 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -482,6 +482,15 @@ class Variable_Method_Caller(object):
frame = frame.f_back
return None
+# if env[version_var] id defined, returns env[flags_var], otherwise returns None
+def __libversionflags(env, version_var, flags_var):
+ try:
+ if env[version_var]:
+ return env[flags_var]
+ except KeyError:
+ pass
+ return None
+
ConstructionEnvironment = {
'BUILDERS' : {},
'SCANNERS' : [],
@@ -499,6 +508,12 @@ ConstructionEnvironment = {
'_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
'_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
'_CPPDEFFLAGS' : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
+
+ '__libversionflags' : __libversionflags,
+ '__SHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',
+ '__LDMODULEVERSIONFLAGS' : '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}',
+ '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}',
+
'TEMPFILE' : NullCmdGenerator,
'Dir' : Variable_Method_Caller('TARGET', 'Dir'),
'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'),
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 6886e85..7e2f896 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -365,9 +365,6 @@ class SubstitutionEnvironment(object):
class actually becomes useful.)
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
def __init__(self, **kw):
"""Initialization of an underlying SubstitutionEnvironment class.
"""
@@ -615,7 +612,7 @@ class SubstitutionEnvironment(object):
def Override(self, overrides):
"""
- Produce a modified environment whose variables are overriden by
+ Produce a modified environment whose variables are overridden by
the overrides dictionaries. "overrides" is a dictionary that
will override the variables of this environment.
@@ -902,8 +899,6 @@ class Base(SubstitutionEnvironment):
Environment.
"""
- memoizer_counters = []
-
#######################################################################
# This is THE class for interacting with the SCons build engine,
# and it contains a lot of stuff, so we're going to try to keep this
@@ -1071,8 +1066,7 @@ class Base(SubstitutionEnvironment):
factory = getattr(self.fs, name)
return factory
- memoizer_counters.append(SCons.Memoize.CountValue('_gsm'))
-
+ @SCons.Memoize.CountMethodCall
def _gsm(self):
try:
return self._memo['_gsm']
@@ -1528,8 +1522,8 @@ class Base(SubstitutionEnvironment):
def Dump(self, key = None):
"""
- Using the standard Python pretty printer, dump the contents of the
- scons build environment to stdout.
+ Using the standard Python pretty printer, return the contents of the
+ scons build environment as a string.
If the key passed in is anything other than None, then that will
be used as an index into the build environment dictionary and
@@ -1802,7 +1796,7 @@ class Base(SubstitutionEnvironment):
self.Replace(**kw)
def _find_toolpath_dir(self, tp):
- return self.fs.Dir(self.subst(tp)).srcnode().abspath
+ return self.fs.Dir(self.subst(tp)).srcnode().get_abspath()
def Tool(self, tool, toolpath=None, **kw):
if SCons.Util.is_String(tool):
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index a0869e8..9a9d2b0 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -2703,25 +2703,25 @@ def generate(env):
t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
env.fs.Dir('dir'), env.fs.File('file'))
assert t[0].__class__.__name__ == 'Entry'
- assert t[0].path == 'a'
+ assert t[0].get_internal_path() == 'a'
assert t[0].always_build
assert t[1].__class__.__name__ == 'Entry'
- assert t[1].path == 'bfff'
+ assert t[1].get_internal_path() == 'bfff'
assert t[1].always_build
assert t[2].__class__.__name__ == 'Entry'
- assert t[2].path == 'c'
+ assert t[2].get_internal_path() == 'c'
assert t[2].always_build
assert t[3].__class__.__name__ == 'Entry'
- assert t[3].path == 'd'
+ assert t[3].get_internal_path() == 'd'
assert t[3].always_build
assert t[4].__class__.__name__ == 'Entry'
- assert t[4].path == 'bbb'
+ assert t[4].get_internal_path() == 'bbb'
assert t[4].always_build
assert t[5].__class__.__name__ == 'Dir'
- assert t[5].path == 'dir'
+ assert t[5].get_internal_path() == 'dir'
assert t[5].always_build
assert t[6].__class__.__name__ == 'File'
- assert t[6].path == 'file'
+ assert t[6].get_internal_path() == 'file'
assert t[6].always_build
def test_VariantDir(self):
@@ -2811,13 +2811,13 @@ def generate(env):
assert t.builder is not None
assert t.builder.action.__class__.__name__ == 'CommandAction'
assert t.builder.action.cmd_list == 'buildfoo $target $source'
- assert 'foo1.in' in [x.path for x in t.sources]
- assert 'foo2.in' in [x.path for x in t.sources]
+ assert 'foo1.in' in [x.get_internal_path() for x in t.sources]
+ assert 'foo2.in' in [x.get_internal_path() for x in t.sources]
sub = env.fs.Dir('sub')
t = env.Command(target='bar.out', source='sub',
action='buildbar $target $source')[0]
- assert 'sub' in [x.path for x in t.sources]
+ assert 'sub' in [x.get_internal_path() for x in t.sources]
def testFunc(env, target, source):
assert str(target[0]) == 'foo.out'
@@ -2828,8 +2828,8 @@ def generate(env):
assert t.builder is not None
assert t.builder.action.__class__.__name__ == 'FunctionAction'
t.build()
- assert 'foo1.in' in [x.path for x in t.sources]
- assert 'foo2.in' in [x.path for x in t.sources]
+ assert 'foo1.in' in [x.get_internal_path() for x in t.sources]
+ assert 'foo2.in' in [x.get_internal_path() for x in t.sources]
x = []
def test2(baz, x=x):
@@ -2846,7 +2846,7 @@ def generate(env):
action = 'foo',
X = 'xxx')[0]
assert str(t) == 'xxx.out', str(t)
- assert 'xxx.in' in [x.path for x in t.sources]
+ assert 'xxx.in' in [x.get_internal_path() for x in t.sources]
env = self.TestEnvironment(source_scanner = 'should_not_find_this')
t = env.Command(target='file.out', source='file.in',
@@ -2890,27 +2890,27 @@ def generate(env):
t = env.Depends(target='EnvironmentTest.py',
dependency='Environment.py')[0]
assert t.__class__.__name__ == 'Entry', t.__class__.__name__
- assert t.path == 'EnvironmentTest.py'
+ assert t.get_internal_path() == 'EnvironmentTest.py'
assert len(t.depends) == 1
d = t.depends[0]
assert d.__class__.__name__ == 'Entry', d.__class__.__name__
- assert d.path == 'Environment.py'
+ assert d.get_internal_path() == 'Environment.py'
t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
assert t.__class__.__name__ == 'File', t.__class__.__name__
- assert t.path == 'xxx.py'
+ assert t.get_internal_path() == 'xxx.py'
assert len(t.depends) == 1
d = t.depends[0]
assert d.__class__.__name__ == 'File', d.__class__.__name__
- assert d.path == 'yyy.py'
+ assert d.get_internal_path() == 'yyy.py'
t = env.Depends(target='dir1', dependency='dir2')[0]
assert t.__class__.__name__ == 'Dir', t.__class__.__name__
- assert t.path == 'dir1'
+ assert t.get_internal_path() == 'dir1'
assert len(t.depends) == 1
d = t.depends[0]
assert d.__class__.__name__ == 'Dir', d.__class__.__name__
- assert d.path == 'dir2'
+ assert d.get_internal_path() == 'dir2'
def test_Dir(self):
"""Test the Dir() method"""
@@ -2944,19 +2944,19 @@ def generate(env):
t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
- assert t[0].path == 'p_a'
+ assert t[0].get_internal_path() == 'p_a'
assert t[0].noclean
assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
- assert t[1].path == 'p_hhhb'
+ assert t[1].get_internal_path() == 'p_hhhb'
assert t[1].noclean
assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
- assert t[2].path == 'p_c'
+ assert t[2].get_internal_path() == 'p_c'
assert t[2].noclean
assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
- assert t[3].path == 'p_d'
+ assert t[3].get_internal_path() == 'p_d'
assert t[3].noclean
assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
- assert t[4].path == 'p_ggg'
+ assert t[4].get_internal_path() == 'p_ggg'
assert t[4].noclean
def test_Dump(self):
@@ -3074,27 +3074,27 @@ def generate(env):
t = env.Ignore(target='targ.py', dependency='dep.py')[0]
assert t.__class__.__name__ == 'Entry', t.__class__.__name__
- assert t.path == 'targ.py'
+ assert t.get_internal_path() == 'targ.py'
assert len(t.ignore) == 1
i = t.ignore[0]
assert i.__class__.__name__ == 'Entry', i.__class__.__name__
- assert i.path == 'dep.py'
+ assert i.get_internal_path() == 'dep.py'
t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
assert t.__class__.__name__ == 'File', t.__class__.__name__
- assert t.path == 'yyyzzz'
+ assert t.get_internal_path() == 'yyyzzz'
assert len(t.ignore) == 1
i = t.ignore[0]
assert i.__class__.__name__ == 'File', i.__class__.__name__
- assert i.path == 'zzzyyy'
+ assert i.get_internal_path() == 'zzzyyy'
t = env.Ignore(target='dir1', dependency='dir2')[0]
assert t.__class__.__name__ == 'Dir', t.__class__.__name__
- assert t.path == 'dir1'
+ assert t.get_internal_path() == 'dir1'
assert len(t.ignore) == 1
i = t.ignore[0]
assert i.__class__.__name__ == 'Dir', i.__class__.__name__
- assert i.path == 'dir2'
+ assert i.get_internal_path() == 'dir2'
def test_Literal(self):
"""Test the Literal() method"""
@@ -3123,19 +3123,19 @@ def generate(env):
t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
- assert t[0].path == 'p_a'
+ assert t[0].get_internal_path() == 'p_a'
assert t[0].precious
assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
- assert t[1].path == 'p_hhhb'
+ assert t[1].get_internal_path() == 'p_hhhb'
assert t[1].precious
assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
- assert t[2].path == 'p_c'
+ assert t[2].get_internal_path() == 'p_c'
assert t[2].precious
assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
- assert t[3].path == 'p_d'
+ assert t[3].get_internal_path() == 'p_d'
assert t[3].precious
assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
- assert t[4].path == 'p_ggg'
+ assert t[4].get_internal_path() == 'p_ggg'
assert t[4].precious
def test_Pseudo(self):
@@ -3146,19 +3146,19 @@ def generate(env):
t = env.Pseudo('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
- assert t[0].path == 'p_a'
+ assert t[0].get_internal_path() == 'p_a'
assert t[0].pseudo
assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
- assert t[1].path == 'p_hhhb'
+ assert t[1].get_internal_path() == 'p_hhhb'
assert t[1].pseudo
assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
- assert t[2].path == 'p_c'
+ assert t[2].get_internal_path() == 'p_c'
assert t[2].pseudo
assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
- assert t[3].path == 'p_d'
+ assert t[3].get_internal_path() == 'p_d'
assert t[3].pseudo
assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
- assert t[4].path == 'p_ggg'
+ assert t[4].get_internal_path() == 'p_ggg'
assert t[4].pseudo
def test_Repository(self):
@@ -3263,7 +3263,7 @@ def generate(env):
bar = env.Object('bar.obj', 'bar.cpp')[0]
s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0]
assert s.__class__.__name__ == 'Entry', s.__class__.__name__
- assert s.path == 'mylib.pdb'
+ assert s.get_internal_path() == 'mylib.pdb'
assert s.side_effect
assert foo.side_effects == [s]
assert bar.side_effects == [s]
@@ -3272,7 +3272,7 @@ def generate(env):
bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0]
assert s.__class__.__name__ == 'File', s.__class__.__name__
- assert s.path == 'mylll.pdb'
+ assert s.get_internal_path() == 'mylll.pdb'
assert s.side_effect
assert fff.side_effects == [s], fff.side_effects
assert bbb.side_effects == [s], bbb.side_effects
@@ -3281,7 +3281,7 @@ def generate(env):
ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0]
assert s.__class__.__name__ == 'Dir', s.__class__.__name__
- assert s.path == 'mymmm.pdb'
+ assert s.get_internal_path() == 'mymmm.pdb'
assert s.side_effect
assert ggg.side_effects == [s], ggg.side_effects
assert ccc.side_effects == [s], ccc.side_effects
@@ -3290,18 +3290,18 @@ def generate(env):
"""Test the SourceCode() method."""
env = self.TestEnvironment(FOO='mmm', BAR='nnn')
e = env.SourceCode('foo', None)[0]
- assert e.path == 'foo'
+ assert e.get_internal_path() == 'foo'
s = e.src_builder()
assert s is None, s
b = Builder()
e = env.SourceCode(e, b)[0]
- assert e.path == 'foo'
+ assert e.get_internal_path() == 'foo'
s = e.src_builder()
assert s is b, s
e = env.SourceCode('$BAR$FOO', None)[0]
- assert e.path == 'nnnmmm'
+ assert e.get_internal_path() == 'nnnmmm'
s = e.src_builder()
assert s is None, s
diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py
index 388f8ac..98ed758 100644
--- a/src/engine/SCons/Executor.py
+++ b/src/engine/SCons/Executor.py
@@ -40,6 +40,10 @@ import SCons.Memoize
class Batch(object):
"""Remembers exact association between targets
and sources of executor."""
+
+ __slots__ = ('targets',
+ 'sources')
+
def __init__(self, targets=[], sources=[]):
self.targets = targets
self.sources = sources
@@ -109,6 +113,48 @@ def rfile(node):
return rfile()
+def execute_nothing(obj, target, kw):
+ return 0
+
+def execute_action_list(obj, target, kw):
+ """Actually execute the action list."""
+ env = obj.get_build_env()
+ kw = obj.get_kw(kw)
+ status = 0
+ for act in obj.get_action_list():
+ #args = (self.get_all_targets(), self.get_all_sources(), env)
+ args = ([], [], env)
+ status = act(*args, **kw)
+ if isinstance(status, SCons.Errors.BuildError):
+ status.executor = obj
+ raise status
+ elif status:
+ msg = "Error %s" % status
+ raise SCons.Errors.BuildError(
+ errstr=msg,
+ node=obj.batches[0].targets,
+ executor=obj,
+ action=act)
+ return status
+
+_do_execute_map = {0 : execute_nothing,
+ 1 : execute_action_list}
+
+
+def execute_actions_str(obj):
+ env = obj.get_build_env()
+ return "\n".join([action.genstring(obj.get_all_targets(),
+ obj.get_all_sources(),
+ env)
+ for action in obj.get_action_list()])
+
+def execute_null_str(obj):
+ return ''
+
+_execute_str_map = {0 : execute_null_str,
+ 1 : execute_actions_str}
+
+
class Executor(object):
"""A class for controlling instances of executing an action.
@@ -117,10 +163,21 @@ class Executor(object):
and sources for later processing as needed.
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
+ __slots__ = ('pre_actions',
+ 'post_actions',
+ 'env',
+ 'overridelist',
+ 'batches',
+ 'builder_kw',
+ '_memo',
+ 'lvars',
+ '_changed_sources_list',
+ '_changed_targets_list',
+ '_unchanged_sources_list',
+ '_unchanged_targets_list',
+ 'action_list',
+ '_do_execute',
+ '_execute_str')
def __init__(self, action, env=None, overridelist=[{}],
targets=[], sources=[], builder_kw={}):
@@ -135,6 +192,8 @@ class Executor(object):
else:
self.batches = []
self.builder_kw = builder_kw
+ self._do_execute = 1
+ self._execute_str = 1
self._memo = {}
def get_lvars(self):
@@ -284,8 +343,7 @@ class Executor(object):
result.extend(target.side_effects)
return result
- memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
-
+ @SCons.Memoize.CountMethodCall
def get_build_env(self):
"""Fetch or create the appropriate build Environment
for this Executor.
@@ -330,36 +388,12 @@ class Executor(object):
result['executor'] = self
return result
- def do_nothing(self, target, kw):
- return 0
-
- def do_execute(self, target, kw):
- """Actually execute the action list."""
- env = self.get_build_env()
- kw = self.get_kw(kw)
- status = 0
- for act in self.get_action_list():
- #args = (self.get_all_targets(), self.get_all_sources(), env)
- args = ([], [], env)
- status = act(*args, **kw)
- if isinstance(status, SCons.Errors.BuildError):
- status.executor = self
- raise status
- elif status:
- msg = "Error %s" % status
- raise SCons.Errors.BuildError(
- errstr=msg,
- node=self.batches[0].targets,
- executor=self,
- action=act)
- return status
-
# use extra indirection because with new-style objects (Python 2.2
# and above) we can't override special methods, and nullify() needs
# to be able to do this.
def __call__(self, target, **kw):
- return self.do_execute(target, kw)
+ return _do_execute_map[self._do_execute](self, target, kw)
def cleanup(self):
self._memo = {}
@@ -403,24 +437,15 @@ class Executor(object):
# another extra indirection for new-style objects and nullify...
- def my_str(self):
- env = self.get_build_env()
- return "\n".join([action.genstring(self.get_all_targets(),
- self.get_all_sources(),
- env)
- for action in self.get_action_list()])
-
-
def __str__(self):
- return self.my_str()
+ return _execute_str_map[self._execute_str](self)
def nullify(self):
self.cleanup()
- self.do_execute = self.do_nothing
- self.my_str = lambda: ''
-
- memoizer_counters.append(SCons.Memoize.CountValue('get_contents'))
+ self._do_execute = 0
+ self._execute_str = 0
+ @SCons.Memoize.CountMethodCall
def get_contents(self):
"""Fetch the signature contents. This is the main reason this
class exists, so we can compute this once and cache it regardless
@@ -493,8 +518,7 @@ class Executor(object):
def _get_unignored_sources_key(self, node, ignore=()):
return (node,) + tuple(ignore)
- memoizer_counters.append(SCons.Memoize.CountDict('get_unignored_sources', _get_unignored_sources_key))
-
+ @SCons.Memoize.CountDictCall(_get_unignored_sources_key)
def get_unignored_sources(self, node, ignore=()):
key = (node,) + tuple(ignore)
try:
@@ -579,6 +603,23 @@ class Null(object):
disassociate Builders from Nodes entirely, so we're not
going to worry about unit tests for this--at least for now.
"""
+
+ __slots__ = ('pre_actions',
+ 'post_actions',
+ 'env',
+ 'overridelist',
+ 'batches',
+ 'builder_kw',
+ '_memo',
+ 'lvars',
+ '_changed_sources_list',
+ '_changed_targets_list',
+ '_unchanged_sources_list',
+ '_unchanged_targets_list',
+ 'action_list',
+ '_do_execute',
+ '_execute_str')
+
def __init__(self, *args, **kw):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Null')
self.batches = [Batch(kw['targets'][:], [])]
diff --git a/src/engine/SCons/Memoize.py b/src/engine/SCons/Memoize.py
index e77aacf..5144f83 100644
--- a/src/engine/SCons/Memoize.py
+++ b/src/engine/SCons/Memoize.py
@@ -25,17 +25,17 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
__doc__ = """Memoizer
-A metaclass implementation to count hits and misses of the computed
+A decorator-based implementation to count hits and misses of the computed
values that various methods cache in memory.
Use of this modules assumes that wrapped methods be coded to cache their
-values in a consistent way. Here is an example of wrapping a method
-that returns a computed value, with no input parameters:
+values in a consistent way. In particular, it requires that the class uses a
+dictionary named "_memo" to store the cached values.
- memoizer_counters = [] # Memoization
-
- memoizer_counters.append(SCons.Memoize.CountValue('foo')) # Memoization
+Here is an example of wrapping a method that returns a computed value,
+with no input parameters:
+ @SCons.Memoize.CountMethodCall
def foo(self):
try: # Memoization
@@ -55,8 +55,7 @@ based on one or more input arguments:
def _bar_key(self, argument): # Memoization
return argument # Memoization
- memoizer_counters.append(SCons.Memoize.CountDict('bar', _bar_key)) # Memoization
-
+ @SCons.Memoize.CountDictCall(_bar_key)
def bar(self, argument):
memo_key = argument # Memoization
@@ -77,10 +76,6 @@ based on one or more input arguments:
return result
-At one point we avoided replicating this sort of logic in all the methods
-by putting it right into this module, but we've moved away from that at
-present (see the "Historical Note," below.).
-
Deciding what to cache is tricky, because different configurations
can have radically different performance tradeoffs, and because the
tradeoffs involved are often so non-obvious. Consequently, deciding
@@ -102,51 +97,37 @@ cache return values from a method that's being called a lot:
input arguments, you don't need to use all of the arguments
if some of them don't affect the return values.
-Historical Note: The initial Memoizer implementation actually handled
-the caching of values for the wrapped methods, based on a set of generic
-algorithms for computing hashable values based on the method's arguments.
-This collected caching logic nicely, but had two drawbacks:
-
- Running arguments through a generic key-conversion mechanism is slower
- (and less flexible) than just coding these things directly. Since the
- methods that need memoized values are generally performance-critical,
- slowing them down in order to collect the logic isn't the right
- tradeoff.
-
- Use of the memoizer really obscured what was being called, because
- all the memoized methods were wrapped with re-used generic methods.
- This made it more difficult, for example, to use the Python profiler
- to figure out how to optimize the underlying methods.
"""
-import types
-
# A flag controlling whether or not we actually use memoization.
use_memoizer = None
-CounterList = []
+# Global list of counter objects
+CounterList = {}
class Counter(object):
"""
Base class for counting memoization hits and misses.
- We expect that the metaclass initialization will have filled in
- the .name attribute that represents the name of the function
- being counted.
+ We expect that the initialization in a matching decorator will
+ fill in the correct class name and method name that represents
+ the name of the function being counted.
"""
- def __init__(self, method_name):
+ def __init__(self, cls_name, method_name):
"""
"""
+ self.cls_name = cls_name
self.method_name = method_name
self.hit = 0
self.miss = 0
- CounterList.append(self)
+ def key(self):
+ return self.cls_name+'.'+self.method_name
def display(self):
fmt = " %7d hits %7d misses %s()"
- print fmt % (self.hit, self.miss, self.name)
+ print fmt % (self.hit, self.miss, self.key())
def __cmp__(self, other):
try:
- return cmp(self.name, other.name)
+ return cmp(self.key(), other.key())
except AttributeError:
return 0
@@ -154,45 +135,39 @@ class CountValue(Counter):
"""
A counter class for simple, atomic memoized values.
- A CountValue object should be instantiated in a class for each of
+ A CountValue object should be instantiated in a decorator for each of
the class's methods that memoizes its return value by simply storing
the return value in its _memo dictionary.
-
- We expect that the metaclass initialization will fill in the
- .underlying_method attribute with the method that we're wrapping.
- We then call the underlying_method method after counting whether
- its memoized value has already been set (a hit) or not (a miss).
"""
- def __call__(self, *args, **kw):
+ def count(self, *args, **kw):
+ """ Counts whether the memoized value has already been
+ set (a hit) or not (a miss).
+ """
obj = args[0]
if self.method_name in obj._memo:
self.hit = self.hit + 1
else:
self.miss = self.miss + 1
- return self.underlying_method(*args, **kw)
class CountDict(Counter):
"""
A counter class for memoized values stored in a dictionary, with
keys based on the method's input arguments.
- A CountDict object is instantiated in a class for each of the
+ A CountDict object is instantiated in a decorator for each of the
class's methods that memoizes its return value in a dictionary,
indexed by some key that can be computed from one or more of
its input arguments.
-
- We expect that the metaclass initialization will fill in the
- .underlying_method attribute with the method that we're wrapping.
- We then call the underlying_method method after counting whether the
- computed key value is already present in the memoization dictionary
- (a hit) or not (a miss).
"""
- def __init__(self, method_name, keymaker):
+ def __init__(self, cls_name, method_name, keymaker):
"""
"""
- Counter.__init__(self, method_name)
+ Counter.__init__(self, cls_name, method_name)
self.keymaker = keymaker
- def __call__(self, *args, **kw):
+ def count(self, *args, **kw):
+ """ Counts whether the computed key value is already present
+ in the memoization dictionary (a hit) or not (a miss).
+ """
obj = args[0]
try:
memo_dict = obj._memo[self.method_name]
@@ -204,39 +179,65 @@ class CountDict(Counter):
self.hit = self.hit + 1
else:
self.miss = self.miss + 1
- return self.underlying_method(*args, **kw)
-
-class Memoizer(object):
- """Object which performs caching of method calls for its 'primary'
- instance."""
-
- def __init__(self):
- pass
def Dump(title=None):
+ """ Dump the hit/miss count for all the counters
+ collected so far.
+ """
if title:
print title
- CounterList.sort()
- for counter in CounterList:
- counter.display()
-
-class Memoized_Metaclass(type):
- def __init__(cls, name, bases, cls_dict):
- super(Memoized_Metaclass, cls).__init__(name, bases, cls_dict)
-
- for counter in cls_dict.get('memoizer_counters', []):
- method_name = counter.method_name
-
- counter.name = cls.__name__ + '.' + method_name
- counter.underlying_method = cls_dict[method_name]
-
- replacement_method = types.MethodType(counter, None, cls)
- setattr(cls, method_name, replacement_method)
+ for counter in sorted(CounterList):
+ CounterList[counter].display()
def EnableMemoization():
global use_memoizer
use_memoizer = 1
+def CountMethodCall(fn):
+ """ Decorator for counting memoizer hits/misses while retrieving
+ a simple value in a class method. It wraps the given method
+ fn and uses a CountValue object to keep track of the
+ caching statistics.
+ Wrapping gets enabled by calling EnableMemoization().
+ """
+ if use_memoizer:
+ def wrapper(self, *args, **kwargs):
+ global CounterList
+ key = self.__class__.__name__+'.'+fn.__name__
+ if key not in CounterList:
+ CounterList[key] = CountValue(self.__class__.__name__, fn.__name__)
+ CounterList[key].count(self, *args, **kwargs)
+ return fn(self, *args, **kwargs)
+ wrapper.__name__= fn.__name__
+ return wrapper
+ else:
+ return fn
+
+def CountDictCall(keyfunc):
+ """ Decorator for counting memoizer hits/misses while accessing
+ dictionary values with a key-generating function. Like
+ CountMethodCall above, it wraps the given method
+ fn and uses a CountDict object to keep track of the
+ caching statistics. The dict-key function keyfunc has to
+ get passed in the decorator call and gets stored in the
+ CountDict instance.
+ Wrapping gets enabled by calling EnableMemoization().
+ """
+ def decorator(fn):
+ if use_memoizer:
+ def wrapper(self, *args, **kwargs):
+ global CounterList
+ key = self.__class__.__name__+'.'+fn.__name__
+ if key not in CounterList:
+ CounterList[key] = CountDict(self.__class__.__name__, fn.__name__, keyfunc)
+ CounterList[key].count(self, *args, **kwargs)
+ return fn(self, *args, **kwargs)
+ wrapper.__name__= fn.__name__
+ return wrapper
+ else:
+ return fn
+ return decorator
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/src/engine/SCons/MemoizeTests.py b/src/engine/SCons/MemoizeTests.py
index 3606d57..77ff6bc 100644
--- a/src/engine/SCons/MemoizeTests.py
+++ b/src/engine/SCons/MemoizeTests.py
@@ -30,22 +30,18 @@ import TestUnit
import SCons.Memoize
-
+# Enable memoization counting
+SCons.Memoize.EnableMemoization()
class FakeObject(object):
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
-
def __init__(self):
self._memo = {}
def _dict_key(self, argument):
return argument
- memoizer_counters.append(SCons.Memoize.CountDict('dict', _dict_key))
-
+ @SCons.Memoize.CountDictCall(_dict_key)
def dict(self, argument):
memo_key = argument
@@ -66,8 +62,7 @@ class FakeObject(object):
return result
- memoizer_counters.append(SCons.Memoize.CountValue('value'))
-
+ @SCons.Memoize.CountMethodCall
def value(self):
try:
@@ -82,10 +77,7 @@ class FakeObject(object):
return result
def get_memoizer_counter(self, name):
- for mc in self.memoizer_counters:
- if mc.method_name == name:
- return mc
- return None
+ return SCons.Memoize.CounterList.get(self.__class__.__name__+'.'+name, None)
class Returner(object):
def __init__(self, result):
diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py
index f817356..a035816 100644
--- a/src/engine/SCons/Node/Alias.py
+++ b/src/engine/SCons/Node/Alias.py
@@ -56,13 +56,47 @@ class AliasNameSpace(collections.UserDict):
return None
class AliasNodeInfo(SCons.Node.NodeInfoBase):
- current_version_id = 1
+ __slots__ = ('csig',)
+ current_version_id = 2
field_list = ['csig']
def str_to_node(self, s):
return default_ans.Alias(s)
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
+
class AliasBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
class Alias(SCons.Node.Node):
@@ -72,7 +106,9 @@ class Alias(SCons.Node.Node):
def __init__(self, name):
SCons.Node.Node.__init__(self)
self.name = name
-
+ self.changed_since_last_build = 1
+ self.store_info = 0
+
def str_for_display(self):
return '"' + self.__str__() + '"'
@@ -105,13 +141,6 @@ class Alias(SCons.Node.Node):
#
#
- def changed_since_last_build(self, target, prev_ni):
- cur_csig = self.get_csig()
- try:
- return cur_csig != prev_ni.csig
- except AttributeError:
- return 1
-
def build(self):
"""A "builder" for aliases."""
pass
diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py
index 2d11bdf..8e31875 100644
--- a/src/engine/SCons/Node/AliasTests.py
+++ b/src/engine/SCons/Node/AliasTests.py
@@ -103,14 +103,14 @@ class AliasNodeInfoTestCase(unittest.TestCase):
"""Test AliasNodeInfo initialization"""
ans = SCons.Node.Alias.AliasNameSpace()
aaa = ans.Alias('aaa')
- ni = SCons.Node.Alias.AliasNodeInfo(aaa)
+ ni = SCons.Node.Alias.AliasNodeInfo()
class AliasBuildInfoTestCase(unittest.TestCase):
def test___init__(self):
"""Test AliasBuildInfo initialization"""
ans = SCons.Node.Alias.AliasNameSpace()
aaa = ans.Alias('aaa')
- bi = SCons.Node.Alias.AliasBuildInfo(aaa)
+ bi = SCons.Node.Alias.AliasBuildInfo()
if __name__ == "__main__":
suite = unittest.TestSuite()
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index eec4e42..4e78852 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -56,10 +56,23 @@ import SCons.Warnings
from SCons.Debug import Trace
-do_store_info = True
print_duplicate = 0
+def sconsign_none(node):
+ raise NotImplementedError
+
+def sconsign_dir(node):
+ """Return the .sconsign file info for this directory,
+ creating it first if necessary."""
+ if not node._sconsign:
+ import SCons.SConsign
+ node._sconsign = SCons.SConsign.ForDirectory(node)
+ return node._sconsign
+
+_sconsign_map = {0 : sconsign_none,
+ 1 : sconsign_dir}
+
class EntryProxyAttributeError(AttributeError):
"""
An AttributeError subclass for recording and displaying the name
@@ -268,8 +281,8 @@ def LinkFunc(target, source, env):
# who want to move their soft-linked src-trees around. Those
# people should use the 'hard-copy' mode, softlinks cannot be
# used for that; at least I have no idea how ...
- src = source[0].abspath
- dest = target[0].abspath
+ src = source[0].get_abspath()
+ dest = target[0].get_abspath()
dir, file = os.path.split(dest)
if dir and not target[0].fs.isdir(dir):
os.makedirs(dir)
@@ -302,7 +315,7 @@ LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
def UnlinkFunc(target, source, env):
t = target[0]
- t.fs.unlink(t.abspath)
+ t.fs.unlink(t.get_abspath())
return 0
Unlink = SCons.Action.Action(UnlinkFunc, None)
@@ -310,7 +323,7 @@ Unlink = SCons.Action.Action(UnlinkFunc, None)
def MkdirFunc(target, source, env):
t = target[0]
if not t.exists():
- t.fs.mkdir(t.abspath)
+ t.fs.mkdir(t.get_abspath())
return 0
Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
@@ -403,7 +416,7 @@ def do_diskcheck_match(node, predicate, errorfmt):
except (AttributeError, KeyError):
pass
if result:
- raise TypeError(errorfmt % node.abspath)
+ raise TypeError(errorfmt % node.get_abspath())
def ignore_diskcheck_match(node, predicate, errorfmt):
pass
@@ -573,7 +586,20 @@ class Base(SCons.Node.Node):
object identity comparisons.
"""
- memoizer_counters = []
+ __slots__ = ['name',
+ 'fs',
+ '_abspath',
+ '_labspath',
+ '_path',
+ '_tpath',
+ '_path_elements',
+ 'dir',
+ 'cwd',
+ 'duplicate',
+ '_local',
+ 'sbuilder',
+ '_proxy',
+ '_func_sconsign']
def __init__(self, name, directory, fs):
"""Initialize a generic Node.FS.Base object.
@@ -591,27 +617,26 @@ class Base(SCons.Node.Node):
#: Filename with extension as it was specified when the object was
#: created; to obtain filesystem path, use Python str() function
self.name = SCons.Util.silent_intern(name)
- #: Cached filename extension
- self.suffix = SCons.Util.silent_intern(SCons.Util.splitext(name)[1])
self.fs = fs #: Reference to parent Node.FS object
assert directory, "A directory must be provided"
- self.abspath = SCons.Util.silent_intern(directory.entry_abspath(name))
- self.labspath = SCons.Util.silent_intern(directory.entry_labspath(name))
- if directory.path == '.':
- self.path = SCons.Util.silent_intern(name)
- else:
- self.path = SCons.Util.silent_intern(directory.entry_path(name))
- if directory.tpath == '.':
- self.tpath = SCons.Util.silent_intern(name)
- else:
- self.tpath = SCons.Util.silent_intern(directory.entry_tpath(name))
- self.path_elements = directory.path_elements + [self]
+ self._abspath = None
+ self._labspath = None
+ self._path = None
+ self._tpath = None
+ self._path_elements = None
self.dir = directory
self.cwd = None # will hold the SConscript directory for target nodes
self.duplicate = directory.duplicate
+ self.changed_since_last_build = 2
+ self._func_sconsign = 0
+ self._func_exists = 2
+ self._func_rexists = 2
+ self._func_get_contents = 0
+ self._func_target_from_source = 1
+ self.store_info = 1
def str_for_display(self):
return '"' + self.__str__() + '"'
@@ -624,17 +649,38 @@ class Base(SCons.Node.Node):
if isinstance(self, klass) or klass is Entry:
return
raise TypeError("Tried to lookup %s '%s' as a %s." %\
- (self.__class__.__name__, self.path, klass.__name__))
+ (self.__class__.__name__, self.get_internal_path(), klass.__name__))
def get_dir(self):
return self.dir
def get_suffix(self):
- return self.suffix
+ return SCons.Util.splitext(self.name)[1]
def rfile(self):
return self
+ def __getattr__(self, attr):
+ """ Together with the node_bwcomp dict defined below,
+ this method provides a simple backward compatibility
+ layer for the Node attributes 'abspath', 'labspath',
+ 'path', 'tpath', 'suffix' and 'path_elements'. These Node
+ attributes used to be directly available in v2.3 and earlier, but
+ have been replaced by getter methods that initialize the
+ single variables lazily when required, in order to save memory.
+ The redirection to the getters lets older Tools and
+ SConstruct continue to work without any additional changes,
+ fully transparent to the user.
+ Note, that __getattr__ is only called as fallback when the
+ requested attribute can't be found, so there should be no
+ speed performance penalty involved for standard builds.
+ """
+ if attr in node_bwcomp:
+ return node_bwcomp[attr](self)
+
+ raise AttributeError("%r object has no attribute %r" %
+ (self.__class__, attr))
+
def __str__(self):
"""A Node.FS.Base object's string representation is its path
name."""
@@ -643,8 +689,7 @@ class Base(SCons.Node.Node):
return self._save_str()
return self._get_str()
- memoizer_counters.append(SCons.Memoize.CountValue('_save_str'))
-
+ @SCons.Memoize.CountMethodCall
def _save_str(self):
try:
return self._memo['_save_str']
@@ -681,21 +726,20 @@ class Base(SCons.Node.Node):
rstr = __str__
- memoizer_counters.append(SCons.Memoize.CountValue('stat'))
-
+ @SCons.Memoize.CountMethodCall
def stat(self):
try: return self._memo['stat']
except KeyError: pass
- try: result = self.fs.stat(self.abspath)
+ try: result = self.fs.stat(self.get_abspath())
except os.error: result = None
self._memo['stat'] = result
return result
def exists(self):
- return self.stat() is not None
+ return SCons.Node._exists_map[self._func_exists](self)
def rexists(self):
- return self.rfile().exists()
+ return SCons.Node._rexists_map[self._func_rexists](self)
def getmtime(self):
st = self.stat()
@@ -717,7 +761,7 @@ class Base(SCons.Node.Node):
if hasattr(os, 'symlink'):
def islink(self):
- try: st = self.fs.lstat(self.abspath)
+ try: st = self.fs.lstat(self.get_abspath())
except os.error: return 0
return stat.S_ISLNK(st[stat.ST_MODE])
else:
@@ -752,7 +796,7 @@ class Base(SCons.Node.Node):
dir = self.fs.getcwd()
if self == dir:
return '.'
- path_elems = self.path_elements
+ path_elems = self.get_path_elements()
pathname = ''
try: i = path_elems.index(dir)
except ValueError:
@@ -785,7 +829,26 @@ class Base(SCons.Node.Node):
def get_abspath(self):
"""Get the absolute path of the file."""
- return self.abspath
+ return self.dir.entry_abspath(self.name)
+
+ def get_labspath(self):
+ """Get the absolute path of the file."""
+ return self.dir.entry_labspath(self.name)
+
+ def get_internal_path(self):
+ if self.dir._path == '.':
+ return self.name
+ else:
+ return self.dir.entry_path(self.name)
+
+ def get_tpath(self):
+ if self.dir._tpath == '.':
+ return self.name
+ else:
+ return self.dir.entry_tpath(self.name)
+
+ def get_path_elements(self):
+ return self.dir._path_elements + [self]
def for_signature(self):
# Return just our name. Even an absolute path would not work,
@@ -811,13 +874,12 @@ class Base(SCons.Node.Node):
files that need different behavior. See Tool/swig.py for
an example.
"""
- return self.dir.Entry(prefix + splitext(self.name)[0] + suffix)
+ return SCons.Node._target_from_source_map[self._func_target_from_source](self, prefix, suffix, splitext)
def _Rfindalldirs_key(self, pathlist):
return pathlist
- memoizer_counters.append(SCons.Memoize.CountDict('Rfindalldirs', _Rfindalldirs_key))
-
+ @SCons.Memoize.CountDictCall(_Rfindalldirs_key)
def Rfindalldirs(self, pathlist):
"""
Return all of the directories for a given path list, including
@@ -856,8 +918,7 @@ class Base(SCons.Node.Node):
cwd = self.cwd or self.fs._cwd
return cwd.Rfindalldirs(pathlist)
- memoizer_counters.append(SCons.Memoize.CountValue('rentry'))
-
+ @SCons.Memoize.CountMethodCall
def rentry(self):
try:
return self._memo['rentry']
@@ -878,6 +939,17 @@ class Base(SCons.Node.Node):
def _glob1(self, pattern, ondisk=True, source=False, strings=False):
return []
+
+# Dict that provides a simple backward compatibility
+# layer for the Node attributes 'abspath', 'labspath',
+# 'path', 'tpath' and 'path_elements'.
+# @see Base.__getattr__ above
+node_bwcomp = {'abspath' : Base.get_abspath,
+ 'labspath' : Base.get_labspath,
+ 'path' : Base.get_internal_path,
+ 'tpath' : Base.get_tpath,
+ 'path_elements' : Base.get_path_elements,
+ 'suffix' : Base.get_suffix}
class Entry(Base):
"""This is the class for generic Node.FS entries--that is, things
@@ -887,6 +959,28 @@ class Entry(Base):
time comes, and then call the same-named method in the transformed
class."""
+ __slots__ = ['scanner_paths',
+ 'cachedir_csig',
+ 'cachesig',
+ 'repositories',
+ 'srcdir',
+ 'entries',
+ 'searched',
+ '_sconsign',
+ 'variant_dirs',
+ 'root',
+ 'dirname',
+ 'on_disk_entries',
+ 'sccs_dir',
+ 'rcs_dir',
+ 'released_target_info',
+ 'contentsig']
+
+ def __init__(self, name, directory, fs):
+ Base.__init__(self, name, directory, fs)
+ self._func_exists = 3
+ self._func_get_contents = 1
+
def diskcheck_match(self):
pass
@@ -917,7 +1011,7 @@ class Entry(Base):
self.__class__ = Dir
self._morph()
elif must_exist:
- msg = "No such file or directory: '%s'" % self.abspath
+ msg = "No such file or directory: '%s'" % self.get_abspath()
raise SCons.Errors.UserError(msg)
else:
self.__class__ = File
@@ -939,17 +1033,7 @@ class Entry(Base):
def get_contents(self):
"""Fetch the contents of the entry. Returns the exact binary
contents of the file."""
- try:
- self = self.disambiguate(must_exist=1)
- except SCons.Errors.UserError:
- # There was nothing on disk with which to disambiguate
- # this entry. Leave it as an Entry, but return a null
- # string so calls to get_contents() in emitters and the
- # like (e.g. in qt.py) don't have to disambiguate by hand
- # or catch the exception.
- return ''
- else:
- return self.get_contents()
+ return SCons.Node._get_contents_map[self._func_get_contents](self)
def get_text_contents(self):
"""Fetch the decoded text contents of a Unicode encoded Entry.
@@ -989,10 +1073,7 @@ class Entry(Base):
# to make various tests pass.
def exists(self):
- """Return if the Entry exists. Check the file system to see
- what we should turn into first. Assume a file if there's no
- directory."""
- return self.disambiguate().exists()
+ return SCons.Node._exists_map[self._func_exists](self)
def rel_path(self, other):
d = self.disambiguate()
@@ -1003,9 +1084,6 @@ class Entry(Base):
def new_ninfo(self):
return self.disambiguate().new_ninfo()
- def changed_since_last_build(self, target, prev_ni):
- return self.disambiguate().changed_since_last_build(target, prev_ni)
-
def _glob1(self, pattern, ondisk=True, source=False, strings=False):
return self.disambiguate()._glob1(pattern, ondisk, source, strings)
@@ -1019,9 +1097,6 @@ _classEntry = Entry
class LocalFS(object):
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
# This class implements an abstraction layer for operations involving
# a local file system. Essentially, this wraps any function in
# the os, os.path or shutil modules that we use to actually go do
@@ -1101,8 +1176,6 @@ class LocalFS(object):
class FS(LocalFS):
- memoizer_counters = []
-
def __init__(self, path = None):
"""Initialize the Node.FS subsystem.
@@ -1128,8 +1201,8 @@ class FS(LocalFS):
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
self.Top = self.Dir(self.pathTop)
- self.Top.path = '.'
- self.Top.tpath = '.'
+ self.Top._path = '.'
+ self.Top._tpath = '.'
self._cwd = self.Top
DirNodeInfo.fs = self
@@ -1160,7 +1233,7 @@ class FS(LocalFS):
if dir is not None:
self._cwd = dir
if change_os_dir:
- os.chdir(dir.abspath)
+ os.chdir(dir.get_abspath())
except OSError:
self._cwd = curr
raise
@@ -1249,9 +1322,9 @@ class FS(LocalFS):
# The path is relative to the top-level SCons directory.
if p in ('', '.'):
- p = directory.labspath
+ p = directory.get_labspath()
else:
- p = directory.labspath + '/' + p
+ p = directory.get_labspath() + '/' + p
else:
if do_splitdrive:
drive, p = _my_splitdrive(p)
@@ -1285,9 +1358,9 @@ class FS(LocalFS):
directory = self._cwd
if p in ('', '.'):
- p = directory.labspath
+ p = directory.get_labspath()
else:
- p = directory.labspath + '/' + p
+ p = directory.get_labspath() + '/' + p
if drive:
root = self.get_root(drive)
@@ -1393,7 +1466,7 @@ class FS(LocalFS):
if start_dir.is_under(bd):
# If already in the build-dir location, don't reflect
return [orig], fmt % str(orig)
- p = os.path.join(bd.path, *tail)
+ p = os.path.join(bd._path, *tail)
targets.append(self.Entry(p))
tail = [dir.name] + tail
dir = dir.up()
@@ -1412,8 +1485,9 @@ class FS(LocalFS):
return cwd.glob(pathname, ondisk, source, strings, exclude)
class DirNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ()
# This should get reset by the FS initialization.
- current_version_id = 1
+ current_version_id = 2
fs = None
@@ -1425,11 +1499,12 @@ class DirNodeInfo(SCons.Node.NodeInfoBase):
if drive:
root = self.fs.get_root(drive)
if not os.path.isabs(s):
- s = top.labspath + '/' + s
+ s = top.get_labspath() + '/' + s
return root._lookup_abs(s, Entry)
class DirBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
glob_magic_check = re.compile('[*?[]')
@@ -1440,7 +1515,22 @@ class Dir(Base):
"""A class for directories in a file system.
"""
- memoizer_counters = []
+ __slots__ = ['scanner_paths',
+ 'cachedir_csig',
+ 'cachesig',
+ 'repositories',
+ 'srcdir',
+ 'entries',
+ 'searched',
+ '_sconsign',
+ 'variant_dirs',
+ 'root',
+ 'dirname',
+ 'on_disk_entries',
+ 'sccs_dir',
+ 'rcs_dir',
+ 'released_target_info',
+ 'contentsig']
NodeInfo = DirNodeInfo
BuildInfo = DirBuildInfo
@@ -1470,6 +1560,22 @@ class Dir(Base):
self._sconsign = None
self.variant_dirs = []
self.root = self.dir.root
+ self.changed_since_last_build = 3
+ self._func_sconsign = 1
+ self._func_exists = 2
+ self._func_get_contents = 2
+
+ self._abspath = SCons.Util.silent_intern(self.dir.entry_abspath(self.name))
+ self._labspath = SCons.Util.silent_intern(self.dir.entry_labspath(self.name))
+ if self.dir._path == '.':
+ self._path = SCons.Util.silent_intern(self.name)
+ else:
+ self._path = SCons.Util.silent_intern(self.dir.entry_path(self.name))
+ if self.dir._tpath == '.':
+ self._tpath = SCons.Util.silent_intern(self.name)
+ else:
+ self._tpath = SCons.Util.silent_intern(self.dir.entry_tpath(self.name))
+ self._path_elements = self.dir._path_elements + [self]
# For directories, we make a difference between the directory
# 'name' and the directory 'dirname'. The 'name' attribute is
@@ -1562,8 +1668,7 @@ class Dir(Base):
return self.srcdir.get_all_rdirs() + self.repositories
return self.repositories
- memoizer_counters.append(SCons.Memoize.CountValue('get_all_rdirs'))
-
+ @SCons.Memoize.CountMethodCall
def get_all_rdirs(self):
try:
return list(self._memo['get_all_rdirs'])
@@ -1589,7 +1694,7 @@ class Dir(Base):
def addRepository(self, dir):
if dir != self and not dir in self.repositories:
self.repositories.append(dir)
- dir.tpath = '.'
+ dir._tpath = '.'
self.__clearRepositoryCache()
def up(self):
@@ -1598,8 +1703,7 @@ class Dir(Base):
def _rel_path_key(self, other):
return str(other)
- memoizer_counters.append(SCons.Memoize.CountDict('rel_path', _rel_path_key))
-
+ @SCons.Memoize.CountDictCall(_rel_path_key)
def rel_path(self, other):
"""Return a path to "other" relative to this directory.
"""
@@ -1628,7 +1732,7 @@ class Dir(Base):
if self is other:
result = '.'
- elif not other in self.path_elements:
+ elif not other in self._path_elements:
try:
other_dir = other.get_dir()
except AttributeError:
@@ -1643,10 +1747,10 @@ class Dir(Base):
else:
result = dir_rel_path + OS_SEP + other.name
else:
- i = self.path_elements.index(other) + 1
+ i = self._path_elements.index(other) + 1
- path_elems = ['..'] * (len(self.path_elements) - i) \
- + [n.name for n in other.path_elements[i:]]
+ path_elems = ['..'] * (len(self._path_elements) - i) \
+ + [n.name for n in other._path_elements[i:]]
result = OS_SEP.join(path_elems)
@@ -1713,7 +1817,7 @@ class Dir(Base):
if p is None:
# Don't use while: - else: for this condition because
# if so, then parent is None and has no .path attribute.
- raise SCons.Errors.StopError(parent.path)
+ raise SCons.Errors.StopError(parent._path)
parent = p
listDirs.reverse()
for dirnode in listDirs:
@@ -1753,10 +1857,7 @@ class Dir(Base):
def get_contents(self):
"""Return content signatures and names of all our children
separated by new-lines. Ensure that the nodes are sorted."""
- contents = []
- for node in sorted(self.children(), key=lambda t: t.name):
- contents.append('%s %s\n' % (node.get_csig(), node.name))
- return ''.join(contents)
+ return SCons.Node._get_contents_map[self._func_get_contents](self)
def get_csig(self):
"""Compute the content signature for Directory nodes. In
@@ -1770,8 +1871,6 @@ class Dir(Base):
def do_duplicate(self, src):
pass
- changed_since_last_build = SCons.Node.Node.state_has_changed
-
def is_up_to_date(self):
"""If any child is not up-to-date, then this directory isn't,
either."""
@@ -1795,12 +1894,8 @@ class Dir(Base):
return self
def sconsign(self):
- """Return the .sconsign file info for this directory,
- creating it first if necessary."""
- if not self._sconsign:
- import SCons.SConsign
- self._sconsign = SCons.SConsign.ForDirectory(self)
- return self._sconsign
+ """Return the .sconsign file info for this directory. """
+ return _sconsign_map[self._func_sconsign](self)
def srcnode(self):
"""Dir has a special need for srcnode()...if we
@@ -1817,17 +1912,34 @@ class Dir(Base):
stamp = kid.get_timestamp()
return stamp
+ def get_abspath(self):
+ """Get the absolute path of the file."""
+ return self._abspath
+
+ def get_labspath(self):
+ """Get the absolute path of the file."""
+ return self._labspath
+
+ def get_internal_path(self):
+ return self._path
+
+ def get_tpath(self):
+ return self._tpath
+
+ def get_path_elements(self):
+ return self._path_elements
+
def entry_abspath(self, name):
- return self.abspath + OS_SEP + name
+ return self._abspath + OS_SEP + name
def entry_labspath(self, name):
- return self.labspath + '/' + name
+ return self._labspath + '/' + name
def entry_path(self, name):
- return self.path + OS_SEP + name
+ return self._path + OS_SEP + name
def entry_tpath(self, name):
- return self.tpath + OS_SEP + name
+ return self._tpath + OS_SEP + name
def entry_exists_on_disk(self, name):
""" Searches through the file/dir entries of the current
@@ -1841,7 +1953,7 @@ class Dir(Base):
except AttributeError:
d = {}
try:
- entries = os.listdir(self.abspath)
+ entries = os.listdir(self._abspath)
except OSError:
pass
else:
@@ -1854,7 +1966,7 @@ class Dir(Base):
if result is None:
# Belt-and-suspenders for Windows: check directly for
# 8.3 file names that don't show up in os.listdir().
- result = os.path.exists(self.abspath + OS_SEP + name)
+ result = os.path.exists(self._abspath + OS_SEP + name)
d[name] = result
return result
else:
@@ -1887,8 +1999,7 @@ class Dir(Base):
break
return rentry_exists
- memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list'))
-
+ @SCons.Memoize.CountMethodCall
def srcdir_list(self):
try:
return self._memo['srcdir_list']
@@ -1929,8 +2040,7 @@ class Dir(Base):
def _srcdir_find_file_key(self, filename):
return filename
- memoizer_counters.append(SCons.Memoize.CountDict('srcdir_find_file', _srcdir_find_file_key))
-
+ @SCons.Memoize.CountDictCall(_srcdir_find_file_key)
def srcdir_find_file(self, filename):
try:
memo_dict = self._memo['srcdir_find_file']
@@ -2072,7 +2182,12 @@ class Dir(Base):
r = [os.path.join(str(dir), x) for x in r]
result.extend(r)
if exclude:
- result = filter(lambda x: not any(fnmatch.fnmatch(str(x), e) for e in SCons.Util.flatten(exclude)), result)
+ excludes = []
+ excludeList = SCons.Util.flatten(exclude)
+ for x in excludeList:
+ r = self.glob(x, ondisk, source, strings)
+ excludes.extend(r)
+ result = filter(lambda x: not any(fnmatch.fnmatch(str(x), str(e)) for e in SCons.Util.flatten(excludes)), result)
return sorted(result, key=lambda a: str(a))
def _glob1(self, pattern, ondisk=True, source=False, strings=False):
@@ -2105,7 +2220,7 @@ class Dir(Base):
for name in node_names: selfEntry(name)
if ondisk:
try:
- disk_names = os.listdir(dir.abspath)
+ disk_names = os.listdir(dir._abspath)
except os.error:
continue
names.extend(disk_names)
@@ -2151,18 +2266,12 @@ class RootDir(Dir):
add a separator when creating the path names of entries within
this directory.
"""
+
+ __slots__ = ['_lookupDict']
+
def __init__(self, drive, fs):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.RootDir')
- # We're going to be our own parent directory (".." entry and .dir
- # attribute) so we have to set up some values so Base.__init__()
- # won't gag won't it calls some of our methods.
- self.abspath = ''
- self.labspath = ''
- self.path = ''
- self.tpath = ''
- self.path_elements = []
- self.duplicate = 0
- self.root = self
+ SCons.Node.Node.__init__(self)
# Handle all the types of drives:
if drive == '':
@@ -2178,33 +2287,85 @@ class RootDir(Dir):
name = drive
dirname = drive + OS_SEP
- Base.__init__(self, name, self, fs)
+ #: Filename with extension as it was specified when the object was
+ #: created; to obtain filesystem path, use Python str() function
+ self.name = SCons.Util.silent_intern(name)
+ self.fs = fs #: Reference to parent Node.FS object
+
+ self._path_elements = [self]
+ self.dir = self
+ self._func_rexists = 2
+ self._func_target_from_source = 1
+ self.store_info = 1
# Now set our paths to what we really want them to be. The
# name should already contain any necessary separators, such
# as the initial drive letter (the name) plus the directory
# separator, except for the "lookup abspath," which does not
# have the drive letter.
- self.abspath = dirname
- self.labspath = ''
- self.path = dirname
- self.tpath = dirname
- self._morph()
-
- # Must be reset after Dir._morph() is invoked...
+ self._abspath = dirname
+ self._labspath = ''
+ self._path = dirname
+ self._tpath = dirname
self.dirname = dirname
+ self._morph()
+
+ self.duplicate = 0
self._lookupDict = {}
self._lookupDict[''] = self
self._lookupDict['/'] = self
-
+ self.root = self
# The // entry is necessary because os.path.normpath()
# preserves double slashes at the beginning of a path on Posix
# platforms.
if not has_unc:
self._lookupDict['//'] = self
+ def _morph(self):
+ """Turn a file system Node (either a freshly initialized directory
+ object or a separate Entry object) into a proper directory object.
+
+ Set up this directory's entries and hook it into the file
+ system tree. Specify that directories (this Node) don't use
+ signatures for calculating whether they're current.
+ """
+
+ self.repositories = []
+ self.srcdir = None
+
+ self.entries = {}
+ self.entries['.'] = self
+ self.entries['..'] = self.dir
+ self.cwd = self
+ self.searched = 0
+ self._sconsign = None
+ self.variant_dirs = []
+ self.changed_since_last_build = 3
+ self._func_sconsign = 1
+ self._func_exists = 2
+ self._func_get_contents = 2
+
+ # Don't just reset the executor, replace its action list,
+ # because it might have some pre-or post-actions that need to
+ # be preserved.
+ #
+ # But don't reset the executor if there is a non-null executor
+ # attached already. The existing executor might have other
+ # targets, in which case replacing the action list with a
+ # Mkdir action is a big mistake.
+ if not hasattr(self, 'executor'):
+ self.builder = get_MkdirBuilder()
+ self.get_executor().set_action_list(self.builder.action)
+ else:
+ # Prepend MkdirBuilder action to existing action list
+ l = self.get_executor().action_list
+ a = get_MkdirBuilder().action
+ l.insert(0, a)
+ self.get_executor().set_action_list(l)
+
+
def must_be_same(self, klass):
if klass is Dir:
return
@@ -2253,19 +2414,19 @@ class RootDir(Dir):
return result
def __str__(self):
- return self.abspath
+ return self._abspath
def entry_abspath(self, name):
- return self.abspath + name
+ return self._abspath + name
def entry_labspath(self, name):
return '/' + name
def entry_path(self, name):
- return self.path + name
+ return self._path + name
def entry_tpath(self, name):
- return self.tpath + name
+ return self._tpath + name
def is_under(self, dir):
if self is dir:
@@ -2283,7 +2444,8 @@ class RootDir(Dir):
return _null
class FileNodeInfo(SCons.Node.NodeInfoBase):
- current_version_id = 1
+ __slots__ = ('csig', 'timestamp', 'size')
+ current_version_id = 2
field_list = ['csig', 'timestamp', 'size']
@@ -2298,11 +2460,43 @@ class FileNodeInfo(SCons.Node.NodeInfoBase):
if drive:
root = self.fs.get_root(drive)
if not os.path.isabs(s):
- s = top.labspath + '/' + s
+ s = top.get_labspath() + '/' + s
return root._lookup_abs(s, Entry)
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
class FileBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
def convert_to_sconsign(self):
"""
@@ -2317,7 +2511,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
else:
def node_to_str(n):
try:
- s = n.path
+ s = n.get_internal_path()
except AttributeError:
s = str(n)
else:
@@ -2358,6 +2552,8 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
nodeinfos = getattr(self, sattr)
except AttributeError:
continue
+ if strings is None or nodeinfos is None:
+ continue
nodes = []
for s, ni in zip(strings, nodeinfos):
if not isinstance(s, SCons.Node.Node):
@@ -2371,6 +2567,8 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
for bkid, bkidsig in zip(bkids, bkidsigs):
result.append(str(bkid) + ': ' +
' '.join(bkidsig.format(names=names)))
+ if not hasattr(self,'bact'):
+ self.bact = "none"
result.append('%s [%s]' % (self.bactsig, self.bact))
return '\n'.join(result)
@@ -2378,7 +2576,22 @@ class File(Base):
"""A class for files in a file system.
"""
- memoizer_counters = []
+ __slots__ = ['scanner_paths',
+ 'cachedir_csig',
+ 'cachesig',
+ 'repositories',
+ 'srcdir',
+ 'entries',
+ 'searched',
+ '_sconsign',
+ 'variant_dirs',
+ 'root',
+ 'dirname',
+ 'on_disk_entries',
+ 'sccs_dir',
+ 'rcs_dir',
+ 'released_target_info',
+ 'contentsig']
NodeInfo = FileNodeInfo
BuildInfo = FileBuildInfo
@@ -2429,6 +2642,14 @@ class File(Base):
if not hasattr(self, 'released_target_info'):
self.released_target_info = False
+ self.store_info = 1
+ self._func_exists = 4
+ self._func_get_contents = 3
+
+ # Initialize this Node's decider function to decide_source() because
+ # every file is a source file until it has a Builder attached...
+ self.changed_since_last_build = 4
+
# If there was already a Builder set on this entry, then
# we need to make sure we call the target-decider function,
# not the source-decider. Reaching in and doing this by hand
@@ -2440,22 +2661,13 @@ class File(Base):
# not clear right now how to fix that, stick with what works
# until it becomes clear...
if self.has_builder():
- self.changed_since_last_build = self.decide_target
+ self.changed_since_last_build = 5
def scanner_key(self):
return self.get_suffix()
def get_contents(self):
- if not self.rexists():
- return ''
- fname = self.rfile().abspath
- try:
- contents = open(fname, "rb").read()
- except EnvironmentError, e:
- if not e.filename:
- e.filename = fname
- raise
- return contents
+ return SCons.Node._get_contents_map[self._func_get_contents](self)
# This attempts to figure out what the encoding of the text is
# based upon the BOM bytes, and then decodes the contents so that
@@ -2482,7 +2694,7 @@ class File(Base):
"""
if not self.rexists():
return SCons.Util.MD5signature('')
- fname = self.rfile().abspath
+ fname = self.rfile().get_abspath()
try:
cs = SCons.Util.MD5filesignature(fname,
chunksize=SCons.Node.FS.File.md5_chunksize*1024)
@@ -2492,9 +2704,7 @@ class File(Base):
raise
return cs
-
- memoizer_counters.append(SCons.Memoize.CountValue('get_size'))
-
+ @SCons.Memoize.CountMethodCall
def get_size(self):
try:
return self._memo['get_size']
@@ -2510,8 +2720,7 @@ class File(Base):
return size
- memoizer_counters.append(SCons.Memoize.CountValue('get_timestamp'))
-
+ @SCons.Memoize.CountMethodCall
def get_timestamp(self):
try:
return self._memo['get_timestamp']
@@ -2527,14 +2736,6 @@ class File(Base):
return timestamp
- def store_info(self):
- # Merge our build information into the already-stored entry.
- # This accomodates "chained builds" where a file that's a target
- # in one build (SConstruct file) is a source in a different build.
- # See test/chained-build.py for the use case.
- if do_store_info:
- self.dir.sconsign().store_info(self.name, self)
-
convert_copy_attrs = [
'bsources',
'bimplicit',
@@ -2647,8 +2848,7 @@ class File(Base):
delattr(old_entry, attr)
return new_entry
- memoizer_counters.append(SCons.Memoize.CountValue('get_stored_info'))
-
+ @SCons.Memoize.CountMethodCall
def get_stored_info(self):
try:
return self._memo['get_stored_info']
@@ -2688,8 +2888,7 @@ class File(Base):
def _get_found_includes_key(self, env, scanner, path):
return (id(env), id(scanner), path)
- memoizer_counters.append(SCons.Memoize.CountDict('get_found_includes', _get_found_includes_key))
-
+ @SCons.Memoize.CountDictCall(_get_found_includes_key)
def get_found_includes(self, env, scanner, path):
"""Return the included implicit dependencies in this file.
Cache results so we only scan the file once per path
@@ -2773,9 +2972,9 @@ class File(Base):
# any build information that's stored in the .sconsign file
# into our binfo object so it doesn't get lost.
old = self.get_stored_info()
- self.get_binfo().__dict__.update(old.binfo.__dict__)
+ self.get_binfo().merge(old.binfo)
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
def release_target_info(self):
"""Called just after this node has been marked
@@ -2875,7 +3074,7 @@ class File(Base):
def _rmv_existing(self):
self.clear_memoized_values()
- if print_duplicate:
+ if SCons.Node.print_duplicate:
print "dup: removing existing target %s"%self
e = Unlink(self, [], None)
if isinstance(e, SCons.Errors.BuildError):
@@ -2911,18 +3110,18 @@ class File(Base):
def remove(self):
"""Remove this file."""
if self.exists() or self.islink():
- self.fs.unlink(self.path)
+ self.fs.unlink(self.get_internal_path())
return 1
return None
def do_duplicate(self, src):
self._createDir()
- if print_duplicate:
+ if SCons.Node.print_duplicate:
print "dup: relinking variant '%s' from '%s'"%(self, src)
Unlink(self, None, None)
e = Link(self, src, None)
if isinstance(e, SCons.Errors.BuildError):
- desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
+ desc = "Cannot duplicate `%s' in `%s': %s." % (src.get_internal_path(), self.dir._path, e.errstr)
raise SCons.Errors.StopError(desc)
self.linked = 1
# The Link() action may or may not have actually
@@ -2931,36 +3130,14 @@ class File(Base):
# _rexists attributes so they can be reevaluated.
self.clear()
- memoizer_counters.append(SCons.Memoize.CountValue('exists'))
-
+ @SCons.Memoize.CountMethodCall
def exists(self):
try:
return self._memo['exists']
except KeyError:
pass
- # Duplicate from source path if we are set up to do this.
- if self.duplicate and not self.is_derived() and not self.linked:
- src = self.srcnode()
- if src is not self:
- # At this point, src is meant to be copied in a variant directory.
- src = src.rfile()
- if src.abspath != self.abspath:
- if src.exists():
- self.do_duplicate(src)
- # Can't return 1 here because the duplication might
- # not actually occur if the -n option is being used.
- else:
- # The source file does not exist. Make sure no old
- # copy remains in the variant directory.
- if print_duplicate:
- print "dup: no src for %s, unlinking old variant copy"%self
- if Base.exists(self) or self.islink():
- self.fs.unlink(self.path)
- # Return None explicitly because the Base.exists() call
- # above will have cached its value if the file existed.
- self._memo['exists'] = None
- return None
- result = Base.exists(self)
+
+ result = SCons.Node._exists_map[self._func_exists](self)
self._memo['exists'] = result
return result
@@ -3037,7 +3214,7 @@ class File(Base):
def builder_set(self, builder):
SCons.Node.Node.builder_set(self, builder)
- self.changed_since_last_build = self.decide_target
+ self.changed_since_last_build = 5
def built(self):
"""Called just after this File node is successfully built.
@@ -3054,10 +3231,10 @@ class File(Base):
if (not SCons.Node.interactive and
not hasattr(self.attributes, 'keep_targetinfo')):
# Ensure that the build infos get computed and cached...
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
# ... then release some more variables.
self._specific_sources = False
- self.labspath = None
+ self._labspath = None
self._save_str()
self.cwd = None
@@ -3116,16 +3293,6 @@ class File(Base):
except AttributeError:
return 1
- def decide_source(self, target, prev_ni):
- return target.get_build_env().decide_source(self, target, prev_ni)
-
- def decide_target(self, target, prev_ni):
- return target.get_build_env().decide_target(self, target, prev_ni)
-
- # Initialize this Node's decider function to decide_source() because
- # every file is a source file until it has a Builder attached...
- changed_since_last_build = decide_source
-
def is_up_to_date(self):
T = 0
if T: Trace('is_up_to_date(%s):' % self)
@@ -3143,7 +3310,7 @@ class File(Base):
e = LocalCopy(self, r, None)
if isinstance(e, SCons.Errors.BuildError):
raise
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
if T: Trace(' 1\n')
return 1
self.changed()
@@ -3154,8 +3321,7 @@ class File(Base):
if T: Trace(' self.exists(): %s\n' % r)
return not r
- memoizer_counters.append(SCons.Memoize.CountValue('rfile'))
-
+ @SCons.Memoize.CountMethodCall
def rfile(self):
try:
return self._memo['rfile']
@@ -3257,7 +3423,7 @@ class File(Base):
# Append this node's signature...
sigs.append(self.get_contents_sig())
# ...and it's path
- sigs.append(self.path)
+ sigs.append(self.get_internal_path())
# Merge this all into a single signature
result = self.cachesig = SCons.Util.MD5collect(sigs)
return result
@@ -3273,10 +3439,6 @@ def get_default_fs():
class FileFinder(object):
"""
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
def __init__(self):
self._memo = {}
@@ -3319,8 +3481,7 @@ class FileFinder(object):
def _find_file_key(self, filename, paths, verbose=None):
return (filename, paths)
- memoizer_counters.append(SCons.Memoize.CountDict('find_file', _find_file_key))
-
+ @SCons.Memoize.CountDictCall(_find_file_key)
def find_file(self, filename, paths, verbose=None):
"""
find_file(str, [Dir()]) -> [nodes]
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 0326717..0c1e71f 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -130,34 +130,34 @@ class VariantDirTestCase(unittest.TestCase):
fs.VariantDir('build', 'src')
f2 = fs.File('build/test2')
d1 = fs.Dir('build')
- assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
- assert f2.srcnode().path == os.path.normpath('src/test2'), f2.srcnode().path
- assert d1.srcnode().path == 'src', d1.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test1'), f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test2'), f2.srcnode().get_internal_path()
+ assert d1.srcnode().get_internal_path() == 'src', d1.srcnode().get_internal_path()
fs = SCons.Node.FS.FS()
f1 = fs.File('build/test1')
fs.VariantDir('build', '.')
f2 = fs.File('build/test2')
d1 = fs.Dir('build')
- assert f1.srcnode().path == 'test1', f1.srcnode().path
- assert f2.srcnode().path == 'test2', f2.srcnode().path
- assert d1.srcnode().path == '.', d1.srcnode().path
+ assert f1.srcnode().get_internal_path() == 'test1', f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == 'test2', f2.srcnode().get_internal_path()
+ assert d1.srcnode().get_internal_path() == '.', d1.srcnode().get_internal_path()
fs = SCons.Node.FS.FS()
fs.VariantDir('build/var1', 'src')
fs.VariantDir('build/var2', 'src')
f1 = fs.File('build/var1/test1')
f2 = fs.File('build/var2/test1')
- assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
- assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test1'), f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test1'), f2.srcnode().get_internal_path()
fs = SCons.Node.FS.FS()
fs.VariantDir('../var1', 'src')
fs.VariantDir('../var2', 'src')
f1 = fs.File('../var1/test1')
f2 = fs.File('../var2/test1')
- assert f1.srcnode().path == os.path.normpath('src/test1'), f1.srcnode().path
- assert f2.srcnode().path == os.path.normpath('src/test1'), f2.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test1'), f1.srcnode().get_internal_path()
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test1'), f2.srcnode().get_internal_path()
# Set up some files
test.subdir('work', ['work', 'src'])
@@ -210,8 +210,8 @@ class VariantDirTestCase(unittest.TestCase):
f2out_2.builder = 1
fs.Repository(test.workpath('rep1'))
- assert f1.srcnode().path == os.path.normpath('src/test.in'),\
- f1.srcnode().path
+ assert f1.srcnode().get_internal_path() == os.path.normpath('src/test.in'),\
+ f1.srcnode().get_internal_path()
# str(node) returns source path for duplicate = 0
assert str(f1) == os.path.normpath('src/test.in'), str(f1)
# Build path does not exist
@@ -221,11 +221,11 @@ class VariantDirTestCase(unittest.TestCase):
# And duplicate=0 should also work just like a Repository
assert f1.rexists()
# rfile() should point to the source path
- assert f1.rfile().path == os.path.normpath('src/test.in'),\
- f1.rfile().path
+ assert f1.rfile().get_internal_path() == os.path.normpath('src/test.in'),\
+ f1.rfile().get_internal_path()
- assert f2.srcnode().path == os.path.normpath('src/test.in'),\
- f2.srcnode().path
+ assert f2.srcnode().get_internal_path() == os.path.normpath('src/test.in'),\
+ f2.srcnode().get_internal_path()
# str(node) returns build path for duplicate = 1
assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2)
# Build path exists
@@ -239,8 +239,8 @@ class VariantDirTestCase(unittest.TestCase):
f3 = fs.File('build/var1/test2.in')
f4 = fs.File('build/var2/test2.in')
- assert f3.srcnode().path == os.path.normpath('src/test2.in'),\
- f3.srcnode().path
+ assert f3.srcnode().get_internal_path() == os.path.normpath('src/test2.in'),\
+ f3.srcnode().get_internal_path()
# str(node) returns source path for duplicate = 0
assert str(f3) == os.path.normpath('src/test2.in'), str(f3)
# Build path does not exist
@@ -250,11 +250,11 @@ class VariantDirTestCase(unittest.TestCase):
# But we do have a file in the Repository
assert f3.rexists()
# rfile() should point to the source path
- assert f3.rfile().path == os.path.normpath(test.workpath('rep1/src/test2.in')),\
- f3.rfile().path
+ assert f3.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/src/test2.in')),\
+ f3.rfile().get_internal_path()
- assert f4.srcnode().path == os.path.normpath('src/test2.in'),\
- f4.srcnode().path
+ assert f4.srcnode().get_internal_path() == os.path.normpath('src/test2.in'),\
+ f4.srcnode().get_internal_path()
# str(node) returns build path for duplicate = 1
assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4)
# Build path should exist
@@ -264,8 +264,8 @@ class VariantDirTestCase(unittest.TestCase):
# should exist in repository, since exists() is true
assert f4.rexists()
# rfile() should point to ourselves
- assert f4.rfile().path == os.path.normpath('build/var2/test2.in'),\
- f4.rfile().path
+ assert f4.rfile().get_internal_path() == os.path.normpath('build/var2/test2.in'),\
+ f4.rfile().get_internal_path()
f5 = fs.File('build/var1/test.out')
f6 = fs.File('build/var2/test.out')
@@ -285,14 +285,14 @@ class VariantDirTestCase(unittest.TestCase):
assert not f7.exists()
assert f7.rexists()
- r = f7.rfile().path
+ r = f7.rfile().get_internal_path()
expect = os.path.normpath(test.workpath('rep1/build/var1/test2.out'))
assert r == expect, (repr(r), repr(expect))
assert not f8.exists()
assert f8.rexists()
- assert f8.rfile().path == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\
- f8.rfile().path
+ assert f8.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\
+ f8.rfile().get_internal_path()
# Verify the Mkdir and Link actions are called
d9 = fs.Dir('build/var2/new_dir')
@@ -319,9 +319,9 @@ class VariantDirTestCase(unittest.TestCase):
d9.reset_executor()
f9.exists()
expect = os.path.join('build', 'var2', 'new_dir')
- assert dir_made[0].path == expect, dir_made[0].path
+ assert dir_made[0].get_internal_path() == expect, dir_made[0].get_internal_path()
expect = os.path.join('build', 'var2', 'new_dir', 'test9.out')
- assert link_made[0].path == expect, link_made[0].path
+ assert link_made[0].get_internal_path() == expect, link_made[0].get_internal_path()
assert f9.linked
finally:
SCons.Node.FS.Link = save_Link
@@ -338,7 +338,7 @@ class VariantDirTestCase(unittest.TestCase):
f11 = fs.File('src/file11')
t, m = f11.alter_targets()
- bdt = [n.path for n in t]
+ bdt = [n.get_internal_path() for n in t]
var1_file11 = os.path.normpath('build/var1/file11')
var2_file11 = os.path.normpath('build/var2/file11')
assert bdt == [var1_file11, var2_file11], bdt
@@ -346,11 +346,11 @@ class VariantDirTestCase(unittest.TestCase):
f12 = fs.File('src/file12')
f12.builder = 1
bdt, m = f12.alter_targets()
- assert bdt == [], [n.path for n in bdt]
+ assert bdt == [], [n.get_internal_path() for n in bdt]
d13 = fs.Dir('src/new_dir')
t, m = d13.alter_targets()
- bdt = [n.path for n in t]
+ bdt = [n.get_internal_path() for n in t]
var1_new_dir = os.path.normpath('build/var1/new_dir')
var2_new_dir = os.path.normpath('build/var2/new_dir')
assert bdt == [var1_new_dir, var2_new_dir], bdt
@@ -566,13 +566,13 @@ class VariantDirTestCase(unittest.TestCase):
f = dir + '/f'
fnode = fs.File(dir + '/f')
- dp = dnode.srcnode().path
+ dp = dnode.srcnode().get_internal_path()
expect = os.path.normpath(srcnode_map.get(dir, dir))
if dp != expect:
print "Dir `%s' srcnode() `%s' != expected `%s'" % (dir, dp, expect)
errors = errors + 1
- fp = fnode.srcnode().path
+ fp = fnode.srcnode().get_internal_path()
expect = os.path.normpath(srcnode_map.get(f, f))
if fp != expect:
print "File `%s' srcnode() `%s' != expected `%s'" % (f, fp, expect)
@@ -584,14 +584,14 @@ class VariantDirTestCase(unittest.TestCase):
fnode = fs.File(dir + '/f')
t, m = dnode.alter_targets()
- tp = t[0].path
+ tp = t[0].get_internal_path()
expect = os.path.normpath(alter_map.get(dir, dir))
if tp != expect:
print "Dir `%s' alter_targets() `%s' != expected `%s'" % (dir, tp, expect)
errors = errors + 1
t, m = fnode.alter_targets()
- tp = t[0].path
+ tp = t[0].get_internal_path()
expect = os.path.normpath(alter_map.get(f, f))
if tp != expect:
print "File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect)
@@ -698,19 +698,19 @@ class DirNodeInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test DirNodeInfo initialization"""
ddd = self.fs.Dir('ddd')
- ni = SCons.Node.FS.DirNodeInfo(ddd)
+ ni = SCons.Node.FS.DirNodeInfo()
class DirBuildInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test DirBuildInfo initialization"""
ddd = self.fs.Dir('ddd')
- bi = SCons.Node.FS.DirBuildInfo(ddd)
+ bi = SCons.Node.FS.DirBuildInfo()
class FileNodeInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test FileNodeInfo initialization"""
fff = self.fs.File('fff')
- ni = SCons.Node.FS.FileNodeInfo(fff)
+ ni = SCons.Node.FS.FileNodeInfo()
assert isinstance(ni, SCons.Node.FS.FileNodeInfo)
def test_update(self):
@@ -718,7 +718,7 @@ class FileNodeInfoTestCase(_tempdirTestCase):
test = self.test
fff = self.fs.File('fff')
- ni = SCons.Node.FS.FileNodeInfo(fff)
+ ni = SCons.Node.FS.FileNodeInfo()
test.write('fff', "fff\n")
@@ -765,37 +765,41 @@ class FileBuildInfoTestCase(_tempdirTestCase):
def test___init__(self):
"""Test File.BuildInfo initialization"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
assert bi, bi
def test_convert_to_sconsign(self):
"""Test converting to .sconsign file format"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
assert hasattr(bi, 'convert_to_sconsign')
def test_convert_from_sconsign(self):
"""Test converting from .sconsign file format"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
assert hasattr(bi, 'convert_from_sconsign')
def test_prepare_dependencies(self):
"""Test that we have a prepare_dependencies() method"""
fff = self.fs.File('fff')
- bi = SCons.Node.FS.FileBuildInfo(fff)
+ bi = SCons.Node.FS.FileBuildInfo()
bi.prepare_dependencies()
def test_format(self):
"""Test the format() method"""
f1 = self.fs.File('f1')
- bi1 = SCons.Node.FS.FileBuildInfo(f1)
+ bi1 = SCons.Node.FS.FileBuildInfo()
- s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1'))
+ self.fs.File('n1')
+ self.fs.File('n2')
+ self.fs.File('n3')
+
+ s1sig = SCons.Node.FS.FileNodeInfo()
s1sig.csig = 1
- d1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n2'))
+ d1sig = SCons.Node.FS.FileNodeInfo()
d1sig.timestamp = 2
- i1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n3'))
+ i1sig = SCons.Node.FS.FileNodeInfo()
i1sig.size = 3
bi1.bsources = [self.fs.File('s1')]
@@ -953,7 +957,7 @@ class FSTestCase(_tempdirTestCase):
assert isinstance(f1, SCons.Node.FS.File)
d1_f1 = os.path.join('d1', 'f1')
- assert f1.path == d1_f1, "f1.path %s != %s" % (f1.path, d1_f1)
+ assert f1.get_internal_path() == d1_f1, "f1.path %s != %s" % (f1.get_internal_path(), d1_f1)
assert str(f1) == d1_f1, "str(f1) %s != %s" % (str(f1), d1_f1)
x1 = d1.File('x1')
@@ -1045,16 +1049,16 @@ class FSTestCase(_tempdirTestCase):
name = os.sep
if dir.up() is None:
- dir_up_path = dir.path
+ dir_up_path = dir.get_internal_path()
else:
- dir_up_path = dir.up().path
+ dir_up_path = dir.up().get_internal_path()
assert dir.name == name, \
"dir.name %s != expected name %s" % \
(dir.name, name)
- assert dir.path == path, \
+ assert dir.get_internal_path() == path, \
"dir.path %s != expected path %s" % \
- (dir.path, path)
+ (dir.get_internal_path(), path)
assert str(dir) == path, \
"str(dir) %s != expected path %s" % \
(str(dir), path)
@@ -1136,9 +1140,9 @@ class FSTestCase(_tempdirTestCase):
# Test for a bug in 0.04 that did not like looking up
# dirs with a trailing slash on Windows.
d=fs.Dir('./')
- assert d.path == '.', d.abspath
+ assert d.get_internal_path() == '.', d.get_abspath()
d=fs.Dir('foo/')
- assert d.path == 'foo', d.abspath
+ assert d.get_internal_path() == 'foo', d.get_abspath()
# Test for sub-classing of node building.
global built_it
@@ -1167,50 +1171,50 @@ class FSTestCase(_tempdirTestCase):
e1 = fs.Entry("d1")
assert e1.__class__.__name__ == 'Dir'
- match(e1.path, "d1")
- match(e1.dir.path, ".")
+ match(e1.get_internal_path(), "d1")
+ match(e1.dir.get_internal_path(), ".")
e2 = fs.Entry("d1/f1")
assert e2.__class__.__name__ == 'File'
- match(e2.path, "d1/f1")
- match(e2.dir.path, "d1")
+ match(e2.get_internal_path(), "d1/f1")
+ match(e2.dir.get_internal_path(), "d1")
e3 = fs.Entry("e3")
assert e3.__class__.__name__ == 'Entry'
- match(e3.path, "e3")
- match(e3.dir.path, ".")
+ match(e3.get_internal_path(), "e3")
+ match(e3.dir.get_internal_path(), ".")
e4 = fs.Entry("d1/e4")
assert e4.__class__.__name__ == 'Entry'
- match(e4.path, "d1/e4")
- match(e4.dir.path, "d1")
+ match(e4.get_internal_path(), "d1/e4")
+ match(e4.dir.get_internal_path(), "d1")
e5 = fs.Entry("e3/e5")
assert e3.__class__.__name__ == 'Dir'
- match(e3.path, "e3")
- match(e3.dir.path, ".")
+ match(e3.get_internal_path(), "e3")
+ match(e3.dir.get_internal_path(), ".")
assert e5.__class__.__name__ == 'Entry'
- match(e5.path, "e3/e5")
- match(e5.dir.path, "e3")
+ match(e5.get_internal_path(), "e3/e5")
+ match(e5.dir.get_internal_path(), "e3")
e6 = fs.Dir("d1/e4")
assert e6 is e4
assert e4.__class__.__name__ == 'Dir'
- match(e4.path, "d1/e4")
- match(e4.dir.path, "d1")
+ match(e4.get_internal_path(), "d1/e4")
+ match(e4.dir.get_internal_path(), "d1")
e7 = fs.File("e3/e5")
assert e7 is e5
assert e5.__class__.__name__ == 'File'
- match(e5.path, "e3/e5")
- match(e5.dir.path, "e3")
+ match(e5.get_internal_path(), "e3/e5")
+ match(e5.dir.get_internal_path(), "e3")
fs.chdir(fs.Dir('subdir'))
f11 = fs.File("f11")
- match(f11.path, "subdir/f11")
+ match(f11.get_internal_path(), "subdir/f11")
d12 = fs.Dir("d12")
e13 = fs.Entry("subdir/e13")
- match(e13.path, "subdir/subdir/e13")
+ match(e13.get_internal_path(), "subdir/subdir/e13")
fs.chdir(fs.Dir('..'))
# Test scanning
@@ -1220,13 +1224,13 @@ class FSTestCase(_tempdirTestCase):
f1.builder.target_scanner = Scanner(xyz)
f1.scan()
- assert f1.implicit[0].path == "xyz"
+ assert f1.implicit[0].get_internal_path() == "xyz"
f1.implicit = []
f1.scan()
assert f1.implicit == []
f1.implicit = None
f1.scan()
- assert f1.implicit[0].path == "xyz"
+ assert f1.implicit[0].get_internal_path() == "xyz"
# Test underlying scanning functionality in get_found_includes()
env = Environment()
@@ -1284,9 +1288,9 @@ class FSTestCase(_tempdirTestCase):
fs.chdir(fs.Dir('subdir'))
# The cwd's path is always "."
assert str(fs.getcwd()) == ".", str(fs.getcwd())
- assert fs.getcwd().path == 'subdir', fs.getcwd().path
+ assert fs.getcwd().get_internal_path() == 'subdir', fs.getcwd().get_internal_path()
fs.chdir(fs.Dir('../..'))
- assert fs.getcwd().path == test.workdir, fs.getcwd().path
+ assert fs.getcwd().get_internal_path() == test.workdir, fs.getcwd().get_internal_path()
f1 = fs.File(test.workpath("do_i_exist"))
assert not f1.exists()
@@ -1589,15 +1593,15 @@ class FSTestCase(_tempdirTestCase):
assert dir.name == name, \
"dir.name %s != expected name %s" % \
(dir.name, name)
- assert dir.path == path, \
+ assert dir.get_internal_path() == path, \
"dir.path %s != expected path %s" % \
- (dir.path, path)
+ (dir.get_internal_path(), path)
assert str(dir) == path, \
"str(dir) %s != expected path %s" % \
(str(dir), path)
- assert dir.up().path == up_path, \
+ assert dir.up().get_internal_path() == up_path, \
"dir.up().path %s != expected parent path %s" % \
- (dir.up().path, up_path)
+ (dir.up().get_internal_path(), up_path)
save_os_path = os.path
save_os_sep = os.sep
@@ -1691,22 +1695,22 @@ class FSTestCase(_tempdirTestCase):
name = path.split(os.sep)[-1]
if dir.up() is None:
- dir_up_path = dir.path
+ dir_up_path = dir.get_internal_path()
else:
- dir_up_path = dir.up().path
+ dir_up_path = dir.up().get_internal_path()
assert dir.name == name, \
"dir.name %s != expected name %s" % \
(dir.name, name)
- assert dir.path == path, \
+ assert dir.get_internal_path() == path, \
"dir.path %s != expected path %s" % \
- (dir.path, path)
+ (dir.get_internal_path(), path)
assert str(dir) == path, \
"str(dir) %s != expected path %s" % \
(str(dir), path)
assert dir_up_path == up_path, \
"dir.up().path %s != expected parent path %s" % \
- (dir.up().path, up_path)
+ (dir.up().get_internal_path(), up_path)
save_os_path = os.path
save_os_sep = os.sep
@@ -1792,7 +1796,7 @@ class FSTestCase(_tempdirTestCase):
d1 = fs.Dir('d1')
d2 = d1.Dir('d2')
- dirs = os.path.normpath(d2.abspath).split(os.sep)
+ dirs = os.path.normpath(d2.get_abspath()).split(os.sep)
above_path = os.path.join(*['..']*len(dirs) + ['above'])
above = d2.Dir(above_path)
@@ -1831,9 +1835,9 @@ class FSTestCase(_tempdirTestCase):
fs = self.fs
if sys.platform not in ('win32',):
return
- p = fs.Dir(r"\\computername\sharename").abspath
+ p = fs.Dir(r"\\computername\sharename").get_abspath()
assert p == r"\\computername\sharename", p
- p = fs.Dir(r"\\\computername\sharename").abspath
+ p = fs.Dir(r"\\\computername\sharename").get_abspath()
assert p == r"\\computername\sharename", p
def test_rel_path(self):
@@ -1980,7 +1984,7 @@ class DirTestCase(_tempdirTestCase):
fs.Dir(os.path.join('ddd', 'd1', 'f4'))
fs.Dir(os.path.join('ddd', 'd1', 'f5'))
dir.scan()
- kids = sorted([x.path for x in dir.children(None)])
+ kids = sorted([x.get_internal_path() for x in dir.children(None)])
assert kids == [os.path.join('ddd', 'd1'),
os.path.join('ddd', 'f1'),
os.path.join('ddd', 'f2'),
@@ -2024,12 +2028,12 @@ class DirTestCase(_tempdirTestCase):
fs.File(os.path.join('ddd', 'f1'))
dir.scan()
- kids = sorted([x.path for x in dir.children()])
+ kids = sorted([x.get_internal_path() for x in dir.children()])
assert kids == [os.path.join('ddd', 'f1')], kids
fs.File(os.path.join('ddd', 'f2'))
dir.scan()
- kids = sorted([x.path for x in dir.children()])
+ kids = sorted([x.get_internal_path() for x in dir.children()])
assert kids == [os.path.join('ddd', 'f1'),
os.path.join('ddd', 'f2')], kids
@@ -2171,8 +2175,12 @@ class DirTestCase(_tempdirTestCase):
"""
test = self.test
- return_true = lambda: 1
+ def return_true(node):
+ return 1
+ SCons.Node._is_derived_map[2] = return_true
+ SCons.Node._exists_map[5] = return_true
+
test.subdir('src0')
test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n")
test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n")
@@ -2184,14 +2192,14 @@ class DirTestCase(_tempdirTestCase):
self.fs.VariantDir(bld0, src0, duplicate=0)
derived_f = src0.File('derived-f')
- derived_f.is_derived = return_true
+ derived_f._func_is_derived = 2
exists_f = src0.File('exists-f')
- exists_f.exists = return_true
+ exists_f._func_exists = 5
derived_e = src0.Entry('derived-e')
- derived_e.is_derived = return_true
+ derived_e._func_is_derived = 2
exists_e = src0.Entry('exists-e')
- exists_e.exists = return_true
+ exists_e._func_exists = 5
def check(result, expect):
result = list(map(str, result))
@@ -2245,14 +2253,14 @@ class DirTestCase(_tempdirTestCase):
self.fs.VariantDir(bld1, src1, duplicate=1)
derived_f = src1.File('derived-f')
- derived_f.is_derived = return_true
+ derived_f._func_is_derived = 2
exists_f = src1.File('exists-f')
- exists_f.exists = return_true
+ exists_f._func_exists = 5
derived_e = src1.Entry('derived-e')
- derived_e.is_derived = return_true
+ derived_e._func_is_derived = 2
exists_e = src1.Entry('exists-e')
- exists_e.exists = return_true
+ exists_e._func_exists = 5
# First check from the source directory.
n = src1.srcdir_find_file('does_not_exist')
@@ -2432,7 +2440,7 @@ class FileTestCase(_tempdirTestCase):
build_f1 = fs.File('build/f1')
assert build_f1.exists(), "%s did not realize that %s exists" % (build_f1, src_f1)
- assert os.path.exists(build_f1.abspath), "%s did not get duplicated on disk" % build_f1.abspath
+ assert os.path.exists(build_f1.get_abspath()), "%s did not get duplicated on disk" % build_f1.get_abspath()
test.unlink(['src', 'f1'])
src_f1.clear() # so the next exists() call will look on disk again
@@ -2441,7 +2449,7 @@ class FileTestCase(_tempdirTestCase):
build_f1.clear()
build_f1.linked = None
assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1)
- assert not os.path.exists(build_f1.abspath), "%s did not get removed after %s was removed" % (build_f1, src_f1)
+ assert not os.path.exists(build_f1.get_abspath()), "%s did not get removed after %s was removed" % (build_f1, src_f1)
@@ -2535,7 +2543,7 @@ class GlobTestCase(_tempdirTestCase):
for input, string_expect, node_expect in cases:
r = self.fs.Glob(input, **kwargs)
if node_expect:
- r = sorted(r, key=lambda a: a.path)
+ r = sorted(r, key=lambda a: a.get_internal_path())
result = []
for n in node_expect:
if isinstance(n, str):
@@ -2906,8 +2914,14 @@ class RepositoryTestCase(_tempdirTestCase):
def test_rdir(self):
"""Test the Dir.rdir() method"""
- return_true = lambda: 1
- return_false = lambda: 0
+ def return_true(obj):
+ return 1
+ def return_false(obj):
+ return 0
+ SCons.Node._exists_map[5] = return_true
+ SCons.Node._exists_map[6] = return_false
+ SCons.Node._is_derived_map[2] = return_true
+ SCons.Node._is_derived_map[3] = return_false
d1 = self.fs.Dir('d1')
d2 = self.fs.Dir('d2')
@@ -2931,19 +2945,19 @@ class RepositoryTestCase(_tempdirTestCase):
assert r == os.path.join(self.rep3, 'd3'), r
e1 = self.fs.Dir('e1')
- e1.exists = return_false
+ e1._func_exists = 6
e2 = self.fs.Dir('e2')
- e2.exists = return_false
+ e2._func_exists = 6
# Make sure we match entries in repositories,
# regardless of whether they're derived or not.
re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
- re1.exists = return_true
- re1.is_derived = return_true
+ re1._func_exists = 5
+ re1._func_is_derived = 2
re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
- re2.exists = return_true
- re2.is_derived = return_false
+ re2._func_exists = 5
+ re2._func_is_derived = 3
r = e1.rdir()
assert r is re1, r
@@ -2953,8 +2967,14 @@ class RepositoryTestCase(_tempdirTestCase):
def test_rfile(self):
"""Test the File.rfile() method"""
- return_true = lambda: 1
- return_false = lambda: 0
+ def return_true(obj):
+ return 1
+ def return_false(obj):
+ return 0
+ SCons.Node._exists_map[5] = return_true
+ SCons.Node._exists_map[6] = return_false
+ SCons.Node._is_derived_map[2] = return_true
+ SCons.Node._is_derived_map[3] = return_false
f1 = self.fs.File('f1')
f2 = self.fs.File('f2')
@@ -2978,19 +2998,19 @@ class RepositoryTestCase(_tempdirTestCase):
assert r == os.path.join(self.rep3, 'f3'), r
e1 = self.fs.File('e1')
- e1.exists = return_false
+ e1._func_exists = 6
e2 = self.fs.File('e2')
- e2.exists = return_false
+ e2._func_exists = 6
# Make sure we match entries in repositories,
# regardless of whether they're derived or not.
re1 = self.fs.Entry(os.path.join(self.rep1, 'e1'))
- re1.exists = return_true
- re1.is_derived = return_true
+ re1._func_exists = 5
+ re1._func_is_derived = 2
re2 = self.fs.Entry(os.path.join(self.rep2, 'e2'))
- re2.exists = return_true
- re2.is_derived = return_false
+ re2._func_exists = 5
+ re2._func_is_derived = 3
r = e1.rfile()
assert r is re1, r
@@ -3244,9 +3264,13 @@ class stored_infoTestCase(unittest.TestCase):
self.xyzzy = 7
def get_entry(self, name):
return self.Null()
+
+ def test_sconsign(node):
+ return MySConsign()
f = fs.File('file2', d)
- f.dir.sconsign = MySConsign
+ SCons.Node.FS._sconsign_map[2] = test_sconsign
+ f.dir._func_sconsign = 2
bi = f.get_stored_info()
assert bi.xyzzy == 7, bi
@@ -3360,7 +3384,7 @@ class prepareTestCase(unittest.TestCase):
xyz.set_state(0)
xyz.prepare()
- assert dir_made[0].path == "new_dir", dir_made[0]
+ assert dir_made[0].get_internal_path() == "new_dir", dir_made[0]
dir = fs.Dir("dir")
dir.prepare()
@@ -3373,7 +3397,7 @@ class SConstruct_dirTestCase(unittest.TestCase):
fs = SCons.Node.FS.FS()
fs.set_SConstruct_dir(fs.Dir('xxx'))
- assert fs.SConstruct_dir.path == 'xxx'
+ assert fs.SConstruct_dir.get_internal_path() == 'xxx'
@@ -3552,7 +3576,7 @@ class SpecialAttrTestCase(unittest.TestCase):
for_sig = f.suffix.for_signature()
assert for_sig == 'baz.blat_suffix', for_sig
- s = str(f.abspath)
+ s = str(f.get_abspath())
assert s == test.workpath('work', 'foo', 'bar', 'baz.blat'), s
assert f.abspath.is_literal(), f.abspath
for_sig = f.abspath.for_signature()
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index da502b0..a6471b4 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -184,7 +184,7 @@ class Scanner(object):
called = None
def __call__(self, node):
self.called = 1
- return node.found_includes
+ return node.GetTag('found_includes')
def path(self, env, dir, target=None, source=None):
return ()
def select(self, node):
@@ -200,7 +200,7 @@ class MyNode(SCons.Node.Node):
def __init__(self, name):
SCons.Node.Node.__init__(self)
self.name = name
- self.found_includes = []
+ self.Tag('found_includes', [])
def __str__(self):
return self.name
def get_found_includes(self, env, scanner, target):
@@ -224,11 +224,18 @@ class Calculator(object):
class NodeInfoBaseTestCase(unittest.TestCase):
+ # The abstract class NodeInfoBase has not enough default slots to perform
+ # the merge and format test (arbitrary attributes do not work). Do it with a
+ # derived class that does provide the slots.
def test_merge(self):
"""Test merging NodeInfoBase attributes"""
- ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
- ni2 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('a1', 'a2', 'a3')
+
+ ni1 = TestNodeInfo()
+ ni2 = TestNodeInfo()
ni1.a1 = 1
ni1.a2 = 2
@@ -237,27 +244,32 @@ class NodeInfoBaseTestCase(unittest.TestCase):
ni2.a3 = 333
ni1.merge(ni2)
- expect = {'a1':1, 'a2':222, 'a3':333, '_version_id':1}
- assert ni1.__dict__ == expect, ni1.__dict__
+ assert ni1.a1 == 1, ni1.a1
+ assert ni1.a2 == 222, ni1.a2
+ assert ni1.a3 == 333, ni1.a3
def test_update(self):
"""Test the update() method"""
- ni = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni = SCons.Node.NodeInfoBase()
ni.update(SCons.Node.Node())
def test_format(self):
"""Test the NodeInfoBase.format() method"""
- ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('xxx', 'yyy', 'zzz')
+
+ ni1 = TestNodeInfo()
ni1.xxx = 'x'
ni1.yyy = 'y'
ni1.zzz = 'z'
f = ni1.format()
- assert f == ['1', 'x', 'y', 'z'], f
+ assert f == ['x', 'y', 'z'], f
+
+ field_list = ['xxx', 'zzz', 'aaa']
- ni1.field_list = ['xxx', 'zzz', 'aaa']
-
- f = ni1.format()
+ f = ni1.format(field_list)
assert f == ['x', 'z', 'None'], f
@@ -267,26 +279,26 @@ class BuildInfoBaseTestCase(unittest.TestCase):
def test___init__(self):
"""Test BuildInfoBase initialization"""
n = SCons.Node.Node()
- bi = SCons.Node.BuildInfoBase(n)
+ bi = SCons.Node.BuildInfoBase()
assert bi
def test_merge(self):
"""Test merging BuildInfoBase attributes"""
n1 = SCons.Node.Node()
- bi1 = SCons.Node.BuildInfoBase(n1)
+ bi1 = SCons.Node.BuildInfoBase()
n2 = SCons.Node.Node()
- bi2 = SCons.Node.BuildInfoBase(n2)
+ bi2 = SCons.Node.BuildInfoBase()
- bi1.a1 = 1
- bi1.a2 = 2
+ bi1.bsources = 1
+ bi1.bdepends = 2
- bi2.a2 = 222
- bi2.a3 = 333
+ bi2.bdepends = 222
+ bi2.bact = 333
bi1.merge(bi2)
- assert bi1.a1 == 1, bi1.a1
- assert bi1.a2 == 222, bi1.a2
- assert bi1.a3 == 333, bi1.a3
+ assert bi1.bsources == 1, bi1.bsources
+ assert bi1.bdepends == 222, bi1.bdepends
+ assert bi1.bact == 333, bi1.bact
class NodeTestCase(unittest.TestCase):
@@ -427,6 +439,7 @@ class NodeTestCase(unittest.TestCase):
def test_built(self):
"""Test the built() method"""
class SubNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('updated',)
def update(self, node):
self.updated = 1
class SubNode(SCons.Node.Node):
@@ -434,7 +447,7 @@ class NodeTestCase(unittest.TestCase):
self.cleared = 1
n = SubNode()
- n.ninfo = SubNodeInfo(n)
+ n.ninfo = SubNodeInfo()
n.built()
assert n.cleared, n.cleared
assert n.ninfo.updated, n.ninfo.cleared
@@ -568,32 +581,56 @@ class NodeTestCase(unittest.TestCase):
def test_get_csig(self):
"""Test generic content signature calculation
"""
- node = SCons.Node.Node()
- node.get_contents = lambda: 444
- result = node.get_csig()
- assert result == '550a141f12de6341fba65b0ad0433500', result
+
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('csig',)
+ try:
+ SCons.Node.Node.NodeInfo = TestNodeInfo
+ def my_contents(obj):
+ return 444
+ SCons.Node._get_contents_map[4] = my_contents
+ node = SCons.Node.Node()
+ node._func_get_contents = 4
+ result = node.get_csig()
+ assert result == '550a141f12de6341fba65b0ad0433500', result
+ finally:
+ SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase
def test_get_cachedir_csig(self):
"""Test content signature calculation for CacheDir
"""
- node = SCons.Node.Node()
- node.get_contents = lambda: 555
- result = node.get_cachedir_csig()
- assert result == '15de21c670ae7c3f6f3f1f37029303c9', result
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('csig',)
+ try:
+ SCons.Node.Node.NodeInfo = TestNodeInfo
+ def my_contents(obj):
+ return 555
+ SCons.Node._get_contents_map[4] = my_contents
+ node = SCons.Node.Node()
+ node._func_get_contents = 4
+ result = node.get_cachedir_csig()
+ assert result == '15de21c670ae7c3f6f3f1f37029303c9', result
+ finally:
+ SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase
def test_get_binfo(self):
"""Test fetching/creating a build information structure
"""
+ class TestNodeInfo(SCons.Node.NodeInfoBase):
+ __slots__ = ('csig',)
+ SCons.Node.Node.NodeInfo = TestNodeInfo
node = SCons.Node.Node()
-
+
binfo = node.get_binfo()
assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
node = SCons.Node.Node()
d = SCons.Node.Node()
- d.get_ninfo().csig = 777
+ ninfo = d.get_ninfo()
+ assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo
i = SCons.Node.Node()
- i.get_ninfo().csig = 888
+ ninfo = i.get_ninfo()
+ assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo
node.depends = [d]
node.implicit = [i]
@@ -655,7 +692,7 @@ class NodeTestCase(unittest.TestCase):
"""Test calling the method to store build information
"""
node = SCons.Node.Node()
- node.store_info()
+ SCons.Node.store_info_map[node.store_info](node)
def test_get_stored_info(self):
"""Test calling the method to fetch stored build information
@@ -888,7 +925,7 @@ class NodeTestCase(unittest.TestCase):
s = Scanner()
d1 = MyNode("d1")
d2 = MyNode("d2")
- node.found_includes = [d1, d2]
+ node.Tag('found_includes', [d1, d2])
# Simple return of the found includes
deps = node.get_implicit_deps(env, s, target)
@@ -898,14 +935,14 @@ class NodeTestCase(unittest.TestCase):
e = MyNode("eee")
f = MyNode("fff")
g = MyNode("ggg")
- d1.found_includes = [e, f]
- d2.found_includes = [e, f]
- f.found_includes = [g]
+ d1.Tag('found_includes', [e, f])
+ d2.Tag('found_includes', [e, f])
+ f.Tag('found_includes', [g])
deps = node.get_implicit_deps(env, s, target)
assert deps == [d1, d2, e, f, g], list(map(str, deps))
# Recursive scanning eliminates duplicates
- e.found_includes = [f]
+ e.Tag('found_includes', [f])
deps = node.get_implicit_deps(env, s, target)
assert deps == [d1, d2, e, f, g], list(map(str, deps))
@@ -994,7 +1031,7 @@ class NodeTestCase(unittest.TestCase):
s = Scanner()
d = MyNode("ddd")
- node.found_includes = [d]
+ node.Tag('found_includes', [d])
node.builder.target_scanner = s
assert node.implicit is None
@@ -1207,7 +1244,7 @@ class NodeTestCase(unittest.TestCase):
def test_Annotate(self):
"""Test using an interface-specific Annotate function."""
def my_annotate(node, self=self):
- node.annotation = self.node_string
+ node.Tag('annotation', self.node_string)
save_Annotate = SCons.Node.Annotate
SCons.Node.Annotate = my_annotate
@@ -1215,11 +1252,13 @@ class NodeTestCase(unittest.TestCase):
try:
self.node_string = '#1'
n = SCons.Node.Node()
- assert n.annotation == '#1', n.annotation
+ a = n.GetTag('annotation')
+ assert a == '#1', a
self.node_string = '#2'
n = SCons.Node.Node()
- assert n.annotation == '#2', n.annotation
+ a = n.GetTag('annotation')
+ assert a == '#2', a
finally:
SCons.Node.Annotate = save_Annotate
@@ -1230,7 +1269,7 @@ class NodeTestCase(unittest.TestCase):
n.set_state(3)
n.binfo = 'xyz'
n.includes = 'testincludes'
- n.found_include = {'testkey':'testvalue'}
+ n.Tag('found_includes', {'testkey':'testvalue'})
n.implicit = 'testimplicit'
x = MyExecutor()
diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py
index 3d8bdaa..f151fc5 100644
--- a/src/engine/SCons/Node/Python.py
+++ b/src/engine/SCons/Node/Python.py
@@ -32,15 +32,49 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Node
class ValueNodeInfo(SCons.Node.NodeInfoBase):
- current_version_id = 1
+ __slots__ = ('csig',)
+ current_version_id = 2
field_list = ['csig']
def str_to_node(self, s):
return Value(s)
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
+
class ValueBuildInfo(SCons.Node.BuildInfoBase):
- current_version_id = 1
+ __slots__ = ()
+ current_version_id = 2
class Value(SCons.Node.Node):
"""A class for Python variables, typically passed on the command line
@@ -53,6 +87,8 @@ class Value(SCons.Node.Node):
def __init__(self, value, built_value=None):
SCons.Node.Node.__init__(self)
self.value = value
+ self.changed_since_last_build = 6
+ self.store_info = 0
if built_value is not None:
self.built_value = built_value
diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py
index fcdfe77..e2e36bf 100644
--- a/src/engine/SCons/Node/PythonTests.py
+++ b/src/engine/SCons/Node/PythonTests.py
@@ -104,13 +104,13 @@ class ValueNodeInfoTestCase(unittest.TestCase):
def test___init__(self):
"""Test ValueNodeInfo initialization"""
vvv = SCons.Node.Python.Value('vvv')
- ni = SCons.Node.Python.ValueNodeInfo(vvv)
+ ni = SCons.Node.Python.ValueNodeInfo()
class ValueBuildInfoTestCase(unittest.TestCase):
def test___init__(self):
"""Test ValueBuildInfo initialization"""
vvv = SCons.Node.Python.Value('vvv')
- bi = SCons.Node.Python.ValueBuildInfo(vvv)
+ bi = SCons.Node.Python.ValueBuildInfo()
if __name__ == "__main__":
suite = unittest.TestSuite()
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 1f62971..f2d37c2 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -55,6 +55,8 @@ import SCons.Util
from SCons.Debug import Trace
+print_duplicate = 0
+
def classname(obj):
return str(obj.__class__).split('.')[-1]
@@ -105,6 +107,233 @@ Annotate = do_nothing
# clean builds and update runs (see release_target_info).
interactive = False
+def is_derived_none(node):
+ raise NotImplementedError
+
+def is_derived_node(node):
+ """
+ Returns true if this node is derived (i.e. built).
+ """
+ return node.has_builder() or node.side_effect
+
+_is_derived_map = {0 : is_derived_none,
+ 1 : is_derived_node}
+
+def exists_none(node):
+ raise NotImplementedError
+
+def exists_always(node):
+ return 1
+
+def exists_base(node):
+ return node.stat() is not None
+
+def exists_entry(node):
+ """Return if the Entry exists. Check the file system to see
+ what we should turn into first. Assume a file if there's no
+ directory."""
+ node.disambiguate()
+ return _exists_map[node._func_exists](node)
+
+def exists_file(node):
+ # Duplicate from source path if we are set up to do this.
+ if node.duplicate and not node.is_derived() and not node.linked:
+ src = node.srcnode()
+ if src is not node:
+ # At this point, src is meant to be copied in a variant directory.
+ src = src.rfile()
+ if src.get_abspath() != node.get_abspath():
+ if src.exists():
+ node.do_duplicate(src)
+ # Can't return 1 here because the duplication might
+ # not actually occur if the -n option is being used.
+ else:
+ # The source file does not exist. Make sure no old
+ # copy remains in the variant directory.
+ if print_duplicate:
+ print "dup: no src for %s, unlinking old variant copy"%self
+ if exists_base(node) or node.islink():
+ node.fs.unlink(node.get_internal_path())
+ # Return None explicitly because the Base.exists() call
+ # above will have cached its value if the file existed.
+ return None
+ return exists_base(node)
+
+_exists_map = {0 : exists_none,
+ 1 : exists_always,
+ 2 : exists_base,
+ 3 : exists_entry,
+ 4 : exists_file}
+
+
+def rexists_none(node):
+ raise NotImplementedError
+
+def rexists_node(node):
+ return node.exists()
+
+def rexists_base(node):
+ return node.rfile().exists()
+
+_rexists_map = {0 : rexists_none,
+ 1 : rexists_node,
+ 2 : rexists_base}
+
+def get_contents_none(node):
+ raise NotImplementedError
+
+def get_contents_entry(node):
+ """Fetch the contents of the entry. Returns the exact binary
+ contents of the file."""
+ try:
+ node = node.disambiguate(must_exist=1)
+ except SCons.Errors.UserError:
+ # There was nothing on disk with which to disambiguate
+ # this entry. Leave it as an Entry, but return a null
+ # string so calls to get_contents() in emitters and the
+ # like (e.g. in qt.py) don't have to disambiguate by hand
+ # or catch the exception.
+ return ''
+ else:
+ return _get_contents_map[node._func_get_contents](node)
+
+def get_contents_dir(node):
+ """Return content signatures and names of all our children
+ separated by new-lines. Ensure that the nodes are sorted."""
+ contents = []
+ for n in sorted(node.children(), key=lambda t: t.name):
+ contents.append('%s %s\n' % (n.get_csig(), n.name))
+ return ''.join(contents)
+
+def get_contents_file(node):
+ if not node.rexists():
+ return ''
+ fname = node.rfile().get_abspath()
+ try:
+ contents = open(fname, "rb").read()
+ except EnvironmentError, e:
+ if not e.filename:
+ e.filename = fname
+ raise
+ return contents
+
+_get_contents_map = {0 : get_contents_none,
+ 1 : get_contents_entry,
+ 2 : get_contents_dir,
+ 3 : get_contents_file}
+
+def target_from_source_none(node, prefix, suffix, splitext):
+ raise NotImplementedError
+
+def target_from_source_base(node, prefix, suffix, splitext):
+ return node.dir.Entry(prefix + splitext(node.name)[0] + suffix)
+
+_target_from_source_map = {0 : target_from_source_none,
+ 1 : target_from_source_base}
+
+#
+# The new decider subsystem for Nodes
+#
+# We would set and overwrite the changed_since_last_build function
+# before, but for being able to use slots (less memory!) we now have
+# a dictionary of the different decider functions. Then in the Node
+# subclasses we simply store the index to the decider that should be
+# used by it.
+#
+
+#
+# First, the single decider functions
+#
+def changed_since_last_build_node(node, target, prev_ni):
+ """
+
+ Must be overridden in a specific subclass to return True if this
+ Node (a dependency) has changed since the last time it was used
+ to build the specified target. prev_ni is this Node's state (for
+ example, its file timestamp, length, maybe content signature)
+ as of the last time the target was built.
+
+ Note that this method is called through the dependency, not the
+ target, because a dependency Node must be able to use its own
+ logic to decide if it changed. For example, File Nodes need to
+ obey if we're configured to use timestamps, but Python Value Nodes
+ never use timestamps and always use the content. If this method
+ were called through the target, then each Node's implementation
+ of this method would have to have more complicated logic to
+ handle all the different Node types on which it might depend.
+ """
+ raise NotImplementedError
+
+def changed_since_last_build_alias(node, target, prev_ni):
+ cur_csig = node.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+def changed_since_last_build_entry(node, target, prev_ni):
+ node.disambiguate()
+ return _decider_map[node.changed_since_last_build](node, target, prev_ni)
+
+def changed_since_last_build_state_changed(node, target, prev_ni):
+ return (node.state != SCons.Node.up_to_date)
+
+def decide_source(node, target, prev_ni):
+ return target.get_build_env().decide_source(node, target, prev_ni)
+
+def decide_target(node, target, prev_ni):
+ return target.get_build_env().decide_target(node, target, prev_ni)
+
+def changed_since_last_build_python(node, target, prev_ni):
+ cur_csig = node.get_csig()
+ try:
+ return cur_csig != prev_ni.csig
+ except AttributeError:
+ return 1
+
+
+#
+# Now, the mapping from indices to decider functions
+#
+_decider_map = {0 : changed_since_last_build_node,
+ 1 : changed_since_last_build_alias,
+ 2 : changed_since_last_build_entry,
+ 3 : changed_since_last_build_state_changed,
+ 4 : decide_source,
+ 5 : decide_target,
+ 6 : changed_since_last_build_python}
+
+do_store_info = True
+
+#
+# The new store_info subsystem for Nodes
+#
+# We would set and overwrite the store_info function
+# before, but for being able to use slots (less memory!) we now have
+# a dictionary of the different functions. Then in the Node
+# subclasses we simply store the index to the info method that should be
+# used by it.
+#
+
+#
+# First, the single info functions
+#
+
+def store_info_pass(node):
+ pass
+
+def store_info_file(node):
+ # Merge our build information into the already-stored entry.
+ # This accommodates "chained builds" where a file that's a target
+ # in one build (SConstruct file) is a source in a different build.
+ # See test/chained-build.py for the use case.
+ if do_store_info:
+ node.dir.sconsign().store_info(node.name, node)
+
+
+store_info_map = {0 : store_info_pass,
+ 1 : store_info_file}
+
# Classes for signature info for Nodes.
class NodeInfoBase(object):
@@ -114,11 +343,8 @@ class NodeInfoBase(object):
Node subclasses should subclass NodeInfoBase to provide their own
logic for dealing with their own Node-specific signature information.
"""
- current_version_id = 1
- def __init__(self, node=None):
- # Create an object attribute from the class attribute so it ends up
- # in the pickled data in the .sconsign file.
- self._version_id = self.current_version_id
+ __slots__ = ('__weakref__',)
+ current_version_id = 2
def update(self, node):
try:
field_list = self.field_list
@@ -138,13 +364,25 @@ class NodeInfoBase(object):
def convert(self, node, val):
pass
def merge(self, other):
- self.__dict__.update(other.__dict__)
+ """
+ Merge the fields of another object into this object. Already existing
+ information is overwritten by the other instance's data.
+ WARNING: If a '__dict__' slot is added, it should be updated instead of
+ replaced.
+ """
+ state = other.__getstate__()
+ self.__setstate__(state)
def format(self, field_list=None, names=0):
if field_list is None:
try:
field_list = self.field_list
except AttributeError:
- field_list = sorted(self.__dict__.keys())
+ field_list = getattr(self, '__dict__', {}).keys()
+ for obj in type(self).mro():
+ for slot in getattr(obj, '__slots__', ()):
+ if slot not in ('__weakref__', '__dict__'):
+ field_list.append(slot)
+ field_list.sort()
fields = []
for field in field_list:
try:
@@ -157,6 +395,38 @@ class NodeInfoBase(object):
fields.append(f)
return fields
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state. The version is discarded.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
+
+
class BuildInfoBase(object):
"""
The generic base class for build information for a Node.
@@ -167,30 +437,106 @@ class BuildInfoBase(object):
generic build stuff we have to track: sources, explicit dependencies,
implicit dependencies, and action information.
"""
- current_version_id = 1
- def __init__(self, node=None):
+ __slots__ = ("bsourcesigs", "bdependsigs", "bimplicitsigs", "bactsig",
+ "bsources", "bdepends", "bact", "bimplicit", "__weakref__")
+ current_version_id = 2
+ def __init__(self):
# Create an object attribute from the class attribute so it ends up
# in the pickled data in the .sconsign file.
- self._version_id = self.current_version_id
self.bsourcesigs = []
self.bdependsigs = []
self.bimplicitsigs = []
self.bactsig = None
def merge(self, other):
- self.__dict__.update(other.__dict__)
+ """
+ Merge the fields of another object into this object. Already existing
+ information is overwritten by the other instance's data.
+ WARNING: If a '__dict__' slot is added, it should be updated instead of
+ replaced.
+ """
+ state = other.__getstate__()
+ self.__setstate__(state)
+
+ def __getstate__(self):
+ """
+ Return all fields that shall be pickled. Walk the slots in the class
+ hierarchy and add those to the state dictionary. If a '__dict__' slot is
+ available, copy all entries to the dictionary. Also include the version
+ id, which is fixed for all instances of a class.
+ """
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+ return state
+
+ def __setstate__(self, state):
+ """
+ Restore the attributes from a pickled state.
+ """
+ # TODO check or discard version
+ del state['_version_id']
+ for key, value in state.items():
+ if key not in ('__weakref__',):
+ setattr(self, key, value)
class Node(object):
"""The base Node class, for entities that we know how to
build, or use to build other Nodes.
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
+ __slots__ = ['sources',
+ 'sources_set',
+ '_specific_sources',
+ 'depends',
+ 'depends_set',
+ 'ignore',
+ 'ignore_set',
+ 'prerequisites',
+ 'implicit',
+ 'waiting_parents',
+ 'waiting_s_e',
+ 'ref_count',
+ 'wkids',
+ 'env',
+ 'state',
+ 'precious',
+ 'noclean',
+ 'nocache',
+ 'cached',
+ 'always_build',
+ 'includes',
+ 'attributes',
+ 'side_effect',
+ 'side_effects',
+ 'linked',
+ '_memo',
+ 'executor',
+ 'binfo',
+ 'ninfo',
+ 'builder',
+ 'is_explicit',
+ 'implicit_set',
+ 'changed_since_last_build',
+ 'store_info',
+ 'pseudo',
+ '_tags',
+ '_func_is_derived',
+ '_func_exists',
+ '_func_rexists',
+ '_func_get_contents',
+ '_func_target_from_source']
class Attrs(object):
- pass
+ __slots__ = ('shared', '__dict__')
+
def __init__(self):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.Node')
@@ -234,7 +580,15 @@ class Node(object):
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
self.linked = 0 # is this node linked to the variant directory?
-
+ self.changed_since_last_build = 0
+ self.store_info = 0
+ self._tags = None
+ self._func_is_derived = 1
+ self._func_exists = 1
+ self._func_rexists = 1
+ self._func_get_contents = 0
+ self._func_target_from_source = 0
+
self.clear_memoized_values()
# Let the interface in which the build engine is embedded
@@ -248,8 +602,7 @@ class Node(object):
def get_suffix(self):
return ''
- memoizer_counters.append(SCons.Memoize.CountValue('get_build_env'))
-
+ @SCons.Memoize.CountMethodCall
def get_build_env(self):
"""Fetch the appropriate Environment to build this node.
"""
@@ -418,7 +771,7 @@ class Node(object):
pass
else:
self.ninfo.update(self)
- self.store_info()
+ SCons.Node.store_info_map[self.store_info](self)
def release_target_info(self):
"""Called just after this node has been marked
@@ -546,7 +899,7 @@ class Node(object):
example: source with source builders are not derived in this sense,
and hence should not return true.
"""
- return self.has_builder() or self.side_effect
+ return _is_derived_map[self._func_is_derived](self)
def alter_targets(self):
"""Return a list of alternate targets for this Node.
@@ -706,7 +1059,7 @@ class Node(object):
BuildInfo = BuildInfoBase
def new_ninfo(self):
- ninfo = self.NodeInfo(self)
+ ninfo = self.NodeInfo()
return ninfo
def get_ninfo(self):
@@ -717,7 +1070,7 @@ class Node(object):
return self.ninfo
def new_binfo(self):
- binfo = self.BuildInfo(self)
+ binfo = self.BuildInfo()
return binfo
def get_binfo(self):
@@ -802,14 +1155,6 @@ class Node(object):
def get_cachedir_csig(self):
return self.get_csig()
- def store_info(self):
- """Make the build signature permanent (that is, store it in the
- .sconsign file or equivalent)."""
- pass
-
- def do_not_store_info(self):
- pass
-
def get_stored_info(self):
return None
@@ -847,13 +1192,16 @@ class Node(object):
def exists(self):
"""Does this node exists?"""
- # All node exist by default:
- return 1
+ return _exists_map[self._func_exists](self)
def rexists(self):
"""Does this node exist locally or in a repositiory?"""
# There are no repositories by default:
- return self.exists()
+ return _rexists_map[self._func_rexists](self)
+
+ def get_contents(self):
+ """Fetch the contents of the entry."""
+ return _get_contents_map[self._func_get_contents](self)
def missing(self):
return not self.is_derived() and \
@@ -941,11 +1289,10 @@ class Node(object):
# build info that it's cached so we can re-calculate it.
self.executor_cleanup()
- memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
-
+ @SCons.Memoize.CountMethodCall
def _children_get(self):
try:
- return self._memo['children_get']
+ return self._memo['_children_get']
except KeyError:
pass
@@ -976,7 +1323,7 @@ class Node(object):
else:
children = self.all_children(scan=0)
- self._memo['children_get'] = children
+ self._memo['_children_get'] = children
return children
def all_children(self, scan=1):
@@ -1016,9 +1363,6 @@ class Node(object):
def get_state(self):
return self.state
- def state_has_changed(self, target, prev_ni):
- return (self.state != SCons.Node.up_to_date)
-
def get_env(self):
env = self.env
if not env:
@@ -1026,28 +1370,28 @@ class Node(object):
env = SCons.Defaults.DefaultEnvironment()
return env
- def changed_since_last_build(self, target, prev_ni):
- """
-
- Must be overridden in a specific subclass to return True if this
- Node (a dependency) has changed since the last time it was used
- to build the specified target. prev_ni is this Node's state (for
- example, its file timestamp, length, maybe content signature)
- as of the last time the target was built.
-
- Note that this method is called through the dependency, not the
- target, because a dependency Node must be able to use its own
- logic to decide if it changed. For example, File Nodes need to
- obey if we're configured to use timestamps, but Python Value Nodes
- never use timestamps and always use the content. If this method
- were called through the target, then each Node's implementation
- of this method would have to have more complicated logic to
- handle all the different Node types on which it might depend.
- """
- raise NotImplementedError
-
def Decider(self, function):
- SCons.Util.AddMethod(self, function, 'changed_since_last_build')
+ foundkey = None
+ for k, v in _decider_map.iteritems():
+ if v == function:
+ foundkey = k
+ break
+ if not foundkey:
+ foundkey = len(_decider_map)
+ _decider_map[foundkey] = function
+ self.changed_since_last_build = foundkey
+
+ def Tag(self, key, value):
+ """ Add a user-defined tag. """
+ if not self._tags:
+ self._tags = {}
+ self._tags[key] = value
+
+ def GetTag(self, key):
+ """ Return a user-defined tag. """
+ if not self._tags:
+ return None
+ return self._tags.get(key, None)
def changed(self, node=None, allowcache=False):
"""
@@ -1095,7 +1439,7 @@ class Node(object):
result = True
for child, prev_ni in zip(children, then):
- if child.changed_since_last_build(self, prev_ni):
+ if _decider_map[child.changed_since_last_build](child, self, prev_ni):
if t: Trace(': %s changed' % child)
result = True
@@ -1266,7 +1610,7 @@ class Node(object):
for k in new_bkids:
if not k in old_bkids:
lines.append("`%s' is a new dependency\n" % stringify(k))
- elif k.changed_since_last_build(self, osig[k]):
+ elif _decider_map[k.changed_since_last_build](k, self, osig[k]):
lines.append("`%s' changed\n" % stringify(k))
if len(lines) == 0 and old_bkids != new_bkids:
diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py
index f3de57c..350e1ac 100644
--- a/src/engine/SCons/PathList.py
+++ b/src/engine/SCons/PathList.py
@@ -171,11 +171,6 @@ class PathListCache(object):
cheaply avoid re-parsing both values of CPPPATH by using the
common value from this cache.
"""
- if SCons.Memoize.use_memoizer:
- __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
- memoizer_counters = []
-
def __init__(self):
self._memo = {}
@@ -196,8 +191,7 @@ class PathListCache(object):
pathlist = tuple(SCons.Util.flatten(pathlist))
return pathlist
- memoizer_counters.append(SCons.Memoize.CountDict('PathList', _PathList_key))
-
+ @SCons.Memoize.CountDictCall(_PathList_key)
def PathList(self, pathlist):
"""
Returns the cached _PathList object for the specified pathlist,
diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py
index a012682..34c79ff 100644
--- a/src/engine/SCons/Platform/cygwin.py
+++ b/src/engine/SCons/Platform/cygwin.py
@@ -42,8 +42,8 @@ def generate(env):
env['PROGSUFFIX'] = '.exe'
env['SHLIBPREFIX'] = ''
env['SHLIBSUFFIX'] = '.dll'
- env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ]
- env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX', '$IMPLIBPREFIX' ]
+ env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX', '$IMPLIBSUFFIX' ]
env['TEMPFILE'] = TempFileMunge
env['TEMPFILEPREFIX'] = '@'
env['MAXLINELENGTH'] = 2048
diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py
index 87432ef..8bce8ce 100644
--- a/src/engine/SCons/SConf.py
+++ b/src/engine/SCons/SConf.py
@@ -175,8 +175,11 @@ class SConfBuildInfo(SCons.Node.FS.FileBuildInfo):
are result (did the builder succeed last time?) and string, which
contains messages of the original build phase.
"""
- result = None # -> 0/None -> no error, != 0 error
- string = None # the stdout / stderr output when building the target
+ __slots__ = ('result', 'string')
+
+ def __init__(self):
+ self.result = None # -> 0/None -> no error, != 0 error
+ self.string = None # the stdout / stderr output when building the target
def set_build_result(self, result, string):
self.result = result
@@ -352,8 +355,10 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code)
except Exception, e:
for t in self.targets:
- binfo = t.get_binfo()
- binfo.__class__ = SConfBuildInfo
+ #binfo = t.get_binfo()
+ #binfo.__class__ = SConfBuildInfo
+ binfo = SConfBuildInfo()
+ binfo.merge(t.get_binfo())
binfo.set_build_result(1, s.getvalue())
sconsign_entry = SCons.SConsign.SConsignEntry()
sconsign_entry.binfo = binfo
@@ -370,8 +375,10 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
raise e
else:
for t in self.targets:
- binfo = t.get_binfo()
- binfo.__class__ = SConfBuildInfo
+ #binfo = t.get_binfo()
+ #binfo.__class__ = SConfBuildInfo
+ binfo = SConfBuildInfo()
+ binfo.merge(t.get_binfo())
binfo.set_build_result(0, s.getvalue())
sconsign_entry = SCons.SConsign.SConsignEntry()
sconsign_entry.binfo = binfo
@@ -500,7 +507,7 @@ class SConfBase(object):
# we override the store_info() method with a null place-holder
# so we really control how it gets written.
for n in nodes:
- n.store_info = n.do_not_store_info
+ n.store_info = 0
if not hasattr(n, 'attributes'):
n.attributes = SCons.Node.Node.Attrs()
n.attributes.keep_targetinfo = 1
@@ -640,7 +647,7 @@ class SConfBase(object):
ok = self.TryLink(text, extension)
if( ok ):
prog = self.lastTarget
- pname = prog.path
+ pname = prog.get_internal_path()
output = self.confdir.File(os.path.basename(pname)+'.out')
node = self.env.Command(output, prog, [ [ pname, ">", "${TARGET}"] ])
ok = self.BuildNodes(node)
@@ -684,7 +691,6 @@ class SConfBase(object):
else:
if not os.path.isdir( dirName ):
os.makedirs( dirName )
- node._exists = 1
def _startup(self):
"""Private method. Set up logstream, and set the environment
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
index 1c4b401..233ee78 100644
--- a/src/engine/SCons/SConfTests.py
+++ b/src/engine/SCons/SConfTests.py
@@ -221,8 +221,6 @@ class SConfTestCase(unittest.TestCase):
pass
def get_stored_info(self):
pass
- def do_not_store_info(self):
- pass
def get_executor(self):
class Executor(object):
def __init__(self, targets):
diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py
index 6555fcb..74ee804 100644
--- a/src/engine/SCons/SConsign.py
+++ b/src/engine/SCons/SConsign.py
@@ -122,16 +122,40 @@ class SConsignEntry(object):
XXX As coded below, we do expect a '.binfo' attribute to be added,
but we'll probably generalize this in the next refactorings.
"""
- current_version_id = 1
+ __slots__ = ("binfo", "ninfo", "__weakref__")
+ current_version_id = 2
+
def __init__(self):
# Create an object attribute from the class attribute so it ends up
# in the pickled data in the .sconsign file.
- _version_id = self.current_version_id
+ #_version_id = self.current_version_id
+ pass
+
def convert_to_sconsign(self):
self.binfo.convert_to_sconsign()
+
def convert_from_sconsign(self, dir, name):
self.binfo.convert_from_sconsign(dir, name)
+ def __getstate__(self):
+ state = getattr(self, '__dict__', {}).copy()
+ for obj in type(self).mro():
+ for name in getattr(obj,'__slots__',()):
+ if hasattr(self, name):
+ state[name] = getattr(self, name)
+
+ state['_version_id'] = self.current_version_id
+ try:
+ del state['__weakref__']
+ except KeyError:
+ pass
+ return state
+
+ def __setstate__(self, state):
+ for key, value in state.items():
+ if key not in ('_version_id','__weakref__'):
+ setattr(self, key, value)
+
class Base(object):
"""
This is the controlling class for the signatures for the collection of
@@ -202,7 +226,7 @@ class DB(Base):
# Read using the path relative to the top of the Repository
# (self.dir.tpath) from which we're fetching the signature
# information.
- path = normcase(dir.tpath)
+ path = normcase(dir.get_tpath())
try:
rawentries = db[path]
except KeyError:
@@ -217,7 +241,7 @@ class DB(Base):
raise
except Exception, e:
SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
- "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.tpath, e))
+ "Ignoring corrupt sconsign entry : %s (%s)\n"%(self.dir.get_tpath(), e))
for key, entry in self.entries.items():
entry.convert_from_sconsign(dir, key)
@@ -244,7 +268,7 @@ class DB(Base):
# directory (self.dir.path), not relative to the top of
# the Repository; we only write to our own .sconsign file,
# not to .sconsign files in Repositories.
- path = normcase(self.dir.path)
+ path = normcase(self.dir.get_internal_path())
for key, entry in self.entries.items():
entry.convert_to_sconsign()
db[path] = pickle.dumps(self.entries, 1)
@@ -287,7 +311,7 @@ class DirFile(Dir):
"""
self.dir = dir
- self.sconsign = os.path.join(dir.path, '.sconsign')
+ self.sconsign = os.path.join(dir.get_internal_path(), '.sconsign')
try:
fp = open(self.sconsign, 'rb')
@@ -323,7 +347,7 @@ class DirFile(Dir):
self.merge()
- temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
+ temp = os.path.join(self.dir.get_internal_path(), '.scons%d' % os.getpid())
try:
file = open(temp, 'wb')
fname = temp
diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py
index f71e53e..d40a7b6 100644
--- a/src/engine/SCons/SConsignTests.py
+++ b/src/engine/SCons/SConsignTests.py
@@ -62,6 +62,10 @@ class DummyNode(object):
return self.binfo
def get_binfo(self):
return self.binfo
+ def get_internal_path(self):
+ return self.path
+ def get_tpath(self):
+ return self.tpath
class SConsignTestCase(unittest.TestCase):
def setUp(self):
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index 6418754..9c7df12 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -270,17 +270,18 @@ class CScannerTestCase5(unittest.TestCase):
path = s.path(env)
n = env.File('f3.cpp')
- def my_rexists(s=n):
- s.rexists_called = 1
- return s.old_rexists()
- setattr(n, 'old_rexists', n.rexists)
- setattr(n, 'rexists', my_rexists)
+ def my_rexists(s):
+ s.Tag('rexists_called', 1)
+ return SCons.Node._rexists_map[s.GetTag('old_rexists')](s)
+ n.Tag('old_rexists', n._func_rexists)
+ SCons.Node._rexists_map[3] = my_rexists
+ n._func_rexists = 3
deps = s(n, env, path)
# Make sure rexists() got called on the file node being
# scanned, essential for cooperation with VariantDir functionality.
- assert n.rexists_called
+ assert n.GetTag('rexists_called')
headers = ['f1.h', 'f2.h', 'f3-test.h',
'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h']
diff --git a/src/engine/SCons/Scanner/Dir.py b/src/engine/SCons/Scanner/Dir.py
index 1cecfb7..cbfb6fb 100644
--- a/src/engine/SCons/Scanner/Dir.py
+++ b/src/engine/SCons/Scanner/Dir.py
@@ -77,7 +77,7 @@ def scan_on_disk(node, env, path=()):
that and then call the in-memory scanning function.
"""
try:
- flist = node.fs.listdir(node.abspath)
+ flist = node.fs.listdir(node.get_abspath())
except (IOError, OSError):
return []
e = node.Entry
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
index 252da64..aaefa79 100644
--- a/src/engine/SCons/Scanner/FortranTests.py
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -356,17 +356,18 @@ class FortranScannerTestCase9(unittest.TestCase):
path = s.path(env)
n = env.File('fff3.f')
- def my_rexists(s=n):
- s.rexists_called = 1
- return s.old_rexists()
- setattr(n, 'old_rexists', n.rexists)
- setattr(n, 'rexists', my_rexists)
+ def my_rexists(s):
+ s.Tag('rexists_called', 1)
+ return SCons.Node._rexists_map[s.GetTag('old_rexists')](s)
+ n.Tag('old_rexists', n._func_rexists)
+ SCons.Node._rexists_map[3] = my_rexists
+ n._func_rexists = 3
deps = s(n, env, path)
# Make sure rexists() got called on the file node being
# scanned, essential for cooperation with VariantDir functionality.
- assert n.rexists_called
+ assert n.GetTag('rexists_called')
headers = ['d1/f3.f', 'f3.f']
deps_match(self, deps, headers)
diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py
index 675c70c..227799e 100644
--- a/src/engine/SCons/Scanner/IDLTests.py
+++ b/src/engine/SCons/Scanner/IDLTests.py
@@ -290,17 +290,18 @@ class IDLScannerTestCase5(unittest.TestCase):
path = s.path(env)
n = env.File('t3.idl')
- def my_rexists(s=n):
- s.rexists_called = 1
- return s.old_rexists()
- setattr(n, 'old_rexists', n.rexists)
- setattr(n, 'rexists', my_rexists)
+ def my_rexists(s):
+ s.Tag('rexists_called', 1)
+ return SCons.Node._rexists_map[s.GetTag('old_rexists')](s)
+ n.Tag('old_rexists', n._func_rexists)
+ SCons.Node._rexists_map[3] = my_rexists
+ n._func_rexists = 3
deps = s(n, env, path)
# Make sure rexists() got called on the file node being
# scanned, essential for cooperation with VariantDir functionality.
- assert n.rexists_called
+ assert n.GetTag('rexists_called')
headers = ['d1/f1.idl', 'd1/f2.idl',
'f1.idl', 'f2.idl', 'f3-test.idl',
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
index c7a9d27..bd227bd 100644
--- a/src/engine/SCons/Script/Main.py
+++ b/src/engine/SCons/Script/Main.py
@@ -214,7 +214,7 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask):
if self.top and not t.has_builder() and not t.side_effect:
if not t.exists():
if t.__class__.__name__ in ('File', 'Dir', 'Entry'):
- errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.abspath)
+ errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.get_abspath())
else: # Alias or Python or ...
errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t)
sys.stderr.write("scons: *** " + errstr)
@@ -351,7 +351,7 @@ class CleanTask(SCons.Taskmaster.AlwaysTask):
if target in SCons.Environment.CleanTargets:
files = SCons.Environment.CleanTargets[target]
for f in files:
- self.fs_delete(f.abspath, str(f), remove)
+ self.fs_delete(f.get_abspath(), str(f), remove)
def show(self):
for t in self._get_files_to_clean():
@@ -484,6 +484,9 @@ def GetOption(name):
def SetOption(name, value):
return OptionsParser.values.set_option(name, value)
+def PrintHelp(file=None):
+ OptionsParser.print_help(file=file)
+
#
class Stats(object):
def __init__(self):
@@ -672,7 +675,7 @@ def _set_debug_values(options):
if "prepare" in debug_values:
SCons.Taskmaster.print_prepare = 1
if "duplicate" in debug_values:
- SCons.Node.FS.print_duplicate = 1
+ SCons.Node.print_duplicate = 1
def _create_path(plist):
path = '.'
@@ -946,9 +949,9 @@ def _main(parser):
progress_display.set_mode(0)
if options.site_dir:
- _load_site_scons_dir(d.path, options.site_dir)
+ _load_site_scons_dir(d.get_internal_path(), options.site_dir)
elif not options.no_site_dir:
- _load_all_site_scons_dirs(d.path)
+ _load_all_site_scons_dirs(d.get_internal_path())
if options.include_dir:
sys.path = options.include_dir + sys.path
@@ -1111,7 +1114,6 @@ def _build_targets(fs, options, targets, target_top):
display.set_mode(not options.silent)
SCons.Action.print_actions = not options.silent
SCons.Action.execute_actions = not options.no_exec
- SCons.Node.FS.do_store_info = not options.no_exec
SCons.Node.do_store_info = not options.no_exec
SCons.SConf.dryrun = options.no_exec
diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py
index 8ecc093..68c60cc 100644
--- a/src/engine/SCons/Script/SConsOptions.py
+++ b/src/engine/SCons/Script/SConsOptions.py
@@ -268,7 +268,7 @@ class SConsOptionParser(optparse.OptionParser):
preserve_unknown_options = False
def error(self, msg):
- # overriden OptionValueError exception handler
+ # overridden OptionValueError exception handler
self.print_usage(sys.stderr)
sys.stderr.write("SCons Error: %s\n" % msg)
sys.exit(2)
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index f4a7f07..b832ff7 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -265,7 +265,7 @@ def _SConscript(fs, *files, **kw):
call_stack[-1].globals.update({__file__:old_file})
else:
SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
- "Ignoring missing SConscript '%s'" % f.path)
+ "Ignoring missing SConscript '%s'" % f.get_internal_path())
finally:
SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1
@@ -438,7 +438,7 @@ class SConsEnvironment(SCons.Environment.Base):
fname = fn.get_path(src_dir)
files = [os.path.join(str(variant_dir), fname)]
else:
- files = [fn.abspath]
+ files = [fn.get_abspath()]
kw['src_dir'] = variant_dir
self.fs.VariantDir(variant_dir, src_dir, duplicate)
@@ -499,9 +499,9 @@ class SConsEnvironment(SCons.Environment.Base):
name = self.subst(name)
return SCons.Script.Main.GetOption(name)
- def Help(self, text):
+ def Help(self, text, append=False):
text = self.subst(text, raw=1)
- SCons.Script.HelpFunction(text)
+ SCons.Script.HelpFunction(text, append=append)
def Import(self, *vars):
try:
diff --git a/src/engine/SCons/Script/SConscript.xml b/src/engine/SCons/Script/SConscript.xml
index c74ad5e..8553fbe 100644
--- a/src/engine/SCons/Script/SConscript.xml
+++ b/src/engine/SCons/Script/SConscript.xml
@@ -240,7 +240,7 @@ file is found.
<scons_function name="Help">
<arguments>
-(text)
+(text, append=False)
</arguments>
<summary>
<para>
@@ -248,12 +248,18 @@ This specifies help text to be printed if the
<option>-h</option>
argument is given to
&scons;.
-If
+If
&f-Help;
-is called multiple times, the text is appended together in the order
-that
+is called multiple times, the text is appended together in the order that
&f-Help;
-is called.
+is called. With append set to False, any
+&f-Help;
+text generated with
+&f-AddOption;
+is clobbered. If append is True, the AddOption help is prepended to the help
+string, thus preserving the
+<option>-h</option>
+message.
</para>
</summary>
</scons_function>
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index bb7b632..6bfc36f 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -41,6 +41,7 @@ start_time = time.time()
import collections
import os
+import StringIO
import sys
# Special chicken-and-egg handling of the "--debug=memoizer" flag:
@@ -107,6 +108,7 @@ QuestionTask = Main.QuestionTask
#SConscriptSettableOptions = Main.SConscriptSettableOptions
AddOption = Main.AddOption
+PrintHelp = Main.PrintHelp
GetOption = Main.GetOption
SetOption = Main.SetOption
Progress = Main.Progress
@@ -258,12 +260,25 @@ def _Set_Default_Targets(env, tlist):
#
help_text = None
-def HelpFunction(text):
+def HelpFunction(text, append=False):
global help_text
- if SCons.Script.help_text is None:
- SCons.Script.help_text = text
- else:
- help_text = help_text + text
+ if help_text is None:
+ if append:
+ s = StringIO.StringIO()
+ PrintHelp(s)
+ help_text = s.getvalue()
+ s.close()
+ else:
+ help_text = ""
+#
+# Was in original patch but this text is arbitrary and breaks tests
+# so I removed it (Deegan)
+# help_text = help_text + "\nLocal Build Variables:\n" + text
+# else:
+# help_text = help_text + text
+
+ help_text= help_text + text
+
#
# Will be non-zero if we are reading an SConscript file.
@@ -318,6 +333,7 @@ GlobalDefaultEnvironmentFunctions = [
'Ignore',
'Install',
'InstallAs',
+ 'InstallVersionedLib',
'Literal',
'Local',
'ParseDepends',
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
index 5de1cda..345534e 100644
--- a/src/engine/SCons/Taskmaster.py
+++ b/src/engine/SCons/Taskmaster.py
@@ -242,7 +242,7 @@ class Task(object):
#
for t in cached_targets:
try:
- t.fs.unlink(t.path)
+ t.fs.unlink(t.get_internal_path())
except (IOError, OSError):
pass
self.targets[0].build()
diff --git a/src/engine/SCons/Tool/MSCommon/arch.py b/src/engine/SCons/Tool/MSCommon/arch.py
index 2c90950..9bcc221 100644
--- a/src/engine/SCons/Tool/MSCommon/arch.py
+++ b/src/engine/SCons/Tool/MSCommon/arch.py
@@ -51,6 +51,12 @@ SupportedArchitectureList = [
'ia64',
['IA64'],
),
+
+ ArchitectureDefinition(
+ 'arm',
+ ['ARM'],
+ ),
+
]
SupportedArchitectureMap = {}
diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py
index 97cb349..0ee6324 100644
--- a/src/engine/SCons/Tool/MSCommon/vc.py
+++ b/src/engine/SCons/Tool/MSCommon/vc.py
@@ -134,9 +134,13 @@ def get_host_target(env):
# If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the
# MSVC_VERSION documentation in Tool/msvc.xml.
-_VCVER = ["12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"]
+_VCVER = ["14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"]
_VCVER_TO_PRODUCT_DIR = {
+ '14.0' : [
+ r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir'],
+ '14.0Exp' : [
+ r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir'],
'12.0' : [
r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'],
'12.0Exp' : [
diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py
index 2ec403b..d9eb1e3 100644
--- a/src/engine/SCons/Tool/MSCommon/vs.py
+++ b/src/engine/SCons/Tool/MSCommon/vs.py
@@ -203,6 +203,28 @@ class VisualStudio(object):
# Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml.
SupportedVSList = [
+ # Visual Studio 2015
+ VisualStudio('14.0',
+ vc_version='14.0',
+ sdk_version='10.0A',
+ hkeys=[r'Microsoft\VisualStudio\14.0\Setup\VS\ProductDir'],
+ common_tools_var='VS140COMNTOOLS',
+ executable_path=r'Common7\IDE\devenv.com',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ supported_arch=['x86', 'amd64', "arm"],
+ ),
+
+ # Visual C++ 2015 Express Edition (for Desktop)
+ VisualStudio('14.0Exp',
+ vc_version='14.0',
+ sdk_version='10.0A',
+ hkeys=[r'Microsoft\VisualStudio\14.0\Setup\VS\ProductDir'],
+ common_tools_var='VS140COMNTOOLS',
+ executable_path=r'Common7\IDE\WDExpress.exe',
+ batch_file_path=r'Common7\Tools\vsvars32.bat',
+ supported_arch=['x86', 'amd64', "arm"],
+ ),
+
# Visual Studio 2013
VisualStudio('12.0',
vc_version='12.0',
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index 2621cf9..7374687 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -236,150 +236,436 @@ def createStaticLibBuilder(env):
return static_lib
-def VersionShLibLinkNames(version, libname, env):
- """Generate names of symlinks to the versioned shared library"""
+def _call_linker_cb(env, callback, args, result = None):
+ """Returns the result of env['LINKCALLBACKS'][callback](*args)
+ if env['LINKCALLBACKS'] is a dictionary and env['LINKCALLBACKS'][callback]
+ is callable. If these conditions are not met, return the value provided as
+ the *result* argument. This function is mainly used for generating library
+ info such as versioned suffixes, symlink maps, sonames etc. by delegating
+ the core job to callbacks configured by current linker tool"""
+
Verbose = False
- platform = env.subst('$PLATFORM')
- shlib_suffix = env.subst('$SHLIBSUFFIX')
- shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS'))
-
- linknames = []
- if version.count(".") != 2:
- # We need a version string of the form x.y.z to proceed
- # Several changes need to be made to support versions like x.y
- raise ValueError
-
- if platform == 'darwin':
- # For libfoo.x.y.z.dylib, linknames libfoo.so
- suffix_re = re.escape('.' + version + shlib_suffix)
- linkname = re.sub(suffix_re, shlib_suffix, libname)
+
+ if Verbose:
+ print '_call_linker_cb: args=%r' % args
+ print '_call_linker_cb: callback=%r' % callback
+
+ try:
+ cbfun = env['LINKCALLBACKS'][callback]
+ except (KeyError, TypeError):
if Verbose:
- print "VersionShLibLinkNames: linkname = ",linkname
- linknames.append(linkname)
- elif platform == 'posix' or platform == 'sunos':
- if sys.platform.startswith('openbsd'):
- # OpenBSD uses x.y shared library versioning numbering convention
- # and doesn't use symlinks to backwards-compatible libraries
- return []
- # For libfoo.so.x.y.z, linknames libfoo.so libfoo.so.x.y libfoo.so.x
- suffix_re = re.escape(shlib_suffix + '.' + version)
- # First linkname has no version number
- linkname = re.sub(suffix_re, shlib_suffix, libname)
+ print '_call_linker_cb: env["LINKCALLBACKS"][%r] not found or can not be used' % callback
+ pass
+ else:
if Verbose:
- print "VersionShLibLinkNames: linkname = ",linkname
- linknames.append(linkname)
- versionparts = version.split('.')
- major_name = linkname + "." + versionparts[0]
- minor_name = major_name + "." + versionparts[1]
- #Only add link for major_name
- #for linkname in [major_name, minor_name]:
- for linkname in [major_name, ]:
+ print '_call_linker_cb: env["LINKCALLBACKS"][%r] found' % callback
+ print '_call_linker_cb: env["LINKCALLBACKS"][%r]=%r' % (callback, cbfun)
+ if(callable(cbfun)):
if Verbose:
- print "VersionShLibLinkNames: linkname ",linkname, ", target ",libname
- linknames.append(linkname)
- # note: no Windows case here (win32 or cygwin);
- # MSVC doesn't support this type of versioned shared libs.
- # (could probably do something for MinGW though)
- return linknames
-
-def VersionedSharedLibrary(target = None, source= None, env=None):
- """Build a shared library. If the environment has SHLIBVERSION
-defined make a versioned shared library and create the appropriate
-symlinks for the platform we are on"""
- Verbose = False
- try:
- version = env.subst('$SHLIBVERSION')
- except KeyError:
- version = None
+ print '_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback
+ result = cbfun(env, *args)
+ return result
- # libname includes the version number if one was given
- libname = getattr(target[0].attributes, 'shlibname', target[0].name)
- platform = env.subst('$PLATFORM')
- shlib_suffix = env.subst('$SHLIBSUFFIX')
- shlink_flags = SCons.Util.CLVar(env.subst('$SHLINKFLAGS'))
- if Verbose:
- print "VersionShLib: libname = ",libname
- print "VersionShLib: platform = ",platform
- print "VersionShLib: shlib_suffix = ",shlib_suffix
- print "VersionShLib: target = ",str(target[0])
-
- if version:
- # set the shared library link flags
- if platform == 'posix':
- shlink_flags += [ '-Wl,-Bsymbolic' ]
- # OpenBSD doesn't usually use SONAME for libraries
- if not sys.platform.startswith('openbsd'):
- # continue setup of shlink flags for all other POSIX systems
- suffix_re = re.escape(shlib_suffix + '.' + version)
- (major, age, revision) = version.split(".")
- # soname will have only the major version number in it
- soname = re.sub(suffix_re, shlib_suffix, libname) + '.' + major
- shlink_flags += [ '-Wl,-soname=%s' % soname ]
- if Verbose:
- print " soname ",soname,", shlink_flags ",shlink_flags
- elif platform == 'sunos':
- suffix_re = re.escape(shlib_suffix + '.' + version)
- (major, age, revision) = version.split(".")
- soname = re.sub(suffix_re, shlib_suffix, libname) + '.' + major
- shlink_flags += [ '-h', soname ]
- elif platform == 'cygwin':
- shlink_flags += [ '-Wl,-Bsymbolic',
- '-Wl,--out-implib,${TARGET.base}.a' ]
- elif platform == 'darwin':
- shlink_flags += [ '-current_version', '%s' % version,
- '-compatibility_version', '%s' % version,
- '-undefined', 'dynamic_lookup' ]
+def _call_env_subst(env, string, *args, **kw):
+ kw2 = {}
+ for k in ('raw', 'target', 'source', 'conv', 'executor'):
+ try: kw2[k] = kw[k]
+ except KeyError: pass
+ return env.subst(string, *args, **kw2)
+
+class _ShLibInfoSupport(object):
+ def get_libtype(self):
+ return 'ShLib'
+ def get_lib_prefix(self, env, *args, **kw):
+ return _call_env_subst(env,'$SHLIBPREFIX', *args, **kw)
+ def get_lib_suffix(self, env, *args, **kw):
+ return _call_env_subst(env,'$SHLIBSUFFIX', *args, **kw)
+ def get_lib_version(self, env, *args, **kw):
+ return _call_env_subst(env,'$SHLIBVERSION', *args, **kw)
+ def get_lib_noversionsymlinks(self, env, *args, **kw):
+ return _call_env_subst(env,'$SHLIBNOVERSIONSYMLINKS', *args, **kw)
+
+class _LdModInfoSupport(object):
+ def get_libtype(self):
+ return 'LdMod'
+ def get_lib_prefix(self, env, *args, **kw):
+ return _call_env_subst(env,'$LDMODULEPREFIX', *args, **kw)
+ def get_lib_suffix(self, env, *args, **kw):
+ return _call_env_subst(env,'$LDMODULESUFFIX', *args, **kw)
+ def get_lib_version(self, env, *args, **kw):
+ return _call_env_subst(env,'$LDMODULEVERSION', *args, **kw)
+ def get_lib_noversionsymlinks(self, env, *args, **kw):
+ return _call_env_subst(env,'$LDMODULENOVERSIONSYMLINKS', *args, **kw)
+
+class _ImpLibInfoSupport(object):
+ def get_libtype(self):
+ return 'ImpLib'
+ def get_lib_prefix(self, env, *args, **kw):
+ return _call_env_subst(env,'$IMPLIBPREFIX', *args, **kw)
+ def get_lib_suffix(self, env, *args, **kw):
+ return _call_env_subst(env,'$IMPLIBSUFFIX', *args, **kw)
+ def get_lib_version(self, env, *args, **kw):
+ version = _call_env_subst(env,'$IMPLIBVERSION', *args, **kw)
+ if not version:
+ try: lt = kw['implib_libtype']
+ except KeyError: pass
+ else:
+ if lt == 'ShLib':
+ version = _call_env_subst(env,'$SHLIBVERSION', *args, **kw)
+ elif lt == 'LdMod':
+ version = _call_env_subst(env,'$LDMODULEVERSION', *args, **kw)
+ return version
+ def get_lib_noversionsymlinks(self, env, *args, **kw):
+ disable = None
+ try: env['IMPLIBNOVERSIONSYMLINKS']
+ except KeyError:
+ try: lt = kw['implib_libtype']
+ except KeyError: pass
+ else:
+ if lt == 'ShLib':
+ disable = _call_env_subst(env,'$SHLIBNOVERSIONSYMLINKS', *args, **kw)
+ elif lt == 'LdMod':
+ disable = _call_env_subst(env,'$LDMODULENOVERSIONSYMLINKS', *args, **kw)
+ else:
+ disable = _call_env_subst(env,'$IMPLIBNOVERSIONSYMLINKS', *args, **kw)
+ return disable
+
+class _LibInfoGeneratorBase(object):
+ """Generator base class for library-related info such as suffixes for
+ versioned libraries, symlink maps, sonames etc. It handles commonities
+ of SharedLibrary and LoadableModule
+ """
+ _support_classes = { 'ShLib' : _ShLibInfoSupport,
+ 'LdMod' : _LdModInfoSupport,
+ 'ImpLib' : _ImpLibInfoSupport }
+ def __init__(self, libtype, infoname):
+ self.set_libtype(libtype)
+ self.set_infoname(infoname)
+
+ def set_libtype(self, libtype):
+ try:
+ support_class = self._support_classes[libtype]
+ except KeyError:
+ raise ValueError('unsupported libtype %r' % libtype)
+ self._support = support_class()
+
+ def get_libtype(self):
+ return self._support.get_libtype()
+
+ def set_infoname(self, infoname):
+ self.infoname = infoname
+
+ def get_infoname(self):
+ return self.infoname
+
+ def get_lib_prefix(self, env, *args, **kw):
+ return self._support.get_lib_prefix(env,*args,**kw)
+
+ def get_lib_suffix(self, env, *args, **kw):
+ return self._support.get_lib_suffix(env,*args,**kw)
+
+ def get_lib_version(self, env, *args, **kw):
+ return self._support.get_lib_version(env,*args,**kw)
+
+ def get_lib_noversionsymlinks(self, env, *args, **kw):
+ return self._support.get_lib_noversionsymlinks(env,*args,**kw)
+
+ # Returns name of generator linker callback that shall be used to generate
+ # our info for a versioned library. For example, if our libtype is 'ShLib'
+ # and infoname is 'Prefix', it would return 'VersionedShLibPrefix'.
+ def get_versioned_lib_info_generator(self, **kw):
+ try: libtype = kw['generator_libtype']
+ except KeyError: libtype = self.get_libtype()
+ infoname = self.get_infoname()
+ return 'Versioned%s%s' % (libtype, infoname)
+
+ def generate_versioned_lib_info(self, env, args, result = None, **kw):
+ callback = self.get_versioned_lib_info_generator(**kw)
+ return _call_linker_cb(env, callback, args, result)
+
+class _LibPrefixGenerator(_LibInfoGeneratorBase):
+ """Library prefix generator, used as target_prefix in SharedLibrary and
+ LoadableModule builders"""
+ def __init__(self, libtype):
+ super(_LibPrefixGenerator, self).__init__(libtype, 'Prefix')
+
+ def __call__(self, env, sources = None, **kw):
+ Verbose = False
+
+ if sources and 'source' not in kw:
+ kw2 = kw.copy()
+ kw2['source'] = sources
+ else:
+ kw2 = kw
+
+ prefix = self.get_lib_prefix(env,**kw2)
if Verbose:
- print "VersionShLib: shlink_flags = ",shlink_flags
- envlink = env.Clone()
- envlink['SHLINKFLAGS'] = shlink_flags
- else:
- envlink = env
+ print "_LibPrefixGenerator: input prefix=%r" % prefix
+
+ version = self.get_lib_version(env, **kw2)
+ if Verbose:
+ print "_LibPrefixGenerator: version=%r" % version
- result = SCons.Defaults.ShLinkAction(target, source, envlink)
+ if version:
+ prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw2)
- if version:
- # here we need the full pathname so the links end up in the right directory
- libname = getattr(target[0].attributes, 'shlibpath', target[0].path)
if Verbose:
- print "VerShLib: target lib is = ", libname
- print "VerShLib: name is = ", target[0].name
- print "VerShLib: dir is = ", target[0].dir.path
- linknames = VersionShLibLinkNames(version, libname, env)
+ print "_LibPrefixGenerator: return prefix=%r" % prefix
+ return prefix
+
+ShLibPrefixGenerator = _LibPrefixGenerator('ShLib')
+LdModPrefixGenerator = _LibPrefixGenerator('LdMod')
+ImpLibPrefixGenerator = _LibPrefixGenerator('ImpLib')
+
+class _LibSuffixGenerator(_LibInfoGeneratorBase):
+ """Library suffix generator, used as target_suffix in SharedLibrary and
+ LoadableModule builders"""
+ def __init__(self, libtype):
+ super(_LibSuffixGenerator, self).__init__(libtype, 'Suffix')
+
+ def __call__(self, env, sources = None, **kw):
+ Verbose = False
+
+ if sources and 'source' not in kw:
+ kw2 = kw.copy()
+ kw2['source'] = sources
+ else:
+ kw2 = kw
+
+ suffix = self.get_lib_suffix(env, **kw2)
if Verbose:
- print "VerShLib: linknames ",linknames
- # Here we just need the file name w/o path as the target of the link
- lib_ver = getattr(target[0].attributes, 'shlibname', target[0].name)
- # make symlink of adjacent names in linknames
- for count in range(len(linknames)):
- linkname = linknames[count]
- if count > 0:
- try:
- os.remove(lastlinkname)
- except:
- pass
- os.symlink(os.path.basename(linkname),lastlinkname)
- if Verbose:
- print "VerShLib: made sym link of %s -> %s" % (lastlinkname,linkname)
- lastlinkname = linkname
- # finish chain of sym links with link to the actual library
- if len(linknames)>0:
+ print "_LibSuffixGenerator: input suffix=%r" % suffix
+
+ version = self.get_lib_version(env, **kw2)
+ if Verbose:
+ print "_LibSuffixGenerator: version=%r" % version
+
+ if version:
+ suffix = self.generate_versioned_lib_info(env, [suffix, version], suffix, **kw2)
+
+ if Verbose:
+ print "_LibSuffixGenerator: return suffix=%r" % suffix
+ return suffix
+
+ShLibSuffixGenerator = _LibSuffixGenerator('ShLib')
+LdModSuffixGenerator = _LibSuffixGenerator('LdMod')
+ImpLibSuffixGenerator = _LibSuffixGenerator('ImpLib')
+
+class _LibSymlinkGenerator(_LibInfoGeneratorBase):
+ """Library symlink map generator. It generates a list of symlinks that
+ should be created by SharedLibrary or LoadableModule builders"""
+ def __init__(self, libtype):
+ super(_LibSymlinkGenerator, self).__init__(libtype, 'Symlinks')
+
+ def __call__(self, env, libnode, **kw):
+ Verbose = False
+
+ if libnode and 'target' not in kw:
+ kw2 = kw.copy()
+ kw2['target'] = libnode
+ else:
+ kw2 = kw
+
+ if Verbose:
+ print "_LibSymLinkGenerator: libnode=%r" % libnode.get_path()
+
+ symlinks = None
+
+ version = self.get_lib_version(env, **kw2)
+ disable = self.get_lib_noversionsymlinks(env, **kw2)
+ if Verbose:
+ print '_LibSymlinkGenerator: version=%r' % version
+ print '_LibSymlinkGenerator: disable=%r' % disable
+
+ if version and not disable:
+ prefix = self.get_lib_prefix(env,**kw2)
+ suffix = self.get_lib_suffix(env,**kw2)
+ symlinks = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2)
+
+ if Verbose:
+ print '_LibSymlinkGenerator: return symlinks=%r' % StringizeLibSymlinks(symlinks)
+ return symlinks
+
+ShLibSymlinkGenerator = _LibSymlinkGenerator('ShLib')
+LdModSymlinkGenerator = _LibSymlinkGenerator('LdMod')
+ImpLibSymlinkGenerator = _LibSymlinkGenerator('ImpLib')
+
+class _LibNameGenerator(_LibInfoGeneratorBase):
+ """Generates "unmangled" library name from a library file node.
+
+ Generally, it's thought to revert modifications done by prefix/suffix
+ generators (_LibPrefixGenerator/_LibSuffixGenerator) used by a library
+ builder. For example, on gnulink the suffix generator used by SharedLibrary
+ builder appends $SHLIBVERSION to $SHLIBSUFFIX producing node name which
+ ends with "$SHLIBSUFFIX.$SHLIBVERSION". Correspondingly, the implementation
+ of _LibNameGenerator replaces "$SHLIBSUFFIX.$SHLIBVERSION" with
+ "$SHLIBSUFFIX" in the node's basename. So that, if $SHLIBSUFFIX is ".so",
+ $SHLIBVERSION is "0.1.2" and the node path is "/foo/bar/libfoo.so.0.1.2",
+ the _LibNameGenerator shall return "libfoo.so". Other link tools may
+ implement it's own way of library name unmangling.
+ """
+ def __init__(self, libtype):
+ super(_LibNameGenerator, self).__init__(libtype, 'Name')
+
+ def __call__(self, env, libnode, **kw):
+ """Returns "demangled" library name"""
+ Verbose = False
+
+ if libnode and 'target' not in kw:
+ kw2 = kw.copy()
+ kw2['target'] = libnode
+ else:
+ kw2 = kw
+
+ if Verbose:
+ print "_LibNameGenerator: libnode=%r" % libnode.get_path()
+
+ version = self.get_lib_version(env, **kw2)
+ if Verbose:
+ print '_LibNameGenerator: version=%r' % version
+
+ name = None
+ if version:
+ prefix = self.get_lib_prefix(env,**kw2)
+ suffix = self.get_lib_suffix(env,**kw2)
+ name = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2)
+
+ if not name:
+ name = os.path.basename(libnode.get_path())
+
+ if Verbose:
+ print '_LibNameGenerator: return name=%r' % name
+
+ return name
+
+ShLibNameGenerator = _LibNameGenerator('ShLib')
+LdModNameGenerator = _LibNameGenerator('LdMod')
+ImpLibNameGenerator = _LibNameGenerator('ImpLib')
+
+class _LibSonameGenerator(_LibInfoGeneratorBase):
+ """Library soname generator. Returns library soname (e.g. libfoo.so.0) for
+ a given node (e.g. /foo/bar/libfoo.so.0.1.2)"""
+ def __init__(self, libtype):
+ super(_LibSonameGenerator, self).__init__(libtype, 'Soname')
+
+ def __call__(self, env, libnode, **kw):
+ """Returns a SONAME based on a shared library's node path"""
+ Verbose = False
+
+ if libnode and 'target' not in kw:
+ kw2 = kw.copy()
+ kw2['target'] = libnode
+ else:
+ kw2 = kw
+
+ if Verbose:
+ print "_LibSonameGenerator: libnode=%r" % libnode.get_path()
+
+ soname = _call_env_subst(env, '$SONAME', **kw2)
+ if not soname:
+ version = self.get_lib_version(env,**kw2)
+ if Verbose:
+ print "_LibSonameGenerator: version=%r" % version
+ if version:
+ prefix = self.get_lib_prefix(env,**kw2)
+ suffix = self.get_lib_suffix(env,**kw2)
+ soname = self.generate_versioned_lib_info(env, [libnode, version, prefix, suffix], **kw2)
+
+ if not soname:
+ # fallback to library name (as returned by appropriate _LibNameGenerator)
+ soname = _LibNameGenerator(self.get_libtype())(env, libnode)
+ if Verbose:
+ print "_LibSonameGenerator: FALLBACK: soname=%r" % soname
+
+ if Verbose:
+ print "_LibSonameGenerator: return soname=%r" % soname
+
+ return soname
+
+ShLibSonameGenerator = _LibSonameGenerator('ShLib')
+LdModSonameGenerator = _LibSonameGenerator('LdMod')
+
+def StringizeLibSymlinks(symlinks):
+ """Converts list with pairs of nodes to list with pairs of node paths
+ (strings). Used mainly for debugging."""
+ if SCons.Util.is_List(symlinks):
+ try:
+ return [ (k.get_path(), v.get_path()) for k,v in symlinks ]
+ except (TypeError, ValueError):
+ return symlinks
+ else:
+ return symlinks
+
+def EmitLibSymlinks(env, symlinks, libnode, **kw):
+ """Used by emitters to handle (shared/versioned) library symlinks"""
+ Verbose = False
+
+ # nodes involved in process... all symlinks + library
+ nodes = list(set([ x for x,y in symlinks ] + [libnode]))
+
+ clean_targets = kw.get('clean_targets', [])
+ if not SCons.Util.is_List(clean_targets):
+ clean_targets = [ clean_targets ]
+
+ for link, linktgt in symlinks:
+ env.SideEffect(link, linktgt)
+ if(Verbose):
+ print "EmitLibSymlinks: SideEffect(%r,%r)" % (link.get_path(), linktgt.get_path())
+ clean_list = filter(lambda x : x != linktgt, nodes)
+ env.Clean(list(set([linktgt] + clean_targets)), clean_list)
+ if(Verbose):
+ print "EmitLibSymlinks: Clean(%r,%r)" % (linktgt.get_path(), map(lambda x : x.get_path(), clean_list))
+
+def CreateLibSymlinks(env, symlinks):
+ """Physically creates symlinks. The symlinks argument must be a list in
+ form [ (link, linktarget), ... ], where link and linktarget are SCons
+ nodes.
+ """
+
+ Verbose = False
+ for link, linktgt in symlinks:
+ linktgt = link.get_dir().rel_path(linktgt)
+ link = link.get_path()
+ if(Verbose):
+ print "CreateLibSymlinks: preparing to add symlink %r -> %r" % (link, linktgt)
+ # Delete the (previously created) symlink if exists. Let only symlinks
+ # to be deleted to prevent accidental deletion of source files...
+ if env.fs.islink(link):
+ env.fs.unlink(link)
+ if(Verbose):
+ print "CreateLibSymlinks: removed old symlink %r" % link
+ # If a file or directory exists with the same name as link, an OSError
+ # will be thrown, which should be enough, I think.
+ env.fs.symlink(linktgt, link)
+ if(Verbose):
+ print "CreateLibSymlinks: add symlink %r -> %r" % (link, linktgt)
+ return 0
+
+def LibSymlinksActionFunction(target, source, env):
+ for tgt in target:
+ symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None)
+ if symlinks:
+ CreateLibSymlinks(env, symlinks)
+ return 0
+
+def LibSymlinksStrFun(target, source, env, *args):
+ cmd = None
+ for tgt in target:
+ symlinks = getattr(getattr(tgt,'attributes', None), 'shliblinks', None)
+ if symlinks:
+ if cmd is None: cmd = ""
+ if cmd: cmd += "\n"
+ cmd += "Create symlinks for: %r" % tgt.get_path()
try:
- os.remove(lastlinkname)
- except:
+ linkstr = ', '.join([ "%r->%r" %(k,v) for k,v in StringizeLibSymlinks(symlinks)])
+ except (KeyError, ValueError):
pass
- os.symlink(lib_ver,lastlinkname)
- if Verbose:
- print "VerShLib: made sym link of %s -> %s" % (linkname, lib_ver)
- return result
+ else:
+ cmd += ": %s" % linkstr
+ return cmd
+
-# Fix http://scons.tigris.org/issues/show_bug.cgi?id=2903 :
-# Ensure we still depend on SCons.Defaults.ShLinkAction command line which is $SHLINKCOM.
-# This was tricky because we don't want changing LIBPATH to cause a rebuild, but
-# changing other link args should. LIBPATH has $( ... $) around it but until this
-# fix, when the varlist was added to the build sig those ignored parts weren't getting
-# ignored.
-ShLibAction = SCons.Action.Action(VersionedSharedLibrary, None, varlist=['SHLINKCOM'])
+LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun)
def createSharedLibBuilder(env):
"""This is a utility function that creates the SharedLibrary
@@ -393,11 +679,12 @@ def createSharedLibBuilder(env):
except KeyError:
import SCons.Defaults
action_list = [ SCons.Defaults.SharedCheck,
- ShLibAction ]
+ SCons.Defaults.ShLinkAction,
+ LibSymlinksAction ]
shared_lib = SCons.Builder.Builder(action = action_list,
emitter = "$SHLIBEMITTER",
- prefix = '$SHLIBPREFIX',
- suffix = '$SHLIBSUFFIX',
+ prefix = ShLibPrefixGenerator,
+ suffix = ShLibSuffixGenerator,
target_scanner = ProgramScanner,
src_suffix = '$SHOBJSUFFIX',
src_builder = 'SharedObject')
@@ -417,11 +704,12 @@ def createLoadableModuleBuilder(env):
except KeyError:
import SCons.Defaults
action_list = [ SCons.Defaults.SharedCheck,
- SCons.Defaults.LdModuleLinkAction ]
+ SCons.Defaults.LdModuleLinkAction,
+ LibSymlinksAction ]
ld_module = SCons.Builder.Builder(action = action_list,
emitter = "$LDMODULEEMITTER",
- prefix = '$LDMODULEPREFIX',
- suffix = '$LDMODULESUFFIX',
+ prefix = LdModPrefixGenerator,
+ suffix = LdModSuffixGenerator,
target_scanner = ProgramScanner,
src_suffix = '$SHOBJSUFFIX',
src_builder = 'SharedObject')
diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml
index ee56fc3..69cc597 100644
--- a/src/engine/SCons/Tool/__init__.xml
+++ b/src/engine/SCons/Tool/__init__.xml
@@ -200,22 +200,20 @@ For maximum portability, use the &b-LoadableModule; builder for the latter.
When the &cv-link-SHLIBVERSION; construction variable is defined a versioned
shared library is created. This modifies the &cv-link-SHLINKFLAGS; as required,
adds the version number to the library name, and creates the symlinks that
-are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z, where X
-and Y are numbers, and Z is a number but can also contain letters to designate
-alpha, beta, or release candidate patch levels.
+are needed.
</para>
<example_commands>
env.SharedLibrary(target = 'bar', source = ['bar.c', 'foo.o'], SHLIBVERSION='1.5.2')
</example_commands>
-
<para>
-This builder may create multiple links to the library. On a POSIX system,
-for the shared library libbar.so.2.3.1, the links created would be
-libbar.so and libbar.so.2; on a Darwin (OSX) system
-the library would be libbar.2.3.1.dylib and the link would be
-libbar.dylib.
+On a POSIX system, versions with a single token create exactly one symlink:
+libbar.so.6 would have symlinks libbar.so only.
+On a POSIX system, versions with two or more
+tokens create exactly two symlinks: libbar.so.2.3.1 would have symlinks
+libbar.so and libbar.so.2; on a Darwin (OSX) system the library would be
+libbar.2.3.1.dylib and the link would be libbar.dylib.
</para>
<para>
@@ -464,6 +462,17 @@ as C++ files.
</summary>
</cvar>
+<cvar name="IMPLIBVERSION">
+<summary>
+<para>
+Used to override &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; when
+generating versioned import library for a shared library/loadable module. If
+undefined, the &cv-link-SHLIBVERSION;/&cv-link-LDMODULEVERSION; is used to
+determine the version of versioned import library.
+</para>
+</summary>
+</cvar>
+
<cvar name="LIBEMITTER">
<summary>
<para>
@@ -472,6 +481,19 @@ TODO
</summary>
</cvar>
+<cvar name="LDMODULEVERSION">
+<summary>
+<para>
+When this construction variable is defined, a versioned loadable module
+is created by &b-link-LoadableModule; builder. This activates the
+&cv-link-_LDMODULEVERSIONFLAGS; and thus modifies the &cv-link-LDMODULECOM; as
+required, adds the version number to the library name, and creates the symlinks
+that are needed. &cv-link-LDMODULEVERSION; versions should exist in the same
+format as &cv-link-SHLIBVERSION;.
+</para>
+</summary>
+</cvar>
+
<cvar name="SHLIBEMITTER">
<summary>
<para>
@@ -492,11 +514,12 @@ TODO
<summary>
<para>
When this construction variable is defined, a versioned shared library
-is created. This modifies the &cv-link-SHLINKFLAGS; as required, adds
-the version number to the library name, and creates the symlinks that
-are needed. &cv-link-SHLIBVERSION; needs to be of the form X.Y.Z,
-where X and Y are numbers, and Z is a number but can also contain
-letters to designate alpha, beta, or release candidate patch levels.
+is created by &b-link-SharedLibrary; builder. This activates the
+&cv-link-_SHLIBVERSIONFLAGS; and thus modifies the &cv-link-SHLINKCOM; as
+required, adds the version number to the library name, and creates the symlinks
+that are needed. &cv-link-SHLIBVERSION; versions should exist as alpha-numeric,
+decimal-delimited values as defined by the regular expression "\w+[\.\w+]*".
+Example &cv-link-SHLIBVERSION; values include '1', '1.2.3', and '1.2.gitaa412c8b'.
</para>
</summary>
</cvar>
diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py
index 87716cf..5230910 100644
--- a/src/engine/SCons/Tool/cyglink.py
+++ b/src/engine/SCons/Tool/cyglink.py
@@ -7,19 +7,27 @@ It will usually be imported through the generic SCons.Tool.Tool()
selection method.
"""
+import re
+import os
import SCons.Action
import SCons.Util
+import SCons.Tool
import gnulink
+import link
-def shlib_generator(target, source, env, for_signature):
- cmd = SCons.Util.CLVar(['$SHLINK'])
+def _lib_generator(target, source, env, for_signature, **kw):
+ try: cmd = kw['cmd']
+ except KeyError: cmd = SCons.Util.CLVar(['$SHLINK'])
+
+ try: vp = kw['varprefix']
+ except KeyError: vp = 'SHLIB'
- dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp)
if dll: cmd.extend(['-o', dll])
- cmd.extend(['$SHLINKFLAGS', '$__RPATH'])
+ cmd.extend(['$SHLINKFLAGS', '$__%sVERSIONFLAGS' % vp, '$__RPATH'])
implib = env.FindIxes(target, 'IMPLIBPREFIX', 'IMPLIBSUFFIX')
if implib:
@@ -35,37 +43,141 @@ def shlib_generator(target, source, env, for_signature):
return [cmd]
-def shlib_emitter(target, source, env):
- dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+
+def shlib_generator(target, source, env, for_signature):
+ return _lib_generator(target, source, env, for_signature,
+ varprefix='SHLIB',
+ cmd = SCons.Util.CLVar(['$SHLINK']))
+
+def ldmod_generator(target, source, env, for_signature):
+ return _lib_generator(target, source, env, for_signature,
+ varprefix='LDMODULE',
+ cmd = SCons.Util.CLVar(['$LDMODULE']))
+
+def _lib_emitter(target, source, env, **kw):
+ Verbose = False
+
+ if Verbose:
+ print "_lib_emitter: target[0]=%r" % target[0].get_path()
+
+ try: vp = kw['varprefix']
+ except KeyError: vp = 'SHLIB'
+
+ try: libtype = kw['libtype']
+ except KeyError: libtype = 'ShLib'
+
+ dll = env.FindIxes(target, '%sPREFIX' % vp, '%sSUFFIX' % vp)
no_import_lib = env.get('no_import_lib', 0)
+ if Verbose:
+ print "_lib_emitter: dll=%r" % dll.get_path()
+
if not dll or len(target) > 1:
- raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX"))
+ raise SCons.Errors.UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$%sSUFFIX" % vp))
# Remove any "lib" after the prefix
- pre = env.subst('$SHLIBPREFIX')
+ pre = env.subst('$%sPREFIX' % vp)
if dll.name[len(pre):len(pre)+3] == 'lib':
dll.name = pre + dll.name[len(pre)+3:]
+ if Verbose:
+ print "_lib_emitter: dll.name=%r" % dll.name
+
orig_target = target
target = [env.fs.File(dll)]
target[0].attributes.shared = 1
+ if Verbose:
+ print "_lib_emitter: after target=[env.fs.File(dll)]: target[0]=%r" % target[0].get_path()
+
# Append an import lib target
if not no_import_lib:
# Create list of target libraries as strings
target_strings = env.ReplaceIxes(orig_target[0],
- 'SHLIBPREFIX', 'SHLIBSUFFIX',
+ '%sPREFIX' % vp, '%sSUFFIX' % vp,
'IMPLIBPREFIX', 'IMPLIBSUFFIX')
+ if Verbose:
+ print "_lib_emitter: target_strings=%r" % target_strings
implib_target = env.fs.File(target_strings)
+ if Verbose:
+ print "_lib_emitter: implib_target=%r" % implib_target.get_path()
implib_target.attributes.shared = 1
target.append(implib_target)
+ symlinks = SCons.Tool.ImpLibSymlinkGenerator(env, implib_target,
+ implib_libtype=libtype,
+ generator_libtype=libtype+'ImpLib')
+ if Verbose:
+ print "_lib_emitter: implib symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)
+ if symlinks:
+ SCons.Tool.EmitLibSymlinks(env, symlinks, implib_target, clean_targets = target[0])
+ implib_target.attributes.shliblinks = symlinks
+
return (target, source)
+
+def shlib_emitter(target, source, env):
+ return _lib_emitter(target, source, env, varprefix='SHLIB', libtype='ShLib')
+
+def ldmod_emitter(target, source, env):
+ return _lib_emitter(target, source, env, varprefix='LDMODULE', libtype='LdMod')
+def _versioned_lib_suffix(env, suffix, version):
+ """Generate versioned shared library suffix from a unversioned one.
+ If suffix='.dll', and version='0.1.2', then it returns '-0-1-2.dll'"""
+ Verbose = False
+ if Verbose:
+ print "_versioned_lib_suffix: suffix= ", suffix
+ print "_versioned_lib_suffix: version= ", version
+ cygversion = re.sub('\.', '-', version)
+ if not suffix.startswith('-' + cygversion):
+ suffix = '-' + cygversion + suffix
+ if Verbose:
+ print "_versioned_lib_suffix: return suffix= ", suffix
+ return suffix
+
+def _versioned_implib_name(env, libnode, version, prefix, suffix, **kw):
+ return link._versioned_lib_name(env, libnode, version, prefix, suffix,
+ SCons.Tool.ImpLibPrefixGenerator,
+ SCons.Tool.ImpLibSuffixGenerator,
+ implib_libtype=kw['libtype'])
+
+def _versioned_implib_symlinks(env, libnode, version, prefix, suffix, **kw):
+ """Generate link names that should be created for a versioned shared lirbrary.
+ Returns a list in the form [ (link, linktarget), ... ]
+ """
+ Verbose = False
+
+ if Verbose:
+ print "_versioned_implib_symlinks: libnode=%r" % libnode.get_path()
+ print "_versioned_implib_symlinks: version=%r" % version
+
+ try: libtype = kw['libtype']
+ except KeyError: libtype = 'ShLib'
+
+
+ linkdir = os.path.dirname(libnode.get_path())
+ if Verbose:
+ print "_versioned_implib_symlinks: linkdir=%r" % linkdir
+
+ name = SCons.Tool.ImpLibNameGenerator(env, libnode,
+ implib_libtype=libtype,
+ generator_libtype=libtype+'ImpLib')
+ if Verbose:
+ print "_versioned_implib_symlinks: name=%r" % name
+
+ major = version.split('.')[0]
+
+ link0 = env.fs.File(os.path.join(linkdir, name))
+ symlinks = [(link0, libnode)]
+
+ if Verbose:
+ print "_versioned_implib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)
+
+ return symlinks
shlib_action = SCons.Action.Action(shlib_generator, generator=1)
+ldmod_action = SCons.Action.Action(ldmod_generator, generator=1)
def generate(env):
"""Add Builders and construction variables for cyglink to an Environment."""
@@ -74,8 +186,9 @@ def generate(env):
env['LINKFLAGS'] = SCons.Util.CLVar('-Wl,-no-undefined')
env['SHLINKCOM'] = shlib_action
- env['LDMODULECOM'] = shlib_action
+ env['LDMODULECOM'] = ldmod_action
env.Append(SHLIBEMITTER = [shlib_emitter])
+ env.Append(LDMODULEEMITTER = [ldmod_emitter])
env['SHLIBPREFIX'] = 'cyg'
env['SHLIBSUFFIX'] = '.dll'
@@ -83,6 +196,31 @@ def generate(env):
env['IMPLIBPREFIX'] = 'lib'
env['IMPLIBSUFFIX'] = '.dll.a'
+ # Variables used by versioned shared libraries
+ env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS'
+ env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS'
+
+ # SHLIBVERSIONFLAGS and LDMODULEVERSIONFLAGS are same as in gnulink...
+
+ # LINKCALLBACKS are NOT inherited from gnulink
+ env['LINKCALLBACKS'] = {
+ 'VersionedShLibSuffix' : _versioned_lib_suffix,
+ 'VersionedLdModSuffix' : _versioned_lib_suffix,
+ 'VersionedImpLibSuffix' : _versioned_lib_suffix,
+ 'VersionedShLibName' : link._versioned_shlib_name,
+ 'VersionedLdModName' : link._versioned_ldmod_name,
+ 'VersionedShLibImpLibName' : lambda *args: _versioned_implib_name(*args, libtype='ShLib'),
+ 'VersionedLdModImpLibName' : lambda *args: _versioned_implib_name(*args, libtype='LdMod'),
+ 'VersionedShLibImpLibSymlinks' : lambda *args: _versioned_implib_symlinks(*args, libtype='ShLib'),
+ 'VersionedLdModImpLibSymlinks' : lambda *args: _versioned_implib_symlinks(*args, libtype='LdMod'),
+ }
+
+ # these variables were set by gnulink but are not used in cyglink
+ try: del env['_SHLIBSONAME']
+ except KeyError: pass
+ try: del env['_LDMODULESONAME']
+ except KeyError: pass
+
def exists(env):
return gnulink.exists(env)
diff --git a/src/engine/SCons/Tool/cyglink.xml b/src/engine/SCons/Tool/cyglink.xml
new file mode 100644
index 0000000..42208f1
--- /dev/null
+++ b/src/engine/SCons/Tool/cyglink.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+__COPYRIGHT__
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+
+<!DOCTYPE sconsdoc [
+<!ENTITY % scons SYSTEM '../../../../doc/scons.mod'>
+%scons;
+<!ENTITY % builders-mod SYSTEM '../../../../doc/generated/builders.mod'>
+%builders-mod;
+<!ENTITY % functions-mod SYSTEM '../../../../doc/generated/functions.mod'>
+%functions-mod;
+<!ENTITY % tools-mod SYSTEM '../../../../doc/generated/tools.mod'>
+%tools-mod;
+<!ENTITY % variables-mod SYSTEM '../../../../doc/generated/variables.mod'>
+%variables-mod;
+]>
+
+<sconsdoc xmlns="http://www.scons.org/dbxsd/v1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">
+
+<tool name="cyglink">
+<summary>
+<para>
+Set construction variables for cygwin linker/loader.
+</para>
+</summary>
+<sets>
+<item>IMPLIBPREFIX</item>
+<item>IMPLIBSUFFIX</item>
+<item>LDMODULEVERSIONFLAGS</item>
+<item>LINKFLAGS</item>
+<item>RPATHPREFIX</item>
+<item>RPATHSUFFIX</item>
+<item>SHLIBPREFIX</item>
+<item>SHLIBSUFFIX</item>
+<item>SHLIBVERSIONFLAGS</item>
+<item>SHLINKCOM</item>
+<item>SHLINKFLAGS</item>
+<item>_LDMODULEVERSIONFLAGS</item>
+<item>_SHLIBVERSIONFLAGS</item>
+</sets>
+</tool>
+
+</sconsdoc>
diff --git a/src/engine/SCons/Tool/dmd.py b/src/engine/SCons/Tool/dmd.py
index a7d46c6..3722936 100644
--- a/src/engine/SCons/Tool/dmd.py
+++ b/src/engine/SCons/Tool/dmd.py
@@ -114,7 +114,7 @@ def generate(env):
env['DSHLINK'] = '$DC'
env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=libphobos2.so')
- env['SHDLINKCOM'] = '$DLINK -of$TARGET $DSHLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS'
+ env['SHDLINKCOM'] = '$DLINK -of$TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS'
env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l'
env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else ''
@@ -139,6 +139,17 @@ def generate(env):
env['DRPATHSUFFIX'] = ''
env['_DRPATH'] = '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}'
+ # Support for versioned libraries
+ env['_DSHLIBVERSIONFLAGS'] = '$DSHLIBVERSIONFLAGS -L-soname=$_DSHLIBSONAME'
+ env['_DSHLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}'
+ # NOTE: this is a quick hack, the soname will only work if there is
+ # c/c++ linker loaded which provides callback for the ShLibSonameGenerator
+ env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator
+ # NOTE: this is only for further reference, currently $DSHLIBVERSION does
+ # not work, the user must use $SHLIBVERSION
+ env['DSHLIBVERSION'] = '$SHLIBVERSION'
+ env['DSHLIBVERSIONFLAGS'] = []
+
SCons.Tool.createStaticLibBuilder(env)
diff --git a/src/engine/SCons/Tool/docbook/__init__.py b/src/engine/SCons/Tool/docbook/__init__.py
index 26a1a95..aead43c 100644
--- a/src/engine/SCons/Tool/docbook/__init__.py
+++ b/src/engine/SCons/Tool/docbook/__init__.py
@@ -448,7 +448,7 @@ def DocbookEpub(env, target, source=None, *args, **kw):
Ensure all the resources in the manifest are present in the OEBPS directory.
"""
hrefs = []
- content_file = os.path.join(source[0].abspath, 'content.opf')
+ content_file = os.path.join(source[0].get_abspath(), 'content.opf')
if not os.path.isfile(content_file):
return
@@ -491,9 +491,9 @@ def DocbookEpub(env, target, source=None, *args, **kw):
for href in hrefs:
# If the resource was not already created by DocBook XSL itself,
# copy it into the OEBPS folder
- referenced_file = os.path.join(source[0].abspath, href)
+ referenced_file = os.path.join(source[0].get_abspath(), href)
if not os.path.exists(referenced_file):
- shutil.copy(href, os.path.join(source[0].abspath, href))
+ shutil.copy(href, os.path.join(source[0].get_abspath(), href))
# Init list of targets/sources
target, source = __extend_targets_sources(target, source)
diff --git a/src/engine/SCons/Tool/gdc.py b/src/engine/SCons/Tool/gdc.py
index 799c3ab..32199b3 100644
--- a/src/engine/SCons/Tool/gdc.py
+++ b/src/engine/SCons/Tool/gdc.py
@@ -97,7 +97,7 @@ def generate(env):
env['DSHLINK'] = '$DC'
env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared')
- env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['SHDLINKCOM'] = '$DLINK -o $TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr'
env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLINKLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '')
@@ -115,6 +115,17 @@ def generate(env):
env['RPATHSUFFIX'] = ''
env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}'
+ # Support for versioned libraries
+ env['_DSHLIBVERSIONFLAGS'] = '$DSHLIBVERSIONFLAGS -Wl,-soname=$_DSHLIBSONAME'
+ env['_DSHLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}'
+ # NOTE: this is a quick hack, the soname will only work if there is
+ # c/c++ linker loaded which provides callback for the ShLibSonameGenerator
+ env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator
+ # NOTE: this is only for further reference, currently $DSHLIBVERSION does
+ # not work, the user must use $SHLIBVERSION
+ env['DSHLIBVERSION'] = '$SHLIBVERSION'
+ env['DSHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS'
+
SCons.Tool.createStaticLibBuilder(env)
diff --git a/src/engine/SCons/Tool/gettext.xml b/src/engine/SCons/Tool/gettext.xml
index 96e467a..f9f8b81 100644
--- a/src/engine/SCons/Tool/gettext.xml
+++ b/src/engine/SCons/Tool/gettext.xml
@@ -27,7 +27,7 @@ See its __doc__ string for a discussion of the format.
<summary>
<para>
This is actually a toolset, which supports internationalization and
-localization of sofware being constructed with SCons. The toolset loads
+localization of software being constructed with SCons. The toolset loads
following tools:
</para>
diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py
index 3dc8f51..6b0d5b3 100644
--- a/src/engine/SCons/Tool/gnulink.py
+++ b/src/engine/SCons/Tool/gnulink.py
@@ -34,9 +34,14 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Util
+import SCons.Tool
+import os
+import sys
+import re
import link
+
def generate(env):
"""Add Builders and construction variables for gnulink to an Environment."""
link.generate(env)
@@ -49,6 +54,14 @@ def generate(env):
env['RPATHPREFIX'] = '-Wl,-rpath='
env['RPATHSUFFIX'] = ''
env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}'
+
+ # OpenBSD doesn't usually use SONAME for libraries
+ use_soname = not sys.platform.startswith('openbsd')
+ link._setup_versioned_lib_variables(env, tool = 'gnulink', use_soname = use_soname)
+ env['LINKCALLBACKS'] = link._versioned_lib_callbacks()
+
+ # For backward-compatiblity with older SCons versions
+ env['SHLIBVERSIONFLAGS'] = SCons.Util.CLVar('-Wl,-Bsymbolic')
def exists(env):
# TODO: sync with link.smart_link() to choose a linker
diff --git a/src/engine/SCons/Tool/gnulink.xml b/src/engine/SCons/Tool/gnulink.xml
index 2a36de2..0e055c7 100644
--- a/src/engine/SCons/Tool/gnulink.xml
+++ b/src/engine/SCons/Tool/gnulink.xml
@@ -33,6 +33,10 @@ Set construction variables for GNU linker/loader.
<item>SHLINKFLAGS</item>
<item>RPATHPREFIX</item>
<item>RPATHSUFFIX</item>
+<item>_LDMODULESONAME</item>
+<item>_SHLIBSONAME</item>
+<item>LDMODULEVERSIONFLAGS</item>
+<item>SHLIBVERSIONFLAGS</item>
</sets>
</tool>
diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py
index 9f2e937..9d5db9f 100644
--- a/src/engine/SCons/Tool/install.py
+++ b/src/engine/SCons/Tool/install.py
@@ -38,6 +38,7 @@ import shutil
import stat
import SCons.Action
+import SCons.Tool
from SCons.Util import make_path_relative
#
@@ -141,98 +142,35 @@ def copyFuncVersionedLib(dest, source, env):
shutil.copy2(source, dest)
st = os.stat(source)
os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
- versionedLibLinks(dest, source, env)
+ installShlibLinks(dest, source, env)
return 0
-def versionedLibVersion(dest, source, env):
- """Check if dest is a version shared library name. Return version, libname, & install_dir if it is."""
+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)
+ if symlinks:
+ for link, linktgt in symlinks:
+ link_base = os.path.basename(link.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
- platform = env.subst('$PLATFORM')
- if not (platform == 'posix' or platform == 'darwin' or platform == 'sunos'):
- return (None, None, None)
- if (hasattr(source[0], 'attributes') and
- hasattr(source[0].attributes, 'shlibname')):
- libname = source[0].attributes.shlibname
- else:
- libname = os.path.basename(str(dest))
- install_dir = os.path.dirname(str(dest))
- shlib_suffix = env.subst('$SHLIBSUFFIX')
- # See if the source name is a versioned shared library, get the version number
- result = False
-
- version_re = re.compile("[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+")
- version_File = None
- if platform == 'posix' or platform == 'sunos':
- # handle unix names
- versioned_re = re.compile(re.escape(shlib_suffix + '.') + "[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+")
- result = versioned_re.findall(libname)
- if result:
- version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1]
- elif platform == 'darwin':
- # handle OSX names
- versioned_re = re.compile("\\.[0-9]+\\.[0-9]+\\.[0-9a-zA-Z]+" + re.escape(shlib_suffix) )
- result = versioned_re.findall(libname)
- if result:
- version_File = version_re.findall(versioned_re.findall(libname)[-1])[-1]
-
+ symlinks = listShlibLinksToInstall(dest, source, env)
if Verbose:
- print "install: version_File ", version_File
- # result is False if we did not find a versioned shared library name, so return and empty list
- if not result:
- return (None, libname, install_dir)
-
- version = None
- # get version number from the environment
- try:
- version = env.subst('$SHLIBVERSION')
- except KeyError:
- version = None
-
- if version != version_File:
- #raise SCons.Errors.UserError("SHLIBVERSION '%s' does not match the version # '%s' in the filename" % (version, version_File) )
- print "SHLIBVERSION '%s' does not match the version # '%s' in the filename, proceeding based on file name" % (version, version_File)
- version = version_File
- return (version, libname, install_dir)
-
-def versionedLibLinks(dest, source, env):
- """If we are installing a versioned shared library create the required links."""
- Verbose = False
- linknames = []
- version, libname, install_dir = versionedLibVersion(dest, source, env)
-
- if version != None:
- # libname includes the version number if one was given
- linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env)
- if Verbose:
- print "versionedLibLinks: linknames ",linknames
- # Here we just need the file name w/o path as the target of the link
- lib_ver = libname
- # make symlink of adjacent names in linknames
- for count in range(len(linknames)):
- linkname = linknames[count]
- fulllinkname = os.path.join(install_dir, linkname)
- if Verbose:
- print "full link name ",fulllinkname
- if count > 0:
- try:
- os.remove(lastlinkname)
- except:
- pass
- os.symlink(os.path.basename(fulllinkname),lastlinkname)
- if Verbose:
- print "versionedLibLinks: made sym link of %s -> %s" % (lastlinkname,os.path.basename(fulllinkname))
- lastlinkname = fulllinkname
- # finish chain of sym links with link to the actual library
- if len(linknames)>0:
- try:
- os.remove(lastlinkname)
- except:
- pass
- os.symlink(lib_ver,lastlinkname)
- if Verbose:
- print "versionedLibLinks: made sym link of %s -> %s" % (lib_ver,lastlinkname)
+ print 'installShlibLinks: symlinks=%r' % SCons.Tool.StringizeLibSymlinks(symlinks)
+ if symlinks:
+ SCons.Tool.CreateLibSymlinks(env, symlinks)
return
def installFunc(target, source, env):
@@ -306,22 +244,11 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env):
Verbose = False
_INSTALLED_FILES.extend(target)
if Verbose:
- print "ver lib emitter ",repr(target)
-
- # see if we have a versioned shared library, if so generate side effects
- version, libname, install_dir = versionedLibVersion(target[0], source, env)
- if version != None:
- # generate list of link names
- linknames = SCons.Tool.VersionShLibLinkNames(version,libname,env)
- for linkname in linknames:
- if Verbose:
- print "make side effect of %s" % os.path.join(install_dir, linkname)
- fulllinkname = os.path.join(install_dir, linkname)
- env.SideEffect(fulllinkname,target[0])
- env.Clean(target[0],fulllinkname)
- _INSTALLED_FILES.append(fulllinkname)
- if Verbose:
- print "installed list ", _INSTALLED_FILES
+ print "add_versioned_targets_to_INSTALLED_FILES: target=%r" % map(str, target)
+
+ symlinks = listShlibLinksToInstall(target[0], source, env)
+ if symlinks:
+ SCons.Tool.EmitLibSymlinks(env, symlinks, target[0])
_UNIQUE_INSTALLED_FILES = None
return (target, source)
diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml
index 0aa9384..6ae3e30 100644
--- a/src/engine/SCons/Tool/install.xml
+++ b/src/engine/SCons/Tool/install.xml
@@ -82,20 +82,13 @@ env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'],
<builder name="InstallVersionedLib">
<summary>
<para>
-Installs a versioned shared library. The &cv-link-SHLIBVERSION;
-construction variable should be defined in the environment
-to confirm the version number in the library name.
-If &cv-link-SHLIBVERSION; is not defined a warning will be issued
-and the name of the library will be parsed to derive the version.
-The symlinks appropriate to the architecture will be generated.
+Installs a versioned shared library. The symlinks appropriate to the
+architecture will be generated based on symlinks of the source library.
</para>
<example_commands>
env.InstallVersionedLib(target = '/usr/local/bin/foo',
source = 'libxyz.1.5.2.so')
-env.InstallVersionedLib(target = '/usr/local/bin/foo',
- source = 'libxyz.1.5.2.so',
- SHLIBVERSION='1.5.2')
</example_commands>
</summary>
</builder>
diff --git a/src/engine/SCons/Tool/ldc.py b/src/engine/SCons/Tool/ldc.py
index 8f9b117..ade95db 100644
--- a/src/engine/SCons/Tool/ldc.py
+++ b/src/engine/SCons/Tool/ldc.py
@@ -105,7 +105,7 @@ def generate(env):
# Hack for Fedora the packages of which use the wrong name :-(
if os.path.exists('/usr/lib64/libphobos-ldc.so') or os.path.exists('/usr/lib32/libphobos-ldc.so') or os.path.exists('/usr/lib/libphobos-ldc.so') :
env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=phobos-ldc')
- env['SHDLINKCOM'] = '$DLINK -of=$TARGET $DSHLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS'
+ env['SHDLINKCOM'] = '$DLINK -of=$TARGET $DSHLINKFLAGS $__DSHLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS'
env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l'
env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else ''
@@ -131,6 +131,17 @@ def generate(env):
env['DRPATHSUFFIX'] = ''
env['_DRPATH'] = '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}'
+ # Support for versioned libraries
+ env['_DSHLIBVERSIONFLAGS'] = '$DSHLIBVERSIONFLAGS -L-soname=$_DSHLIBSONAME'
+ env['_DSHLIBSONAME'] = '${DShLibSonameGenerator(__env__,TARGET)}'
+ # NOTE: this is a quick hack, the soname will only work if there is
+ # c/c++ linker loaded which provides callback for the ShLibSonameGenerator
+ env['DShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator
+ # NOTE: this is only for further reference, currently $DSHLIBVERSION does
+ # not work, the user must use $SHLIBVERSION
+ env['DSHLIBVERSION'] = '$SHLIBVERSION'
+ env['DSHLIBVERSIONFLAGS'] = []
+
SCons.Tool.createStaticLibBuilder(env)
diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py
index 2624946..a4a2a4c 100644
--- a/src/engine/SCons/Tool/link.py
+++ b/src/engine/SCons/Tool/link.py
@@ -33,9 +33,10 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import sys
import re
+import os
-import SCons.Defaults
import SCons.Tool
import SCons.Util
import SCons.Warnings
@@ -72,97 +73,205 @@ def smart_link(source, target, env, for_signature):
return '$CXX'
return '$CC'
-def shlib_emitter(target, source, env):
+def _lib_emitter(target, source, env, **kw):
Verbose = False
- platform = env.subst('$PLATFORM')
+ if Verbose:
+ print "_lib_emitter: target[0]=%r" % target[0].get_path()
for tgt in target:
tgt.attributes.shared = 1
+
try:
- # target[0] comes in as libtest.so. Add the version extensions
- version = env.subst('$SHLIBVERSION')
- if version:
- version_names = shlib_emitter_names(target, source, env)
- # mark the target with the shared libraries name, including
- # the version number
- target[0].attributes.shlibname = version_names[0]
- shlib = env.File(version_names[0], directory=target[0].get_dir())
- target[0].attributes.shlibpath = shlib.path
- for name in version_names[1:]:
- env.SideEffect(name, shlib)
- env.Clean(shlib, name)
- if Verbose:
- print "shlib_emitter: add side effect - ",name
- env.Clean(shlib, target[0])
- return ([shlib], source)
+ symlink_generator = kw['symlink_generator']
except KeyError:
- version = None
+ pass
+ else:
+ if Verbose:
+ print "_lib_emitter: symlink_generator=%r" % symlink_generator
+ symlinks = symlink_generator(env, target[0])
+ if Verbose:
+ print "_lib_emitter: symlinks=%r" % symlinks
+
+ if symlinks:
+ SCons.Tool.EmitLibSymlinks(env, symlinks, target[0])
+ target[0].attributes.shliblinks = symlinks
return (target, source)
-def shlib_emitter_names(target, source, env):
- """Return list of file names that are side effects for a versioned library build. The first name in the list is the new name for the target"""
+def shlib_emitter(target, source, env):
+ return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.ShLibSymlinkGenerator)
+
+def ldmod_emitter(target, source, env):
+ return _lib_emitter(target, source, env, symlink_generator = SCons.Tool.LdModSymlinkGenerator)
+
+# This is generic enough to be included here...
+def _versioned_lib_name(env, libnode, version, prefix, suffix, prefix_generator, suffix_generator, **kw):
+ """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so'"""
Verbose = False
- platform = env.subst('$PLATFORM')
- version_names = []
- try:
- # target[0] comes in as libtest.so. Add the version extensions
- version = env.subst('$SHLIBVERSION')
- if version.count(".") != 2:
- # We need a version of the form x.y.z to proceed
- raise ValueError
- if version:
- if platform == 'posix' or platform == 'sunos':
- versionparts = version.split('.')
- if hasattr(target[0].attributes, 'shlibname'):
- name = target[0].attributes.shlibname
- else:
- name = target[0].name
- # generate library name with the version number
- version_name = name + '.' + version
- if Verbose:
- print "shlib_emitter_names: target is ", version_name
- print "shlib_emitter_names: side effect: ", name
- # add version_name to list of names to be a Side effect
- version_names.append(version_name)
- if Verbose:
- print "shlib_emitter_names: versionparts ",versionparts
- for ver in versionparts[0:-1]:
- name = name + '.' + ver
- if Verbose:
- print "shlib_emitter_names: side effect: ", name
- # add name to list of names to be a Side effect
- version_names.append(name)
- elif platform == 'darwin':
- shlib_suffix = env.subst('$SHLIBSUFFIX')
- if hasattr(target[0].attributes, 'shlibname'):
- name = target[0].attributes.shlibname
- else:
- name = target[0].name
- # generate library name with the version number
- suffix_re = re.escape(shlib_suffix)
- version_name = re.sub(suffix_re, '.' + version + shlib_suffix, name)
- if Verbose:
- print "shlib_emitter_names: target is ", version_name
- print "shlib_emitter_names: side effect: ", name
- # add version_name to list of names to be a Side effect
- version_names.append(version_name)
- elif platform == 'cygwin':
- shlib_suffix = env.subst('$SHLIBSUFFIX')
- if hasattr(target[0].attributes, 'shlibname'):
- name = target[0].attributes.shlibname
- else:
- name = target[0].name
- # generate library name with the version number
- suffix_re = re.escape(shlib_suffix)
- version_name = re.sub(suffix_re, '-' + re.sub('\.', '-', version) + shlib_suffix, name)
- if Verbose:
- print "shlib_emitter_names: target is ", version_name
- print "shlib_emitter_names: side effect: ", name
- # add version_name to list of names to be a Side effect
- version_names.append(version_name)
- except KeyError:
- version = None
- return version_names
+ if Verbose:
+ print "_versioned_lib_name: libnode=%r" % libnode.get_path()
+ print "_versioned_lib_name: version=%r" % version
+ print "_versioned_lib_name: prefix=%r" % prefix
+ print "_versioned_lib_name: suffix=%r" % suffix
+ print "_versioned_lib_name: suffix_generator=%r" % suffix_generator
+
+ versioned_name = os.path.basename(libnode.get_path())
+ if Verbose:
+ print "_versioned_lib_name: versioned_name=%r" % versioned_name
+
+ versioned_prefix = prefix_generator(env, **kw)
+ versioned_suffix = suffix_generator(env, **kw)
+ if Verbose:
+ print "_versioned_lib_name: versioned_prefix=%r" % versioned_prefix
+ print "_versioned_lib_name: versioned_suffix=%r" % versioned_suffix
+
+ versioned_prefix_re = '^' + re.escape(versioned_prefix)
+ versioned_suffix_re = re.escape(versioned_suffix) + '$'
+ name = re.sub(versioned_prefix_re, prefix, versioned_name)
+ name = re.sub(versioned_suffix_re, suffix, name)
+ if Verbose:
+ print "_versioned_lib_name: name=%r" % name
+ return name
+
+def _versioned_shlib_name(env, libnode, version, prefix, suffix, **kw):
+ pg = SCons.Tool.ShLibPrefixGenerator
+ sg = SCons.Tool.ShLibSuffixGenerator
+ return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **kw)
+
+def _versioned_ldmod_name(env, libnode, version, prefix, suffix, **kw):
+ pg = SCons.Tool.LdModPrefixGenerator
+ sg = SCons.Tool.LdModSuffixGenerator
+ return _versioned_lib_name(env, libnode, version, prefix, suffix, pg, sg, **kw)
+
+def _versioned_lib_suffix(env, suffix, version):
+ """For suffix='.so' and version='0.1.2' it returns '.so.0.1.2'"""
+ Verbose = False
+ if Verbose:
+ print "_versioned_lib_suffix: suffix=%r" % suffix
+ print "_versioned_lib_suffix: version=%r" % version
+ if not suffix.endswith(version):
+ suffix = suffix + '.' + version
+ if Verbose:
+ print "_versioned_lib_suffix: return suffix=%r" % suffix
+ return suffix
+
+def _versioned_lib_soname(env, libnode, version, prefix, suffix, name_func):
+ """For libnode='/optional/dir/libfoo.so.X.Y.Z' it returns 'libfoo.so.X'"""
+ Verbose = False
+ if Verbose:
+ print "_versioned_lib_soname: version=%r" % version
+ name = name_func(env, libnode, version, prefix, suffix)
+ if Verbose:
+ print "_versioned_lib_soname: name=%r" % name
+ major = version.split('.')[0]
+ soname = name + '.' + major
+ if Verbose:
+ print "_versioned_lib_soname: soname=%r" % soname
+ return soname
+
+def _versioned_shlib_soname(env, libnode, version, prefix, suffix):
+ return _versioned_lib_soname(env, libnode, version, prefix, suffix, _versioned_shlib_name)
+
+def _versioned_ldmod_soname(env, libnode, version, prefix, suffix):
+ return _versioned_lib_soname(env, libnode, version, prefix, suffix, _versioned_ldmod_name)
+
+def _versioned_lib_symlinks(env, libnode, version, prefix, suffix, name_func, soname_func):
+ """Generate link names that should be created for a versioned shared lirbrary.
+ Returns a dictionary in the form { linkname : linktarget }
+ """
+ Verbose = False
+
+ if Verbose:
+ print "_versioned_lib_symlinks: libnode=%r" % libnode.get_path()
+ print "_versioned_lib_symlinks: version=%r" % version
+
+ if sys.platform.startswith('openbsd'):
+ # OpenBSD uses x.y shared library versioning numbering convention
+ # and doesn't use symlinks to backwards-compatible libraries
+ if Verbose:
+ print "_versioned_lib_symlinks: return symlinks=%r" % None
+ return None
+
+ linkdir = libnode.get_dir()
+ if Verbose:
+ print "_versioned_lib_symlinks: linkdir=%r" % linkdir.get_path()
+
+ name = name_func(env, libnode, version, prefix, suffix)
+ if Verbose:
+ print "_versioned_lib_symlinks: name=%r" % name
+
+ soname = soname_func(env, libnode, version, prefix, suffix)
+
+ link0 = env.fs.File(soname, linkdir)
+ link1 = env.fs.File(name, linkdir)
+
+ # We create direct symlinks, not daisy-chained.
+ if link0 == libnode:
+ # This enables SHLIBVERSION without periods (e.g. SHLIBVERSION=1)
+ symlinks = [ (link1, libnode) ]
+ else:
+ # This handles usual SHLIBVERSION, i.e. '1.2', '1.2.3', etc.
+ symlinks = [ (link0, libnode), (link1, libnode) ]
+
+ if Verbose:
+ print "_versioned_lib_symlinks: return symlinks=%r" % SCons.Tool.StringizeLibSymlinks(symlinks)
+
+ return symlinks
+
+def _versioned_shlib_symlinks(env, libnode, version, prefix, suffix):
+ nf = _versioned_shlib_name
+ sf = _versioned_shlib_soname
+ return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf)
+
+def _versioned_ldmod_symlinks(env, libnode, version, prefix, suffix):
+ nf = _versioned_ldmod_name
+ sf = _versioned_ldmod_soname
+ return _versioned_lib_symlinks(env, libnode, version, prefix, suffix, nf, sf)
+
+def _versioned_lib_callbacks():
+ return {
+ 'VersionedShLibSuffix' : _versioned_lib_suffix,
+ 'VersionedLdModSuffix' : _versioned_lib_suffix,
+ 'VersionedShLibSymlinks' : _versioned_shlib_symlinks,
+ 'VersionedLdModSymlinks' : _versioned_ldmod_symlinks,
+ 'VersionedShLibName' : _versioned_shlib_name,
+ 'VersionedLdModName' : _versioned_ldmod_name,
+ 'VersionedShLibSoname' : _versioned_shlib_soname,
+ 'VersionedLdModSoname' : _versioned_ldmod_soname,
+ }.copy()
+
+# Setup all variables required by the versioning machinery
+def _setup_versioned_lib_variables(env, **kw):
+
+ tool = None
+ try: tool = kw['tool']
+ except KeyError: pass
+
+ use_soname = False
+ try: use_soname = kw['use_soname']
+ except KeyError: pass
+
+ # The $_SHLIBVERSIONFLAGS define extra commandline flags used when
+ # building VERSIONED shared libraries. It's always set, but used only
+ # when VERSIONED library is built (see __SHLIBVERSIONFLAGS in SCons/Defaults.py).
+ if use_soname:
+ # If the linker uses SONAME, then we need this little automata
+ if tool == 'sunlink':
+ env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -h $_SHLIBSONAME'
+ env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -h $_LDMODULESONAME'
+ else:
+ env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS -Wl,-soname=$_SHLIBSONAME'
+ env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS -Wl,-soname=$_LDMODULESONAME'
+ env['_SHLIBSONAME'] = '${ShLibSonameGenerator(__env__,TARGET)}'
+ env['_LDMODULESONAME'] = '${LdModSonameGenerator(__env__,TARGET)}'
+ env['ShLibSonameGenerator'] = SCons.Tool.ShLibSonameGenerator
+ env['LdModSonameGenerator'] = SCons.Tool.LdModSonameGenerator
+ else:
+ env['_SHLIBVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS'
+ env['_LDMODULEVERSIONFLAGS'] = '$LDMODULEVERSIONFLAGS'
+
+ # LDOMDULVERSIONFLAGS should always default to $SHLIBVERSIONFLAGS
+ env['LDMODULEVERSIONFLAGS'] = '$SHLIBVERSIONFLAGS'
+
def generate(env):
"""Add Builders and construction variables for gnulink to an Environment."""
@@ -171,7 +280,7 @@ def generate(env):
env['SHLINK'] = '$LINK'
env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
- env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['SHLINKCOM'] = '$SHLINK -o $TARGET $SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
# don't set up the emitter, cause AppendUnique will generate a list
# starting with None :-(
env.Append(SHLIBEMITTER = [shlib_emitter])
@@ -196,15 +305,13 @@ def generate(env):
# setting them the same means that LoadableModule works everywhere.
SCons.Tool.createLoadableModuleBuilder(env)
env['LDMODULE'] = '$SHLINK'
- # don't set up the emitter, cause AppendUnique will generate a list
- # starting with None :-(
- env.Append(LDMODULEEMITTER='$SHLIBEMITTER')
+ env.Append(LDMODULEEMITTER = [ldmod_emitter])
env['LDMODULEPREFIX'] = '$SHLIBPREFIX'
env['LDMODULESUFFIX'] = '$SHLIBSUFFIX'
env['LDMODULEFLAGS'] = '$SHLINKFLAGS'
- env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
-
-
+ env['LDMODULECOM'] = '$LDMODULE -o $TARGET $LDMODULEFLAGS $__LDMODULEVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
+ env['LDMODULEVERSION'] = '$SHLIBVERSION'
+ env['LDMODULENOVERSIONSYMLINKS'] = '$SHLIBNOVERSIONSYMLINKS'
def exists(env):
# This module isn't really a Tool on its own, it's common logic for
diff --git a/src/engine/SCons/Tool/link.xml b/src/engine/SCons/Tool/link.xml
index d58b9e2..2f913fe 100644
--- a/src/engine/SCons/Tool/link.xml
+++ b/src/engine/SCons/Tool/link.xml
@@ -41,11 +41,16 @@ Sets construction variables for generic POSIX linkers.
<item>LIBLINKPREFIX</item>
<item>LIBLINKSUFFIX</item>
<item>SHLIBSUFFIX</item>
+<item>__SHLIBVERSIONFLAGS</item>
<item>LDMODULE</item>
<item>LDMODULEPREFIX</item>
<item>LDMODULESUFFIX</item>
<item>LDMODULEFLAGS</item>
<item>LDMODULECOM</item>
+<item>LDMODULEVERSION</item>
+<item>LDMODULENOVERSIONSYMLINKS</item>
+<item>LDMODULEVERSIONFLAGS</item>
+<item>__LDMODULEVERSIONFLAGS</item>
</sets>
<uses>
<item>SHLINKCOMSTR</item>
@@ -54,6 +59,105 @@ Sets construction variables for generic POSIX linkers.
</uses>
</tool>
+<cvar name="__LDMODULEVERSIONFLAGS">
+<summary>
+<para>
+This construction variable automatically introduces &cv-link-_LDMODULEVERSIONFLAGS;
+if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string.
+</para>
+</summary>
+</cvar>
+
+<cvar name="__SHLIBVERSIONFLAGS">
+<summary>
+<para>
+This construction variable automatically introduces &cv-link-_SHLIBVERSIONFLAGS;
+if &cv-link-SHLIBVERSION; is set. Othervise it evaluates to an empty string.
+</para>
+</summary>
+</cvar>
+
+<cvar name="_LDMODULESONAME">
+<summary>
+<para>
+A macro that automatically generates loadable module's SONAME based on $TARGET,
+$LDMODULEVERSION and $LDMODULESUFFIX. Used by &b-link-LoadableModule; builder
+when the linker tool supports SONAME (e.g. &t-link-gnulink;).
+</para>
+</summary>
+</cvar>
+
+<cvar name="_LDMODULEVERSIONFLAGS">
+<summary>
+<para>
+This macro automatically introduces extra flags to &cv-link-LDMODULECOM; when
+building versioned &b-link-LoadableModule; (that is when
+&cv-link-LDMODULEVERSION; is set). <literal>_LDMODULEVERSIONFLAGS</literal>
+usually adds &cv-link-SHLIBVERSIONFLAGS; and some extra dynamically generated
+options (such as <literal>-Wl,-soname=$_LDMODULESONAME</literal>). It is unused
+by plain (unversioned) loadable modules.
+</para>
+</summary>
+</cvar>
+
+<cvar name="_SHLIBVERSIONFLAGS">
+<summary>
+<para>
+This macro automatically introduces extra flags to &cv-link-SHLINKCOM; when
+building versioned &b-link-SharedLibrary; (that is when &cv-link-SHLIBVERSION;
+is set). <literal>_SHLIBVERSIONFLAGS</literal> usually adds &cv-link-SHLIBVERSIONFLAGS;
+and some extra dynamically generated options (such as
+<literal>-Wl,-soname=$_SHLIBSONAME</literal>. It is unused by "plain"
+(unversioned) shared libraries.
+</para>
+</summary>
+</cvar>
+
+<cvar name="_SHLIBSONAME">
+<summary>
+<para>
+A macro that automatically generates shared library's SONAME based on $TARGET,
+$SHLIBVERSION and $SHLIBSUFFIX. Used by &b-link-SharedLibrary; builder when
+the linker tool supports SONAME (e.g. &t-link-gnulink;).
+</para>
+</summary>
+</cvar>
+
+<cvar name="IMPLIBPREFIX">
+<summary>
+<para>
+The prefix used for import library names. For example, cygwin uses import
+libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries
+(<literal>cygfoo.dll</literal>). The &t-link-cyglink; linker sets
+&cv-link-IMPLIBPREFIX; to <literal>'lib'</literal> and &cv-link-SHLIBPREFIX;
+to <literal>'cyg'</literal>.
+</para>
+</summary>
+</cvar>
+
+<cvar name="IMPLIBSUFFIX">
+<summary>
+<para>
+The suffix used for import library names. For example, cygwin uses import
+libraries (<literal>libfoo.dll.a</literal>) in pair with dynamic libraries
+(<literal>cygfoo.dll</literal>). The &t-link-cyglink; linker sets
+&cv-link-IMPLIBSUFFIX; to <literal>'.dll.a'</literal> and &cv-link-SHLIBSUFFIX;
+to <literal>'.dll'</literal>.
+</para>
+</summary>
+</cvar>
+
+<cvar name="IMPLIBNOVERSIONSYMLINKS">
+<summary>
+<para>
+Used to override &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; when
+creating versioned import library for a shared library/loadable module. If not defined,
+then &cv-link-SHLIBNOVERSIONSYMLINKS;/&cv-link-LDMODULENOVERSIONSYMLINKS; is used to determine
+whether to disable symlink generation or not.
+</para>
+</summary>
+</cvar>
+
<cvar name="LDMODULE">
<summary>
<para>
@@ -92,6 +196,15 @@ General user options passed to the linker for building loadable modules.
</summary>
</cvar>
+<cvar name="LDMODULENOVERSIONSYMLINKS">
+<summary>
+<para>
+Instructs the &b-link-LoadableModule; builder to not automatically create symlinks
+for versioned modules. Defaults to <literal>$SHLIBNOVERSIONSYMLINKS</literal>
+</para>
+</summary>
+</cvar>
+
<cvar name="LDMODULEPREFIX">
<summary>
<para>
@@ -114,6 +227,16 @@ the same as $SHLIBSUFFIX.
</summary>
</cvar>
+<cvar name="LDMODULEVERSIONFLAGS">
+<summary>
+<para>
+Extra flags added to &cv-link-LDMODULECOM; when building versioned
+&b-link-LoadableModule;. These flags are only used when &cv-link-LDMODULEVERSION; is
+set.
+</para>
+</summary>
+</cvar>
+
<cvar name="LINK">
<summary>
<para>
@@ -169,6 +292,25 @@ for the variable that expands to library search path options.
</summary>
</cvar>
+<cvar name="SHLIBNOVERSIONSYMLINKS">
+<summary>
+<para>
+Instructs the &b-link-SharedLibrary; builder to not create symlinks for versioned
+shared libraries.
+</para>
+</summary>
+</cvar>
+
+<cvar name="SHLIBVERSIONFLAGS">
+<summary>
+<para>
+Extra flags added to &cv-link-SHLINKCOM; when building versioned
+&b-link-SharedLibrary;. These flags are only used when &cv-link-SHLIBVERSION; is
+set.
+</para>
+</summary>
+</cvar>
+
<cvar name="SHLINK">
<summary>
<para>
@@ -223,6 +365,18 @@ for the variable that expands to library search path options.
</summary>
</cvar>
+<cvar name="SONAME">
+<summary>
+<para>
+Variable used to hard-code SONAME for versioned shared library/loadable module.
+<example_commands>
+env.SharedLibrary('test', 'test.c', SHLIBVERSION='0.1.2', SONAME='libtest.so.2')
+</example_commands>
+The variable is used, for example, by &t-link-gnulink; linker tool.
+</para>
+</summary>
+</cvar>
+
<cvar name="STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME">
<summary>
<para>
diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py
index 9c58561..bd643f7 100644
--- a/src/engine/SCons/Tool/linkloc.py
+++ b/src/engine/SCons/Tool/linkloc.py
@@ -86,6 +86,7 @@ def generate(env):
env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS')
env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -dll $TARGET $SOURCES")}'
env['SHLIBEMITTER']= None
+ env['LDMODULEEMITTER']= None
env['LINK'] = "linkloc"
env['LINKFLAGS'] = SCons.Util.CLVar('')
env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $_LIBDIRFLAGS $_LIBFLAGS -exe $TARGET $SOURCES")}'
diff --git a/src/engine/SCons/Tool/midl.xml b/src/engine/SCons/Tool/midl.xml
index 1420978..efd83cc 100644
--- a/src/engine/SCons/Tool/midl.xml
+++ b/src/engine/SCons/Tool/midl.xml
@@ -44,7 +44,7 @@ Sets construction variables for the Microsoft IDL compiler.
<para>
Builds a Windows type library (<filename>.tlb</filename>)
file from an input IDL file (<filename>.idl</filename>).
-In addition, it will build the associated inteface stub and
+In addition, it will build the associated interface stub and
proxy source files,
naming them according to the base name of the <filename>.idl</filename> file.
For example,
diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py
index 601ec3b..948ebe5 100644
--- a/src/engine/SCons/Tool/mingw.py
+++ b/src/engine/SCons/Tool/mingw.py
@@ -146,6 +146,7 @@ def generate(env):
env['SHLINKCOM'] = shlib_action
env['LDMODULECOM'] = shlib_action
env.Append(SHLIBEMITTER = [shlib_emitter])
+ env.Append(LDMODULEEMITTER = [shlib_emitter])
env['AS'] = 'as'
env['WIN32DEFPREFIX'] = ''
diff --git a/src/engine/SCons/Tool/msgmerge.xml b/src/engine/SCons/Tool/msgmerge.xml
index 2bfc6df..139b21c 100644
--- a/src/engine/SCons/Tool/msgmerge.xml
+++ b/src/engine/SCons/Tool/msgmerge.xml
@@ -64,7 +64,7 @@ Target nodes defined through &b-POUpdate; are not built by default
(they're <literal>Ignore</literal>d from <literal>'.'</literal> node). Instead,
they are added automatically to special <literal>Alias</literal>
(<literal>'po-update'</literal> by default). The alias name may be changed
-through the &cv-link-POUPDATE_ALIAS; construction variable. You can easilly
+through the &cv-link-POUPDATE_ALIAS; construction variable. You can easily
update <literal>PO</literal> files in your project by <command>scons
po-update</command>.
</para>
diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py
index 827161e..1390c20 100644
--- a/src/engine/SCons/Tool/mslink.py
+++ b/src/engine/SCons/Tool/mslink.py
@@ -216,7 +216,7 @@ def embedManifestDllCheck(target, source, env):
"""Function run by embedManifestDllCheckAction to check for existence of manifest
and other conditions, and embed the manifest by calling embedManifestDllAction if so."""
if env.get('WINDOWS_EMBED_MANIFEST', 0):
- manifestSrc = target[0].abspath + '.manifest'
+ manifestSrc = target[0].get_abspath() + '.manifest'
if os.path.exists(manifestSrc):
ret = (embedManifestDllAction) ([target[0]],None,env)
if ret:
@@ -230,7 +230,7 @@ def embedManifestExeCheck(target, source, env):
"""Function run by embedManifestExeCheckAction to check for existence of manifest
and other conditions, and embed the manifest by calling embedManifestExeAction if so."""
if env.get('WINDOWS_EMBED_MANIFEST', 0):
- manifestSrc = target[0].abspath + '.manifest'
+ manifestSrc = target[0].get_abspath() + '.manifest'
if os.path.exists(manifestSrc):
ret = (embedManifestExeAction) ([target[0]],None,env)
if ret:
@@ -263,6 +263,7 @@ def generate(env):
env['_SHLINK_SOURCES'] = windowsShlinkSources
env['SHLINKCOM'] = compositeShLinkAction
env.Append(SHLIBEMITTER = [windowsLibEmitter])
+ env.Append(LDMODULEEMITTER = [windowsLibEmitter])
env['LINK'] = 'link'
env['LINKFLAGS'] = SCons.Util.CLVar('/nologo')
env['_PDB'] = pdbGenerator
diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml
index 793784f..2b4619e 100644
--- a/src/engine/SCons/Tool/msvc.xml
+++ b/src/engine/SCons/Tool/msvc.xml
@@ -86,7 +86,7 @@ file as the second element. Normally the object file is ignored.
This builder method is only
provided when Microsoft Visual C++ is being used as the compiler.
The PCH builder method is generally used in
-conjuction with the PCH construction variable to force object files to use
+conjunction with the PCH construction variable to force object files to use
the precompiled header:
</para>
diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py
index d00413d..355eeed 100644
--- a/src/engine/SCons/Tool/msvs.py
+++ b/src/engine/SCons/Tool/msvs.py
@@ -1535,7 +1535,9 @@ class _GenerateV7DSW(_DSWGenerator):
def PrintSolution(self):
"""Writes a solution file"""
self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr)
- if self.version_num >= 11.0:
+ if self.version_num >= 12.0:
+ self.file.write('# Visual Studio 14\n')
+ elif self.version_num >= 11.0:
self.file.write('# Visual Studio 11\n')
elif self.version_num >= 10.0:
self.file.write('# Visual Studio 2010\n')
@@ -1981,7 +1983,7 @@ def generate(env):
env['MSVSSCONSCRIPT'] = default_MSVS_SConscript
env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env))
- env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}'
+ env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.get_abspath()}" -f ${MSVSSCONSCRIPT.name}'
env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS'
env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py
index e762d55..2067660 100644
--- a/src/engine/SCons/Tool/mwld.py
+++ b/src/engine/SCons/Tool/mwld.py
@@ -56,6 +56,7 @@ def generate(env):
env['SHLINKFLAGS'] = '$LINKFLAGS'
env['SHLINKCOM'] = shlib_action
env['SHLIBEMITTER']= shlib_emitter
+ env['LDMODULEEMITTER']= shlib_emitter
def exists(env):
diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py
index 95311a2..1a979ab 100644
--- a/src/engine/SCons/Tool/packaging/__init__.py
+++ b/src/engine/SCons/Tool/packaging/__init__.py
@@ -80,7 +80,7 @@ def Tag(env, target, source, *more_tags, **kw_tags):
#if not k.startswith('PACKAGING_'):
if k[:10] != 'PACKAGING_':
k='PACKAGING_'+k
- setattr(t, k, v)
+ t.Tag(k, v)
def Package(env, target=None, source=None, **kw):
""" Entry point for the package tool.
@@ -235,9 +235,11 @@ def copy_attr(f1, f2):
#pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\
# x.startswith('PACKAGING_')]
copyit = lambda x: not hasattr(f2, x) and x[:10] == 'PACKAGING_'
- pattrs = list(filter(copyit, dir(f1)))
- for attr in pattrs:
- setattr(f2, attr, getattr(f1, attr))
+ if f1._tags:
+ pattrs = list(filter(copyit, f1._tags))
+ for attr in pattrs:
+ f2.Tag(attr, f1.GetTag(attr))
+
def putintopackageroot(target, source, env, pkgroot, honor_install_location=1):
""" Uses the CopyAs builder to copy all source files to the directory given
in pkgroot.
@@ -262,9 +264,9 @@ def putintopackageroot(target, source, env, pkgroot, honor_install_location=1):
if file.is_under(pkgroot):
new_source.append(file)
else:
- if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\
+ if file.GetTag('PACKAGING_INSTALL_LOCATION') and\
honor_install_location:
- new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION)
+ new_name=make_path_relative(file.GetTag('PACKAGING_INSTALL_LOCATION'))
else:
new_name=make_path_relative(file.get_path())
@@ -301,7 +303,7 @@ def stripinstallbuilder(target, source, env):
for ss in s.sources:
n_source.append(ss)
copy_attr(s, ss)
- setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path())
+ ss.Tag('PACKAGING_INSTALL_LOCATION', s.get_path())
return (target, n_source)
diff --git a/src/engine/SCons/Tool/packaging/ipk.py b/src/engine/SCons/Tool/packaging/ipk.py
index 6549445..84f4e20 100644
--- a/src/engine/SCons/Tool/packaging/ipk.py
+++ b/src/engine/SCons/Tool/packaging/ipk.py
@@ -120,7 +120,7 @@ def build_specfiles(source, target, env):
return opened_files[needle]
except KeyError:
file=filter(lambda x: x.get_path().rfind(needle)!=-1, haystack)[0]
- opened_files[needle]=open(file.abspath, 'w')
+ opened_files[needle]=open(file.get_abspath(), 'w')
return opened_files[needle]
control_file=open_file('control', target)
diff --git a/src/engine/SCons/Tool/packaging/msi.py b/src/engine/SCons/Tool/packaging/msi.py
index fe78c9c..172038f 100644
--- a/src/engine/SCons/Tool/packaging/msi.py
+++ b/src/engine/SCons/Tool/packaging/msi.py
@@ -189,7 +189,7 @@ def build_wxsfile(target, source, env):
""" compiles a .wxs file from the keywords given in env['msi_spec'] and
by analyzing the tree of source nodes and their tags.
"""
- file = open(target[0].abspath, 'w')
+ file = open(target[0].get_abspath(), 'w')
try:
# Create a document with the Wix root tag
diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py
index 2bc3063..92977d0 100644
--- a/src/engine/SCons/Tool/packaging/rpm.py
+++ b/src/engine/SCons/Tool/packaging/rpm.py
@@ -130,8 +130,7 @@ def build_specfile(target, source, env):
""" Builds a RPM specfile from a dictionary with string metadata and
by analyzing a tree of nodes.
"""
- file = open(target[0].abspath, 'w')
- str = ""
+ file = open(target[0].get_abspath(), 'w')
try:
file.write( build_specfile_header(env) )
@@ -169,7 +168,7 @@ def build_specfile_sections(spec):
'X_RPM_POSTUNINSTALL' : '%%postun\n%s\n\n',
'X_RPM_VERIFY' : '%%verify\n%s\n\n',
- # These are for internal use but could possibly be overriden
+ # These are for internal use but could possibly be overridden
'X_RPM_PREP' : '%%prep\n%s\n\n',
'X_RPM_BUILD' : '%%build\n%s\n\n',
'X_RPM_INSTALL' : '%%install\n%s\n\n',
@@ -279,7 +278,9 @@ def build_specfile_filesection(spec, files):
tags = {}
for k in supported_tags.keys():
try:
- tags[k]=getattr(file, k)
+ v = file.GetTag(k)
+ if v:
+ tags[k] = v
except AttributeError:
pass
@@ -287,7 +288,7 @@ def build_specfile_filesection(spec, files):
str = str + SimpleTagCompiler(supported_tags, mandatory=0).compile( tags )
str = str + ' '
- str = str + file.PACKAGING_INSTALL_LOCATION
+ str = str + file.GetTag('PACKAGING_INSTALL_LOCATION')
str = str + '\n\n'
return str
diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py
index 716c7d5..7bc0ef6 100644
--- a/src/engine/SCons/Tool/qt.py
+++ b/src/engine/SCons/Tool/qt.py
@@ -320,6 +320,7 @@ def generate(env):
# correctly later by our emitter.
env.AppendUnique(PROGEMITTER =[AutomocStatic],
SHLIBEMITTER=[AutomocShared],
+ LDMODULEEMITTER=[AutomocShared],
LIBEMITTER =[AutomocStatic],
# Of course, we need to link against the qt libraries
CPPPATH=["$QT_CPPPATH"],
diff --git a/src/engine/SCons/Tool/rpm.py b/src/engine/SCons/Tool/rpm.py
index 1f6eafe..b7d65a8 100644
--- a/src/engine/SCons/Tool/rpm.py
+++ b/src/engine/SCons/Tool/rpm.py
@@ -51,11 +51,11 @@ def get_cmd(source, env):
if SCons.Util.is_List(source):
tar_file_with_included_specfile = source[0]
return "%s %s %s"%(env['RPM'], env['RPMFLAGS'],
- tar_file_with_included_specfile.abspath )
+ tar_file_with_included_specfile.get_abspath() )
def build_rpm(target, source, env):
# create a temporary rpm build root.
- tmpdir = os.path.join( os.path.dirname( target[0].abspath ), 'rpmtemp' )
+ tmpdir = os.path.join( os.path.dirname( target[0].get_abspath() ), 'rpmtemp' )
if os.path.exists(tmpdir):
shutil.rmtree(tmpdir)
@@ -87,7 +87,7 @@ def build_rpm(target, source, env):
expected = os.path.basename(input.get_path())
assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected)
- shutil.copy( output, input.abspath )
+ shutil.copy( output, input.get_abspath() )
# cleanup before leaving.
diff --git a/src/engine/SCons/Tool/sunar.py b/src/engine/SCons/Tool/sunar.py
index 779414f..eb58457 100644
--- a/src/engine/SCons/Tool/sunar.py
+++ b/src/engine/SCons/Tool/sunar.py
@@ -51,9 +51,6 @@ def generate(env):
env['ARFLAGS'] = SCons.Util.CLVar('r')
env['ARCOM'] = '$AR $ARFLAGS $TARGET $SOURCES'
- env['SHLINK'] = '$LINK'
- env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -G')
- env['SHLINKCOM'] = '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
env['LIBPREFIX'] = 'lib'
env['LIBSUFFIX'] = '.a'
diff --git a/src/engine/SCons/Tool/sunar.xml b/src/engine/SCons/Tool/sunar.xml
index 65f0c9e..f875217 100644
--- a/src/engine/SCons/Tool/sunar.xml
+++ b/src/engine/SCons/Tool/sunar.xml
@@ -33,15 +33,11 @@ Sets construction variables for the Sun library archiver.
<item>AR</item>
<item>ARFLAGS</item>
<item>ARCOM</item>
-<item>SHLINK</item>
-<item>SHLINKFLAGS</item>
-<item>SHLINKCOM</item>
<item>LIBPREFIX</item>
<item>LIBSUFFIX</item>
</sets>
<uses>
<item>ARCOMSTR</item>
-<item>SHLINKCOMSTR</item>
</uses>
</tool>
diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py
index 5996a30..680af03 100644
--- a/src/engine/SCons/Tool/sunlink.py
+++ b/src/engine/SCons/Tool/sunlink.py
@@ -66,6 +66,10 @@ def generate(env):
env['RPATHSUFFIX'] = ''
env['_RPATH'] = '${_concat(RPATHPREFIX, RPATH, RPATHSUFFIX, __env__)}'
+ # Support for versioned libraries
+ link._setup_versioned_lib_variables(env, tool = 'sunlink', use_soname = True)
+ env['LINKCALLBACKS'] = link._versioned_lib_callbacks()
+
def exists(env):
return ccLinker
diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py
index f166174..a315182 100644
--- a/src/engine/SCons/Tool/swig.py
+++ b/src/engine/SCons/Tool/swig.py
@@ -42,6 +42,7 @@ import SCons.Defaults
import SCons.Scanner
import SCons.Tool
import SCons.Util
+import SCons.Node
SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR')
@@ -117,9 +118,13 @@ def _swigEmitter(target, source, env):
if outdir:
java_files = [os.path.join(outdir, j) for j in java_files]
java_files = list(map(env.fs.File, java_files))
+ def t_from_s(t, p, s, x):
+ return t.dir
+ tsm = SCons.Node._target_from_source_map
+ tkey = len(tsm)
+ tsm[tkey] = t_from_s
for jf in java_files:
- t_from_s = lambda t, p, s, x: t.dir
- SCons.Util.AddMethod(jf, t_from_s, 'target_from_source')
+ jf._func_target_from_source = tkey
target.extend(java_files)
return (target, source)
diff --git a/src/engine/SCons/Tool/swig.xml b/src/engine/SCons/Tool/swig.xml
index 2b817dc..1160804 100644
--- a/src/engine/SCons/Tool/swig.xml
+++ b/src/engine/SCons/Tool/swig.xml
@@ -202,9 +202,7 @@ and translated into the
The list of directories that the scripting language wrapper
and interface generate will search for included files.
The SWIG implicit dependency scanner will search these
-directories for include files.
-The default is to use the same path
-specified as &cv-CPPPATH;.
+directories for include files. The default value is an empty list.
</para>
<para>
@@ -251,7 +249,7 @@ include &cv-_SWIGINCFLAGS;:
</para>
<example_commands>
-env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SORUCES")
+env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SOURCES")
</example_commands>
</summary>
</cvar>
diff --git a/src/engine/SCons/Tool/xgettext.xml b/src/engine/SCons/Tool/xgettext.xml
index 6e28a9d..380f92a 100644
--- a/src/engine/SCons/Tool/xgettext.xml
+++ b/src/engine/SCons/Tool/xgettext.xml
@@ -108,7 +108,7 @@ the results shall be as the comments above say.
<emphasis>Example 2.</emphasis>
The &b-POTUpdate; builder may be used with no target specified, in which
case default target <filename>messages.pot</filename> will be used. The
-default target may also be overriden by setting &cv-link-POTDOMAIN; construction
+default target may also be overridden by setting &cv-link-POTDOMAIN; construction
variable or providing it as an override to &b-POTUpdate; builder:
</para>
<example_commands>
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 6dd64ec..4890ba2 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -264,10 +264,10 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
children = child_func(root)
if prune and rname in visited and children:
- sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + '\n')
+ sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + u'\n')
return
- sys.stdout.write(''.join(tags + margins + ['+-', rname]) + '\n')
+ sys.stdout.write(''.join(tags + margins + ['+-', rname]) + u'\n')
visited[rname] = 1
@@ -992,7 +992,7 @@ class Selector(OrderedDict):
def __call__(self, env, source, ext=None):
if ext is None:
try:
- ext = source[0].suffix
+ ext = source[0].get_suffix()
except IndexError:
ext = ""
try:
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index b0c15c5..795bc46 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -591,10 +591,11 @@ class UtilTestCase(unittest.TestCase):
class MyNode(object):
def __init__(self, name):
self.name = name
- self.suffix = os.path.splitext(name)[1]
def __str__(self):
return self.name
+ def get_suffix(self):
+ return os.path.splitext(self.name)[1]
s = Selector({'a' : 'AAA', 'b' : 'BBB'})
assert s['a'] == 'AAA', s['a']
diff --git a/src/script/scons.py b/src/script/scons.py
index 0c7b44c..e522f57 100644
--- a/src/script/scons.py
+++ b/src/script/scons.py
@@ -191,7 +191,7 @@ if __name__ == "__main__":
except:
print("Import failed. Unable to find SCons files in:")
for path in libs:
- print " %s" % path
+ print(" %s" % path)
raise
# this does all the work, and calls sys.exit
diff --git a/src/script/sconsign.py b/src/script/sconsign.py
index e5e9d4f..ef32a93 100644
--- a/src/script/sconsign.py
+++ b/src/script/sconsign.py
@@ -278,7 +278,7 @@ def field(name, entry, verbose=Verbose):
def nodeinfo_raw(name, ninfo, prefix=""):
# This just formats the dictionary, which we would normally use str()
# to do, except that we want the keys sorted for deterministic output.
- d = ninfo.__dict__
+ d = ninfo.__getstate__()
try:
keys = ninfo.field_list + ['_version_id']
except AttributeError:
diff --git a/test/Actions/addpost-link.py b/test/Actions/addpost-link.py
index a148051..04cd68d 100644
--- a/test/Actions/addpost-link.py
+++ b/test/Actions/addpost-link.py
@@ -54,7 +54,7 @@ myprog = env.Program('test1.c',
OBJSUFFIX = '.obj',
PROGSUFFIX = '.exe')
if ARGUMENTS['case']=='2':
- AddPostAction(myprog, Action(r'%(_python_)s strip.py ' + myprog[0].abspath))
+ AddPostAction(myprog, Action(r'%(_python_)s strip.py ' + myprog[0].get_abspath()))
""" % locals())
test.write('test1.c', """\
diff --git a/test/CacheDir/NoCache.py b/test/CacheDir/NoCache.py
index 7e9b540..b035b44 100644
--- a/test/CacheDir/NoCache.py
+++ b/test/CacheDir/NoCache.py
@@ -45,7 +45,7 @@ CacheDir(r'%s')
g = '%s'
def ActionWithUndeclaredInputs(target,source,env):
- open(target[0].abspath,'w').write(g)
+ open(target[0].get_abspath(),'w').write(g)
Command('foo_cached', [], ActionWithUndeclaredInputs)
NoCache(Command('foo_notcached', [], ActionWithUndeclaredInputs))
diff --git a/test/Glob/Repository.py b/test/Glob/Repository.py
index 0a2e326..22a7f88 100644
--- a/test/Glob/Repository.py
+++ b/test/Glob/Repository.py
@@ -75,7 +75,7 @@ test.write(['repository', 'src', 'SConscript'], """
Import("env")
env.Build('xxx.out', Glob('x*.in'))
env.Build('yyy.out', Glob('yy?.in'))
-env.Build('zzz.out', sorted(Glob('*/zzz.in'), key=lambda t: t.abspath))
+env.Build('zzz.out', sorted(Glob('*/zzz.in'), key=lambda t: t.get_abspath()))
""")
test.write(['repository', 'src', 'xxx.in'], "repository/src/xxx.in\n")
diff --git a/test/Glob/VariantDir.py b/test/Glob/VariantDir.py
index 175e5b9..c9c1d07 100644
--- a/test/Glob/VariantDir.py
+++ b/test/Glob/VariantDir.py
@@ -34,6 +34,7 @@ import TestSCons
test = TestSCons.TestSCons()
test.subdir('src')
+test.subdir('src/sub1')
test.write('SConstruct', """\
VariantDir('var1', 'src')
@@ -41,6 +42,9 @@ VariantDir('var2', 'src')
SConscript('var1/SConscript')
SConscript('var2/SConscript')
+SConscript('var1/sub1/SConscript')
+SConscript('var2/sub1/SConscript')
+SConscript('src/sub1/SConscript', src_dir = 'src', variant_dir = 'var3', duplicate=0)
""")
test.write(['src', 'SConscript'], """\
@@ -55,16 +59,46 @@ def concatenate(target, source, env):
env['BUILDERS']['Concatenate'] = Builder(action=concatenate)
env.Concatenate('f.out', sorted(Glob('f*.in'), key=lambda t: t.name))
+env.Concatenate('fex.out', sorted(Glob('f*.in', exclude = 'f1.in'), key=lambda t: t.name))
+""")
+
+test.write(['src', 'sub1', 'SConscript'], """\
+env = Environment()
+
+def concatenate(target, source, env):
+ fp = open(str(target[0]), 'wb')
+ for s in source:
+ fp.write(open(str(s), 'rb').read())
+ fp.close()
+
+env['BUILDERS']['Concatenate'] = Builder(action=concatenate)
+
+env.Concatenate('f.out', sorted(Glob('f*.in'), key=lambda t: t.name))
+env.Concatenate('fex.out', sorted(Glob('f*.in', exclude = 'f1.in'), key=lambda t: t.name))
""")
test.write(['src', 'f1.in'], "src/f1.in\n")
test.write(['src', 'f2.in'], "src/f2.in\n")
test.write(['src', 'f3.in'], "src/f3.in\n")
+test.write(['src', 'sub1', 'f1.in'], "src/sub1/f1.in\n")
+test.write(['src', 'sub1', 'f2.in'], "src/sub1/f2.in\n")
+test.write(['src', 'sub1', 'f3.in'], "src/sub1/f3.in\n")
+
test.run(arguments = '.')
test.must_match(['var1', 'f.out'], "src/f1.in\nsrc/f2.in\nsrc/f3.in\n")
test.must_match(['var2', 'f.out'], "src/f1.in\nsrc/f2.in\nsrc/f3.in\n")
+test.must_match(['var1', 'fex.out'], "src/f2.in\nsrc/f3.in\n")
+test.must_match(['var2', 'fex.out'], "src/f2.in\nsrc/f3.in\n")
+
+test.must_match(['var1', 'sub1', 'f.out'], "src/sub1/f1.in\nsrc/sub1/f2.in\nsrc/sub1/f3.in\n")
+test.must_match(['var2', 'sub1', 'f.out'], "src/sub1/f1.in\nsrc/sub1/f2.in\nsrc/sub1/f3.in\n")
+test.must_match(['var1', 'sub1', 'fex.out'], "src/sub1/f2.in\nsrc/sub1/f3.in\n")
+test.must_match(['var2', 'sub1', 'fex.out'], "src/sub1/f2.in\nsrc/sub1/f3.in\n")
+
+test.must_match(['var3', 'sub1', 'f.out'], "src/sub1/f1.in\nsrc/sub1/f2.in\nsrc/sub1/f3.in\n")
+test.must_match(['var3', 'sub1', 'fex.out'], "src/sub1/f2.in\nsrc/sub1/f3.in\n")
test.pass_test()
diff --git a/test/Glob/exclude.py b/test/Glob/exclude.py
index 56b58a8..fe93b82 100644
--- a/test/Glob/exclude.py
+++ b/test/Glob/exclude.py
@@ -46,9 +46,9 @@ def concatenate(target, source, env):
env['BUILDERS']['Concatenate'] = Builder(action=concatenate)
-env.Concatenate('fa.out', sorted(Glob('f*.in' , exclude=['f2.in', 'f4.*'] , strings=False), key=lambda t: t.path))
+env.Concatenate('fa.out', sorted(Glob('f*.in' , exclude=['f2.in', 'f4.*'] , strings=False), key=lambda t: t.get_internal_path()))
env.Concatenate('fb.out', sorted(Glob('f*.in' , exclude=['f2.in', 'f4.*'] , strings=True)))
-env.Concatenate('fc.out', sorted(Glob('d?/f*.in', exclude=['d?/f1.*', 'f2.in'], strings=False), key=lambda t: t.path))
+env.Concatenate('fc.out', sorted(Glob('d?/f*.in', exclude=['d?/f1.*', 'f2.in'], strings=False), key=lambda t: t.get_internal_path()))
env.Concatenate('fd.out', sorted(Glob('d?/f*.in', exclude=['d?/f1.*', 'f2.in'], strings=True)))
env.Concatenate('fe.out', sorted(Glob('f*.in', exclude='f1.in' , strings=True)))
env.Concatenate('ff.out', sorted(Glob('f*.in', exclude='other' , strings=True)))
diff --git a/test/Help.py b/test/Help.py
index 747fe59..693fd28 100644
--- a/test/Help.py
+++ b/test/Help.py
@@ -82,6 +82,69 @@ Use scons -H for help about command-line options.
test.run(arguments = '-h', stdout = expect)
+# Bug #2831 - append flag to Help doesn't wipe out addoptions and variables used together
+test.write('SConstruct', r"""
+
+AddOption('--debugging',
+ dest='debugging',
+ action='store_true',
+ default=False,
+ metavar='BDEBUGGING',
+ help='Compile with debugging symbols')
+
+vars = Variables()
+vars.Add(ListVariable('buildmod', 'List of modules to build', 'none',
+ ['python']))
+
+env = Environment()
+
+Help(vars.GenerateHelpText(env),append=True)
+""")
+
+expect = ".*--debugging.*Compile with debugging symbols.*buildmod: List of modules to build.*"
+
+test.run(arguments = '-h', stdout = expect, match=TestSCons.match_re_dotall)
+
+
+# Bug 2831
+# This test checks to verify that append=False doesn't include anything
+# but the expected help for the specified Variable()
+
+test.write('SConstruct', r"""
+
+AddOption('--debugging',
+ dest='debugging',
+ action='store_true',
+ default=False,
+ metavar='BDEBUGGING',
+ help='Compile with debugging symbols')
+
+vars = Variables()
+vars.Add(ListVariable('buildmod', 'List of modules to build', 'none',
+ ['python']))
+
+env = Environment()
+
+Help(vars.GenerateHelpText(env),append=False)
+""")
+
+expect = """\
+scons: Reading SConscript files ...
+scons: done reading SConscript files.
+
+buildmod: List of modules to build
+ (all|none|comma-separated list of names)
+ allowed names: python
+ default: none
+ actual: None
+
+Use scons -H for help about command-line options.
+"""
+
+test.run(arguments = '-h', stdout = expect)
+
+
+
test.pass_test()
# Local Variables:
diff --git a/test/Install/wrap-by-attribute.py b/test/Install/wrap-by-attribute.py
index 912551e..02513af 100644
--- a/test/Install/wrap-by-attribute.py
+++ b/test/Install/wrap-by-attribute.py
@@ -58,10 +58,10 @@ env.SconsInternalInstallFunc = env.Install
env.SconsInternalInstallAsFunc = env.InstallAs
def InstallWithDestDir(dir, source):
- abspath = os.path.splitdrive(env.Dir(dir).abspath)[1]
+ abspath = os.path.splitdrive(env.Dir(dir).get_abspath())[1]
return env.SconsInternalInstallFunc('$DESTDIR'+abspath, source)
def InstallAsWithDestDir(target, source):
- abspath = os.path.splitdrive(env.File(target).abspath)[1]
+ abspath = os.path.splitdrive(env.File(target).get_abspath())[1]
return env.SconsInternalInstallAsFunc('$DESTDIR'+abspath, source)
# Add the wrappers directly as attributes.
diff --git a/test/Interactive/variant_dir.py b/test/Interactive/variant_dir.py
index f36bd0a..7f2c42f 100644
--- a/test/Interactive/variant_dir.py
+++ b/test/Interactive/variant_dir.py
@@ -41,10 +41,10 @@ marker_2 = test.workpath('markers', '2')
test.write(['work', 'SConstruct'], """\
# build the plugin binaries
-basepath = str(Dir('#').abspath)
+basepath = str(Dir('#').get_abspath())
env = Environment()
env.Append(BASEPATH=basepath)
-env.Append(ENV = {'BASEPATH' : str(Dir('#').abspath)})
+env.Append(ENV = {'BASEPATH' : str(Dir('#').get_abspath())})
SConscript( 'sub1/SConscript',
variant_dir = 'build',
duplicate=False,
diff --git a/test/LINK/SHLIBVERSIONFLAGS.py b/test/LINK/SHLIBVERSIONFLAGS.py
new file mode 100644
index 0000000..d8fd2e6
--- /dev/null
+++ b/test/LINK/SHLIBVERSIONFLAGS.py
@@ -0,0 +1,59 @@
+#!/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__"
+
+import os
+import re
+
+import TestSCons
+import SCons.Platform
+import SCons.Defaults
+
+linkers = [ 'gnulink', 'cyglink', 'sunlink' ]
+
+foo_c_src = "void foo() {}\n"
+
+env = SCons.Defaults.DefaultEnvironment()
+platform = SCons.Platform.platform_default()
+tool_list = SCons.Platform.DefaultToolList(platform, env)
+
+test = TestSCons.TestSCons()
+test.write('foo.c', foo_c_src)
+test.write('SConstruct', "SharedLibrary('foo','foo.c',SHLIBVERSION='1.2.3')\n")
+
+if 'gnulink' in tool_list:
+ test.run(stdout = r".+ -Wl,-Bsymbolic -Wl,-soname=libfoo.so.1( .+)+", match = TestSCons.match_re_dotall)
+ test.run(arguments = ['-c'])
+elif 'sunlink' in tool_list:
+ test.run(stdout = r".+ -h libfoo.so.1( .+)+", match = TestSCons.match_re_dotall)
+ test.run(arguments = ['-c'])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LINK/VersionedLib-VariantDir.py b/test/LINK/VersionedLib-VariantDir.py
new file mode 100644
index 0000000..0a631b0
--- /dev/null
+++ b/test/LINK/VersionedLib-VariantDir.py
@@ -0,0 +1,168 @@
+#!/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__"
+
+"""
+Ensure that SharedLibrary builder with SHLIBVERSION set works with VariantDir.
+"""
+
+import TestSCons
+import os
+import sys
+
+import SCons.Platform
+import SCons.Defaults
+
+env = SCons.Defaults.DefaultEnvironment()
+platform = SCons.Platform.platform_default()
+tool_list = SCons.Platform.DefaultToolList(platform, env)
+
+test = TestSCons.TestSCons()
+
+test.subdir(['src'])
+test.subdir(['src','lib'])
+test.subdir(['src','bin'])
+
+test.write(['src','lib','foo.c'], """
+#if _WIN32
+__declspec(dllexport)
+#endif
+int foo() { return 0; }
+""")
+
+test.write(['src','bin','main.c'], """
+#if _WIN32
+__declspec(dllimport)
+#endif
+int foo();
+int main()
+{
+ return foo();
+}
+""")
+
+test.write('SConstruct', """
+env = Environment()
+variant = { 'variant_dir' : 'build',
+ 'src_dir' : 'src',
+ 'duplicate' : 0,
+ 'exports' : { 'env' : env } }
+SConscript('src/lib/SConscript', **variant)
+SConscript('src/bin/SConscript', **variant)
+""")
+
+test.write(['src','lib','SConscript'], """
+Import('env')
+env.SharedLibrary('foo', 'foo.c', SHLIBVERSION = '0.1.2')
+""" )
+
+test.write(['src','bin','SConscript'], """
+Import('env')
+env.Program('main.c', LIBS=['foo'], LIBPATH=['../lib'])
+""")
+
+test.run(arguments = ['--tree=all'])
+
+if platform == 'cygwin' or platform == 'win32':
+ # PATH is used to search for *.dll libraries on windows
+ path = os.environ.get('PATH','')
+ if path: path = path + os.pathsep
+ path = path + test.workpath('build/lib')
+ os.environ['PATH'] = path
+
+if os.name == 'posix':
+ os.environ['LD_LIBRARY_PATH'] = test.workpath('build/lib')
+if sys.platform.find('irix') != -1:
+ os.environ['LD_LIBRARYN32_PATH'] = test.workpath('build/lib')
+
+test.run(program = test.workpath('build/bin/main'))
+
+if 'gnulink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.so',
+ 'libfoo.so.0',
+ 'libfoo.so.0.1.2',
+ ]
+ obj = 'foo.os'
+elif 'applelink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.dylib',
+ 'libfoo.0.1.2.dylib',
+ ]
+ obj = 'foo.os'
+elif 'cyglink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'cygfoo-0-1-2.dll',
+ 'libfoo-0-1-2.dll.a',
+ 'libfoo.dll.a',
+ ]
+ obj = 'foo.os'
+elif 'mslink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'foo.dll',
+ 'foo.lib',
+ ]
+ obj = 'foo.obj'
+elif 'sunlink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.so',
+ 'libfoo.so.0',
+ 'libfoo.so.0.1.2',
+ ]
+ obj = 'so_foo.os'
+else:
+ # All (?) the files we expect will get created in the current directory
+ files= [
+ 'libfoo.so',
+ ]
+ obj = 'foo.os'
+
+test.must_exist([ 'build', 'lib', obj ])
+for f in files:
+ test.must_exist([ 'build', 'lib', f ])
+
+test.run(arguments = ['-c'])
+
+test.must_not_exist([ 'build', 'lib', obj ])
+for f in files:
+ test.must_not_exist([ 'build', 'lib', f ])
+
+test.must_exist(['src', 'lib', 'foo.c'])
+test.must_exist(['SConstruct'])
+test.must_exist(['src', 'lib', 'SConscript'])
+test.must_exist(['src', 'bin', 'SConscript'])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py
new file mode 100644
index 0000000..249b54f
--- /dev/null
+++ b/test/LINK/VersionedLib-j2.py
@@ -0,0 +1,146 @@
+#!/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__"
+
+"""
+Ensure that SharedLibrary builder works with SHLIBVERSION and -j2.
+This is regression test for:
+http://article.gmane.org/gmane.comp.programming.tools.scons.user/27049
+"""
+
+import TestSCons
+import os
+import sys
+
+import SCons.Platform
+import SCons.Defaults
+
+test = TestSCons.TestSCons()
+
+test.write('foo.c', """
+#if _WIN32
+__declspec(dllexport)
+#endif
+int foo() { return 0; }
+""")
+
+
+test.write('main.c', """
+#if _WIN32
+__declspec(dllimport)
+#endif
+int foo();
+int main() { return foo(); }
+""")
+
+test.write('SConstruct', """
+env = Environment()
+env.AppendUnique(LIBPATH = ['.'])
+env.Program('main.c', LIBS = ['foo'])
+env.SharedLibrary('foo', 'foo.c', SHLIBVERSION = '0.1.2')
+""")
+
+test.run(arguments = ['-j 2', '--tree=all'])
+
+env = SCons.Defaults.DefaultEnvironment()
+platform = SCons.Platform.platform_default()
+tool_list = SCons.Platform.DefaultToolList(platform, env)
+
+if platform == 'cygwin':
+ # PATH is used to search for *.dll librarier (cygfoo-0-2-1.dll in our case)
+ path = os.environ.get('PATH','')
+ if path: path = path + os.pathsep
+ path = path + test.workpath('.')
+ os.environ['PATH'] = path
+
+if os.name == 'posix':
+ os.environ['LD_LIBRARY_PATH'] = test.workpath('.')
+if sys.platform.find('irix') != -1:
+ os.environ['LD_LIBRARYN32_PATH'] = test.workpath('.')
+
+test.run(program = test.workpath('main'))
+
+test.run(arguments = ['-c'])
+
+platform = SCons.Platform.platform_default()
+
+if 'gnulink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.so',
+ 'libfoo.so.0',
+ 'libfoo.so.0.1.2',
+ 'foo.os',
+ ]
+elif 'applelink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.dylib',
+ 'libfoo.0.1.2.dylib',
+ 'foo.os',
+ ]
+elif 'cyglink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'cygfoo-0-1-2.dll',
+ 'libfoo-0-1-2.dll.a',
+ 'libfoo.dll.a',
+ 'foo.os',
+ ]
+elif 'mslink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'foo.dll',
+ 'foo.lib',
+ 'foo.obj',
+ ]
+elif 'sunlink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.so',
+ 'libfoo.so.0',
+ 'libfoo.so.0.1.2',
+ 'so_foo.os',
+ ]
+else:
+ # All (?) the files we expect will get created in the current directory
+ files= [
+ 'libfoo.so',
+ 'foo.os']
+
+for f in files:
+ test.must_not_exist([ f])
+
+test.must_exist(['main.c'])
+test.must_exist(['foo.c'])
+test.must_exist(['SConstruct'])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py
new file mode 100644
index 0000000..a2e141b
--- /dev/null
+++ b/test/LINK/VersionedLib-subdir.py
@@ -0,0 +1,160 @@
+#!/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__"
+
+"""
+Ensure that SharedLibrary builder with SHLIBVERSION='0.1.2' can build its target
+in a subdirectory containing .so.0.1.2 in name.
+
+This is regression test for issue mentioned in:
+http://thread.gmane.org/gmane.comp.programming.tools.scons.user/27081
+"""
+
+import TestSCons
+import os
+import sys
+
+import SCons.Platform
+import SCons.Defaults
+
+test = TestSCons.TestSCons()
+
+test.write('foo.c', """
+#if _WIN32
+__declspec(dllexport)
+#endif
+int foo() { return 0; }
+""")
+
+test.write('main.c', """
+#if _WIN32
+__declspec(dllimport)
+#endif
+int foo();
+int main()
+{
+ return foo();
+}
+""")
+
+env = SCons.Defaults.DefaultEnvironment()
+platform = SCons.Platform.platform_default()
+tool_list = SCons.Platform.DefaultToolList(platform, env)
+
+if 'applelink' in tool_list:
+ subdir = 'blah.0.1.2.dylib.blah'
+elif 'cyglink' in tool_list:
+ subdir = 'blah-0-1-2.dll.a.blah'
+else:
+ subdir = 'blah.so.0.1.2.blah'
+
+test.write('SConstruct', """
+env = Environment()
+env.AppendUnique(LIBPATH = [ '%s' ])
+env.SharedLibrary('%s/foo', 'foo.c', SHLIBVERSION = '0.1.2')
+env.Program('main.c', LIBS=['foo'])
+""" % (subdir,subdir))
+
+test.run(arguments = ['--tree=all'])
+
+if platform == 'cygwin' or platform == 'win32':
+ # PATH is used to search for *.dll libraries on windows
+ path = os.environ.get('PATH','')
+ if path: path = path + os.pathsep
+ path = path + test.workpath(subdir)
+ os.environ['PATH'] = path
+
+if os.name == 'posix':
+ os.environ['LD_LIBRARY_PATH'] = subdir
+if sys.platform.find('irix') != -1:
+ os.environ['LD_LIBRARYN32_PATH'] = subdir
+
+test.run(program = test.workpath('main'))
+
+if 'gnulink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.so',
+ 'libfoo.so.0',
+ 'libfoo.so.0.1.2',
+ ]
+ obj = 'foo.os'
+elif 'applelink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.dylib',
+ 'libfoo.0.1.2.dylib',
+ ]
+ obj = 'foo.os'
+elif 'cyglink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'cygfoo-0-1-2.dll',
+ 'libfoo-0-1-2.dll.a',
+ 'libfoo.dll.a',
+ ]
+ obj = 'foo.os'
+elif 'mslink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'foo.dll',
+ 'foo.lib',
+ ]
+ obj = 'foo.obj'
+elif 'sunlink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ files = [
+ 'libfoo.so',
+ 'libfoo.so.0',
+ 'libfoo.so.0.1.2',
+ ]
+ obj = 'so_foo.os'
+else:
+ # All (?) the files we expect will get created in the current directory
+ files= [
+ 'libfoo.so',
+ ]
+ obj = 'foo.os'
+
+test.must_exist([ obj ])
+for f in files:
+ test.must_exist([ subdir, f ])
+
+test.run(arguments = ['-c'])
+
+test.must_not_exist([ obj ])
+for f in files:
+ test.must_not_exist([ subdir, f ])
+
+test.must_exist(['foo.c'])
+test.must_exist(['SConstruct'])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py
index a2345d6..3f4a912 100644
--- a/test/LINK/VersionedLib.py
+++ b/test/LINK/VersionedLib.py
@@ -29,131 +29,253 @@ import sys
import TestSCons
import SCons.Platform
+import SCons.Defaults
-_exe = TestSCons._exe
+env = SCons.Defaults.DefaultEnvironment()
+platform = SCons.Platform.platform_default()
+tool_list = SCons.Platform.DefaultToolList(platform, env)
-test = TestSCons.TestSCons()
+if 'gnulink' in tool_list:
+ test_plan = [
+ {
+ 'libversion' : '2',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2') ],
+ },
+ {
+ 'libversion' : '2.5',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5', 'test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.5'), ('libtest.so.2', 'libtest.so.2.5') ],
+ },
+ {
+ 'libversion' : '2.5.4',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4', 'test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4'), ('libtest.so.2', 'libtest.so.2.5.4') ],
+ },
+ {
+ 'libversion' : '2.5.4.7.8',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8', 'test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4.7.8'), ('libtest.so.2', 'libtest.so.2.5.4.7.8') ],
+ },
+ {
+ 'libversion' : 'aabf114f',
+ 'files' : [ 'libtest.so', 'libtest.so.aabf114f', 'test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.aabf114f' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.aabf114f') ],
+ },
+ {
+ 'libversion' : '2.dfffa11',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11', 'test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.dfffa11'), ('libtest.so.2', 'libtest.so.2.dfffa11') ],
+ },
+ ]
+elif 'applelink' in tool_list:
+ # All (?) the files we expect will get created in the current directory
+ test_plan = [
+ {
+ 'libversion' : '2.5.4',
+ 'files' : [ 'libtest.dylib', 'libtest.2.5.4.dylib', 'test.os' ],
+ 'instfiles' : [ 'libtest.dylib', 'libtest.2.5.4.dylib' ],
+ 'symlinks' : [],
+ },
+ ]
+elif 'cyglink' in tool_list:
+ test_plan = [
+ {
+ 'libversion' : '2',
+ 'files' : [ 'cygtest-2.dll', 'libtest-2.dll.a', 'libtest.dll.a', 'test.os' ],
+ 'instfiles' : [ 'cygtest-2.dll', 'libtest-2.dll.a', 'libtest.dll.a' ],
+ 'symlinks' : [ ('libtest.dll.a', 'libtest-2.dll.a') ],
+ },
+ {
+ 'libversion' : '2.5',
+ 'files' : [ 'cygtest-2-5.dll', 'libtest-2-5.dll.a', 'libtest.dll.a', 'test.os' ],
+ 'instfiles' : [ 'cygtest-2-5.dll', 'libtest-2-5.dll.a', 'libtest.dll.a' ],
+ 'symlinks' : [ ('libtest.dll.a', 'libtest-2-5.dll.a') ],
+ },
+ {
+ 'libversion' : '2.5.4',
+ 'files' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a', 'test.os' ],
+ 'instfiles' : [ 'cygtest-2-5-4.dll', 'libtest-2-5-4.dll.a', 'libtest.dll.a' ],
+ 'symlinks' : [ ('libtest.dll.a', 'libtest-2-5-4.dll.a') ],
+ },
+ {
+ 'libversion' : '2.5.4.7.8',
+ 'files' : [ 'cygtest-2-5-4-7-8.dll', 'libtest-2-5-4-7-8.dll.a', 'libtest.dll.a', 'test.os' ],
+ 'instfiles' : [ 'cygtest-2-5-4-7-8.dll', 'libtest-2-5-4-7-8.dll.a', 'libtest.dll.a' ],
+ 'symlinks' : [ ('libtest.dll.a', 'libtest-2-5-4-7-8.dll.a') ],
+ },
+ {
+ 'libversion' : 'aabf114f',
+ 'files' : [ 'cygtest-aabf114f.dll', 'libtest-aabf114f.dll.a', 'libtest.dll.a', 'test.os' ],
+ 'instfiles' : [ 'cygtest-aabf114f.dll', 'libtest-aabf114f.dll.a', 'libtest.dll.a' ],
+ 'symlinks' : [ ('libtest.dll.a', 'libtest-aabf114f.dll.a') ],
+ },
+ {
+ 'libversion' : '2.dfffa11',
+ 'files' : [ 'cygtest-2-dfffa11.dll', 'libtest-2-dfffa11.dll.a', 'libtest.dll.a', 'test.os' ],
+ 'instfiles' : [ 'cygtest-2-dfffa11.dll', 'libtest-2-dfffa11.dll.a', 'libtest.dll.a' ],
+ 'symlinks' : [ ('libtest.dll.a', 'libtest-2-dfffa11.dll.a') ],
+ },
+ ]
+elif 'mslink' in tool_list:
+ test_plan = [
+ {
+ 'libversion' : '2.5.4',
+ 'files' : [ 'test.dll', 'test.lib', 'test.obj' ],
+ 'instfiles' : [ 'test.dll', 'test.lib' ],
+ 'symlinks' : [],
+ },
+ ]
+elif 'sunlink' in tool_list:
+ test_plan = [
+ {
+ 'libversion' : '2',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'so_test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2') ],
+ },
+ {
+ 'libversion' : '2.5',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5', 'so_test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.5'), ('libtest.so.2', 'libtest.so.2.5') ],
+ },
+ {
+ 'libversion' : '2.5.4',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4', 'so_test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4'), ('libtest.so.2', 'libtest.so.2.5.4') ],
+ },
+ {
+ 'libversion' : '2.5.4.7.8',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8', 'so_test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.5.4.7.8' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.5.4.7.8'), ('libtest.so.2', 'libtest.so.2.5.4.7.8') ],
+ },
+ {
+ 'libversion' : 'aabf114f',
+ 'files' : [ 'libtest.so', 'libtest.so.aabf114f', 'so_test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.aabf114f' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.aabf114f') ],
+ },
+ {
+ 'libversion' : '2.dfffa11',
+ 'files' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11', 'so_test.os' ],
+ 'instfiles' : [ 'libtest.so', 'libtest.so.2', 'libtest.so.2.dfffa11' ],
+ 'symlinks' : [ ('libtest.so', 'libtest.so.2.dfffa11'), ('libtest.so.2', 'libtest.so.2.dfffa11') ],
+ },
+ ]
+else:
+ test_plan = [
+ {
+ 'libversion' : '2.5.4',
+ 'files' : [ 'libtest.so', 'test.os' ],
+ 'instfiles' : [ ],
+ },
+ ]
-test.write('SConstruct', """\
-import os
-env = Environment()
-objs = env.SharedObject('test.c')
-mylib = env.SharedLibrary('test', objs, SHLIBVERSION = '2.5.4')
-env.Program(source=['testapp.c',mylib])
-env.Program(target=['testapp2'],source=['testapp.c','libtest.dylib'])
-instnode = env.InstallVersionedLib("#/installtest",mylib)
-env.Default(instnode)
-""")
+test_c_src = """\
+#if _WIN32
+__declspec(dllexport)
+#endif
+int testlib(int n) { return n+1 ; }
+"""
-test.write('test.c', """\
+test_c_src2 = """\
#if _WIN32
__declspec(dllexport)
#endif
-int testlib(int n)
-{
-return n+1 ;
-}
-""")
+int testlib(int n) { return n+11 ; }
+"""
-test.write('testapp.c', """\
+testapp_c_src = """\
+#if _WIN32
+__declspec(dllimport)
+#endif
+int testlib(int n);
#include <stdio.h>
int main(int argc, char **argv)
{
int itest ;
itest = testlib(2) ;
-printf("results: testlib(2) = %d\n",itest) ;
+printf("results: testlib(2) = %d\\n",itest) ;
return 0 ;
}
-""")
+"""
-platform = SCons.Platform.platform_default()
+for t in test_plan:
+ test = TestSCons.TestSCons()
-test.run()
+ libversion = t['libversion']
+ files = t['files']
+ symlinks = t['symlinks']
+ instfiles = t['instfiles']
-if platform == 'posix':
- # All (?) the files we expect will get created in the current directory
- files = [
- 'libtest.so',
- 'libtest.so.2',
- 'libtest.so.2.5.4',
- 'test.os',
- ]
- # All (?) the files we expect will get created in the 'installtest' directory
- instfiles = [
- 'libtest.so',
- 'libtest.so.2',
- 'libtest.so.2.5.4',
- ]
-elif platform == 'darwin':
- # All (?) the files we expect will get created in the current directory
- files = [
- 'libtest.dylib',
- 'libtest.2.5.4.dylib',
- 'test.os',
- ]
- # All (?) the files we expect will get created in the 'installtest' directory
- instfiles = [
- 'libtest.dylib',
- 'libtest.2.5.4.dylib',
- ]
-elif platform == 'cygwin':
- # All (?) the files we expect will get created in the current directory
- files = [
- 'cygtest-2-5-4.dll',
- 'libtest-2-5-4.dll.a',
- 'test.os',
- ]
- # All (?) the files we expect will get created in the 'installtest' directory
- instfiles = [
- 'cygtest-2-5-4.dll',
- 'libtest-2-5-4.dll.a',
- ]
-elif platform == 'win32':
- # All (?) the files we expect will get created in the current directory
- files = [
- 'test.dll',
- 'test.lib',
- 'test.obj',
- ]
- # All (?) the files we expect will get created in the 'installtest' directory
- instfiles = [
- 'test.dll',
- 'test.lib',
- ]
-else:
- # All (?) the files we expect will get created in the current directory
- files= [
- 'libtest.so',
- 'test.os']
- # All (?) the files we expect will get created in the 'installtest' directory
- instfiles = []
-
-for f in files:
- test.must_exist([ f])
-for f in instfiles:
- test.must_exist(['installtest', f])
-
-# modify test.c and make sure it can recompile when links already exist
-test.write('test.c', """\
-#if _WIN32
-__declspec(dllexport)
-#endif
-int testlib(int n)
-{
-return n+11 ;
-}
-""")
+ test.write('SConstruct', """\
+import os
+env = Environment()
+objs = env.SharedObject('test.c')
+mylib = env.SharedLibrary('test', objs, SHLIBVERSION = '%s')
+env.Program('testapp1.c', LIBS = mylib, LIBPATH='.')
+env.Program('testapp2.c', LIBS = ['test'], LIBPATH='.')
+instnode = env.InstallVersionedLib("#/installtest",mylib)
+env.Default(instnode)
+
+# Extra test to ensure that InstallVersionedLib can be called from the DefaultEnvironment
+# Ensures orthogonality where InstallVersionedLib wasn't previously available: SCons gave NameError.
+instnode = InstallVersionedLib("defaultenv-installtest",mylib)
+Default(instnode)
+
+""" % libversion)
+
+ test.write('test.c', test_c_src)
+ test.write('testapp1.c', testapp_c_src)
+ test.write('testapp2.c', testapp_c_src)
+
+ test.run(arguments = ['--tree=all'])
+
+ for f in files:
+ test.must_exist([ f])
+ for f in instfiles:
+ test.must_exist(['installtest', f])
+ test.must_exist(['defaultenv-installtest', f])
+
+ wrong_symlinks = []
+ for (linkname,expected) in symlinks:
+ try:
+ endpoint = os.readlink(linkname)
+ except OSError, err:
+ print "%s (expected symlink %r -> %r)" % (err, linkname, expected)
+ wrong_symlinks.append(linkname)
+ else:
+ if endpoint != expected:
+ print "Wrong symlink: %r -> %r (expected symlink: %r -> %r)" % (linkname, endpoint, linkname, expected)
+ wrong_symlinks.append(linkname)
+
+ if wrong_symlinks:
+ test.fail_test(wrong_symlinks)
+
+ # modify test.c and make sure it can recompile when links already exist
+ test.write('test.c', test_c_src2)
+
+ test.run()
-test.run()
+ test.run(arguments = ['-c'])
-test.run(arguments = '-c')
+ for f in files:
+ test.must_not_exist([ f])
-for f in files:
- test.must_not_exist([ f])
-for f in instfiles:
- test.must_not_exist(['installtest', f])
+ for f in instfiles:
+ test.must_not_exist(['installtest', f])
+ test.must_not_exist(['defaultenv-installtest', f])
test.pass_test()
diff --git a/test/MSVS/vs-14.0-exec.py b/test/MSVS/vs-14.0-exec.py
new file mode 100644
index 0000000..b96bdab
--- /dev/null
+++ b/test/MSVS/vs-14.0-exec.py
@@ -0,0 +1,109 @@
+#!/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 that we can actually build a simple program using our generated
+Visual Studio 14.0 project (.vcxproj) and solution (.sln) files
+using Visual Studio 14.0 (Professional edition).
+"""
+
+import os
+import sys
+
+import TestSConsMSVS
+
+test = TestSConsMSVS.TestSConsMSVS()
+
+if sys.platform != 'win32':
+ msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform
+ test.skip_test(msg)
+
+msvs_version = '14.0'
+
+if not msvs_version in test.msvs_versions():
+ msg = "Visual Studio %s not installed; skipping test.\n" % msvs_version
+ test.skip_test(msg)
+
+
+
+# Let SCons figure out the Visual Studio environment variables for us and
+# print out a statement that we can exec to suck them into our external
+# environment so we can execute devenv and really try to build something.
+
+test.run(arguments = '-n -q -Q -f -', stdin = """\
+env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s')
+print "os.environ.update(%%s)" %% repr(env['ENV'])
+""" % locals())
+
+exec(test.stdout())
+
+
+
+test.subdir('sub dir')
+
+test.write(['sub dir', 'SConstruct'], """\
+env=Environment(MSVS_VERSION = '%(msvs_version)s')
+
+env.MSVSProject(target = 'foo.vcxproj',
+ srcs = ['foo.c'],
+ buildtarget = 'foo.exe',
+ variant = 'Release')
+
+env.Program('foo.c')
+""" % locals())
+
+test.write(['sub dir', 'foo.c'], r"""
+int
+main(int argc, char *argv)
+{
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+test.run(chdir='sub dir', arguments='.')
+
+test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj'))
+
+import SCons.Platform.win32
+system_dll_path = os.path.join( SCons.Platform.win32.get_system_root(), 'System32' )
+os.environ['PATH'] = os.environ['PATH'] + os.pathsep + system_dll_path
+
+test.run(chdir='sub dir',
+ program=[test.get_msvs_executable(msvs_version)],
+ arguments=['foo.sln', '/build', 'Release'])
+
+test.run(program=test.workpath('sub dir', 'foo'), stdout="foo.c\n")
+
+
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVS/vs-14.0-files.py b/test/MSVS/vs-14.0-files.py
new file mode 100644
index 0000000..e4ba8e2
--- /dev/null
+++ b/test/MSVS/vs-14.0-files.py
@@ -0,0 +1,110 @@
+#!/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 that we can generate Visual Studio 14.0 project (.vcxproj) and
+solution (.sln) files that look correct.
+"""
+
+import os
+
+import TestSConsMSVS
+
+test = TestSConsMSVS.TestSConsMSVS()
+host_arch = test.get_vs_host_arch()
+
+
+# Make the test infrastructure think we have this version of MSVS installed.
+test._msvs_versions = ['14.0']
+
+
+
+expected_slnfile = TestSConsMSVS.expected_slnfile_14_0
+expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_14_0
+SConscript_contents = TestSConsMSVS.SConscript_contents_14_0
+
+
+
+test.write('SConstruct', SConscript_contents%{'HOST_ARCH': host_arch})
+
+test.run(arguments="Test.vcxproj")
+
+test.must_exist(test.workpath('Test.vcxproj'))
+test.must_exist(test.workpath('Test.vcxproj.filters'))
+vcxproj = test.read('Test.vcxproj', 'r')
+expect = test.msvs_substitute(expected_vcprojfile, '14.0', None, 'SConstruct')
+# don't compare the pickled data
+assert vcxproj[:len(expect)] == expect, test.diff_substr(expect, vcxproj)
+
+test.must_exist(test.workpath('Test.sln'))
+sln = test.read('Test.sln', 'r')
+expect = test.msvs_substitute(expected_slnfile, '14.0', None, 'SConstruct')
+# don't compare the pickled data
+assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)
+
+test.run(arguments='-c .')
+
+test.must_not_exist(test.workpath('Test.vcxproj'))
+test.must_not_exist(test.workpath('Test.vcxproj.filters'))
+test.must_not_exist(test.workpath('Test.sln'))
+
+test.run(arguments='Test.vcxproj')
+
+test.must_exist(test.workpath('Test.vcxproj'))
+test.must_exist(test.workpath('Test.vcxproj.filters'))
+test.must_exist(test.workpath('Test.sln'))
+
+test.run(arguments='-c Test.sln')
+
+test.must_not_exist(test.workpath('Test.vcxproj'))
+test.must_not_exist(test.workpath('Test.vcxproj.filters'))
+test.must_not_exist(test.workpath('Test.sln'))
+
+
+
+# Test that running SCons with $PYTHON_ROOT in the environment
+# changes the .vcxproj output as expected.
+os.environ['PYTHON_ROOT'] = 'xyzzy'
+python = os.path.join('$(PYTHON_ROOT)', os.path.split(TestSConsMSVS.python)[1])
+
+test.run(arguments='Test.vcxproj')
+
+test.must_exist(test.workpath('Test.vcxproj'))
+vcxproj = test.read('Test.vcxproj', 'r')
+expect = test.msvs_substitute(expected_vcprojfile, '14.0', None, 'SConstruct',
+ python=python)
+# don't compare the pickled data
+assert vcxproj[:len(expect)] == expect, test.diff_substr(expect, vcxproj)
+
+
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVS/vs-14.0-scc-files.py b/test/MSVS/vs-14.0-scc-files.py
new file mode 100644
index 0000000..c934ac9
--- /dev/null
+++ b/test/MSVS/vs-14.0-scc-files.py
@@ -0,0 +1,115 @@
+#!/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 that we can generate Visual Studio 14.0 project (.vcxproj) and
+solution (.sln) files that contain SCC information and look correct.
+"""
+
+import os
+
+import TestSConsMSVS
+
+test = TestSConsMSVS.TestSConsMSVS()
+
+# Make the test infrastructure think we have this version of MSVS installed.
+test._msvs_versions = ['14.0']
+
+
+
+expected_slnfile = TestSConsMSVS.expected_slnfile_14_0
+expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_14_0
+SConscript_contents = """\
+env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0',
+ CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')],
+ CPPPATH=['inc1', 'inc2'],
+ MSVS_SCC_CONNECTION_ROOT='.',
+ MSVS_SCC_PROVIDER='MSSCCI:Perforce SCM',
+ MSVS_SCC_PROJECT_NAME='Perforce Project')
+
+testsrc = ['test1.cpp', 'test2.cpp']
+testincs = ['sdk_dir\sdk.h']
+testlocalincs = ['test.h']
+testresources = ['test.rc']
+testmisc = ['readme.txt']
+
+env.MSVSProject(target = 'Test.vcxproj',
+ srcs = testsrc,
+ incs = testincs,
+ localincs = testlocalincs,
+ resources = testresources,
+ misc = testmisc,
+ buildtarget = 'Test.exe',
+ variant = 'Release')
+"""
+
+expected_sln_sccinfo = """\
+\tGlobalSection(SourceCodeControl) = preSolution
+\t\tSccNumberOfProjects = 2
+\t\tSccProjectName0 = Perforce\u0020Project
+\t\tSccLocalPath0 = .
+\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM
+\t\tCanCheckoutShared = true
+\t\tSccProjectUniqueName1 = Test.vcxproj
+\t\tSccLocalPath1 = .
+\t\tCanCheckoutShared = true
+\t\tSccProjectFilePathRelativizedFromConnection1 = .\\\\
+\tEndGlobalSection
+"""
+
+expected_vcproj_sccinfo = """\
+\t\t<SccProjectName>Perforce Project</SccProjectName>
+\t\t<SccLocalPath>.</SccLocalPath>
+\t\t<SccProvider>MSSCCI:Perforce SCM</SccProvider>
+"""
+
+
+test.write('SConstruct', SConscript_contents)
+
+test.run(arguments="Test.vcxproj")
+
+test.must_exist(test.workpath('Test.vcxproj'))
+vcproj = test.read('Test.vcxproj', 'r')
+expect = test.msvs_substitute(expected_vcprojfile, '14.0', None, 'SConstruct',
+ vcproj_sccinfo=expected_vcproj_sccinfo)
+# don't compare the pickled data
+assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)
+
+test.must_exist(test.workpath('Test.sln'))
+sln = test.read('Test.sln', 'r')
+expect = test.msvs_substitute(expected_slnfile, '14.0', None, 'SConstruct',
+ sln_sccinfo=expected_sln_sccinfo)
+# don't compare the pickled data
+assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)
+
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVS/vs-14.0-scc-legacy-files.py b/test/MSVS/vs-14.0-scc-legacy-files.py
new file mode 100644
index 0000000..ba6ebad
--- /dev/null
+++ b/test/MSVS/vs-14.0-scc-legacy-files.py
@@ -0,0 +1,98 @@
+#!/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 that we can generate Visual Studio 14.0 project (.vcxproj) and
+solution (.sln) files that contain SCC information and look correct.
+"""
+
+import os
+
+import TestSConsMSVS
+
+test = TestSConsMSVS.TestSConsMSVS()
+
+# Make the test infrastructure think we have this version of MSVS installed.
+test._msvs_versions = ['14.0']
+
+
+
+expected_slnfile = TestSConsMSVS.expected_slnfile_14_0
+expected_vcprojfile = TestSConsMSVS.expected_vcprojfile_14_0
+SConscript_contents = """\
+env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0',
+ CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')],
+ CPPPATH=['inc1', 'inc2'],
+ MSVS_SCC_LOCAL_PATH='C:\\MyMsVsProjects',
+ MSVS_SCC_PROJECT_NAME='Perforce Project')
+
+testsrc = ['test1.cpp', 'test2.cpp']
+testincs = ['sdk_dir\sdk.h']
+testlocalincs = ['test.h']
+testresources = ['test.rc']
+testmisc = ['readme.txt']
+
+env.MSVSProject(target = 'Test.vcxproj',
+ srcs = testsrc,
+ incs = testincs,
+ localincs = testlocalincs,
+ resources = testresources,
+ misc = testmisc,
+ buildtarget = 'Test.exe',
+ variant = 'Release')
+"""
+
+expected_vcproj_sccinfo = """\
+\t\t<SccProjectName>Perforce Project</SccProjectName>
+\t\t<SccLocalPath>C:\\MyMsVsProjects</SccLocalPath>
+"""
+
+
+test.write('SConstruct', SConscript_contents)
+
+test.run(arguments="Test.vcxproj")
+
+test.must_exist(test.workpath('Test.vcxproj'))
+vcproj = test.read('Test.vcxproj', 'r')
+expect = test.msvs_substitute(expected_vcprojfile, '14.0', None, 'SConstruct',
+ vcproj_sccinfo=expected_vcproj_sccinfo)
+# don't compare the pickled data
+assert vcproj[:len(expect)] == expect, test.diff_substr(expect, vcproj)
+
+test.must_exist(test.workpath('Test.sln'))
+sln = test.read('Test.sln', 'r')
+expect = test.msvs_substitute(expected_slnfile, '14.0', None, 'SConstruct')
+# don't compare the pickled data
+assert sln[:len(expect)] == expect, test.diff_substr(expect, sln)
+
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVS/vs-14.0Exp-exec.py b/test/MSVS/vs-14.0Exp-exec.py
new file mode 100644
index 0000000..44ebece
--- /dev/null
+++ b/test/MSVS/vs-14.0Exp-exec.py
@@ -0,0 +1,109 @@
+#!/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 that we can actually build a simple program using our generated
+Visual Studio 14.0 project (.vcxproj) and solution (.sln) files
+using Visual C++ 14.0 Express edition.
+"""
+
+import os
+import sys
+
+import TestSConsMSVS
+
+test = TestSConsMSVS.TestSConsMSVS()
+
+if sys.platform != 'win32':
+ msg = "Skipping Visual Studio test on non-Windows platform '%s'\n" % sys.platform
+ test.skip_test(msg)
+
+msvs_version = '14.0Exp'
+
+if not msvs_version in test.msvs_versions():
+ msg = "Visual Studio %s not installed; skipping test.\n" % msvs_version
+ test.skip_test(msg)
+
+
+
+# Let SCons figure out the Visual Studio environment variables for us and
+# print out a statement that we can exec to suck them into our external
+# environment so we can execute devenv and really try to build something.
+
+test.run(arguments = '-n -q -Q -f -', stdin = """\
+env = Environment(tools = ['msvc'], MSVS_VERSION='%(msvs_version)s')
+print "os.environ.update(%%s)" %% repr(env['ENV'])
+""" % locals())
+
+exec(test.stdout())
+
+
+
+test.subdir('sub dir')
+
+test.write(['sub dir', 'SConstruct'], """\
+env=Environment(MSVS_VERSION = '%(msvs_version)s')
+
+env.MSVSProject(target = 'foo.vcxproj',
+ srcs = ['foo.c'],
+ buildtarget = 'foo.exe',
+ variant = 'Release')
+
+env.Program('foo.c')
+""" % locals())
+
+test.write(['sub dir', 'foo.c'], r"""
+int
+main(int argc, char *argv)
+{
+ printf("foo.c\n");
+ exit (0);
+}
+""")
+
+test.run(chdir='sub dir', arguments='.')
+
+test.vcproj_sys_path(test.workpath('sub dir', 'foo.vcxproj'))
+
+import SCons.Platform.win32
+system_dll_path = os.path.join( SCons.Platform.win32.get_system_root(), 'System32' )
+os.environ['PATH'] = os.environ['PATH'] + os.pathsep + system_dll_path
+
+test.run(chdir='sub dir',
+ program=[test.get_msvs_executable(msvs_version)],
+ arguments=['foo.sln', '/build', 'Release'])
+
+test.run(program=test.workpath('sub dir', 'foo'), stdout="foo.c\n")
+
+
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/NoClean.py b/test/NoClean.py
index 01fe209..63e3e28 100644
--- a/test/NoClean.py
+++ b/test/NoClean.py
@@ -34,14 +34,14 @@ test = TestSCons.TestSCons()
test.write('SConstruct', """
def action(target, source, env):
- for t in target: open(t.path, 'w')
+ for t in target: open(t.get_internal_path(), 'w')
Command('1.out', 'SConstruct', action)
NoClean('1.out')
""")
test.write('SConstruct.force', """
def action(target, source, env):
- for t in target: open(t.path, 'w')
+ for t in target: open(t.get_internal_path(), 'w')
open('4.out', 'w')
res = Command('3.out', 'SConstruct.force', action)
Clean('4.out', res)
@@ -50,7 +50,7 @@ NoClean('4.out')
test.write('SConstruct.multi', """
def action(target, source, env):
- for t in target: open(t.path, 'w')
+ for t in target: open(t.get_internal_path(), 'w')
Command(['5.out', '6.out'], 'SConstruct.multi', action)
NoClean('6.out')
""")
diff --git a/test/Scanner/exception.py b/test/Scanner/exception.py
index 5af7ac3..1a14152 100644
--- a/test/Scanner/exception.py
+++ b/test/Scanner/exception.py
@@ -79,7 +79,6 @@ env.Cat('foo', 'foo.k')
bar_in = File('bar.in')
env.Cat('bar', bar_in)
-bar_in.source_scanner = kscan
""")
test.write('foo.k',
diff --git a/test/TEX/variant_dir_newglossary.py b/test/TEX/variant_dir_newglossary.py
index 8604270..5a28ed4 100644
--- a/test/TEX/variant_dir_newglossary.py
+++ b/test/TEX/variant_dir_newglossary.py
@@ -25,7 +25,7 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
-Validate the use of \newglossary in TeX source files in conjuction
+Validate the use of \newglossary in TeX source files in conjunction
with variant_dir.
Test configuration contributed by Kendrick Boyd.
diff --git a/test/explain/function-actions.py b/test/explain/function-actions.py
index e5f9ba8..bd3ad01 100644
--- a/test/explain/function-actions.py
+++ b/test/explain/function-actions.py
@@ -59,8 +59,8 @@ if mode:
else:
MyCopy = Builder(action = Copy('$TARGET', '$SOURCE'))
def ChangingCopy(target, source, env):
- tgt = str(target[0].abspath)
- src = str(source[0].abspath)
+ tgt = str(target[0].get_abspath())
+ src = str(source[0].get_abspath())
shutil.copy(src, tgt)
ChangingCopy = Builder(action = ChangingCopy)
diff --git a/test/implicit-cache/DualTargets.py b/test/implicit-cache/DualTargets.py
index f0694b1..45174ea 100644
--- a/test/implicit-cache/DualTargets.py
+++ b/test/implicit-cache/DualTargets.py
@@ -37,14 +37,14 @@ test.write('SConstruct', """\
import os.path
def emitter(target, source, env):
- tgt0 = target[0].abspath
+ tgt0 = target[0].get_abspath()
base,ext = os.path.splitext(tgt0)
target.append(base + '.b')
return(target, source)
def source_scan(node, env, path):
- path = node.abspath
+ path = node.get_abspath()
base,ext = os.path.splitext(path)
return [base + '.lib']
diff --git a/test/option--tree.py b/test/option--tree.py
index 519ebe3..a50433c 100644
--- a/test/option--tree.py
+++ b/test/option--tree.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python
-# -*- coding: utf-8 -*-
#
# __COPYRIGHT__
#
@@ -52,20 +51,6 @@ scons: warning: The --debug=tree option is deprecated; please use --tree=all ins
""",
status = 0, match=TestSCons.match_re_dotall)
-
-# Check that printing nodes won't fail with
-# UnicodeDecodeError: 'ascii' codec ... ordinal not in range(128)
-# https://bitbucket.org/scons/scons/pull-request/235
-
-test.write('SConstruct', """\
-# -*- coding: utf-8 -*-
-
-Entry('русский юникод')
-""")
-
-test.run(arguments = '-Q --tree=all')
-
-
test.pass_test()
# Local Variables:
diff --git a/test/option/md5-chunksize.py b/test/option/md5-chunksize.py
index 375208d..dbb2615 100644
--- a/test/option/md5-chunksize.py
+++ b/test/option/md5-chunksize.py
@@ -104,8 +104,8 @@ get_stat(["test.stat"], ["test.big"])
test2.write('SConstruct', """
import os
def get_stat(target, source, env):
- stat = os.stat(source[0].abspath)
- dest = open(target[0].abspath,'w')
+ stat = os.stat(source[0].get_abspath())
+ dest = open(target[0].get_abspath(),'w')
dest.write(str(stat))
dest.close()
env = Environment()
diff --git a/test/packaging/guess-package-name.py b/test/packaging/guess-package-name.py
index 9c85b9a..33c3329 100644
--- a/test/packaging/guess-package-name.py
+++ b/test/packaging/guess-package-name.py
@@ -86,7 +86,7 @@ test.run(stderr = None)
test.must_exist( 'src.tar.gz' )
#
-# TEST: default package name creation with overriden packager.
+# TEST: default package name creation with overridden packager.
#
test.write('SConstruct', """
diff --git a/test/packaging/msi/explicit-target.py b/test/packaging/msi/explicit-target.py
index bc786ee..85bfa85 100644
--- a/test/packaging/msi/explicit-target.py
+++ b/test/packaging/msi/explicit-target.py
@@ -26,7 +26,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
Test the ability to use a explicit target package name and the use
-of FindInstalledFiles() in conjuction with .msi packages.
+of FindInstalledFiles() in conjunction with .msi packages.
"""
import TestSCons
diff --git a/test/sconsign/script/SConsignFile.py b/test/sconsign/script/SConsignFile.py
index a9f79f4..74fb1f0 100644
--- a/test/sconsign/script/SConsignFile.py
+++ b/test/sconsign/script/SConsignFile.py
@@ -173,33 +173,33 @@ inc2.h: %(sig_re)s \d+ \d+
test.run_sconsign(arguments = "--raw .sconsign",
stdout = r"""=== .:
-SConstruct: {'csig': None, 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
-fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
-fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+SConstruct: {'csig': None, 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
=== sub1:
-hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
-hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
%(sig_re)s \[.*\]
-hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
%(sig_re)s \[.*\]
=== sub2:
-hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
-hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub2_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub2_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
%(sig_re)s \[.*\]
-hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub2_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub2_inc1_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub2_inc2_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub2_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub2_inc1_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub2_inc2_h)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
%(sig_re)s \[.*\]
-inc1.h: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
-inc2.h: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+inc1.h: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+inc2.h: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
""" % locals())
expect = r"""=== .:
diff --git a/test/sconsign/script/no-SConsignFile.py b/test/sconsign/script/no-SConsignFile.py
index 829d650..09ecfa2 100644
--- a/test/sconsign/script/no-SConsignFile.py
+++ b/test/sconsign/script/no-SConsignFile.py
@@ -159,14 +159,14 @@ hello.obj: %(sig_re)s \d+ \d+
test.run_sconsign(arguments = "sub1/.sconsign", stdout=expect)
test.run_sconsign(arguments = "--raw sub1/.sconsign",
- stdout = r"""hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
-hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+ stdout = r"""hello.c: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+hello.exe: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub1_hello_obj)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ fake_link\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
%(sig_re)s \[.*\]
-hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
- fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 1}
+hello.obj: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ %(sub1_hello_c)s: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
+ fake_cc\.py: {'csig': '%(sig_re)s', 'timestamp': \d+L?, 'size': \d+L?, '_version_id': 2}
%(sig_re)s \[.*\]
""" % locals())
diff --git a/test/site_scons/sysdirs.py b/test/site_scons/sysdirs.py
index c05ef67..f50485a 100644
--- a/test/site_scons/sysdirs.py
+++ b/test/site_scons/sysdirs.py
@@ -39,7 +39,7 @@ test = TestSCons.TestSCons()
test.write('SConstruct', """
import SCons.Script
-SCons.Script.Main.test_load_all_site_scons_dirs(Dir('.').path)
+SCons.Script.Main.test_load_all_site_scons_dirs(Dir('.').get_internal_path())
""")
test.run(arguments = '-Q .')