diff options
320 files changed, 10280 insertions, 7443 deletions
@@ -9,6 +9,8 @@ syntax:glob *~ *.xcodeproj *.orig +*.DS_Store +*\# doc/user/scons-user doc/user/scons_db.xml diff --git a/QMTest/TestCmd.py b/QMTest/TestCmd.py index e6b128b..d645731 100644 --- a/QMTest/TestCmd.py +++ b/QMTest/TestCmd.py @@ -319,20 +319,6 @@ except ImportError: exec('from UserList import UserList') exec('from UserString import UserString') -try: - # pre-2.7 doesn't have the memoryview() built-in - memoryview -except NameError: - class memoryview: - def __init__(self, obj): - # wrapping buffer in () keeps the fixer from changing it - self.obj = (buffer)(obj) - def __getitem__(self, indx): - if isinstance(indx, slice): - return self.obj[indx.start:indx.stop] - else: - return self.obj[indx] - __all__ = [ 'diff_re', 'fail_test', @@ -488,7 +474,8 @@ def match_re(lines = None, res = None): """ """ if not is_List(lines): - lines = lines.split("\n") + # CRs mess up matching (Windows) so split carefully + lines = re.split('\r?\n', lines) if not is_List(res): res = res.split("\n") if len(lines) != len(res): diff --git a/QMTest/TestCommon.py b/QMTest/TestCommon.py index f1a7407..7387e18 100644 --- a/QMTest/TestCommon.py +++ b/QMTest/TestCommon.py @@ -36,6 +36,8 @@ provided by the TestCommon class: test.must_contain('file', 'required text\n') + test.must_contain_all(output, input, ['title', find]) + test.must_contain_all_lines(output, lines, ['title', find]) test.must_contain_any_line(output, lines, ['title', find]) @@ -122,31 +124,6 @@ __all__.extend([ 'TestCommon', 'dll_suffix', ]) -try: - sorted -except NameError: - # Pre-2.4 Python has no sorted() function. - # - # The pre-2.4 Python list.sort() method does not support - # list.sort(key=) nor list.sort(reverse=) keyword arguments, so - # we must implement the functionality of those keyword arguments - # by hand instead of passing them to list.sort(). - def sorted(iterable, cmp=None, key=None, reverse=False): - if key is not None: - result = [(key(x), x) for x in iterable] - else: - result = iterable[:] - if cmp is None: - # Pre-2.3 Python does not support list.sort(None). - result.sort() - else: - result.sort(cmp) - if key is not None: - result = [t1 for t0,t1 in result] - if reverse: - result.reverse() - return result - # Variables that describe the prefixes and suffixes on this system. if sys.platform == 'win32': exe_suffix = '.exe' @@ -306,6 +283,36 @@ class TestCommon(TestCmd): print(file_contents) self.fail_test(not contains) + def must_contain_all(self, output, input, title=None, find=None): + """Ensures that the specified output string (first argument) + contains all of the specified input as a block (second argument). + + An optional third argument can be used to describe the type + of output being searched, and only shows up in failure output. + + An optional fourth argument can be used to supply a different + function, of the form "find(line, output), to use when searching + for lines in the output. + """ + if find is None: + def find(o, i): + try: + return o.index(i) + except ValueError: + return None + + if is_List(output): + output = os.newline.join(output) + + if find(output, input) is None: + if title is None: + title = 'output' + print 'Missing expected input from %s:' % title + print input + print self.banner(title + ' ') + print output + self.fail_test() + def must_contain_all_lines(self, output, lines, title=None, find=None): """Ensures that the specified output string (first argument) contains all of the specified lines (second argument). diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index 3fe07c3..d98b155 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.1.alpha.yyyymmdd' +default_version = '2.4.2.alpha.yyyymmdd' python_version_unsupported = (2, 3, 0) python_version_deprecated = (2, 7, 0) @@ -693,7 +693,7 @@ class TestSCons(TestCommon): else: jni_dirs = ['/System/Library/Frameworks/JavaVM.framework/Versions/%s*/Headers/jni.h'%version] jni_dirs.extend(['/usr/lib/jvm/java-*-sun-%s*/include/jni.h'%version, - '/usr/lib/jvm/java-%s*-openjdk/include/jni.h'%version, + '/usr/lib/jvm/java-%s*-openjdk*/include/jni.h'%version, '/usr/java/jdk%s*/include/jni.h'%version]) dirs = self.paths(jni_dirs) if not dirs: @@ -714,6 +714,9 @@ class TestSCons(TestCommon): home = '/System/Library/Frameworks/JavaVM.framework/Home' else: home = '/System/Library/Frameworks/JavaVM.framework/Versions/%s/Home' % version + if not os.path.exists(home): + # This works on OSX 10.10 + home = '/System/Library/Frameworks/JavaVM.framework/Versions/Current/' else: jar = self.java_where_jar(version) home = os.path.normpath('%s/..'%jar) diff --git a/QMTest/TestSConsMSVS.py b/QMTest/TestSConsMSVS.py index 478438a..7459af0 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 @@ -709,6 +729,7 @@ expected_vcprojfile_10_0 = """\ \t<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> \t\t<ConfigurationType>Makefile</ConfigurationType> \t\t<UseOfMfc>false</UseOfMfc> +\t\t<PlatformToolset>v100</PlatformToolset> \t</PropertyGroup> \t<Import Project="$(VCTargetsPath)\\Microsoft.Cpp.props" /> \t<ImportGroup Label="ExtensionSettings"> @@ -773,6 +794,72 @@ expected_vcprojfile_11_0 = """\ \t<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> \t\t<ConfigurationType>Makefile</ConfigurationType> \t\t<UseOfMfc>false</UseOfMfc> +\t\t<PlatformToolset>v110</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 && "<PYTHON>" -c "<SCONS_SCRIPT_MAIN_XML>" -C "<WORKPATH>" -f SConstruct "Test.exe"</NMakeBuildCommandLine> +\t\t<NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo Starting SCons && "<PYTHON>" -c "<SCONS_SCRIPT_MAIN_XML>" -C "<WORKPATH>" -f SConstruct "Test.exe"</NMakeReBuildCommandLine> +\t\t<NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo Starting SCons && "<PYTHON>" -c "<SCONS_SCRIPT_MAIN_XML>" -C "<WORKPATH>" -f SConstruct -c "Test.exe"</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> +""" + +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"> @@ -910,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.""" @@ -47,7 +47,7 @@ version at the SCons download page: Execution Requirements ====================== -Running SCons requires Python version 2.4 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.0.0" and "sconsign-2.0.0" scripts in +- Install the version-numbered "scons-2.4.1" and "sconsign-2.4.1" 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.0.0" and "sconsign-2.0.0" + hard links or symbolic links to the "scons-2.4.1" and "sconsign-2.4.1" scripts by specifying the "--hardlink-scons" or "--symlink-scons" options on the command line. -- Install "scons-2.0.0.bat" and "scons.bat" wrapper scripts in the Python +- Install "scons-2.4.1.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.0.0.bat" and "scons.bat" files installed in the default system + have "scons-2.4.1.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.0.0 or - C:\\Python\*\\scons-2.0.0, 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,13 +462,13 @@ running all of "runtest.py -a". Building Packages ================= -We use SCons (version 0.96.93 later) to build its own packages. If you +We use SCons (version 2.4.1 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:: $ scons -If you don't have SCons version 0.96.93 later already installed on your +If you don't have SCons already installed on your system, you can use the supplied bootstrap.py script (see the section above about `Executing SCons Without Installing`_):: @@ -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.0.0-1.noarch.rpm - build/dist/scons-2.0.0-1.src.rpm - build/dist/scons-2.0.0.linux-i686.tar.gz - build/dist/scons-2.3.1.alpha.yyyymmdd.tar.gz - build/dist/scons-2.3.1.alpha.yyyymmdd.win32.exe - build/dist/scons-2.3.1.alpha.yyyymmdd.zip - build/dist/scons-doc-2.3.1.alpha.yyyymmdd.tar.gz - build/dist/scons-local-2.3.1.alpha.yyyymmdd.tar.gz - build/dist/scons-local-2.3.1.alpha.yyyymmdd.zip - build/dist/scons-src-2.3.1.alpha.yyyymmdd.tar.gz - build/dist/scons-src-2.3.1.alpha.yyyymmdd.zip - build/dist/scons_1.3.0-1_all.deb + build/dist/scons-2.4.1-1.noarch.rpm + build/dist/scons-2.4.1-1.src.rpm + build/dist/scons-2.4.1.linux-i686.tar.gz + build/dist/scons-2.4.2.alpha.yyyymmdd.tar.gz + build/dist/scons-2.4.2.alpha.yyyymmdd.win32.exe + build/dist/scons-2.4.2.alpha.yyyymmdd.zip + build/dist/scons-doc-2.4.2.alpha.yyyymmdd.tar.gz + build/dist/scons-local-2.4.2.alpha.yyyymmdd.tar.gz + build/dist/scons-local-2.4.2.alpha.yyyymmdd.zip + build/dist/scons-src-2.4.2.alpha.yyyymmdd.tar.gz + build/dist/scons-src-2.4.2.alpha.yyyymmdd.zip + build/dist/scons_2.4.1-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 6da53b1..e1f462a 100644 --- a/ReleaseConfig +++ b/ReleaseConfig @@ -32,13 +32,13 @@ __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, 1, 'alpha', 0) +version_tuple = (2, 4, 2, '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 # cause a warning to be issued (assuming it's not disabled). These values are # mandatory and must be present in the configuration file. -unsupported_python_version = (2, 3, 0) +unsupported_python_version = (2, 6, 0) deprecated_python_version = (2, 7, 0) # If release_date is (yyyy, mm, dd, hh, mm, ss), that is used as the release @@ -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 @@ -4,7 +4,7 @@ # See the README.rst file for an overview of how SCons is built and tested. from __future__ import print_function -copyright_years = '2001 - 2014' +copyright_years = '2001 - 2015' # This gets inserted into the man pages to reflect the month of release. month_year = 'MONTH YEAR' @@ -44,11 +44,17 @@ import tempfile import bootstrap project = 'scons' -default_version = '2.3.1.alpha.yyyymmdd' +default_version = '2.4.2.alpha.yyyymmdd' copyright = "Copyright (c) %s The SCons Foundation" % copyright_years platform = distutils.util.get_platform() +def is_windows(): + if platform.startswith('win'): + return True + else: + return False + SConsignFile() # @@ -57,7 +63,7 @@ SConsignFile() # def whereis(file): exts = [''] - if platform == "win32": + if is_windows(): exts += ['.exe'] for dir in os.environ['PATH'].split(os.pathsep): f = os.path.join(dir, file) @@ -83,7 +89,6 @@ fakeroot = whereis('fakeroot') gzip = whereis('gzip') rpmbuild = whereis('rpmbuild') hg = os.path.exists('.hg') and whereis('hg') -svn = os.path.exists('.svn') and whereis('svn') unzip = whereis('unzip') zip = whereis('zip') @@ -112,16 +117,11 @@ if not version: version = default_version hg_status_lines = [] -svn_status_lines = [] if hg: cmd = "%s status --all 2> /dev/null" % hg hg_status_lines = os.popen(cmd, "r").readlines() -if svn: - cmd = "%s status --verbose 2> /dev/null" % svn - svn_status_lines = os.popen(cmd, "r").readlines() - revision = ARGUMENTS.get('REVISION', '') def generate_build_id(revision): return revision @@ -140,17 +140,6 @@ if not revision and hg: result = result + '[MODIFIED]' return result -if not revision and svn: - svn_info = os.popen("%s info 2> /dev/null" % svn, "r").read() - m = re.search('Revision: (\d+)', svn_info) - if m: - revision = m.group(1) - def generate_build_id(revision): - result = 'r' + revision - if [l for l in svn_status_lines if l[0] in 'ACDMR']: - result = result + '[MODIFIED]' - return result - checkpoint = ARGUMENTS.get('CHECKPOINT', '') if checkpoint: if checkpoint == 'd': @@ -167,6 +156,14 @@ if build_id is None: else: build_id = '' +import os.path +import distutils.command + +no_winpack_templates = not os.path.exists(os.path.join(os.path.split(distutils.command.__file__)[0],'wininst-9.0.exe')) +skip_win_packages = ARGUMENTS.get('SKIP_WIN_PACKAGES',False) or no_winpack_templates +if skip_win_packages: + print "Skipping the build of Windows packages..." + python_ver = sys.version[0:3] # @@ -223,13 +220,15 @@ command_line_variables = [ ("REVISION=", "The revision number of the source being built. " + "The default is the Subversion revision returned " + - "'svn info', with an appended string of " + + "'hg heads', with an appended string of " + "'[MODIFIED]' if there are any changes in the " + "working copy."), ("VERSION=", "The SCons version being packaged. The default " + "is the hard-coded value '%s' " % default_version + "from this SConstruct file."), + + ("SKIP_WIN_PACKAGES=", "If set, skip building win32 and win64 packages."), ] Default('.', build_dir) @@ -268,7 +267,7 @@ test_local_zip_dir = os.path.join(build_dir, "test-local-zip") unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz") unpack_zip_dir = os.path.join(build_dir, "unpack-zip") -if platform == "win32": +if is_windows(): tar_hflag = '' python_project_subinst_dir = os.path.join("Lib", "site-packages", project) project_script_subinst_dir = 'Scripts' @@ -332,7 +331,8 @@ try: path = os.path.join(dirname, name) if os.path.isfile(path): arg.write(path) - zf = zipfile.ZipFile(str(target[0]), 'w') + # default ZipFile compression is ZIP_STORED + zf = zipfile.ZipFile(str(target[0]), 'w', compression=zipfile.ZIP_DEFLATED) olddir = os.getcwd() os.chdir(env['CD']) try: os.path.walk(env['PSV'], visit, zf) @@ -357,7 +357,7 @@ try: if not os.path.isdir(dest): open(dest, 'wb').write(zf.read(name)) -except: +except ImportError: if unzip and zip: zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV" unzipit = "$UNZIP $UNZIPFLAGS $SOURCES" @@ -490,10 +490,13 @@ Version_values = [Value(version), Value(build_id)] # separate packages. # +from distutils.sysconfig import get_python_lib; + + python_scons = { 'pkg' : 'python-' + project, 'src_subdir' : 'engine', - 'inst_subdir' : os.path.join('lib', 'python1.5', 'site-packages'), + 'inst_subdir' : get_python_lib(), 'rpm_dir' : '/usr/lib/scons', 'debian_deps' : [ @@ -731,10 +734,7 @@ for p in [ scons ]: platform_zip = os.path.join(build, 'dist', "%s.%s.zip" % (pkg_version, platform)) - if platform == "win-amd64": - win32_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version) - else: - win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version) + # # Update the environment with the relevant information @@ -845,12 +845,19 @@ for p in [ scons ]: Local(*build_src_files) distutils_formats = [] + distutils_targets = [] + + if not skip_win_packages: + win64_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version) + win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version) + distutils_targets.extend([ win32_exe , win64_exe ]) - distutils_targets = [ win32_exe ] + dist_distutils_targets = [] - dist_distutils_targets = env.Install('$DISTDIR', distutils_targets) - Local(dist_distutils_targets) - AddPostAction(dist_distutils_targets, Chmod(dist_distutils_targets, 0o644)) + for target in distutils_targets: + dist_target = env.Install('$DISTDIR', target) + AddPostAction(dist_target, Chmod(dist_target, 0o644)) + dist_distutils_targets += dist_target if not gzip: print("gzip not found in %s; skipping .tar.gz package for %s." % (os.environ['PATH'], pkg)) @@ -1082,7 +1089,10 @@ for p in [ scons ]: commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \ ','.join(distutils_formats)) - commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst --plat-name win32 --user-access-control auto") + if not skip_win_packages: + commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst --plat-name=win32 --user-access-control auto") + + commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst --plat-name=win-amd64 --user-access-control auto") env.Command(distutils_targets, build_src_files, commands) @@ -1110,7 +1120,7 @@ for p in [ scons ]: for script in scripts: # add .py extension for scons-local scripts on non-windows platforms - if platform == "win32": + if is_windows(): break local_script = os.path.join(build_dir_local, script) commands.append(Move(local_script + '.py', local_script)) @@ -1134,7 +1144,7 @@ for p in [ scons ]: Local(l) if gzip: - if platform == "win32": + if is_windows(): # avoid problem with tar interpreting c:/ as a remote machine tar_cargs = '-cz --force-local -f' else: @@ -1209,12 +1219,8 @@ sfiles = None if hg_status_lines: slines = [l for l in hg_status_lines if l[0] in 'ACM'] sfiles = [l.split()[-1] for l in slines] -elif svn_status_lines: - slines = [l for l in svn_status_lines if l[0] in ' MA'] - sentries = [l.split()[-1] for l in slines] - sfiles = list(filter(os.path.isfile, sentries)) else: - print("Not building in a Mercurial or Subversion tree; skipping building src package.") + print("Not building in a Mercurial tree; skipping building src package.") if sfiles: remove_patterns = [ diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py index 0d74a36..d566644 100644 --- a/bin/SConsDoc.py +++ b/bin/SConsDoc.py @@ -128,8 +128,7 @@ except: try: import lxml except: - print("Failed to import either libxml2/libxslt or lxml") - sys.exit(1) + raise ImportError("Failed to import either libxml2/libxslt or lxml") has_etree = False if not has_libxml2: @@ -155,8 +154,7 @@ if not has_etree: # normal ElementTree install import elementtree.ElementTree as etree except ImportError: - print("Failed to import ElementTree from any known place") - sys.exit(1) + raise ImportError("Failed to import ElementTree from any known place") re_entity = re.compile("\&([^;]+);") re_entity_header = re.compile("<!DOCTYPE\s+sconsdoc\s+[^\]]+\]>") 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 64dcf2e..73aa31a 100644 --- a/bin/docs-create-example-outputs.py +++ b/bin/docs-create-example-outputs.py @@ -15,6 +15,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 d22cc32..c164baf 100644 --- a/bin/docs-update-generated.py +++ b/bin/docs-update-generated.py @@ -9,6 +9,7 @@ from __future__ import print_function import os +import sys import SConsDoc # Directory where all generated files are stored @@ -42,8 +43,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 e53a89d..342ed43 100644 --- a/bin/docs-validate.py +++ b/bin/docs-validate.py @@ -26,3 +26,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/scons-proc.py b/bin/scons-proc.py index 138cff7..e09c853 100644 --- a/bin/scons-proc.py +++ b/bin/scons-proc.py @@ -16,11 +16,7 @@ import os import re import string import sys -try: - from io import StringIO # usable as of 2.6; takes unicode only -except ImportError: - # No 'io' module or no StringIO in io - exec('from cStringIO import StringIO') +from io import StringIO # usable as of 2.6; takes unicode only import SConsDoc from SConsDoc import tf as stf 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 4a677db..49f2788 100644 --- a/bin/update-release-info.py +++ b/bin/update-release-info.py @@ -296,6 +296,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/bootstrap.py b/bootstrap.py index f3bc105..08df11d 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -27,6 +27,8 @@ import os.path import sys import glob import subprocess +import filecmp +import shutil __doc__ = """bootstrap.py @@ -127,7 +129,7 @@ def main(): def must_copy(dst, src): if not os.path.exists(dst): return 1 - return open(dst, 'rb').read() != open(src, 'rb').read() + return not filecmp.cmp(dst,src) # Note: We don't use the getopt module to process the command-line # arguments because we'd have to teach it about all of the SCons options. @@ -195,7 +197,8 @@ def main(): os.makedirs(dir) try: os.unlink(dst) except: pass - open(dst, 'wb').write( open(src, 'rb').read() ) + + shutil.copyfile(src,dst) if update_only: sys.exit(0) diff --git a/debian/changelog b/debian/changelog index ec049e1..9d60e3a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,45 @@ +scons (2.4.1) unstable; urgency=low + + * Maintenance release. + + -- William Deegan <bill@baddogconsulting.com> Sat, 07 Nov 2015 08:56:00 -0700 + +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. + + -- Gary Oberbrunner <garyo@oberbrunner.com> Sun, 27 Sep 2014 21:00:00 -0500 + +scons (2.3.3) unstable; urgency=low + + * Maintenance release. + + -- Gary Oberbrunner <garyo@oberbrunner.com> Sun, 24 Aug 2014 21:00:00 -0500 + +scons (2.3.2) unstable; urgency=low + + * Maintenance release. + + -- Gary Oberbrunner <garyo@oberbrunner.com> Fri, 4 July 2014 21:00:00 -0500 + scons (2.3.0) unstable; urgency=low * Maintenance release. diff --git a/doc/SConscript b/doc/SConscript index d9afc23..6dbf33d 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -29,25 +29,43 @@ import os.path import re import sys import glob -import SConsDoc -import SConsExamples + import bootstrap Import('build_dir', 'env', 'whereis', 'revaction') -env = env.Clone() +# +# -- Check prerequisites for building the documentation --- +# +skip_doc = False -build = os.path.join(build_dir, 'doc') +try: + import SConsDoc + import SConsExamples +except ImportError as exc: + print("doc: SConsDoc failed to import, the error was:") + print(" ImportError: %s" % exc) + print(" Please make sure that python-libxml2 or python-lxml is installed.") + skip_doc = True fop = whereis('fop') xep = whereis('xep') + +if not fop and not xep: + print "doc: No PDF renderer found (fop|xep)!" + skip_doc = True + +# +# --- Configure build +# +env = env.Clone() + +build = os.path.join(build_dir, 'doc') + epydoc_cli = whereis('epydoc') gs = whereis('gs') lynx = whereis('lynx') -# -# -# dist_doc_tar_gz = '$DISTDIR/scons-doc-${VERSION}.tar.gz' tar_deps = [] @@ -56,7 +74,9 @@ tar_list = [] orig_env = env env = orig_env.Clone(SCONS_PY = File('#src/script/scons.py').rfile()) - +# +# --- Helpers --- +# def writeVersionXml(verfile, date, ver, rev): """ Helper function: Write a version.xml file. """ try: @@ -76,31 +96,54 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY buildrevision "%s"> """ % (date, ver, rev)) + +# The names of the target files for the MAN pages +man_page_list = ['scons.1','scons-time.1','sconsign.1'] + +# Template for the MAN page texts when we can't properly +# create them because the skip_doc flag is set (required +# modules/tools aren't installed in the current system) +man_replace_tpl = """.TH "%(uctitle)s" "1" "%(today)s" "SCons %(version)s" "SCons %(version)s" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.SH "NOTE" +%(title)s \- This is a replacement file, stemming from an incomplete +packaging process without the required doc modules installed. Please +update the system and try running bootstrap.py again. +""" + # -# Check whether we have all tools installed for -# building the documentation. +# --- Processing --- # -skip_doc = False -try: - import libxml2 -except: - try: - import lxml - except: - print("doc: Neither libxml2 nor lxml Python bindings found!") - print(" Please install one of the packages python-libxml2 or python-lxml.") - skip_doc = True - -if not fop and not xep: - print("doc: No PDF renderer found (fop|xep)!") - skip_doc = True - + if skip_doc: print("doc: ...skipping building User Guide.") + print(" ...creating fake MAN pages.") + + # Since the top-level SConstruct requires the MAN + # pages to exist for the basic packaging, we create simple + # stub texts here as replacement... + scdir = os.path.join(build, 'man') + if not os.path.isdir(scdir): + os.makedirs(scdir) + + import datetime + today = datetime.date.today().strftime("%m/%d/%Y") + version = env.subst('$VERSION') + for m in man_page_list: + man, _ = os.path.splitext(m) + fman = open(os.path.join(scdir, m), "w") + fman.write(man_replace_tpl % {'uctitle' : man.upper().replace("-", "\\-"), + 'today' : today, + 'title' : man, + 'version' : version}) + fman.close() else: if not lynx: print("doc: Warning, lynx is not installed...created release packages won't be complete!") - + # # Always create a version.xml file containing the version information # for this run. Ignore it for dependency purposes so we don't @@ -116,7 +159,7 @@ else: # # Builder for copying files to an Install dir, based # on their extension (better: glob matching pattern)... - # + # def _glob_install_action(target, source, env): if not SCons.Util.is_List(target): target = [target] @@ -129,7 +172,7 @@ else: target = [target] if not SCons.Util.is_List(source): source = [source] - + res = [] res_src = [] tdir = env.Dir(target[0]) @@ -144,7 +187,7 @@ else: # # Builder for copying ChunkedHTML files to an Install dir... - # + # def _chunked_install_action(target, source, env): if not SCons.Util.is_List(target): target = [target] @@ -154,13 +197,13 @@ else: spattern = os.path.join(os.path.split(str(source[0]))[0], '*.html') for g in glob.glob(spattern): shutil.copy(g, tdir) - + def _chunked_install_emitter(target, source, env): if not SCons.Util.is_List(target): target = [target] if not SCons.Util.is_List(source): source = [source] - + tdir = env.Dir(target[0]) head, tail = os.path.split(str(source[0])) return os.path.join(str(tdir), tail), source @@ -178,18 +221,18 @@ else: print("OK") else: print("Validation failed! Please correct the errors above and try again.") - sys.exit(0) - + sys.exit(1) + print("Checking whether all example names are unique...") if SConsExamples.exampleNamesAreUnique(os.path.join('doc','user')): 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) + # List of prerequisite files in the build/doc folder buildsuite = [] - + def copy_dbfiles(env, toolpath, paths, fpattern, use_builddir=True): """ Helper function, copies a bunch of files matching the given fpattern to a target directory. @@ -210,7 +253,7 @@ else: target_dir = env.Dir(os.path.join(*(toolpath+paths))) buildsuite.extend(env.GlobInstall(target_dir, os.path.join(*(paths + fpattern)))) - + # # Copy generated files (.gen/.mod/.xml) to the build folder # @@ -259,7 +302,7 @@ else: # installing/copying them to the build directory. It basically # links the original sources to the respective build folder, # such that a simple 'python bootstrap.py' rebuilds the - # documentation when a file, like 'doc/user/depends.xml' + # documentation when a file, like 'doc/user/depends.xml' # for example, changes. # Finally, in DOCNODES we store the created PDF and HTML files, # such that we can then install them in the proper places for @@ -274,10 +317,7 @@ else: 'user' : (['chunked','html','pdf','epub','text'], [], []), 'man' : (['man','epub','text'], [], []) } - - # The names of the target files for the MAN pages - man_page_list = ['scons.1','scons-time.1','sconsign.1'] - + # # We have to tell SCons to scan the top-level XML files which # get included by the document XML files in the subdirectories. @@ -289,15 +329,15 @@ else: continue base, ext = os.path.splitext(s) if ext in ['.fig', '.jpg']: - buildsuite.extend(env.Command(os.path.join(build, s), - s, + buildsuite.extend(env.Command(os.path.join(build, s), + s, Copy("$TARGET", "$SOURCE"))) else: revaction([env.File(os.path.join(build, s))], [env.File(s)], env) for doc in docs: - + # # Read MANIFEST file and copy the listed files to the # build directory, while branding them with the @@ -321,7 +361,7 @@ else: else: target_dir = os.path.join(build, doc) if ext in ['.fig', '.jpg', '.svg']: - docs[doc][DOCDEPENDS].extend(env.Command(build_s, doc_s, + docs[doc][DOCDEPENDS].extend(env.Command(build_s, doc_s, Copy("$TARGET", "$SOURCE"))) else: btarget = env.File(build_s) @@ -351,18 +391,18 @@ else: sctargets.append(env.File(os.path.join(scdir, 'scons-%s.pdf' % doc))) if 'epub' in docs[doc][DOCTARGETS]: sctargets.append(env.File(os.path.join(scdir, 'scons-%s.epub' % doc))) - + if 'man' in docs[doc][DOCTARGETS]: for m in man_page_list: sctargets.append(os.path.join(scdir, m)) man, _1 = os.path.splitext(m) - + sctargets.append(os.path.join(scdir, 'scons-%s.pdf' % man)) sctargets.append(os.path.join(scdir, 'scons-%s.html' % man)) - - docs[doc][DOCNODES].extend(env.Command(sctargets, buildsuite + docs[doc][DOCDEPENDS], + + docs[doc][DOCNODES].extend(env.Command(sctargets, buildsuite + docs[doc][DOCDEPENDS], "cd %s && $PYTHON ${SCONS_PY.abspath}%s" % (scdir, cleanopt))) - + install_css = False for doc in docs: @@ -374,13 +414,13 @@ else: epub = os.path.join(build, 'EPUB', 'scons-%s.epub' % doc) text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc) if 'chunked' in docs[doc][DOCTARGETS]: - installed_chtml = env.ChunkedInstall(env.Dir(htmldir), + installed_chtml = env.ChunkedInstall(env.Dir(htmldir), os.path.join(build, doc,'scons-%s' % doc, 'index.html')) installed_chtml_css = env.Install(env.Dir(htmldir), os.path.join(build, doc, 'scons.css')) env.Depends(installed_chtml, docs[doc][DOCNODES]) env.Depends(installed_chtml_css, docs[doc][DOCNODES]) - + tar_deps.extend([htmlindex, installed_chtml_css]) tar_list.extend([htmldir]) Local(htmlindex) @@ -393,7 +433,7 @@ else: Local(html) env.Ignore(html, version_xml) install_css = True - + if 'pdf' in docs[doc][DOCTARGETS]: env.InstallAs(env.File(pdf), env.File(os.path.join(build, doc,'scons-%s.pdf' % doc))) Local(pdf) @@ -409,14 +449,14 @@ else: tar_deps.append(epub) tar_list.append(epub) - - if ('text' in docs[doc][DOCTARGETS] and lynx and + + if ('text' in docs[doc][DOCTARGETS] and lynx and (('html' in docs[doc][DOCTARGETS]) or (doc == 'man'))): texthtml = os.path.join(build, doc,'index.html') if doc == 'man': # Special handling for single MAN file texthtml = os.path.join(build, doc, 'scons-scons.html') - + env.Command(text, env.File(texthtml), "lynx -dump ${SOURCE.abspath} > $TARGET") Local(text) @@ -424,21 +464,21 @@ else: tar_deps.append(text) tar_list.append(text) - - + + if 'man' in docs[doc][DOCTARGETS]: # # Man page(s) # for m in man_page_list: man, _1 = os.path.splitext(m) - + pdf = os.path.join(build, 'PDF', '%s-man.pdf' % man) html = os.path.join(build, 'HTML' , '%s-man.html' % man) - + env.InstallAs(env.File(pdf), env.File(os.path.join(build, 'man','scons-%s.pdf' % man))) env.InstallAs(env.File(html), env.File(os.path.join(build, 'man','scons-%s.html' % man))) - + tar_deps.extend([pdf, html]) tar_list.extend([pdf, html]) @@ -446,7 +486,7 @@ else: # Install CSS file, common to all single HTMLs if install_css: css_file = os.path.join(build, 'HTML', 'scons.css') - env.InstallAs(env.File(css_file), + env.InstallAs(env.File(css_file), env.File(os.path.join(build, 'user','scons.css'))) tar_deps.extend([css_file]) tar_list.extend([css_file]) @@ -502,7 +542,7 @@ if not epydoc_cli: # http://epydoc.svn.sourceforge.net/viewvc/epydoc/trunk/epydoc/src/epydoc/cli.py elif env['EPYDOCFLAGS'] == '--pdf': - pdf_writer = LatexWriter(docindex, + pdf_writer = LatexWriter(docindex, docformat='restructuredText', prj_name='SCons', prj_url='http://www.scons.org/') @@ -522,7 +562,7 @@ else: # epydoc_cli is found Touch('$TARGET'), ] - + if not epydoc_cli and not epydoc: print("doc: epydoc not found, skipping building API documentation.") else: diff --git a/doc/design/titlepage/SConsBuildBricks_path.svg b/doc/design/titlepage/SConsBuildBricks_path.svg index ed0c60d..0d7f63e 100644 --- a/doc/design/titlepage/SConsBuildBricks_path.svg +++ b/doc/design/titlepage/SConsBuildBricks_path.svg @@ -14,9 +14,9 @@ height="80.330002" id="svg2" sodipodi:version="0.32" - inkscape:version="0.48.1 r9760" + inkscape:version="0.48.4 r9939" version="1.0" - sodipodi:docname="SConsBuildBricks.svg" + sodipodi:docname="SConsBuildBricks_path.svg" inkscape:export-filename="Constructs-using-SCons.png" inkscape:export-xdpi="100" inkscape:export-ydpi="100"> @@ -77,24 +77,22 @@ </cc:Agent> </dc:contributor> <cc:license - rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.0/" /> + rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" /> </cc:Work> <cc:License - rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + rdf:about="http://creativecommons.org/licenses/by-sa/3.0/"> <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> + rdf:resource="http://creativecommons.org/ns#Reproduction" /> <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> + rdf:resource="http://creativecommons.org/ns#Distribution" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> + rdf:resource="http://creativecommons.org/ns#Notice" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Attribution" /> - <cc:prohibits - rdf:resource="http://web.resource.org/cc/CommercialUse" /> + rdf:resource="http://creativecommons.org/ns#Attribution" /> <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> + rdf:resource="http://creativecommons.org/ns#ShareAlike" /> </cc:License> </rdf:RDF> </metadata> diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index 3d534b0..d49156f 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -486,6 +486,14 @@ and source arguments list different numbers of files or directories. </para> + +<example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> +env.InstallAs(target = '/usr/local/bin/foo', + source = 'foo_debug') +env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], + source = ['libFOO.a', 'libBAR.a']) +</example_commands> + </listitem> </varlistentry> <varlistentry id="b-InstallVersionedLib"> @@ -497,17 +505,13 @@ arguments list different numbers of files or directories. </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. -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.InstallAs(target = '/usr/local/bin/foo', - source = 'foo_debug') -env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], - source = ['libFOO.a', 'libBAR.a']) +env.InstallVersionedLib(target = '/usr/local/bin/foo', + source = 'libxyz.1.5.2.so') </example_commands> </listitem> </varlistentry> @@ -832,161 +836,105 @@ Compile files for languages defined in <filename>LINGUAS</filename> file <term> <function>env.MSVSProject()</function> </term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Builds a Microsoft Visual Studio project file, -and by default builds a solution file as well. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -This builds a Visual Studio project file, based on the version of -Visual Studio that is configured (either the latest installed version, -or the version specified by -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVS_VERSION"><envar>$MSVS_VERSION</envar></link> -in the Environment constructor). -For Visual Studio 6, it will generate a -<filename>.dsp</filename> -file. -For Visual Studio 7 (.NET) and later versions, it will generate a -<filename>.vcproj</filename> -file. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -By default, -this also generates a solution file -for the specified project, -a -<filename>.dsw</filename> -file for Visual Studio 6 -or a -<filename>.sln</filename> -file for Visual Studio 7 (.NET). -This behavior may be disabled by specifying -<literal>auto_build_solution=0</literal> -when you call -<function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function>, -in which case you presumably want to -build the solution file(s) -by calling the -<function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSSolution</function> -Builder (see below). -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> builder -takes several lists of filenames -to be placed into the project file. -These are currently limited to -<literal>srcs</literal>, -<literal>incs</literal>, -<literal>localincs</literal>, -<literal>resources</literal>, -and -<literal>misc</literal>. -These are pretty self-explanatory, but it should be noted that these -lists are added to the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SOURCES"><envar>$SOURCES</envar></link> construction variable as strings, -NOT as SCons File Nodes. This is because they represent file -names to be added to the project file, not the source files used to -build the project file. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The above filename lists are all optional, -although at least one must be specified -for the resulting project file to be non-empty. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -In addition to the above lists of values, -the following values may be specified: -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>target</literal>: -The name of the target -<filename>.dsp</filename> -or -<filename>.vcproj</filename> -file. -The correct -suffix for the version of Visual Studio must be used, -but the -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSPROJECTSUFFIX"><envar>$MSVSPROJECTSUFFIX</envar></link> -construction variable -will be defined to the correct value (see example below). -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>variant</literal>: -The name of this particular variant. -For Visual Studio 7 projects, -this can also be a list of variant names. -These are typically things like "Debug" or "Release", but really -can be anything you want. -For Visual Studio 7 projects, -they may also specify a target platform -separated from the variant name by a -<literal>|</literal> -(vertical pipe) -character: -<literal>Debug|Xbox</literal>. -The default target platform is Win32. -Multiple calls to -<function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> -with different variants are allowed; -all variants will be added to the project file with their appropriate -build targets and sources. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>buildtarget</literal>: -An optional string, node, or list of strings or nodes -(one per build variant), to tell the Visual Studio debugger -what output target to use in what build variant. -The number of -<literal>buildtarget</literal> -entries must match the number of -<literal>variant</literal> -entries. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>runfile</literal>: -The name of the file that Visual Studio 7 and later -will run and debug. -This appears as the value of the -<literal>Output</literal> -field in the resutling Visual Studio project file. -If this is not specified, -the default is the same as the specified -<literal>buildtarget</literal> -value. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Note that because <application xmlns="http://www.scons.org/dbxsd/v1.0">SCons</application> always executes its build commands -from the directory in which the <filename xmlns="http://www.scons.org/dbxsd/v1.0">SConstruct</filename> file is located, -if you generate a project file in a different directory -than the <filename xmlns="http://www.scons.org/dbxsd/v1.0">SConstruct</filename> directory, -users will not be able to double-click -on the file name in compilation error messages -displayed in the Visual Studio console output window. -This can be remedied by adding the -Visual C/C++ -<literal>/FC</literal> -compiler option to the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-CCFLAGS"><envar>$CCFLAGS</envar></link> variable -so that the compiler will print -the full path name of any -files that cause compilation errors. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Example usage: -</para> - -<example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> -barsrcs = ['bar.cpp'], + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0"> Builds a Microsoft Visual Studio project +file, and by default builds a solution file as well. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> This +builds a Visual Studio project file, based on the version of Visual Studio +that is configured (either the latest installed version, or the version +specified by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVS_VERSION"><envar>$MSVS_VERSION</envar></link> in the Environment constructor). For +Visual Studio 6, it will generate a <filename>.dsp</filename> file. For Visual +Studio 7 (.NET) and later versions, it will generate a +<filename>.vcproj</filename> file. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> By default, this also +generates a solution file for the specified project, a +<filename>.dsw</filename> file for Visual Studio 6 or a +<filename>.sln</filename> file for Visual Studio 7 (.NET). This behavior may +be disabled by specifying <literal>auto_build_solution=0</literal> when you +call <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function>, in which case you presumably want to build the solution +file(s) by calling the <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSSolution</function> Builder (see below). </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> +The <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> builder takes several lists of filenames to be placed into +the project file. These are currently limited to <literal>srcs</literal>, +<literal>incs</literal>, <literal>localincs</literal>, +<literal>resources</literal>, and <literal>misc</literal>. These are pretty +self-explanatory, but it should be noted that these lists are added to the +<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SOURCES"><envar>$SOURCES</envar></link> construction variable as strings, NOT as SCons File Nodes. +This is because they represent file names to be added to the project file, not +the source files used to build the project file. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> The above +filename lists are all optional, although at least one must be specified for +the resulting project file to be non-empty. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> In addition to the +above lists of values, the following values may be specified: +</para><variablelist xmlns="http://www.scons.org/dbxsd/v1.0"> + <varlistentry> + <term>target</term> + + <listitem> + <para>The name of the target <filename>.dsp</filename> or + <filename>.vcproj</filename> file. The correct suffix for the version + of Visual Studio must be used, but the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSPROJECTSUFFIX"><envar>$MSVSPROJECTSUFFIX</envar></link> + construction variable will be defined to the correct value (see + example below).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>variant</term> + + <listitem> + <para>The name of this particular variant. For Visual Studio 7 + projects, this can also be a list of variant names. These are + typically things like "Debug" or "Release", but really can be anything + you want. For Visual Studio 7 projects, they may also specify a target + platform separated from the variant name by a <literal>|</literal> + (vertical pipe) character: <literal>Debug|Xbox</literal>. The default + target platform is Win32. Multiple calls to <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> with + different variants are allowed; all variants will be added to the + project file with their appropriate build targets and + sources.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>cmdargs</term> + + <listitem> + <para>Additional command line arguments for the different + variants. The number of <literal>cmdargs</literal> entries must match + the number of <literal>variant</literal> entries, or be empty (not + specified). If you give only one, it will automatically be propagated + to all variants.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>buildtarget</term> + + <listitem> + <para>An optional string, node, or list of strings or nodes (one + per build variant), to tell the Visual Studio debugger what output + target to use in what build variant. The number of + <literal>buildtarget</literal> entries must match the number of + <literal>variant</literal> entries.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>runfile</term> + + <listitem> + <para>The name of the file that Visual Studio 7 and later will + run and debug. This appears as the value of the + <literal>Output</literal> field in the resulting Visual Studio project + file. If this is not specified, the default is the same as the + specified <literal>buildtarget</literal> value.</para> + </listitem> + </varlistentry> + </variablelist><para xmlns="http://www.scons.org/dbxsd/v1.0"> Note that because <application xmlns="http://www.scons.org/dbxsd/v1.0">SCons</application> always executes its build +commands from the directory in which the <filename xmlns="http://www.scons.org/dbxsd/v1.0">SConstruct</filename> file is located, if you +generate a project file in a different directory than the <filename xmlns="http://www.scons.org/dbxsd/v1.0">SConstruct</filename> +directory, users will not be able to double-click on the file name in +compilation error messages displayed in the Visual Studio console output +window. This can be remedied by adding the Visual C/C++ <literal>/FC</literal> +compiler option to the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-CCFLAGS"><envar>$CCFLAGS</envar></link> variable so that the compiler will +print the full path name of any files that cause compilation errors. </para> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> Example usage: </para> <example_commands xmlns="http://www.scons.org/dbxsd/v1.0">barsrcs = ['bar.cpp'], barincs = ['bar.h'], barlocalincs = ['StdAfx.h'] barresources = ['bar.rc','resource.h'] @@ -1004,7 +952,143 @@ env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], buildtarget = dll, variant = 'Release') </example_commands> -</listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0">Starting with version 2.4 of +SCons it's also possible to specify the optional argument +<parameter>DebugSettings</parameter>, which creates files for debugging under +Visual Studio:</para><variablelist xmlns="http://www.scons.org/dbxsd/v1.0"> + <varlistentry> + <term>DebugSettings</term> + + <listitem> + <para>A dictionary of debug settings that get written to the + <filename>.vcproj.user</filename> or the + <filename>.vcxproj.user</filename> file, depending on the version + installed. As it is done for cmdargs (see above), you can specify a + <parameter>DebugSettings</parameter> dictionary per variant. If you + give only one, it will be propagated to all variants.</para> + </listitem> + </varlistentry> + </variablelist><para xmlns="http://www.scons.org/dbxsd/v1.0">Currently, only Visual Studio v9.0 and Visual Studio +version v11 are implemented, for other versions no file is generated. To +generate the user file, you just need to add a +<parameter>DebugSettings</parameter> dictionary to the environment with the +right parameters for your MSVS version. If the dictionary is empty, or does +not contain any good value, no file will be generated.</para><para xmlns="http://www.scons.org/dbxsd/v1.0">Following +is a more contrived example, involving the setup of a project for variants and +DebugSettings:</para><example_commands xmlns="http://www.scons.org/dbxsd/v1.0"># Assuming you store your defaults in a file +vars = Variables('variables.py') +msvcver = vars.args.get('vc', '9') + +# Check command args to force one Microsoft Visual Studio version +if msvcver == '9' or msvcver == '11': + env = Environment(MSVC_VERSION=msvcver+'.0', MSVC_BATCH=False) +else: + env = Environment() + +AddOption('--userfile', action='store_true', dest='userfile', default=False, + help="Create Visual Studio Project user file") + +# +# 1. Configure your Debug Setting dictionary with options you want in the list +# of allowed options, for instance if you want to create a user file to launch +# a specific application for testing your dll with Microsoft Visual Studio 2008 (v9): +# +V9DebugSettings = { + 'Command':'c:\\myapp\\using\\thisdll.exe', + 'WorkingDirectory': 'c:\\myapp\\using\\', + 'CommandArguments': '-p password', +# 'Attach':'false', +# 'DebuggerType':'3', +# 'Remote':'1', +# 'RemoteMachine': None, +# 'RemoteCommand': None, +# 'HttpUrl': None, +# 'PDBPath': None, +# 'SQLDebugging': None, +# 'Environment': '', +# 'EnvironmentMerge':'true', +# 'DebuggerFlavor': None, +# 'MPIRunCommand': None, +# 'MPIRunArguments': None, +# 'MPIRunWorkingDirectory': None, +# 'ApplicationCommand': None, +# 'ApplicationArguments': None, +# 'ShimCommand': None, +# 'MPIAcceptMode': None, +# 'MPIAcceptFilter': None, +} + +# +# 2. Because there are a lot of different options depending on the Microsoft +# Visual Studio version, if you use more than one version you have to +# define a dictionary per version, for instance if you want to create a user +# file to launch a specific application for testing your dll with Microsoft +# Visual Studio 2012 (v11): +# +V10DebugSettings = { + 'LocalDebuggerCommand': 'c:\\myapp\\using\\thisdll.exe', + 'LocalDebuggerWorkingDirectory': 'c:\\myapp\\using\\', + 'LocalDebuggerCommandArguments': '-p password', +# 'LocalDebuggerEnvironment': None, +# 'DebuggerFlavor': 'WindowsLocalDebugger', +# 'LocalDebuggerAttach': None, +# 'LocalDebuggerDebuggerType': None, +# 'LocalDebuggerMergeEnvironment': None, +# 'LocalDebuggerSQLDebugging': None, +# 'RemoteDebuggerCommand': None, +# 'RemoteDebuggerCommandArguments': None, +# 'RemoteDebuggerWorkingDirectory': None, +# 'RemoteDebuggerServerName': None, +# 'RemoteDebuggerConnection': None, +# 'RemoteDebuggerDebuggerType': None, +# 'RemoteDebuggerAttach': None, +# 'RemoteDebuggerSQLDebugging': None, +# 'DeploymentDirectory': None, +# 'AdditionalFiles': None, +# 'RemoteDebuggerDeployDebugCppRuntime': None, +# 'WebBrowserDebuggerHttpUrl': None, +# 'WebBrowserDebuggerDebuggerType': None, +# 'WebServiceDebuggerHttpUrl': None, +# 'WebServiceDebuggerDebuggerType': None, +# 'WebServiceDebuggerSQLDebugging': None, +} + +# +# 3. Select the dictionary you want depending on the version of visual Studio +# Files you want to generate. +# +if not env.GetOption('userfile'): + dbgSettings = None +elif env.get('MSVC_VERSION', None) == '9.0': + dbgSettings = V9DebugSettings +elif env.get('MSVC_VERSION', None) == '11.0': + dbgSettings = V10DebugSettings +else: + dbgSettings = None + +# +# 4. Add the dictionary to the DebugSettings keyword. +# +barsrcs = ['bar.cpp', 'dllmain.cpp', 'stdafx.cpp'] +barincs = ['targetver.h'] +barlocalincs = ['StdAfx.h'] +barresources = ['bar.rc','resource.h'] +barmisc = ['ReadMe.txt'] + +dll = env.SharedLibrary(target = 'bar.dll', + source = barsrcs) + +env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], + srcs = barsrcs, + incs = barincs, + localincs = barlocalincs, + resources = barresources, + misc = barmisc, + buildtarget = [dll[0]] * 2, + variant = ('Debug|Win32', 'Release|Win32'), + cmdargs = 'vc=%s' % msvcver, + DebugSettings = (dbgSettings, {})) +</example_commands> </listitem> </varlistentry> <varlistentry id="b-MSVSSolution"> <term> @@ -1013,70 +1097,54 @@ env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], <term> <function>env.MSVSSolution()</function> </term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Builds a Microsoft Visual Studio solution file. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -This builds a Visual Studio solution file, -based on the version of Visual Studio that is configured -(either the latest installed version, -or the version specified by -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVS_VERSION"><envar>$MSVS_VERSION</envar></link> -in the construction environment). -For Visual Studio 6, it will generate a -<filename>.dsw</filename> -file. -For Visual Studio 7 (.NET), it will -generate a -<filename>.sln</filename> -file. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The following values must be specified: -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>target</literal>: -The name of the target .dsw or .sln file. The correct -suffix for the version of Visual Studio must be used, but the value -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSSOLUTIONSUFFIX"><envar>$MSVSSOLUTIONSUFFIX</envar></link> -will be defined to the correct value (see example below). -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>variant</literal>: -The name of this particular variant, or a list of variant -names (the latter is only supported for MSVS 7 solutions). These are -typically things like "Debug" or "Release", but really can be anything -you want. For MSVS 7 they may also specify target platform, like this -"Debug|Xbox". Default platform is Win32. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<literal>projects</literal>: -A list of project file names, or Project nodes returned by calls to the -<function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> -Builder, -to be placed into the solution file. -It should be noted that these file names are NOT added to the $SOURCES -environment variable in form of files, but rather as strings. This -is because they represent file names to be added to the solution file, -not the source files used to build the solution file. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Example Usage: -</para> - -<example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> -env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], - projects = ['bar' + env['MSVSPROJECTSUFFIX']], - variant = 'Release') -</example_commands> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">Builds a Microsoft Visual Studio solution +file. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0">This builds a Visual Studio solution file, based on the +version of Visual Studio that is configured (either the latest installed +version, or the version specified by <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVS_VERSION"><envar>$MSVS_VERSION</envar></link> in the +construction environment). For Visual Studio 6, it will generate a +<filename>.dsw</filename> file. For Visual Studio 7 (.NET), it will generate a +<filename>.sln</filename> file. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> The following values must be +specified: </para><variablelist xmlns="http://www.scons.org/dbxsd/v1.0"> + <varlistentry> + <term>target</term> + + <listitem> + <para>The name of the target .dsw or .sln file. The correct + suffix for the version of Visual Studio must be used, but the value + <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSSOLUTIONSUFFIX"><envar>$MSVSSOLUTIONSUFFIX</envar></link> will be defined to the correct value (see + example below).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>variant</term> + + <listitem> + <para>The name of this particular variant, or a list of variant + names (the latter is only supported for MSVS 7 solutions). These are + typically things like "Debug" or "Release", but really can be anything + you want. For MSVS 7 they may also specify target platform, like this + "Debug|Xbox". Default platform is Win32.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>projects</term> + + <listitem> + <para>A list of project file names, or Project nodes returned by + calls to the <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> Builder, to be placed into the solution + file. It should be noted that these file names are NOT added to the + $SOURCES environment variable in form of files, but rather as strings. + This is because they represent file names to be added to the solution + file, not the source files used to build the solution + file.</para> + </listitem> + </varlistentry> + </variablelist> <para xmlns="http://www.scons.org/dbxsd/v1.0"> Example Usage: </para> <example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> +env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], projects = ['bar' ++ env['MSVSPROJECTSUFFIX']], variant = 'Release') +</example_commands></listitem> </varlistentry> <varlistentry id="b-Object"> <term> @@ -1182,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> @@ -1415,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"> @@ -1548,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> @@ -1920,17 +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"> @@ -2484,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 2ed1a8f..6a0337b 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 f2.o -c f2.c -cc -o f3.o -c f3.c +cc -o f1.o -c f1.c cc -o f5.o -c f5.c +cc -o f3.o -c f3.c cc -o f4.o -c f4.c cc -o prog f1.o f2.o f3.o f4.o f5.o </screen> 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': <function __libversionflags at 0x700000&gt;, '_concat': <function _concat at 0x700000&gt;, '_defines': <function _defines at 0x700000&gt;, '_stripixes': <function _stripixes at 0x700000&gt;} diff --git a/doc/generated/examples/troubleshoot_Dump_2.xml b/doc/generated/examples/troubleshoot_Dump_2.xml index 17c9de5..0ae8fe1 100644 --- a/doc/generated/examples/troubleshoot_Dump_2.xml +++ b/doc/generated/examples/troubleshoot_Dump_2.xml @@ -34,7 +34,7 @@ scons: Reading SConscript files ... '.SPP', '.sx'], 'CXX': '$CC', - 'CXXCOM': '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}', + 'CXXCOM': '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM","$CXXCOMSTR")}', 'CXXFILESUFFIX': '.cc', 'CXXFLAGS': ['$(', '/TP', '$)'], 'DSUFFIXES': ['.d'], @@ -77,7 +77,7 @@ scons: Reading SConscript files ... 'SHCCFLAGS': ['$CCFLAGS'], 'SHCFLAGS': ['$CFLAGS'], 'SHCXX': '$CXX', - 'SHCXXCOM': '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}', + 'SHCXXCOM': '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCXXCOMSTR")}', 'SHCXXFLAGS': ['$CXXFLAGS'], 'SHELL': None, 'SHLIBPREFIX': '', @@ -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': <function msvc_output_flag at 0x700000&gt;, + '__DSHLIBVERSIONFLAGS': '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', + '__LDMODULEVERSIONFLAGS': '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}', + '__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}', + '__libversionflags': <function __libversionflags at 0x700000&gt;, '_concat': <function _concat at 0x700000&gt;, '_defines': <function _defines at 0x700000&gt;, '_stripixes': <function _stripixes at 0x700000&gt;} diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml index 0bdaace..3d8592d 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/bootstrap/src/script/scons.py", line 199, in <module> +File "/scons/as_scons/bootstrap/src/script/scons.py", line 199, in <module> </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 62a9ab3..e80c1c9 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -2426,10 +2426,10 @@ option. </varlistentry> <varlistentry id="f-Glob"> <term> - <literal>Glob(pattern, [ondisk, source, strings])</literal> + <literal>Glob(pattern, [ondisk, source, strings, exclude])</literal> </term> <term> - <literal>env.Glob(pattern, [ondisk, source, strings])</literal> + <literal>env.Glob(pattern, [ondisk, source, strings, exclude])</literal> </term> <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0"> @@ -2543,21 +2543,32 @@ directory.) </para> <para xmlns="http://www.scons.org/dbxsd/v1.0"> +The +<varname>exclude</varname> +argument may be set to a pattern or a list of patterns +(following the same Unix shell semantics) +which must be filtered out of returned elements. +Elements matching a least one pattern of +this list will be excluded. +</para> + +<para xmlns="http://www.scons.org/dbxsd/v1.0"> Examples: </para> <example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> Program('foo', Glob('*.c')) Zip('/tmp/everything', Glob('.??*') + Glob('*')) +sources = Glob('*.cpp', exclude=['os_*_specific_*.cpp']) + Glob('os_%s_specific_*.cpp'%currentOS) </example_commands> </listitem> </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"> @@ -2565,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 dc9b50d..ba12966 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> @@ -307,6 +315,14 @@ Set construction variables for generic POSIX Fortran 03 compilers. </para> <para>Sets: &cv-link-F03;, &cv-link-F03COM;, &cv-link-F03FLAGS;, &cv-link-F03PPCOM;, &cv-link-SHF03;, &cv-link-SHF03COM;, &cv-link-SHF03FLAGS;, &cv-link-SHF03PPCOM;, &cv-link-_F03INCFLAGS;.</para><para>Uses: &cv-link-F03COMSTR;, &cv-link-F03PPCOMSTR;, &cv-link-SHF03COMSTR;, &cv-link-SHF03PPCOMSTR;.</para></listitem> </varlistentry> + <varlistentry id="t-f08"> + <term>f08</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +Set construction variables for generic POSIX Fortran 08 compilers. +</para> +<para>Sets: &cv-link-F08;, &cv-link-F08COM;, &cv-link-F08FLAGS;, &cv-link-F08PPCOM;, &cv-link-SHF08;, &cv-link-SHF08COM;, &cv-link-SHF08FLAGS;, &cv-link-SHF08PPCOM;, &cv-link-_F08INCFLAGS;.</para><para>Uses: &cv-link-F08COMSTR;, &cv-link-F08PPCOMSTR;, &cv-link-SHF08COMSTR;, &cv-link-SHF08PPCOMSTR;.</para></listitem> + </varlistentry> <varlistentry id="t-f77"> <term>f77</term> <listitem> @@ -387,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> @@ -446,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> @@ -615,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> @@ -736,11 +752,8 @@ Sets construction variables for the Microsoft Visual C/C++ compiler. </varlistentry> <varlistentry id="t-msvs"> <term>msvs</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Sets construction variables for Microsoft Visual Studio. -</para> -<para>Sets: &cv-link-MSVSBUILDCOM;, &cv-link-MSVSCLEANCOM;, &cv-link-MSVSENCODING;, &cv-link-MSVSPROJECTCOM;, &cv-link-MSVSREBUILDCOM;, &cv-link-MSVSSCONS;, &cv-link-MSVSSCONSCOM;, &cv-link-MSVSSCONSCRIPT;, &cv-link-MSVSSCONSFLAGS;, &cv-link-MSVSSOLUTIONCOM;.</para></listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0"> +Sets construction variables for Microsoft Visual Studio. </para> <para>Sets: &cv-link-MSVSBUILDCOM;, &cv-link-MSVSCLEANCOM;, &cv-link-MSVSENCODING;, &cv-link-MSVSPROJECTCOM;, &cv-link-MSVSREBUILDCOM;, &cv-link-MSVSSCONS;, &cv-link-MSVSSCONSCOM;, &cv-link-MSVSSCONSCRIPT;, &cv-link-MSVSSCONSFLAGS;, &cv-link-MSVSSOLUTIONCOM;.</para></listitem> </varlistentry> <varlistentry id="t-mwcc"> <term>mwcc</term> @@ -767,19 +780,19 @@ Sets construction variables for the </para> <para>Sets: &cv-link-AS;, &cv-link-ASCOM;, &cv-link-ASFLAGS;, &cv-link-ASPPCOM;, &cv-link-ASPPFLAGS;.</para><para>Uses: &cv-link-ASCOMSTR;, &cv-link-ASPPCOMSTR;.</para></listitem> </varlistentry> - <varlistentry id="t-packaging"> - <term>packaging</term> + <varlistentry id="t-Packaging"> + <term>Packaging</term> <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0"> -A framework for building binary and source packages. +Sets construction variables for the <function xmlns="http://www.scons.org/dbxsd/v1.0">Package</function> Builder. </para> </listitem> </varlistentry> - <varlistentry id="t-Packaging"> - <term>Packaging</term> + <varlistentry id="t-packaging"> + <term>packaging</term> <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0"> -Sets construction variables for the <function xmlns="http://www.scons.org/dbxsd/v1.0">Package</function> Builder. +A framework for building binary and source packages. </para> </listitem> </varlistentry> @@ -896,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 3c6f71c..2ee2270 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>"> @@ -29,6 +30,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY t-dvipdf "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>dvipdf</literal>"> <!ENTITY t-dvips "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>dvips</literal>"> <!ENTITY t-f03 "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>f03</literal>"> +<!ENTITY t-f08 "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>f08</literal>"> <!ENTITY t-f77 "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>f77</literal>"> <!ENTITY t-f90 "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>f90</literal>"> <!ENTITY t-f95 "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>f95</literal>"> @@ -76,8 +78,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY t-mwcc "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>mwcc</literal>"> <!ENTITY t-mwld "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>mwld</literal>"> <!ENTITY t-nasm "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>nasm</literal>"> -<!ENTITY t-packaging "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>packaging</literal>"> <!ENTITY t-Packaging "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>Packaging</literal>"> +<!ENTITY t-packaging "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>packaging</literal>"> <!ENTITY t-pdf "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>pdf</literal>"> <!ENTITY t-pdflatex "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>pdflatex</literal>"> <!ENTITY t-pdftex "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>pdftex</literal>"> @@ -131,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>"> @@ -138,6 +141,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY t-link-dvipdf "<link linkend='t-dvipdf' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>dvipdf</literal></link>"> <!ENTITY t-link-dvips "<link linkend='t-dvips' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>dvips</literal></link>"> <!ENTITY t-link-f03 "<link linkend='t-f03' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>f03</literal></link>"> +<!ENTITY t-link-f08 "<link linkend='t-f08' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>f08</literal></link>"> <!ENTITY t-link-f77 "<link linkend='t-f77' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>f77</literal></link>"> <!ENTITY t-link-f90 "<link linkend='t-f90' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>f90</literal></link>"> <!ENTITY t-link-f95 "<link linkend='t-f95' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>f95</literal></link>"> @@ -185,8 +189,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY t-link-mwcc "<link linkend='t-mwcc' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>mwcc</literal></link>"> <!ENTITY t-link-mwld "<link linkend='t-mwld' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>mwld</literal></link>"> <!ENTITY t-link-nasm "<link linkend='t-nasm' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>nasm</literal></link>"> -<!ENTITY t-link-packaging "<link linkend='t-packaging' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>packaging</literal></link>"> <!ENTITY t-link-Packaging "<link linkend='t-Packaging' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>Packaging</literal></link>"> +<!ENTITY t-link-packaging "<link linkend='t-packaging' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>packaging</literal></link>"> <!ENTITY t-link-pdf "<link linkend='t-pdf' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>pdf</literal></link>"> <!ENTITY t-link-pdflatex "<link linkend='t-pdflatex' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>pdflatex</literal></link>"> <!ENTITY t-link-pdftex "<link linkend='t-pdftex' xmlns='http://www.scons.org/dbxsd/v1.0'><literal>pdftex</literal></link>"> diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index d21b417..b377102 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> @@ -1686,6 +1704,184 @@ F03 dialect will be used. By default, this is empty </para> </listitem> </varlistentry> + <varlistentry id="cv-F08"> + <term>F08</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The Fortran 08 compiler. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRAN"><envar>$FORTRAN</envar></link> variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08"><envar>$F08</envar></link> if you need to use a specific compiler +or compiler version for Fortran 08 files. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08COM"> + <term>F08COM</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The command line used to compile a Fortran 08 source file to an object file. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08COM"><envar>$F08COM</envar></link> if you need to use a specific +command line for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRANCOM"><envar>$FORTRANCOM</envar></link> variable, +which specifies the default command line +for all Fortran versions. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08COMSTR"> + <term>F08COMSTR</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The string displayed when a Fortran 08 source file +is compiled to an object file. +If this is not set, then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08COM"><envar>$F08COM</envar></link> or <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRANCOM"><envar>$FORTRANCOM</envar></link> +(the command line) is displayed. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08FILESUFFIXES"> + <term>F08FILESUFFIXES</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The list of file extensions for which the F08 dialect will be used. By +default, this is ['.f08'] +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08FLAGS"> + <term>F08FLAGS</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +General user-specified options that are passed to the Fortran 08 compiler. +Note that this variable does +<emphasis>not</emphasis> +contain +<option>-I</option> +(or similar) include search path options +that scons generates automatically from <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PATH"><envar>$F08PATH</envar></link>. +See +<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_F08INCFLAGS"><envar>$_F08INCFLAGS</envar></link> +below, +for the variable that expands to those options. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08FLAGS"><envar>$F08FLAGS</envar></link> if you need to define specific +user options for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRANFLAGS"><envar>$FORTRANFLAGS</envar></link> variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-_F08INCFLAGS"> + <term>_F08INCFLAGS</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +An automatically-generated construction variable +containing the Fortran 08 compiler command-line options +for specifying directories to be searched for include files. +The value of <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_F08INCFLAGS"><envar>$_F08INCFLAGS</envar></link> is created +by appending <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-INCPREFIX"><envar>$INCPREFIX</envar></link> and <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-INCSUFFIX"><envar>$INCSUFFIX</envar></link> +to the beginning and end +of each directory in <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PATH"><envar>$F08PATH</envar></link>. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08PATH"> + <term>F08PATH</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The list of directories that the Fortran 08 compiler will search for include +directories. The implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08FLAGS"><envar>$F08FLAGS</envar></link> because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PATH"><envar>$F08PATH</envar></link> will be looked-up relative to the SConscript +directory when they are used in a command. To force +<filename xmlns="http://www.scons.org/dbxsd/v1.0">scons</filename> +to look-up a directory relative to the root of the source tree use #: +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PATH"><envar>$F08PATH</envar></link> if you need to define a specific +include path for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRANPATH"><envar>$FORTRANPATH</envar></link> variable, +which specifies the include path +for the default Fortran compiler +for all Fortran versions. +</para> + +<example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> +env = Environment(F08PATH='#/include') +</example_commands> + +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The directory look-up can also be forced using the +<function xmlns="http://www.scons.org/dbxsd/v1.0">Dir</function>() +function: +</para> + +<example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> +include = Dir('include') +env = Environment(F08PATH=include) +</example_commands> + +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The directory list will be added to command lines +through the automatically-generated +<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_F08INCFLAGS"><envar>$_F08INCFLAGS</envar></link> +construction variable, +which is constructed by +appending the values of the +<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-INCPREFIX"><envar>$INCPREFIX</envar></link> and <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-INCSUFFIX"><envar>$INCSUFFIX</envar></link> +construction variables +to the beginning and end +of each directory in <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PATH"><envar>$F08PATH</envar></link>. +Any command lines you define that need +the F08PATH directory list should +include <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-_F08INCFLAGS"><envar>$_F08INCFLAGS</envar></link>: +</para> + +<example_commands xmlns="http://www.scons.org/dbxsd/v1.0"> +env = Environment(F08COM="my_compiler $_F08INCFLAGS -c -o $TARGET $SOURCE") +</example_commands> +</listitem> + </varlistentry> + <varlistentry id="cv-F08PPCOM"> + <term>F08PPCOM</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The command line used to compile a Fortran 08 source file to an object file +after first running the file through the C preprocessor. +Any options specified in the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08FLAGS"><envar>$F08FLAGS</envar></link> and <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-CPPFLAGS"><envar>$CPPFLAGS</envar></link> construction variables +are included on this command line. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PPCOM"><envar>$F08PPCOM</envar></link> if you need to use a specific +C-preprocessor command line for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRANPPCOM"><envar>$FORTRANPPCOM</envar></link> variable, +which specifies the default C-preprocessor command line +for all Fortran versions. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08PPCOMSTR"> + <term>F08PPCOMSTR</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The string displayed when a Fortran 08 source file +is compiled to an object file +after first running the file through the C preprocessor. +If this is not set, then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-F08PPCOM"><envar>$F08PPCOM</envar></link> or <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-FORTRANPPCOM"><envar>$FORTRANPPCOM</envar></link> +(the command line) is displayed. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-F08PPFILESUFFIXES"> + <term>F08PPFILESUFFIXES</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The list of file extensions for which the compilation + preprocessor pass for +F08 dialect will be used. By default, this is empty +</para> +</listitem> + </varlistentry> <varlistentry id="cv-F77"> <term>F77</term> <listitem> @@ -2642,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. @@ -2657,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> @@ -2696,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> @@ -3230,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> @@ -3241,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> @@ -3252,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> @@ -3978,355 +4275,254 @@ Versions ending in <literal>Exp</literal> refer to "Express" or </varlistentry> <varlistentry id="cv-MSVS"> <term>MSVS</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -When the Microsoft Visual Studio tools are initialized, they set up -this dictionary with the following keys: -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>VERSION</envar>: -the version of MSVS being used (can be set via -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVS_VERSION"><envar>$MSVS_VERSION</envar></link>) -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>VERSIONS</envar>: -the available versions of MSVS installed -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>VCINSTALLDIR</envar>: -installed directory of Visual C++ -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>VSINSTALLDIR</envar>: -installed directory of Visual Studio -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>FRAMEWORKDIR</envar>: -installed directory of the .NET framework -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>FRAMEWORKVERSIONS</envar>: -list of installed versions of the .NET framework, sorted latest to oldest. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>FRAMEWORKVERSION</envar>: -latest installed version of the .NET framework -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>FRAMEWORKSDKDIR</envar>: -installed location of the .NET SDK. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>PLATFORMSDKDIR</envar>: -installed location of the Platform SDK. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -<envar>PLATFORMSDK_MODULES</envar>: -dictionary of installed Platform SDK modules, -where the dictionary keys are keywords for the various modules, and -the values are 2-tuples where the first is the release date, and the -second is the version number. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -If a value isn't set, it wasn't available in the registry. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0"> +When the Microsoft Visual Studio tools are initialized, they set up this +dictionary with the following keys: </para><variablelist xmlns="http://www.scons.org/dbxsd/v1.0"> + <varlistentry> + <term>VERSION</term> + + <listitem> + <para>the version of MSVS being used (can be set via + <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVS_VERSION"><envar>$MSVS_VERSION</envar></link>)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>VERSIONS</term> + + <listitem> + <para>the available versions of MSVS installed</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>VCINSTALLDIR</term> + + <listitem> + <para>installed directory of Visual C++</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>VSINSTALLDIR</term> + + <listitem> + <para>installed directory of Visual Studio</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKDIR</term> + + <listitem> + <para>installed directory of the .NET framework</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKVERSIONS</term> + + <listitem> + <para>list of installed versions of the .NET framework, sorted + latest to oldest.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKVERSION</term> + + <listitem> + <para>latest installed version of the .NET + framework</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKSDKDIR</term> + + <listitem> + <para>installed location of the .NET SDK.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>PLATFORMSDKDIR</term> + + <listitem> + <para>installed location of the Platform SDK.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>PLATFORMSDK_MODULES</term> + + <listitem> + <para>dictionary of installed Platform SDK modules, where the + dictionary keys are keywords for the various modules, and the values + are 2-tuples where the first is the release date, and the second is + the version number.</para> + </listitem> + </varlistentry> + </variablelist><para xmlns="http://www.scons.org/dbxsd/v1.0">If a value isn't set, it wasn't available in the +registry.</para></listitem> </varlistentry> <varlistentry id="cv-MSVS_ARCH"> <term>MSVS_ARCH</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Sets the architecture for which the generated project(s) should build. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The default value is <literal>x86</literal>. -<literal>amd64</literal> is also supported -by <application xmlns="http://www.scons.org/dbxsd/v1.0">SCons</application> for some Visual Studio versions. -Trying to set <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_ARCH</envar> to an architecture that's not -supported for a given Visual Studio version -will generate an error. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">Sets +the architecture for which the generated project(s) should build. </para> +<para xmlns="http://www.scons.org/dbxsd/v1.0">The default value is <literal>x86</literal>. <literal>amd64</literal> is +also supported by <application xmlns="http://www.scons.org/dbxsd/v1.0">SCons</application> for some Visual Studio versions. Trying to set +<envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_ARCH</envar> to an architecture that's not supported for a given Visual +Studio version will generate an error. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVS_PROJECT_GUID"> <term>MSVS_PROJECT_GUID</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The string -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>ProjectGUID</literal> -attribute. -There is no default value. If not defined, a new GUID is generated. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The string placed in a generated +Microsoft Visual Studio project file as the value of the +<literal>ProjectGUID</literal> attribute. There is no default value. If not +defined, a new GUID is generated. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVS_SCC_AUX_PATH"> <term>MSVS_SCC_AUX_PATH</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The path name -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>SccAuxPath</literal> -attribute -if the -<envar>MSVS_SCC_PROVIDER</envar> -construction variable is also set. -There is no default value. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The path name placed in a generated +Microsoft Visual Studio project file as the value of the +<literal>SccAuxPath</literal> attribute if the +<envar>MSVS_SCC_PROVIDER</envar> construction variable is also set. There is +no default value. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVS_SCC_CONNECTION_ROOT"> <term>MSVS_SCC_CONNECTION_ROOT</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The root path of projects in your SCC workspace, i.e the path under which -all project and solution files will be generated. It is used as a -reference path from which the relative paths of the generated -Microsoft Visual Studio project and solution files are computed. -The relative project file path is placed as the value of the -<literal>SccLocalPath</literal> -attribute -of the project file -and as the values of the -<literal>SccProjectFilePathRelativizedFromConnection[i]</literal> -(where [i] ranges from 0 to the number of projects in the solution) -attributes of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -Similarly the relative solution file path is placed as the values of the -<literal>SccLocalPath[i]</literal> -(where [i] ranges from 0 to the number of projects in the solution) -attributes of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -This is used only -if the -<envar>MSVS_SCC_PROVIDER</envar> -construction variable is also set. -The default value is the current working directory. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The root path of projects in +your SCC workspace, i.e the path under which all project and solution files +will be generated. It is used as a reference path from which the relative +paths of the generated Microsoft Visual Studio project and solution files are +computed. The relative project file path is placed as the value of the +<literal>SccLocalPath</literal> attribute of the project file and as the +values of the +<literal>SccProjectFilePathRelativizedFromConnection[i]</literal> (where [i] +ranges from 0 to the number of projects in the solution) attributes of the +<literal>GlobalSection(SourceCodeControl)</literal> section of the Microsoft +Visual Studio solution file. Similarly the relative solution file path is +placed as the values of the <literal>SccLocalPath[i]</literal> (where [i] +ranges from 0 to the number of projects in the solution) attributes of the +<literal>GlobalSection(SourceCodeControl)</literal> section of the Microsoft +Visual Studio solution file. This is used only if the +<envar>MSVS_SCC_PROVIDER</envar> construction variable is also set. The +default value is the current working directory. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVS_SCC_PROJECT_NAME"> <term>MSVS_SCC_PROJECT_NAME</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The project name -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>SccProjectName</literal> -attribute -if the -<envar>MSVS_SCC_PROVIDER</envar> -construction variable is also set. -In this case the string is also placed in the -<literal>SccProjectName0</literal> -attribute of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -There is no default value. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The project name placed in +a generated Microsoft Visual Studio project file as the value of the +<literal>SccProjectName</literal> attribute if the +<envar>MSVS_SCC_PROVIDER</envar> construction variable is also set. In this +case the string is also placed in the <literal>SccProjectName0</literal> +attribute of the <literal>GlobalSection(SourceCodeControl)</literal> section +of the Microsoft Visual Studio solution file. There is no default value. +</para> </listitem> </varlistentry> <varlistentry id="cv-MSVS_SCC_PROVIDER"> <term>MSVS_SCC_PROVIDER</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The string -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>SccProvider</literal> -attribute. -The string is also placed in the -<literal>SccProvider0</literal> -attribute of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -There is no default value. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The +string placed in a generated Microsoft Visual Studio project file as the value +of the <literal>SccProvider</literal> attribute. The string is also placed in +the <literal>SccProvider0</literal> attribute of the +<literal>GlobalSection(SourceCodeControl)</literal> section of the Microsoft +Visual Studio solution file. There is no default value. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVS_VERSION"> <term>MSVS_VERSION</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -Sets the preferred version of Microsoft Visual Studio to use. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -If <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_VERSION</envar> is not set, -<application xmlns="http://www.scons.org/dbxsd/v1.0">SCons</application> will (by default) select the latest version -of Visual Studio installed on your system. -So, if you have version 6 and version 7 (MSVS .NET) installed, -it will prefer version 7. -You can override this by -specifying the -<envar>MSVS_VERSION</envar> -variable in the Environment initialization, setting it to the -appropriate version ('6.0' or '7.0', for example). -If the specified version isn't installed, -tool initialization will fail. -</para> - -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -This is obsolete: use <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVC_VERSION</envar> instead. If <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_VERSION</envar> is set and -<envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVC_VERSION</envar> is not, <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVC_VERSION</envar> will be set automatically to <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_VERSION</envar>. -If both are set to different values, scons will raise an error. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">Sets the preferred version +of Microsoft Visual Studio to use. </para> <para xmlns="http://www.scons.org/dbxsd/v1.0">If <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_VERSION</envar> is not +set, <application xmlns="http://www.scons.org/dbxsd/v1.0">SCons</application> will (by default) select the latest version of Visual Studio +installed on your system. So, if you have version 6 and version 7 (MSVS .NET) +installed, it will prefer version 7. You can override this by specifying the +<envar>MSVS_VERSION</envar> variable in the Environment initialization, +setting it to the appropriate version ('6.0' or '7.0', for example). If the +specified version isn't installed, tool initialization will fail. </para> +<para xmlns="http://www.scons.org/dbxsd/v1.0">This is obsolete: use <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVC_VERSION</envar> instead. If <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_VERSION</envar> is +set and <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVC_VERSION</envar> is not, <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVC_VERSION</envar> will be set automatically +to <envar xmlns="http://www.scons.org/dbxsd/v1.0">$MSVS_VERSION</envar>. If both are set to different values, scons will raise an +error. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSBUILDCOM"> <term>MSVSBUILDCOM</term> <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The build command line placed in -a generated Microsoft Visual Studio project file. -The default is to have Visual Studio invoke SCons with any specified -build targets. -</para> -</listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0">The build command line placed in a generated Microsoft Visual Studio +project file. The default is to have Visual Studio invoke SCons with any +specified build targets. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSCLEANCOM"> <term>MSVSCLEANCOM</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The clean command line placed in -a generated Microsoft Visual Studio project file. -The default is to have Visual Studio invoke SCons with the -c option -to remove any specified targets. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The clean command line placed in a generated Microsoft Visual +Studio project file. The default is to have Visual Studio invoke SCons with +the -c option to remove any specified targets. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSENCODING"> <term>MSVSENCODING</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The encoding string placed in -a generated Microsoft Visual Studio project file. -The default is encoding -<literal>Windows-1252</literal>. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The encoding string placed in a +generated Microsoft Visual Studio project file. The default is encoding +<literal>Windows-1252</literal>. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSPROJECTCOM"> <term>MSVSPROJECTCOM</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The action used to generate Microsoft Visual Studio project files. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The action used to generate Microsoft +Visual Studio project files. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSPROJECTSUFFIX"> <term>MSVSPROJECTSUFFIX</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The suffix used for Microsoft Visual Studio project (DSP) files. -The default value is -<filename>.vcproj</filename> -when using Visual Studio version 7.x (.NET) -or later version, -and -<filename>.dsp</filename> -when using earlier versions of Visual Studio. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The suffix used for Microsoft Visual +Studio project (DSP) files. The default value is <filename>.vcproj</filename> +when using Visual Studio version 7.x (.NET) or later version, and +<filename>.dsp</filename> when using earlier versions of Visual Studio. +</para> </listitem> </varlistentry> <varlistentry id="cv-MSVSREBUILDCOM"> <term>MSVSREBUILDCOM</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The rebuild command line placed in -a generated Microsoft Visual Studio project file. -The default is to have Visual Studio invoke SCons with any specified -rebuild targets. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The +rebuild command line placed in a generated Microsoft Visual Studio project +file. The default is to have Visual Studio invoke SCons with any specified +rebuild targets. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSSCONS"> <term>MSVSSCONS</term> <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The SCons used in generated Microsoft Visual Studio project files. -The default is the version of SCons being -used to generate the project file. -</para> -</listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0">The SCons used in generated Microsoft Visual Studio project files. The +default is the version of SCons being used to generate the project file. +</para> </listitem> </varlistentry> <varlistentry id="cv-MSVSSCONSCOM"> <term>MSVSSCONSCOM</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The default SCons command used in generated Microsoft Visual Studio -project files. -</para> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The default +SCons command used in generated Microsoft Visual Studio project files. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSSCONSCRIPT"> <term>MSVSSCONSCRIPT</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The sconscript file -(that is, -<filename xmlns="http://www.scons.org/dbxsd/v1.0">SConstruct</filename> -or -<filename xmlns="http://www.scons.org/dbxsd/v1.0">SConscript</filename> -file) -that will be invoked by Visual Studio -project files -(through the -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSSCONSCOM"><envar>$MSVSSCONSCOM</envar></link> -variable). -The default is the same sconscript file -that contains the call to -<function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> -to build the project file. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The sconscript +file (that is, <filename xmlns="http://www.scons.org/dbxsd/v1.0">SConstruct</filename> or <filename xmlns="http://www.scons.org/dbxsd/v1.0">SConscript</filename> file) that will be invoked by +Visual Studio project files (through the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSSCONSCOM"><envar>$MSVSSCONSCOM</envar></link> variable). The +default is the same sconscript file that contains the call to <function xmlns="http://www.scons.org/dbxsd/v1.0">MSVSProject</function> +to build the project file. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSSCONSFLAGS"> <term>MSVSSCONSFLAGS</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The SCons flags used in generated Microsoft Visual Studio -project files. -</para> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The +SCons flags used in generated Microsoft Visual Studio project files. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSSOLUTIONCOM"> <term>MSVSSOLUTIONCOM</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The action used to generate Microsoft Visual Studio solution files. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The action used to generate Microsoft +Visual Studio solution files. </para> </listitem> </varlistentry> <varlistentry id="cv-MSVSSOLUTIONSUFFIX"> <term>MSVSSOLUTIONSUFFIX</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The suffix used for Microsoft Visual Studio solution (DSW) files. -The default value is -<filename>.sln</filename> -when using Visual Studio version 7.x (.NET), -and -<filename>.dsw</filename> -when using earlier versions of Visual Studio. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The suffix used for Microsoft +Visual Studio solution (DSW) files. The default value is +<filename>.sln</filename> when using Visual Studio version 7.x (.NET), and +<filename>.dsw</filename> when using earlier versions of Visual Studio. +</para> </listitem> </varlistentry> <varlistentry id="cv-MT"> <term>MT</term> @@ -5672,17 +5868,11 @@ to check out editable files from SCCS. </varlistentry> <varlistentry id="cv-SCONS_HOME"> <term>SCONS_HOME</term> - <listitem> -<para xmlns="http://www.scons.org/dbxsd/v1.0"> -The (optional) path to the SCons library directory, -initialized from the external environment. -If set, this is used to construct a shorter and more -efficient search path in the -<link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSSCONS"><envar>$MSVSSCONS</envar></link> -command line executed -from Microsoft Visual Studio project files. -</para> -</listitem> + <listitem> <para xmlns="http://www.scons.org/dbxsd/v1.0">The +(optional) path to the SCons library directory, initialized from the external +environment. If set, this is used to construct a shorter and more efficient +search path in the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-MSVSSCONS"><envar>$MSVSSCONS</envar></link> command line executed from Microsoft +Visual Studio project files. </para> </listitem> </varlistentry> <varlistentry id="cv-SHCC"> <term>SHCC</term> @@ -5915,6 +6105,88 @@ If this is not set, then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend=" </para> </listitem> </varlistentry> + <varlistentry id="cv-SHF08"> + <term>SHF08</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The Fortran 08 compiler used for generating shared-library objects. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHFORTRAN"><envar>$SHFORTRAN</envar></link> variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08"><envar>$SHF08</envar></link> if you need to use a specific compiler +or compiler version for Fortran 08 files. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-SHF08COM"> + <term>SHF08COM</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The command line used to compile a Fortran 08 source file +to a shared-library object file. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08COM"><envar>$SHF08COM</envar></link> if you need to use a specific +command line for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHFORTRANCOM"><envar>$SHFORTRANCOM</envar></link> variable, +which specifies the default command line +for all Fortran versions. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-SHF08COMSTR"> + <term>SHF08COMSTR</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The string displayed when a Fortran 08 source file +is compiled to a shared-library object file. +If this is not set, then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08COM"><envar>$SHF08COM</envar></link> or <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHFORTRANCOM"><envar>$SHFORTRANCOM</envar></link> +(the command line) is displayed. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-SHF08FLAGS"> + <term>SHF08FLAGS</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +Options that are passed to the Fortran 08 compiler +to generated shared-library objects. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08FLAGS"><envar>$SHF08FLAGS</envar></link> if you need to define specific +user options for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHFORTRANFLAGS"><envar>$SHFORTRANFLAGS</envar></link> variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-SHF08PPCOM"> + <term>SHF08PPCOM</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The command line used to compile a Fortran 08 source file to a +shared-library object file +after first running the file through the C preprocessor. +Any options specified in the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08FLAGS"><envar>$SHF08FLAGS</envar></link> and <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-CPPFLAGS"><envar>$CPPFLAGS</envar></link> construction variables +are included on this command line. +You only need to set <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08PPCOM"><envar>$SHF08PPCOM</envar></link> if you need to use a specific +C-preprocessor command line for Fortran 08 files. +You should normally set the <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHFORTRANPPCOM"><envar>$SHFORTRANPPCOM</envar></link> variable, +which specifies the default C-preprocessor command line +for all Fortran versions. +</para> +</listitem> + </varlistentry> + <varlistentry id="cv-SHF08PPCOMSTR"> + <term>SHF08PPCOMSTR</term> + <listitem> +<para xmlns="http://www.scons.org/dbxsd/v1.0"> +The string displayed when a Fortran 08 source file +is compiled to a shared-library object file +after first running the file through the C preprocessor. +If this is not set, then <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHF08PPCOM"><envar>$SHF08PPCOM</envar></link> or <link xmlns="http://www.scons.org/dbxsd/v1.0" linkend="cv-SHFORTRANPPCOM"><envar>$SHFORTRANPPCOM</envar></link> +(the command line) is displayed. +</para> +</listitem> + </varlistentry> <varlistentry id="cv-SHF77"> <term>SHF77</term> <listitem> @@ -6232,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> @@ -6240,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> @@ -6253,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"> +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> + <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> @@ -6331,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> @@ -6600,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"> @@ -6649,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> @@ -6713,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 @@ -6737,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 0a59605..955a9eb 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>"> @@ -144,6 +146,16 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-F03PPCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F03PPCOM</envar>"> <!ENTITY cv-F03PPCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F03PPCOMSTR</envar>"> <!ENTITY cv-F03PPFILESUFFIXES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F03PPFILESUFFIXES</envar>"> +<!ENTITY cv-F08 "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08</envar>"> +<!ENTITY cv-F08COM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08COM</envar>"> +<!ENTITY cv-F08COMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08COMSTR</envar>"> +<!ENTITY cv-F08FILESUFFIXES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08FILESUFFIXES</envar>"> +<!ENTITY cv-F08FLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08FLAGS</envar>"> +<!ENTITY cv-_F08INCFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_F08INCFLAGS</envar>"> +<!ENTITY cv-F08PATH "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08PATH</envar>"> +<!ENTITY cv-F08PPCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08PPCOM</envar>"> +<!ENTITY cv-F08PPCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08PPCOMSTR</envar>"> +<!ENTITY cv-F08PPFILESUFFIXES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F08PPFILESUFFIXES</envar>"> <!ENTITY cv-F77 "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F77</envar>"> <!ENTITY cv-F77COM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F77COM</envar>"> <!ENTITY cv-F77COMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$F77COMSTR</envar>"> @@ -206,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>"> @@ -243,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>"> @@ -464,6 +485,12 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-SHF03FLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF03FLAGS</envar>"> <!ENTITY cv-SHF03PPCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF03PPCOM</envar>"> <!ENTITY cv-SHF03PPCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF03PPCOMSTR</envar>"> +<!ENTITY cv-SHF08 "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF08</envar>"> +<!ENTITY cv-SHF08COM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF08COM</envar>"> +<!ENTITY cv-SHF08COMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF08COMSTR</envar>"> +<!ENTITY cv-SHF08FLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF08FLAGS</envar>"> +<!ENTITY cv-SHF08PPCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF08PPCOM</envar>"> +<!ENTITY cv-SHF08PPCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF08PPCOMSTR</envar>"> <!ENTITY cv-SHF77 "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF77</envar>"> <!ENTITY cv-SHF77COM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF77COM</envar>"> <!ENTITY cv-SHF77COMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF77COMSTR</envar>"> @@ -489,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>"> @@ -630,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>"> @@ -698,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>"> @@ -766,6 +800,16 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-link-F03PPCOM "<link linkend='cv-F03PPCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F03PPCOM</envar></link>"> <!ENTITY cv-link-F03PPCOMSTR "<link linkend='cv-F03PPCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F03PPCOMSTR</envar></link>"> <!ENTITY cv-link-F03PPFILESUFFIXES "<link linkend='cv-F03PPFILESUFFIXES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F03PPFILESUFFIXES</envar></link>"> +<!ENTITY cv-link-F08 "<link linkend='cv-F08' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08</envar></link>"> +<!ENTITY cv-link-F08COM "<link linkend='cv-F08COM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08COM</envar></link>"> +<!ENTITY cv-link-F08COMSTR "<link linkend='cv-F08COMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08COMSTR</envar></link>"> +<!ENTITY cv-link-F08FILESUFFIXES "<link linkend='cv-F08FILESUFFIXES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08FILESUFFIXES</envar></link>"> +<!ENTITY cv-link-F08FLAGS "<link linkend='cv-F08FLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08FLAGS</envar></link>"> +<!ENTITY cv-link-_F08INCFLAGS "<link linkend='cv-_F08INCFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_F08INCFLAGS</envar></link>"> +<!ENTITY cv-link-F08PATH "<link linkend='cv-F08PATH' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08PATH</envar></link>"> +<!ENTITY cv-link-F08PPCOM "<link linkend='cv-F08PPCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08PPCOM</envar></link>"> +<!ENTITY cv-link-F08PPCOMSTR "<link linkend='cv-F08PPCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08PPCOMSTR</envar></link>"> +<!ENTITY cv-link-F08PPFILESUFFIXES "<link linkend='cv-F08PPFILESUFFIXES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F08PPFILESUFFIXES</envar></link>"> <!ENTITY cv-link-F77 "<link linkend='cv-F77' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F77</envar></link>"> <!ENTITY cv-link-F77COM "<link linkend='cv-F77COM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F77COM</envar></link>"> <!ENTITY cv-link-F77COMSTR "<link linkend='cv-F77COMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$F77COMSTR</envar></link>"> @@ -828,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>"> @@ -865,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>"> @@ -1086,6 +1139,12 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. <!ENTITY cv-link-SHF03FLAGS "<link linkend='cv-SHF03FLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF03FLAGS</envar></link>"> <!ENTITY cv-link-SHF03PPCOM "<link linkend='cv-SHF03PPCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF03PPCOM</envar></link>"> <!ENTITY cv-link-SHF03PPCOMSTR "<link linkend='cv-SHF03PPCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF03PPCOMSTR</envar></link>"> +<!ENTITY cv-link-SHF08 "<link linkend='cv-SHF08' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF08</envar></link>"> +<!ENTITY cv-link-SHF08COM "<link linkend='cv-SHF08COM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF08COM</envar></link>"> +<!ENTITY cv-link-SHF08COMSTR "<link linkend='cv-SHF08COMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF08COMSTR</envar></link>"> +<!ENTITY cv-link-SHF08FLAGS "<link linkend='cv-SHF08FLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF08FLAGS</envar></link>"> +<!ENTITY cv-link-SHF08PPCOM "<link linkend='cv-SHF08PPCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF08PPCOM</envar></link>"> +<!ENTITY cv-link-SHF08PPCOMSTR "<link linkend='cv-SHF08PPCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF08PPCOMSTR</envar></link>"> <!ENTITY cv-link-SHF77 "<link linkend='cv-SHF77' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF77</envar></link>"> <!ENTITY cv-link-SHF77COM "<link linkend='cv-SHF77COM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF77COM</envar></link>"> <!ENTITY cv-link-SHF77COMSTR "<link linkend='cv-SHF77COMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF77COMSTR</envar></link>"> @@ -1111,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-time.xml b/doc/man/scons-time.xml index a1ecadf..e0fa6b1 100644 --- a/doc/man/scons-time.xml +++ b/doc/man/scons-time.xml @@ -32,7 +32,8 @@ <refmeta> <refentrytitle>SCONS-TIME</refentrytitle> <manvolnum>1</manvolnum> -<refmiscinfo class='source'>__MONTH_YEAR__</refmiscinfo> +<refmiscinfo class='source'>SCons __VERSION__</refmiscinfo> +<refmiscinfo class='manual'>SCons __VERSION__</refmiscinfo> </refmeta> <refnamediv id='name'> <refname>scons-time</refname> diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 8d52c1e..5c832c2 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -54,10 +54,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> @@ -75,7 +75,8 @@ <refmeta> <refentrytitle>SCONS</refentrytitle> <manvolnum>1</manvolnum> -<refmiscinfo class='source'>__MONTH_YEAR__</refmiscinfo> +<refmiscinfo class='source'>SCons __VERSION__</refmiscinfo> +<refmiscinfo class='manual'>SCons __VERSION__</refmiscinfo> </refmeta> <refnamediv id='name'> <refname>scons</refname> @@ -425,7 +426,7 @@ try to access a list member that does not exist.</para> <para><command>scons</command> -requires Python version 2.4 or later. +requires Python version 2.7 or later. There should be no other dependencies or requirements to run <emphasis role="bold">scons.</emphasis></para> @@ -7039,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/man/sconsign.xml b/doc/man/sconsign.xml index ca99db6..44a1e5f 100644 --- a/doc/man/sconsign.xml +++ b/doc/man/sconsign.xml @@ -32,7 +32,8 @@ <refmeta> <refentrytitle>SCONSIGN</refentrytitle> <manvolnum>1</manvolnum> -<refmiscinfo class='source'>__MONTH_YEAR__</refmiscinfo> +<refmiscinfo class='source'>SCons __VERSION__</refmiscinfo> +<refmiscinfo class='manual'>SCons __VERSION__</refmiscinfo> </refmeta> <refnamediv id='name'> <refname>sconsign</refname> diff --git a/doc/man/titlepage/SConsBuildBricks_path.svg b/doc/man/titlepage/SConsBuildBricks_path.svg index ed0c60d..0d7f63e 100644 --- a/doc/man/titlepage/SConsBuildBricks_path.svg +++ b/doc/man/titlepage/SConsBuildBricks_path.svg @@ -14,9 +14,9 @@ height="80.330002" id="svg2" sodipodi:version="0.32" - inkscape:version="0.48.1 r9760" + inkscape:version="0.48.4 r9939" version="1.0" - sodipodi:docname="SConsBuildBricks.svg" + sodipodi:docname="SConsBuildBricks_path.svg" inkscape:export-filename="Constructs-using-SCons.png" inkscape:export-xdpi="100" inkscape:export-ydpi="100"> @@ -77,24 +77,22 @@ </cc:Agent> </dc:contributor> <cc:license - rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.0/" /> + rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" /> </cc:Work> <cc:License - rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + rdf:about="http://creativecommons.org/licenses/by-sa/3.0/"> <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> + rdf:resource="http://creativecommons.org/ns#Reproduction" /> <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> + rdf:resource="http://creativecommons.org/ns#Distribution" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> + rdf:resource="http://creativecommons.org/ns#Notice" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Attribution" /> - <cc:prohibits - rdf:resource="http://web.resource.org/cc/CommercialUse" /> + rdf:resource="http://creativecommons.org/ns#Attribution" /> <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> + rdf:resource="http://creativecommons.org/ns#ShareAlike" /> </cc:License> </rdf:RDF> </metadata> diff --git a/doc/python10/process.xml b/doc/python10/process.xml index 24645e6..6c790d0 100644 --- a/doc/python10/process.xml +++ b/doc/python10/process.xml @@ -50,7 +50,7 @@ <listitem> <para> - &SCons; will be written to Python version 2.4 (to ensure + &SCons; will be written to Python version 2.7 (to ensure usability by a wide install base). </para> @@ -296,7 +296,7 @@ </para> <para> - + In practice, simple tests only need to initialize a test object, use the object to write some input files, run &SCons;, and then check whatever criteria diff --git a/doc/reference/titlepage/SConsBuildBricks_path.svg b/doc/reference/titlepage/SConsBuildBricks_path.svg index ed0c60d..0d7f63e 100644 --- a/doc/reference/titlepage/SConsBuildBricks_path.svg +++ b/doc/reference/titlepage/SConsBuildBricks_path.svg @@ -14,9 +14,9 @@ height="80.330002" id="svg2" sodipodi:version="0.32" - inkscape:version="0.48.1 r9760" + inkscape:version="0.48.4 r9939" version="1.0" - sodipodi:docname="SConsBuildBricks.svg" + sodipodi:docname="SConsBuildBricks_path.svg" inkscape:export-filename="Constructs-using-SCons.png" inkscape:export-xdpi="100" inkscape:export-ydpi="100"> @@ -77,24 +77,22 @@ </cc:Agent> </dc:contributor> <cc:license - rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.0/" /> + rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" /> </cc:Work> <cc:License - rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + rdf:about="http://creativecommons.org/licenses/by-sa/3.0/"> <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> + rdf:resource="http://creativecommons.org/ns#Reproduction" /> <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> + rdf:resource="http://creativecommons.org/ns#Distribution" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> + rdf:resource="http://creativecommons.org/ns#Notice" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Attribution" /> - <cc:prohibits - rdf:resource="http://web.resource.org/cc/CommercialUse" /> + rdf:resource="http://creativecommons.org/ns#Attribution" /> <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> + rdf:resource="http://creativecommons.org/ns#ShareAlike" /> </cc:License> </rdf:RDF> </metadata> diff --git a/doc/scons.mod b/doc/scons.mod index 72dc7ff..8d64054 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -276,6 +276,7 @@ <!ENTITY CheckHeader "<function xmlns='http://www.scons.org/dbxsd/v1.0'>CheckHeader</function>"> <!ENTITY CheckLib "<function xmlns='http://www.scons.org/dbxsd/v1.0'>CheckLib</function>"> <!ENTITY CheckLibWithHeader "<function xmlns='http://www.scons.org/dbxsd/v1.0'>CheckLibWithHeader</function>"> +<!ENTITY CheckProg "<function xmlns='http://www.scons.org/dbxsd/v1.0'>CheckProg</function>"> <!ENTITY CheckType "<function xmlns='http://www.scons.org/dbxsd/v1.0'>CheckType</function>"> <!ENTITY CheckTypeSize "<function xmlns='http://www.scons.org/dbxsd/v1.0'>CheckTypeSize</function>"> <!ENTITY TryAction "<function xmlns='http://www.scons.org/dbxsd/v1.0'>TryAction</function>"> diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml index ca40b04..3e66172 100644 --- a/doc/user/build-install.xml +++ b/doc/user/build-install.xml @@ -6,7 +6,7 @@ <!ENTITY % scons SYSTEM "../scons.mod"> %scons; - + <!ENTITY % builders-mod SYSTEM "../generated/builders.mod"> %builders-mod; <!ENTITY % functions-mod SYSTEM "../generated/functions.mod"> @@ -15,7 +15,7 @@ %tools-mod; <!ENTITY % variables-mod SYSTEM "../generated/variables.mod"> %variables-mod; - + ]> <chapter id="chap-build-install" @@ -79,7 +79,7 @@ to configure simple &SCons; builds without knowing Python, so you can skip this section if you want to dive in and pick up things - by example- -or, of course, if you are + by example- -or, of course, if you are already familiar with Python. </para> @@ -147,7 +147,7 @@ Python 2.5.1 <para> - &SCons; will work with any 2.x version of Python from 2.4 on; + &SCons; will work with any 2.x version of Python from 2.7 on; 3.0 and later are not yet supported. If you need to install Python and have a choice, we recommend using the most recent 2.x Python version available. @@ -209,7 +209,7 @@ Python 2.5.1 a specific &SCons; RPM file, you can download and install from the generic RPM provided by the &SCons; project. - This will install the + This will install the SCons script(s) in <filename>/usr/bin</filename>, and the SCons library modules in <filename>/usr/lib/scons</filename>. @@ -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. @@ -530,7 +530,7 @@ Python 2.5.1 This would install the <application>scons</application> script in <filename>/opt/scons/bin</filename> - and the build engine in + and the build engine in <filename>/opt/scons/lib/scons</filename>, </para> @@ -566,7 +566,7 @@ Python 2.5.1 relative to the user's <literal>$HOME</literal> directory, the &scons; script in <filename>$HOME/bin</filename> - and the build engine in + and the build engine in <filename>$HOME/lib/scons</filename>, simply type: @@ -719,7 +719,7 @@ $ <userinput>python setup.py install --prefix=$HOME</userinput> while: statements look like break statements look like - + continue statements look like </para> diff --git a/doc/user/builders-writing.xml b/doc/user/builders-writing.xml index f3713f2..dec176a 100644 --- a/doc/user/builders-writing.xml +++ b/doc/user/builders-writing.xml @@ -502,7 +502,7 @@ def build_function(target, source, env): The builder function may raise an exception or return any non-zero value - to indicate that the build is unsuccessful, + to indicate that the build is unsuccessful. </para> diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index cca6033..9f27738 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -257,6 +257,7 @@ Program('program', Glob('*.c')) (see <xref linkend="chap-variants"></xref>, below) and repositories (see <xref linkend="chap-repositories"></xref>, below), + excluding some files and returning strings rather than Nodes. </para> diff --git a/doc/user/libraries.xml b/doc/user/libraries.xml index 626e939..d7983c9 100644 --- a/doc/user/libraries.xml +++ b/doc/user/libraries.xml @@ -123,7 +123,7 @@ void f3() { printf("f3.c\n"); } list of source files. You can, however, also give the &b-link-Library; call object files, - and it will correctly realize + and it will correctly realize they are object files. In fact, you can arbitrarily mix source code files and object files in the source list: 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/misc.xml b/doc/user/misc.xml index a71663f..286963d 100644 --- a/doc/user/misc.xml +++ b/doc/user/misc.xml @@ -2,7 +2,7 @@ <!DOCTYPE sconsdoc [ <!ENTITY % scons SYSTEM "../scons.mod"> %scons; - + <!ENTITY % builders-mod SYSTEM "../generated/builders.mod"> %builders-mod; <!ENTITY % functions-mod SYSTEM "../generated/functions.mod"> @@ -56,11 +56,10 @@ <para> - Although the &SCons; code itself will run - on any 2.x Python version 2.4 or later, + Although the &SCons; code itself will run + on any 2.x Python version 2.7 or later, you are perfectly free to make use of - Python syntax and modules from more modern versions - (for example, Python 2.5 or 2.6) + Python syntax and modules from later versions when writing your &SConscript; files or your own local modules. If you do this, it's usually helpful to 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 &lt;file.h&gt;</literal>. + to <literal>#include <file.h></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..fe933f1 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 &lt;sys/types.h&gt;\n'): +if not conf.CheckType('off_t', '#include <sys/types.h>\n'): print 'Did not find off_t typedef, assuming int' conf.env.Append(CCFLAGS = '-Doff_t=int') env = conf.Finish() @@ -304,6 +304,26 @@ scons: `.' is up to date. </section> <section> + <title>Checking for the Presence of a program</title> + + <para> + + Check for the presence of a program + by using the &CheckProg; method: + + </para> + + <sconstruct> +env = Environment() +conf = Configure(env) +if not conf.CheckProg('foobar'): + print 'Unable to find the program foobar on the system' + Exit(1) +env = conf.Finish() + </sconstruct> + + </section> + <section> <title>Adding Your Own Custom Checks</title> <para> @@ -324,7 +344,7 @@ scons: `.' is up to date. <sconstruct> mylib_test_source_file = """ -#include &lt;mylib.h&gt; +#include <mylib.h> int main(int argc, char **argv) { MyLibrary mylib(argc, argv); @@ -401,7 +421,7 @@ conf = Configure(env, custom_tests = {'CheckMyLibrary' : CheckMyLibrary}) <sconstruct> mylib_test_source_file = """ -#include &lt;mylib.h&gt; +#include <mylib.h> int main(int argc, char **argv) { MyLibrary mylib(argc, argv); diff --git a/doc/user/tasks.xml b/doc/user/tasks.xml index 0bdf678..8026a53 100644 --- a/doc/user/tasks.xml +++ b/doc/user/tasks.xml @@ -96,8 +96,8 @@ filenames = [x for x in filenames if os.path.splitext(x)[1] in extensions] <example> <title>The "backtick function": run a shell command and capture the output</title> -<programlisting>import os -output = os.popen(command).read() +<programlisting>import subprocess +output = subprocess.check_output(command) </programlisting> </example> diff --git a/doc/user/titlepage/SConsBuildBricks_path.svg b/doc/user/titlepage/SConsBuildBricks_path.svg index ed0c60d..0d7f63e 100644 --- a/doc/user/titlepage/SConsBuildBricks_path.svg +++ b/doc/user/titlepage/SConsBuildBricks_path.svg @@ -14,9 +14,9 @@ height="80.330002" id="svg2" sodipodi:version="0.32" - inkscape:version="0.48.1 r9760" + inkscape:version="0.48.4 r9939" version="1.0" - sodipodi:docname="SConsBuildBricks.svg" + sodipodi:docname="SConsBuildBricks_path.svg" inkscape:export-filename="Constructs-using-SCons.png" inkscape:export-xdpi="100" inkscape:export-ydpi="100"> @@ -77,24 +77,22 @@ </cc:Agent> </dc:contributor> <cc:license - rdf:resource="http://creativecommons.org/licenses/by-nc-sa/2.0/" /> + rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" /> </cc:Work> <cc:License - rdf:about="http://creativecommons.org/licenses/by-nc-sa/2.5/"> + rdf:about="http://creativecommons.org/licenses/by-sa/3.0/"> <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> + rdf:resource="http://creativecommons.org/ns#Reproduction" /> <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> + rdf:resource="http://creativecommons.org/ns#Distribution" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> + rdf:resource="http://creativecommons.org/ns#Notice" /> <cc:requires - rdf:resource="http://web.resource.org/cc/Attribution" /> - <cc:prohibits - rdf:resource="http://web.resource.org/cc/CommercialUse" /> + rdf:resource="http://creativecommons.org/ns#Attribution" /> <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> + rdf:resource="http://creativecommons.org/ns#ShareAlike" /> </cc:License> </rdf:RDF> </metadata> 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"> @@ -17,8 +17,6 @@ # build directory and sets PYTHONPATH to reference modules unpacked # during build process for testing purposes (build/test-*). # -# -3 Run with the python -3 option, -# # -a Run all tests found under the current directory. # It is also possible to specify a list of # subdirectories to search. @@ -113,7 +111,6 @@ list_only = None printcommand = 1 package = None print_passed_summary = None -python3incompatibilities = None scons = None scons_exec = None testlistfile = None @@ -133,7 +130,6 @@ Usage: runtest.py [OPTIONS] [TEST ...] """ helpstr = usagestr + """\ Options: - -3 Warn about Python 3.x incompatibilities. -a --all Run all tests. -b --baseline BASE Run test scripts against baseline BASE. --builddir DIR Directory in which packages were built. @@ -215,7 +211,7 @@ parser.add_option('--xml', #print "args:", args -opts, args = getopt.getopt(args, "3b:def:hj:klnP:p:qsv:Xx:t", +opts, args = getopt.getopt(args, "b:def:hj:klnP:p:qsv:Xx:t", ['baseline=', 'builddir=', 'debug', 'external', 'file=', 'help', 'no-progress', 'jobs=', @@ -228,9 +224,7 @@ opts, args = getopt.getopt(args, "3b:def:hj:klnP:p:qsv:Xx:t", 'verbose=']) for o, a in opts: - if o in ['-3']: - python3incompatibilities = 1 - elif o in ['-b', '--baseline']: + if o in ['-b', '--baseline']: baseline = a elif o in ['--builddir']: builddir = a @@ -639,9 +633,6 @@ if old_pythonpath: os.pathsep + \ old_pythonpath -if python3incompatibilities: - os.environ['SCONS_HORRIBLE_REGRESSION_TEST_HACK'] = '1' - # ---[ test discovery ]------------------------------------ @@ -774,8 +765,6 @@ def run_test(t, io_lock, async=True): global tests_completed header = "" command_args = ['-tt'] - if python3incompatibilities: - command_args.append('-3') if debug: command_args.append(debug) command_args.append(t.path) diff --git a/src/Announce.txt b/src/Announce.txt index 83fe421..c20d7df 100644 --- a/src/Announce.txt +++ b/src/Announce.txt @@ -18,14 +18,131 @@ 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 FOR NEXT VERSION V2.5.0========== +PLEASE READ. CHANGES COMING IN NEXT MAJOR RELEASE V2.5.0 -RELEASE 2.3.2.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE +We're enhancing implicit language scanning functionality to improve +correctness. SCons now honors scanner keys for implicit dependencies +and correctly changes scanner type (if necessary) when traversing +implicit dependency trees. + +This enhancement resolves missing dependencies +with built-in scanners including SWIG (#2264) and QT: +* http://scons.tigris.org/issues/show_bug.cgi?id=2264 + +This enhancement broadens the horizon for handling heterogeneous +data flow environments (E.G. software builds): +* http://article.gmane.org/gmane.comp.programming.tools.scons.user/26596 +Notes: + +* SCons may find new (and correct) dependencies in cross-langauge contexts. +** Update may cause rebuilds, especially in heterogeneous data environments. +** Update may find previously missed dependencies errors (E.G. cycles). +*** Discovered in some QT test cases. +* SCons handles the SCANNERS variable differently. +** Previously, the Install builder would scan implicit dependencies for + a scanner found in SCANNERS (but not for built-in scanners), but now + the Install builder will not scan recursively regardless in order + to optimize Install behaviour and bring orthogonality to previous behaviour. + ++================================================================= + + +RELEASE VERSION/DATE TO BE FILLED IN LATER Please consult the RELEASE.txt file for a summary of changes since the last release and consult the CHANGES.txt file for complete a list of changes since last release. This announcement highlights only the important changes. + Please note the following important changes since release 2.4.0: + - Fix to swig tool - pick-up 'swig', 'swig3.0' and 'swig2.0' (in order). + - Fix to swig tool - respect env['SWIG'] provided by user. + - Fix for Bug # 2791 - Setup.py fails unnecessarily under Jython. + - Fixed license of SVG titlepage files in the context of Debian + packaging, such that they allow for commercial use too (#2985). + - InstallVersionedLib now available in the DefaultEnvironment context. + - Improves orthogonality of use cases between different Install functions. + - Added new configure check, CheckProg, to check for + existence of a program. + - Fix for issue #2840 - Fix for two environments specifying same target with different + actions not throwing hard error. Instead SCons was incorrectly issuing a warning + and continuing. + - Add support `Microsoft Visual C++ Compiler for Python 2.7' + Compiler can be obtained at: https://www.microsoft.com/en-us/download/details.aspx?id=44266 + - Fixed tigris issue #3011: Glob() excludes didn't work when used with VariantDir(duplicate=0) + - Fix bug 2831 and allow Help() text to be appended to AddOption() help. + - 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'. + + 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. + + -- Fix for interactive mode with Configure contexts + + Please note the following important changes since release 2.3.2: + + -- On Windows, .def files did not work as sources to shared + libraries or executables, due to a regression which is + corrected in 2.3.3. + Please note the following important changes since release 2.3.0: -- BitKeeper, CVS, Perforce, RCS, SCCS are deprecated from the diff --git a/src/CHANGES.txt b/src/CHANGES.txt index aaf3bc8..659e651 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -4,7 +4,171 @@ Change Log -RELEASE 2.3.2.alpha.yyyymmdd - NEW DATE WILL BE INSERTED HERE +RELEASE VERSION/DATE TO BE FILLED IN LATER + + From William Blevins: + - Added support for cross-language dependency scanning; + SCons now respects scanner keys for implicit dependencies. + - Resolved missing cross-language dependencies for + SWIG bindings (fixes #2264). + + From Dirk Baechle: + - Removed a lot of compatibility methods and workarounds + for Python versions < 2.7, in order to prepare the work + towards a combined 2.7/3.x version. (PR #284) + Also fixed the default arguments for the print_tree and + render_tree methods. (PR #284, too) + + +RELEASE 2.4.1 - Mon, 07 Nov 2015 10:37:21 -0700 + + From Arfrever Frehtes Taifersar Arahesis: + - Fix for Bug # 2791 - Setup.py fails unnecessarily under Jython. + + From Dirk Baechle: + - Fixed license of SVG titlepage files in the context of Debian + packaging, such that they allow for commercial use too (#2985). + + From William Blevins: + - InstallVersionedLib now available in the DefaultEnvironment context. + - Improves orthogonality of use cases between different Install functions. + + From Carnë Draug: + - Added new configure check, CheckProg, to check for + existence of a program. + + From Andrew Featherstone: + - Fix for issue #2840 - Fix for two environments specifying same target with different + actions not throwing hard error. Instead SCons was incorrectly issuing a warning + and continuing. + + From Hiroaki Itoh : + - Add support `Microsoft Visual C++ Compiler for Python 2.7' + Compiler can be obtained at: https://www.microsoft.com/en-us/download/details.aspx?id=44266 + + From Florian Miedniak: + - Fixed tigris issue #3011: Glob() excludes didn't work when used with VariantDir(duplicate=0) + + 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'. + - Fix to swig tool - pick-up 'swig', 'swig3.0' and 'swig2.0' (in order). + - Fix to swig tool - respect env['SWIG'] provided by user. + + + +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) + + From William Deegan: + - Extended docs for InstallVersionedLib/SharedLibrary, + and added SKIP_WIN_PACKAGES argument to build script + bootstrap.py (PR #230, #3002). + + From William Blevins: + - Fixed symlink support (PR #227, #2395). + - Updated debug-count test case (PR #229). + + From Alexey Klimkin: + - Fixed incomplete LIBS flattening and substitution in + Program scanner(PR #205, #2954). + + From Dirk Baechle: + - Added new method rentry_exists_on_disk to Node.FS (PR #193). + + From Russel Winder: + - Fixed several D tests under the different OS. + - Add support for f08 file extensions for Fortran 2008 code. + + From Anatoly Techtonik: + - 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. + + From Alexandre Feblot: + - Fix for VersionedSharedLibrary under 'sunos' platform. + - Fixed dll link with precompiled headers on MSVC 2012 + - Added an 'exclude' parameter to Glob() + + From Laurent Marchelli: + - 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). + + From Dan Pidcock: + - Added support for the 'PlatformToolset' tag in VS project files (#2978). + + From James McCoy: + - Added support for '-isystem' to ParseFlags. + +RELEASE 2.3.4 - Mon, 27 Sep 2014 12:50:35 -0400 + + From Bernhard Walle and Dirk Baechle: + - Fixed the interactive mode, in connection with + Configure contexts (#2971). + + From Anatoly Techtonik: + - Fix EnsureSConsVersion warning when running packaged version + + From Russel Winder: + - Fix D tools for building shared libraries + +RELEASE 2.3.3 - Sun, 24 Aug 2014 21:08:33 -0400 + + From Roland Stark: + - Fixed false line length calculation in the TempFileMunge class (#2970). + + From Gary Oberbrunner: + - Improve SWIG detection + + From Russel Winder: + - Fix regression on Windows in D language update From Neal Becker and Stefan Zimmermann: - Python 3 port and compatibility 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 7249af4..3bff366 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -1,99 +1,73 @@ - A new SCons release, 2.3.2, is now available + A new SCons checkpoint release, 2.4.3.alpha.yyyymmdd, is now available on the SCons download page: http://www.scons.org/download.php - This is a maintenance release, focusing on bug fixes. + XXX The primary purpose of this release ... XXX - Here is a summary of the changes since 2.3.1: + A SCons "checkpoint release" is intended to provide early access to + new features so they can be tested in the field before being released + for adoption by other software distributions. + + Note that a checkpoint release is developed using the same test-driven + development methodology as all SCons releases. Existing SCons + functionality should all work as it does in previous releases (except + for any changes identified in the release notes) and early adopters + should be able to use a checkpoint release safely for production work + with existing SConscript files. If not, it represents not only a bug + in SCons but also a hole in the regression test suite, and we want to + hear about it. + + New features may be more lightly tested than in past releases, + especially as concerns their interaction with all of the other + functionality in SCons. We are especially interested in hearing bug + reports about new functionality. + + We do not recommend that downstream distributions (Debian, Fedora, + etc.) package a checkpoint release, mainly to avoid confusing the + "public" release numbering with the long checkpoint release names. + + Here is a summary of the changes since 1.3.0: NEW FUNCTIONALITY - - Support for Visual Studio 12.0Exp and 2013 (12.0). + - List new features (presumably why a checkpoint is being released) DEPRECATED FUNCTIONALITY - - RPM and m4 are no longer in the default toolset on Windows. - - BitKeeper, CVS, Perforce, RCS, SCCS are deprecated from the - default toolset and will be removed from the default toolset - in future SCons versions. - - D language, version 1, is now deprecated. Version 2 is - supported. + - List anything that's been deprecated since the last release CHANGED/ENHANCED EXISTING FUNCTIONALITY - - Revamp of D language support. - Tools for DMD, GDC and LDC are provided, and integrated - with the C and C++ linking. - - TeX builder now supports -synctex=1 - - TeX builder cleans auxiliary files correctly with biblatex. + - List modifications to existing features, where the previous behavior + wouldn't actually be considered a bug FIXES - - Fixed handling of nested ifs in CPP scanner PreProcessor class. - - Respect user's CC/CXX values; don't always overwrite in generate() - - Delegate linker Tool.exists() to CC/CXX Tool.exists(). - - Fixed NoClean() for multi-target builders (#2353). - - Fix SConf tests that write output - - get default RPM architecture more robustly when building RPMs - - Allow varlist to be specified as list of strings for Actions (#2754) - - Fixes to Docbook tool + - List fixes of outright bugs + + IMPROVEMENTS + + - List improvements that wouldn't be visible to the user in the + documentation: performance improvements (describe the circumstances + under which they would be observed), or major code cleanups PACKAGING - - Update XML doc editor configuration + - List changes in the way SCons is packaged and/or released + + DOCUMENTATION + + - List any significant changes to the documentation (not individual + typo fixes, even if they're mentioned in src/CHANGES.txt to give + the contributor credit) DEVELOPMENT - - Improvements to running scons.py from the source tree - -Thanks to: - Dirk Baechle, - Vincent Beffar, - Thomas Berg, - Antonio Cavallo, - Jean-François Colson, - Bauke Conijn, - Bill Deegan, - Ken Deeter, - dubcanada on Bitbucket, - Luca Falavigna, - Andrew Featherstone, - Alexandre Feblot, - Shane Gannon, - Alexander Goomenyuk, - Justin Gullingsrud, - Michael Haubenwallner, - Joshua Hughes, - Alexey Klimkin, - Steven Knight, - Arve Knudsen, - Philipp Kraus, - Jean-Baptiste Lab, - Juan Lang, - Rob Managan, - Michael McDougall, - Mortoray, - Manuel Francisco Naranjo, - Gary Oberbrunner, - Alexey Petruchik, - Evgeny Podjachev, - David Rothenberger, - smallbub on Bitbucket, - Sohail Somani, - Stefan Sperling, - Amir Szekely, - Tom Tanner, - Anatoly Techtonik, - Bogdan Tenea, - Paweł Tomulik, - Sye van der Veen, - veon on bitbucket, - Greg Ward, - Allen Weeks, - Russel Winder, - Joe Zuntz - for their contributions to this and prior releases. + - List visible changes in the way SCons is developed + + Thanks to CURLY, LARRY, and MOE for their contributions to this release. + Contributors are listed alphabetically by their last name. __COPYRIGHT__ __FILE__ __REVISION__ __DATE__ __DEVELOPER__ diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index f62d16e..f7d5aa7 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -39,6 +39,7 @@ SCons/Scanner/IDL.py SCons/Scanner/LaTeX.py SCons/Scanner/Prog.py SCons/Scanner/RC.py +SCons/Scanner/SWIG.py SCons/SConf.py SCons/SConsign.py SCons/Script/__init__.py diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 415cc9a..d11f30d 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -99,8 +99,6 @@ way for wrapping up the functions. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import SCons.compat - import dis import os # compat layer imports "cPickle" for us if it's available. @@ -112,7 +110,6 @@ import subprocess import SCons.Debug from SCons.Debug import logInstanceCreation import SCons.Errors -import SCons.Executor import SCons.Util import SCons.Subst @@ -237,12 +234,8 @@ def _code_contents(code): # The code contents depends on the number of local variables # but not their actual names. - contents.append(str.encode("%s,%s" % (code.co_argcount, len(code.co_varnames)))) - try: - contents.append(str.encode(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))) - except AttributeError: - # Older versions of Python do not support closures. - contents.append(",0,0") + contents.append("{}, {}".format(code.co_argcount, len(code.co_varnames))) + contents.append(", {}, {}".format(len(code.co_cellvars), len(code.co_freevars))) # The code contents depends on any constants accessed by the # function. Note that we have to call _object_contents on each @@ -279,11 +272,7 @@ def _function_contents(func): contents.append(b',()') # The function contents depends on the closure captured cell values. - try: - closure = func.__closure__ or [] - except AttributeError: - # Older versions of Python do not support closures. - closure = [] + closure = func.func_closure or [] #xxx = [_object_contents(x.cell_contents) for x in closure] try: @@ -357,21 +346,6 @@ def _do_create_action(act, kw): if isinstance(act, ActionBase): return act - if is_List(act): - return CommandAction(act, **kw) - - if callable(act): - try: - gen = kw['generator'] - del kw['generator'] - except KeyError: - gen = 0 - if gen: - action_type = CommandGeneratorAction - else: - action_type = FunctionAction - return action_type(act, kw) - if is_String(act): var=SCons.Util.get_environment_var(act) if var: @@ -388,6 +362,22 @@ def _do_create_action(act, kw): # The list of string commands may include a LazyAction, so we # reprocess them via _do_create_list_action. return _do_create_list_action(commands, kw) + + if is_List(act): + return CommandAction(act, **kw) + + if callable(act): + try: + gen = kw['generator'] + del kw['generator'] + except KeyError: + gen = 0 + if gen: + action_type = CommandGeneratorAction + else: + action_type = FunctionAction + return action_type(act, kw) + # Catch a common error case with a nice message: if isinstance(act, int) or isinstance(act, float): raise TypeError("Don't know how to create an Action from a number (%s)"%act) @@ -543,7 +533,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: @@ -949,7 +939,6 @@ class LazyAction(CommandGeneratorAction, CommandAction): def __init__(self, var, kw): if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.LazyAction') - #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) CommandAction.__init__(self, '${'+var+'}', **kw) self.var = SCons.Util.to_String(var) self.gen_kw = kw diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 8038f7c..f56948f 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -23,8 +23,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import SCons.compat - # Define a null function and a null class for use as builder actions. # Where these are defined in the file seems to affect their byte-code # contents, so try to minimize changes by defining them here, before we @@ -231,7 +229,6 @@ def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw): def test_positional_args(pos_callback, cmd, **kw): """Test that Action() returns the expected type and that positional args work. """ - #FUTURE act = SCons.Action.Action(cmd, **kw) act = SCons.Action.Action(cmd, **kw) pos_callback(act) assert act.varlist is (), act.varlist @@ -239,7 +236,6 @@ def test_positional_args(pos_callback, cmd, **kw): if not isinstance(act, SCons.Action._ActionAction): # only valid cmdstrfunc is None def none(a): pass - #FUTURE test_varlist(pos_callback, none, cmd, None, **kw) test_varlist(pos_callback, none, cmd, None, **kw) else: # _ActionAction should have set these @@ -253,25 +249,21 @@ def test_positional_args(pos_callback, cmd, **kw): def cmdstr(a): assert hasattr(a, 'strfunction') assert a.cmdstr == 'cmdstr', a.cmdstr - #FUTURE test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw) test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw) def fun(): pass def strfun(a, fun=fun): assert a.strfunction is fun, a.strfunction assert a.cmdstr == _null, a.cmdstr - #FUTURE test_varlist(pos_callback, strfun, cmd, fun, **kw) test_varlist(pos_callback, strfun, cmd, fun, **kw) def none(a): assert hasattr(a, 'strfunction') assert a.cmdstr is None, a.cmdstr - #FUTURE test_varlist(pos_callback, none, cmd, None, **kw) test_varlist(pos_callback, none, cmd, None, **kw) """Test handling of bad cmdstrfunc arguments """ try: - #FUTURE a = SCons.Action.Action(cmd, [], **kw) a = SCons.Action.Action(cmd, [], **kw) except SCons.Errors.UserError as e: s = str(e) @@ -1195,81 +1187,6 @@ class CommandActionTestCase(unittest.TestCase): r = act([], [], env) assert r == 0, r - def _DO_NOT_EXECUTE_test_pipe_execute(self): - """Test capturing piped output from an action - - We used to have PIPE_BUILD support built right into - Action.execute() for the benefit of the SConf subsystem, but we've - moved that logic back into SConf itself. We'll leave this code - here, just in case we ever want to resurrect this functionality - in the future, but change the name of the test so it doesn't - get executed as part of the normal test suite. - """ - pipe = open( pipe_file, "w" ) - self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1, - PSTDOUT = pipe, PSTDERR = pipe) - # everything should also work when piping output - self.test_execute() - self.env['PSTDOUT'].close() - pipe_out = test.read( pipe_file ) - - act_out = "act.py: stdout: executed act.py" - act_err = "act.py: stderr: executed act.py" - - # Since we are now using select(), stdout and stderr can be - # intermixed, so count the lines separately. - outlines = re.findall(act_out, pipe_out) - errlines = re.findall(act_err, pipe_out) - assert len(outlines) == 6, pipe_out + repr(outlines) - assert len(errlines) == 6, pipe_out + repr(errlines) - - # test redirection operators - def test_redirect(self, redir, stdout_msg, stderr_msg): - cmd = r'%s %s %s xyzzy %s' % (_python_, act_py, outfile, redir) - # Write the output and error messages to files because - # Windows can't handle strings that are too big in its - # external environment (os.spawnve() returns EINVAL, - # "Invalid argument"). - stdout_file = test.workpath('stdout_msg') - stderr_file = test.workpath('stderr_msg') - open(stdout_file, 'w').write(stdout_msg) - open(stderr_file, 'w').write(stderr_msg) - pipe = open( pipe_file, "w" ) - act = SCons.Action.CommandAction(cmd) - env = Environment( ENV = {'ACTPY_PIPE' : '1', - 'PIPE_STDOUT_FILE' : stdout_file, - 'PIPE_STDERR_FILE' : stderr_file}, - PIPE_BUILD = 1, - PSTDOUT = pipe, PSTDERR = pipe ) - r = act([], [], env) - pipe.close() - assert r == 0 - return (test.read(outfile2, 'r'), test.read(pipe_file, 'r')) - - (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2, - act_out, act_err) - assert redirected == act_out - assert pipe_out == act_err - - (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2, - act_out, act_err) - assert redirected == act_err - assert pipe_out == act_out - - (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2, - act_out, act_err) - assert (redirected == act_out + act_err or - redirected == act_err + act_out) - assert pipe_out == "" - - act_err = "Long Command Output\n"*3000 - # the size of the string should exceed the system's default block size - act_out = "" - (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2, - act_out, act_err) - assert (redirected == act_out) - assert (pipe_out == act_err) - def test_set_handler(self): """Test setting the command handler... """ diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index db249bf..e034cad 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -107,8 +107,6 @@ from SCons.Debug import logInstanceCreation from SCons.Errors import InternalError, UserError import SCons.Executor import SCons.Memoize -import SCons.Node -import SCons.Node.FS import SCons.Util import SCons.Warnings @@ -294,8 +292,8 @@ def _node_errors(builder, env, tlist, slist): if t.has_explicit_builder(): if not t.env is None and not t.env is env: action = t.builder.action - t_contents = action.get_contents(tlist, slist, t.env) - contents = action.get_contents(tlist, slist, env) + t_contents = t.builder.action.get_contents(tlist, slist, t.env) + contents = builder.action.get_contents(tlist, slist, env) if t_contents == contents: msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env)) @@ -357,11 +355,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 = '', @@ -766,8 +759,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. @@ -803,8 +795,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, @@ -868,7 +859,7 @@ class CompositeBuilder(SCons.Util.Proxy): self.set_src_suffix(self.cmdgen.src_suffixes()) def is_a_Builder(obj): - """"Returns True iff the specified obj is one of our Builder classes. + """"Returns True if the specified obj is one of our Builder classes. The test is complicated a bit by the fact that CompositeBuilder is a proxy, not a subclass of BuilderBase. diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 212872f..1e544a1 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -164,7 +164,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): @@ -350,7 +351,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): @@ -488,20 +489,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'] @@ -521,17 +522,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""" @@ -561,13 +562,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)) @@ -637,14 +638,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'] @@ -664,17 +665,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..3b7d06f 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -32,6 +32,7 @@ import stat import sys import SCons.Action +import SCons.Warnings cache_enabled = True cache_debug = False @@ -50,11 +51,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 +64,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 +107,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/Conftest.py b/src/engine/SCons/Conftest.py index 23441f1..4db7e06 100644 --- a/src/engine/SCons/Conftest.py +++ b/src/engine/SCons/Conftest.py @@ -683,6 +683,22 @@ return 0; return ret +def CheckProg(context, prog_name): + """ + Configure check for a specific program. + + Check whether program prog_name exists in path. If it is found, + returns the path for it, otherwise returns None. + """ + context.Display("Checking whether %s program exists..." % prog_name) + path = context.env.WhereIs(prog_name) + if path: + context.Display(path + "\n") + else: + context.Display("no\n") + return path + + # # END OF PUBLIC FUNCTIONS # diff --git a/src/engine/SCons/Debug.py b/src/engine/SCons/Debug.py index 9974039..9e520ff 100644 --- a/src/engine/SCons/Debug.py +++ b/src/engine/SCons/Debug.py @@ -1,7 +1,10 @@ """SCons.Debug Code for debugging SCons internal things. Shouldn't be -needed by most users. +needed by most users. Quick shortcuts: + +from SCons.Debug import caller_trace +caller_trace() """ @@ -34,6 +37,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 +50,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 +75,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)) @@ -128,8 +140,12 @@ def caller_stack(): caller_bases = {} caller_dicts = {} -# trace a caller's stack def caller_trace(back=0): + """ + Trace caller stack and save info into global dicts, which + are printed automatically at the end of SCons execution. + """ + global caller_bases, caller_dicts import traceback tb = traceback.extract_stack(limit=3+back) tb.reverse() diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 2eb9c86..e26a5c0 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -198,11 +198,10 @@ def copy_func(dest, src, symlinks=True): shutil.copy2(file, dest) return 0 elif os.path.islink(src): - linkto = os.readlink(src) if symlinks: - return os.symlink(linkto, dest) + return os.symlink(os.readlink(src), dest) else: - return copy_func(dest, linkto, symlinks) + return copy_func(dest, os.path.realpath(src)) elif os.path.isfile(src): shutil.copy2(src, dest) return 0 @@ -484,9 +483,18 @@ 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' : [], + 'SCANNERS' : [ SCons.Tool.SourceFileScanner ], 'CONFIGUREDIR' : '#/.sconf_temp', 'CONFIGURELOG' : '#/config.log', 'CPPSUFFIXES' : SCons.Tool.CSuffixes, @@ -501,6 +509,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 59390f7..d979005 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. @@ -719,6 +716,9 @@ class SubstitutionEnvironment(object): t = ('-isysroot', arg) dict['CCFLAGS'].append(t) dict['LINKFLAGS'].append(t) + elif append_next_arg_to == '-isystem': + t = ('-isystem', arg) + dict['CCFLAGS'].append(t) elif append_next_arg_to == '-arch': t = ('-arch', arg) dict['CCFLAGS'].append(t) @@ -791,7 +791,7 @@ class SubstitutionEnvironment(object): elif arg[0] == '+': dict['CCFLAGS'].append(arg) dict['LINKFLAGS'].append(arg) - elif arg in ['-include', '-isysroot', '-arch']: + elif arg in ['-include', '-isysroot', '-isystem', '-arch']: append_next_arg_to = arg else: dict['CCFLAGS'].append(arg) @@ -857,25 +857,6 @@ class SubstitutionEnvironment(object): self[key] = t return self -# def MergeShellPaths(self, args, prepend=1): -# """ -# Merge the dict in args into the shell environment in env['ENV']. -# Shell path elements are appended or prepended according to prepend. - -# Uses Pre/AppendENVPath, so it always appends or prepends uniquely. - -# Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'}) -# prepends /usr/local/lib to env['ENV']['LIBPATH']. -# """ - -# for pathname, pathval in args.items(): -# if not pathval: -# continue -# if prepend: -# self.PrependENVPath(pathname, pathval) -# else: -# self.AppendENVPath(pathname, pathval) - def default_decide_source(dependency, target, prev_ni): f = SCons.Defaults.DefaultEnvironment().decide_source @@ -899,8 +880,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 @@ -1068,8 +1047,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'] @@ -1525,8 +1503,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 @@ -1799,7 +1777,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): @@ -2077,8 +2055,8 @@ class Base(SubstitutionEnvironment): else: return result[0] - def Glob(self, pattern, ondisk=True, source=False, strings=False): - return self.fs.Glob(self.subst(pattern), ondisk, source, strings) + def Glob(self, pattern, ondisk=True, source=False, strings=False, exclude=None): + return self.fs.Glob(self.subst(pattern), ondisk, source, strings, exclude) def Ignore(self, target, dependency): """Ignore a dependency.""" diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml index b3b132e..b90f1d8 100644 --- a/src/engine/SCons/Environment.xml +++ b/src/engine/SCons/Environment.xml @@ -1697,7 +1697,7 @@ Nodes or strings representing path names. <scons_function name="Glob"> <arguments> -(pattern, [ondisk, source, strings]) +(pattern, [ondisk, source, strings, exclude]) </arguments> <summary> <para> @@ -1811,12 +1811,23 @@ directory.) </para> <para> +The +<varname>exclude</varname> +argument may be set to a pattern or a list of patterns +(following the same Unix shell semantics) +which must be filtered out of returned elements. +Elements matching a least one pattern of +this list will be excluded. +</para> + +<para> Examples: </para> <example_commands> Program('foo', Glob('*.c')) Zip('/tmp/everything', Glob('.??*') + Glob('*')) +sources = Glob('*.cpp', exclude=['os_*_specific_*.cpp']) + Glob('os_%s_specific_*.cpp'%currentOS) </example_commands> </summary> </scons_function> diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 2a3852f..99d169a 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -800,7 +800,9 @@ sys.exit(0) "-pthread " + \ "-fopenmp " + \ "-mno-cygwin -mwindows " + \ - "-arch i386 -isysroot /tmp +DD64 " + \ + "-arch i386 -isysroot /tmp " + \ + "-isystem /usr/include/foo " + \ + "+DD64 " + \ "-DFOO -DBAR=value -D BAZ " d = env.ParseFlags(s) @@ -810,6 +812,7 @@ sys.exit(0) assert d['CCFLAGS'] == ['-X', '-Wa,-as', '-pthread', '-fopenmp', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), + ('-isystem', '/usr/include/foo'), '+DD64'], repr(d['CCFLAGS']) assert d['CXXFLAGS'] == ['-std=c++0x'], repr(d['CXXFLAGS']) assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES'] @@ -854,31 +857,6 @@ sys.exit(0) assert env['A'] == ['aaa'], env['A'] assert env['B'] == ['bbb'], env['B'] -# def test_MergeShellPaths(self): -# """Test the MergeShellPaths() method -# """ -# env = Environment() -# env.MergeShellPaths({}) -# assert not env['ENV'].has_key('INCLUDE'), env['INCLUDE'] -# env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}) -# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE'] -# env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}) -# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE'] -# env.MergeShellPaths({'INCLUDE': r'xyz'}) -# assert env['ENV']['INCLUDE'] == r'xyz%sc:\Program Files\Stuff'%os.pathsep, env['ENV']['INCLUDE'] - -# env = Environment() -# env['ENV']['INCLUDE'] = 'xyz' -# env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']} ) -# assert env['ENV']['INCLUDE'] == r'c:/inc1%sc:/inc2%sxyz'%(os.pathsep, os.pathsep), env['ENV']['INCLUDE'] - -# # test prepend=0 -# env = Environment() -# env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}, prepend=0) -# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE'] -# env.MergeShellPaths({'INCLUDE': r'xyz'}, prepend=0) -# assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff%sxyz'%os.pathsep, env['ENV']['INCLUDE'] - class BaseTestCase(unittest.TestCase,TestEnvironmentFixture): @@ -2046,7 +2024,9 @@ def generate(env): "-F fwd3 " + \ "-pthread " + \ "-mno-cygwin -mwindows " + \ - "-arch i386 -isysroot /tmp +DD64 " + \ + "-arch i386 -isysroot /tmp " + \ + "-isystem /usr/include/foo " + \ + "+DD64 " + \ "-DFOO -DBAR=value") env.ParseConfig("fake $COMMAND") assert save_command == ['fake command'], save_command @@ -2054,6 +2034,7 @@ def generate(env): assert env['CCFLAGS'] == ['', '-X', '-Wa,-as', '-pthread', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), + ('-isystem', '/usr/include/foo'), '+DD64'], env['CCFLAGS'] assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES'] assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS'] @@ -2692,25 +2673,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): @@ -2800,13 +2781,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' @@ -2817,8 +2798,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): @@ -2835,7 +2816,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', @@ -2879,27 +2860,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""" @@ -2933,19 +2914,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): @@ -3063,27 +3044,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""" @@ -3112,19 +3093,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): @@ -3135,19 +3116,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): @@ -3252,7 +3233,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] @@ -3261,7 +3242,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 @@ -3270,7 +3251,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 @@ -3279,18 +3260,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 2c53f21..eace84a 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,47 @@ 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 = ([], [], 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 +162,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 +191,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): @@ -185,14 +243,12 @@ class Executor(object): return self._changed_targets_list def _get_source(self, *args, **kw): - #return SCons.Util.NodeList([rfile(self.batches[0].sources[0]).get_subst_proxy()]) return rfile(self.batches[0].sources[0]).get_subst_proxy() def _get_sources(self, *args, **kw): return SCons.Util.NodeList([rfile(n).get_subst_proxy() for n in self.get_all_sources()]) def _get_target(self, *args, **kw): - #return SCons.Util.NodeList([self.batches[0].targets[0].get_subst_proxy()]) return self.batches[0].targets[0].get_subst_proxy() def _get_targets(self, *args, **kw): @@ -284,8 +340,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 +385,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 +434,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 @@ -461,29 +483,15 @@ class Executor(object): each individual target, which is a hell of a lot more efficient. """ env = self.get_build_env() + path = self.get_build_scanner_path + kw = self.get_kw() # TODO(batch): scan by batches) deps = [] - if scanner: - for node in node_list: - node.disambiguate() - s = scanner.select(node) - if not s: - continue - path = self.get_build_scanner_path(s) - deps.extend(node.get_implicit_deps(env, s, path)) - else: - kw = self.get_kw() - for node in node_list: - node.disambiguate() - scanner = node.get_env_scanner(env, kw) - if not scanner: - continue - scanner = scanner.select(node) - if not scanner: - continue - path = self.get_build_scanner_path(scanner) - deps.extend(node.get_implicit_deps(env, scanner, path)) + + for node in node_list: + node.disambiguate() + deps.extend(node.get_implicit_deps(env, scanner, path, kw)) deps.extend(self.get_implicit_deps()) @@ -493,8 +501,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: @@ -554,19 +561,20 @@ def AddBatchExecutor(key, executor): nullenv = None +import SCons.Util +class NullEnvironment(SCons.Util.Null): + import SCons.CacheDir + _CacheDir_path = None + _CacheDir = SCons.CacheDir.CacheDir(None) + def get_CacheDir(self): + return self._CacheDir + + def get_NullEnvironment(): """Use singleton pattern for Null Environments.""" global nullenv - import SCons.Util - class NullEnvironment(SCons.Util.Null): - import SCons.CacheDir - _CacheDir_path = None - _CacheDir = SCons.CacheDir.CacheDir(None) - def get_CacheDir(self): - return self._CacheDir - - if not nullenv: + if nullenv is None: nullenv = NullEnvironment() return nullenv @@ -578,6 +586,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/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py index c649f0e..99c6226 100644 --- a/src/engine/SCons/ExecutorTests.py +++ b/src/engine/SCons/ExecutorTests.py @@ -83,7 +83,9 @@ class MyNode(object): executor(self) def get_env_scanner(self, env, kw): return MyScanner('dep-') - def get_implicit_deps(self, env, scanner, path): + def get_implicit_deps(self, env, scanner, path, kw={}): + if not scanner: + scanner = self.get_env_scanner(env, kw) return [scanner.prefix + str(self)] def add_to_implicit(self, deps): self.implicit.extend(deps) diff --git a/src/engine/SCons/Job.py b/src/engine/SCons/Job.py index 226a34e..c0e80b1 100644 --- a/src/engine/SCons/Job.py +++ b/src/engine/SCons/Job.py @@ -70,7 +70,7 @@ class Jobs(object): def __init__(self, num, taskmaster): """ - create 'num' jobs using the given taskmaster. + Create 'num' jobs using the given taskmaster. If 'num' is 1 or less, then a serial job will be used, otherwise a parallel job with 'num' worker threads will @@ -126,10 +126,10 @@ class Jobs(object): c) SIGHUP: Controlling shell exiting We handle all of these cases by stopping the taskmaster. It - turns out that it very difficult to stop the build process + turns out that it's very difficult to stop the build process by throwing asynchronously an exception such as KeyboardInterrupt. For example, the python Condition - variables (threading.Condition) and queue's do not seem to + variables (threading.Condition) and queues do not seem to be asynchronous-exception-safe. It would require adding a whole bunch of try/finally block and except KeyboardInterrupt all over the place. @@ -177,7 +177,7 @@ class Serial(object): The taskmaster's next_task() method should return the next task that needs to be executed, or None if there are no more tasks. The taskmaster's executed() method will be called for each task when it - is successfully executed or failed() will be called if it failed to + is successfully executed, or failed() will be called if it failed to execute (e.g. execute() raised an exception).""" self.taskmaster = taskmaster @@ -351,7 +351,7 @@ else: The taskmaster's next_task() method should return the next task that needs to be executed, or None if there are no more tasks. The taskmaster's executed() method will be called - for each task when it is successfully executed or failed() + for each task when it is successfully executed, or failed() will be called if the task failed to execute (i.e. execute() raised an exception). diff --git a/src/engine/SCons/Memoize.py b/src/engine/SCons/Memoize.py index d2938c7..9774bdd 100644 --- a/src/engine/SCons/Memoize.py +++ b/src/engine/SCons/Memoize.py @@ -26,17 +26,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 @@ -56,8 +56,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 @@ -78,10 +77,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 @@ -103,51 +98,36 @@ 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(" {:7d} hits {:7d} misses {}()".format(self.hit, self.miss, self.key())) def __eq__(self, other): try: - return self.name == other.name + return self.key() == other.key() except AttributeError: return True @@ -155,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] @@ -205,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 8476349..ba866d2 100644 --- a/src/engine/SCons/MemoizeTests.py +++ b/src/engine/SCons/MemoizeTests.py @@ -31,20 +31,17 @@ import TestUnit import SCons.Memoize +# Enable memoization counting +SCons.Memoize.EnableMemoization() -@add_metaclass(SCons.Memoize.Memoized_Metaclass) class FakeObject(object): - - 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 @@ -65,8 +62,7 @@ class FakeObject(object): return result - memoizer_counters.append(SCons.Memoize.CountValue('value')) - + @SCons.Memoize.CountMethodCall def value(self): try: @@ -81,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 efca0c7..a4dd5d9 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -57,10 +57,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 @@ -110,7 +123,7 @@ def save_strings(val): # tells us whether or not os.path.splitdrive() actually does anything # on this system, and therefore whether we need to bother calling it # when looking up path names in various methods below. -# +# do_splitdrive = None _my_splitdrive =None @@ -145,7 +158,7 @@ def initialize_do_splitdrive(): global OS_SEP global UNC_PREFIX global os_sep_is_slash - + OS_SEP = os.sep UNC_PREFIX = OS_SEP + OS_SEP os_sep_is_slash = OS_SEP == '/' @@ -166,7 +179,7 @@ needs_normpath_check = re.compile( # b) The path starts with '..'. E.g. '../' or '../moredirs' # but we not match '..abc/'. # c) The path ends with '..'. E.g. '/..' or 'dirs/..' - # d) The path contains a '..' in the middle. + # d) The path contains a '..' in the middle. # E.g. dirs/../moredirs (.*/)?\.\.(?:/|$) | @@ -174,7 +187,7 @@ needs_normpath_check = re.compile( # We need to renormalize the path if it contains a '.' # directory, but NOT if it is a single '.' '/' characters. We # do not want to match a single '.' because this case is checked - # for explicitely since this is common enough case. + # for explicitly since this is common enough case. # # Note that we check for all the following cases: # @@ -188,7 +201,7 @@ needs_normpath_check = re.compile( \./|.*/\.(?:/|$) - ''', + ''', re.VERBOSE ) needs_normpath_match = needs_normpath_check.match @@ -269,8 +282,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) @@ -303,7 +316,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) @@ -311,7 +324,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) @@ -404,7 +417,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 @@ -574,7 +587,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. @@ -592,27 +618,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__() + '"' @@ -625,17 +650,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.""" @@ -644,8 +690,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'] @@ -682,21 +727,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() @@ -718,7 +762,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: @@ -753,10 +797,10 @@ 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: + except ValueError: for p in path_elems[:-1]: pathname += p.dirname else: @@ -786,7 +830,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, @@ -812,13 +875,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 @@ -857,8 +919,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'] @@ -880,6 +941,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 that could be a File or a Dir, but we're just not sure yet. @@ -888,6 +960,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 @@ -918,7 +1012,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 @@ -940,17 +1034,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. @@ -990,10 +1074,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() @@ -1004,9 +1085,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) @@ -1020,9 +1098,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 @@ -1091,19 +1166,8 @@ class LocalFS(object): return '' -#class RemoteFS: -# # Skeleton for the obvious methods we might need from the -# # abstraction layer for a remote filesystem. -# def upload(self, local_src, remote_dst): -# pass -# def download(self, remote_src, local_dst): -# pass - - class FS(LocalFS): - memoizer_counters = [] - def __init__(self, path = None): """Initialize the Node.FS subsystem. @@ -1129,13 +1193,13 @@ 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 FileNodeInfo.fs = self - + def set_SConstruct_dir(self, dir): self.SConstruct_dir = dir @@ -1161,7 +1225,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 @@ -1247,12 +1311,12 @@ class FS(LocalFS): p = p.strip('/') needs_normpath = needs_normpath_match(p) - + # 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) @@ -1286,9 +1350,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) @@ -1394,7 +1458,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() @@ -1402,19 +1466,20 @@ class FS(LocalFS): message = fmt % ' '.join(map(str, targets)) return targets, message - def Glob(self, pathname, ondisk=True, source=True, strings=False, cwd=None): + def Glob(self, pathname, ondisk=True, source=True, strings=False, exclude=None, cwd=None): """ Globs - This is mainly a shim layer + This is mainly a shim layer """ if cwd is None: cwd = self.getcwd() - return cwd.glob(pathname, ondisk, source, strings) + 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 @@ -1426,11 +1491,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('[*?[]') @@ -1441,7 +1507,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 @@ -1471,6 +1552,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 @@ -1498,7 +1595,7 @@ class Dir(Base): # Prepend MkdirBuilder action to existing action list l = self.get_executor().action_list a = get_MkdirBuilder().action - l.insert(0, a) + l.insert(0, a) self.get_executor().set_action_list(l) def diskcheck_match(self): @@ -1563,8 +1660,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']) @@ -1590,7 +1686,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): @@ -1599,8 +1695,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. """ @@ -1629,7 +1724,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: @@ -1644,11 +1739,11 @@ 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) memo_dict[other] = result @@ -1714,7 +1809,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: @@ -1754,10 +1849,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 @@ -1771,8 +1863,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.""" @@ -1796,12 +1886,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 @@ -1818,25 +1904,48 @@ 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 + directory, and returns True if a physical entry with the given + name could be found. + + @see rentry_exists_on_disk + """ try: d = self.on_disk_entries except AttributeError: d = {} try: - entries = os.listdir(self.abspath) + entries = os.listdir(self._abspath) except OSError: pass else: @@ -1849,14 +1958,40 @@ 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: return name in d - memoizer_counters.append(SCons.Memoize.CountValue('srcdir_list')) + def rentry_exists_on_disk(self, name): + """ Searches through the file/dir entries of the current + *and* all its remote directories (repos), and returns + True if a physical entry with the given name could be found. + The local directory (self) gets searched first, so + repositories take a lower precedence regarding the + searching order. + + @see entry_exists_on_disk + """ + rentry_exists = self.entry_exists_on_disk(name) + if not rentry_exists: + # Search through the repository folders + norm_name = _my_normcase(name) + for rdir in self.get_all_rdirs(): + try: + node = rdir.entries[norm_name] + if node: + rentry_exists = True + break + except KeyError: + if rdir.entry_exists_on_disk(name): + rentry_exists = True + break + return rentry_exists + + @SCons.Memoize.CountMethodCall def srcdir_list(self): try: return self._memo['srcdir_list'] @@ -1897,8 +2032,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'] @@ -1988,7 +2122,7 @@ class Dir(Base): for dirname in [n for n in names if isinstance(entries[n], Dir)]: entries[dirname].walk(func, arg) - def glob(self, pathname, ondisk=True, source=False, strings=False): + def glob(self, pathname, ondisk=True, source=False, strings=False, exclude=None): """ Returns a list of Nodes (or strings) matching a specified pathname pattern. @@ -2016,24 +2150,36 @@ class Dir(Base): The "strings" argument, when true, returns the matches as strings, not Nodes. The strings are path names relative to this directory. + The "exclude" argument, if not None, must be a pattern or a list + of patterns following the same UNIX shell semantics. + Elements matching a least one pattern of this list will be excluded + from the result. + The underlying algorithm is adapted from the glob.glob() function in the Python library (but heavily modified), and uses fnmatch() under the covers. """ dirname, basename = os.path.split(pathname) if not dirname: - return sorted(self._glob1(basename, ondisk, source, strings), - key=lambda t: str(t)) - if has_glob_magic(dirname): - list = self.glob(dirname, ondisk, source, strings=False) + result = self._glob1(basename, ondisk, source, strings) else: - list = [self.Dir(dirname, create=True)] - result = [] - for dir in list: - r = dir._glob1(basename, ondisk, source, strings) - if strings: - r = [os.path.join(str(dir), x) for x in r] - result.extend(r) + if has_glob_magic(dirname): + list = self.glob(dirname, ondisk, source, False, exclude) + else: + list = [self.Dir(dirname, create=True)] + result = [] + for dir in list: + r = dir._glob1(basename, ondisk, source, strings) + if strings: + r = [os.path.join(str(dir), x) for x in r] + result.extend(r) + if exclude: + 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): @@ -2066,7 +2212,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) @@ -2080,7 +2226,6 @@ class Dir(Base): # the overall list will also be filtered later, # after we exit this loop. if pattern[0] != '.': - #disk_names = [ d for d in disk_names if d[0] != '.' ] disk_names = [x for x in disk_names if x[0] != '.'] disk_names = fnmatch.filter(disk_names, pattern) dirEntry = dir.Entry @@ -2096,14 +2241,12 @@ class Dir(Base): names = set(names) if pattern[0] != '.': - #names = [ n for n in names if n[0] != '.' ] names = [x for x in names if x[0] != '.'] names = fnmatch.filter(names, pattern) if strings: return names - #return [ self.entries[_my_normcase(n)] for n in names ] return [self.entries[_my_normcase(n)] for n in names] class RootDir(Dir): @@ -2114,23 +2257,17 @@ 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 == '': # No drive, regular UNIX root or Windows default drive. - name = OS_SEP + name = OS_SEP dirname = OS_SEP elif drive == '//': # UNC path @@ -2141,33 +2278,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 @@ -2198,17 +2387,7 @@ class RootDir(Dir): raise SCons.Errors.UserError(msg) # There is no Node for this path name, and we're allowed # to create it. - # (note: would like to use p.rsplit('/',1) here but - # that's not in python 2.3) - # e.g.: dir_name, file_name = p.rsplit('/',1) - last_slash = p.rindex('/') - if (last_slash >= 0): - dir_name = p[:last_slash] - file_name = p[last_slash+1:] - else: - dir_name = p # shouldn't happen, just in case - file_name = '' - + dir_name, file_name = p.rsplit('/',1) dir_node = self._lookup_abs(dir_name, Dir) result = klass(file_name, dir_node, self.fs) @@ -2226,19 +2405,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: @@ -2256,7 +2435,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'] @@ -2271,11 +2451,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): """ @@ -2290,7 +2502,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: @@ -2331,6 +2543,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): @@ -2344,6 +2558,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) @@ -2351,7 +2567,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 @@ -2387,13 +2618,6 @@ class File(Base): the directory of this file.""" return self.dir.File(name) - #def generate_build_dict(self): - # """Return an appropriate dictionary of values for building - # this File.""" - # return {'Dir' : self.Dir, - # 'File' : self.File, - # 'RDirs' : self.RDirs} - def _morph(self): """Turn a file system node into a File object.""" self.scanner_paths = {} @@ -2402,6 +2626,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 @@ -2413,22 +2645,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 b'' # Should always be bytes - fname = self.rfile().abspath - try: - contents = open(fname, "rb").read() - except EnvironmentError as 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 @@ -2455,7 +2678,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) @@ -2464,10 +2687,8 @@ class File(Base): e.filename = fname raise return cs - - - memoizer_counters.append(SCons.Memoize.CountValue('get_size')) + @SCons.Memoize.CountMethodCall def get_size(self): try: return self._memo['get_size'] @@ -2483,8 +2704,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'] @@ -2500,14 +2720,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', @@ -2620,8 +2832,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'] @@ -2661,8 +2872,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 @@ -2681,9 +2891,7 @@ class File(Base): pass if scanner: - # result = [n.disambiguate() for n in scanner(self, env, path)] - result = scanner(self, env, path) - result = [N.disambiguate() for N in result] + result = [n.disambiguate() for n in scanner(self, env, path)] else: result = [] @@ -2746,18 +2954,18 @@ 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 up-to-date or was built completely. - + This is where we try to release as many target node infos as possible for clean builds and update runs, in order to minimize the overall memory consumption. - + We'd like to remove a lot more attributes like self.sources and self.sources_set, but they might get used in a next build step. For example, during configuration @@ -2765,18 +2973,18 @@ class File(Base): which linker to use for the resulting Program (gcc vs. g++)! That's why we check for the 'keep_targetinfo' attribute, config Nodes and the Interactive mode just don't allow - an early release of most variables. + an early release of most variables. In the same manner, we can't simply remove the self.attributes here. The smart linking relies on the shared flag, and some parts of the java Tool use it to transport information about nodes... - + @see: built() and Node.release_target_info() """ if (self.released_target_info or SCons.Node.interactive): return - + if not hasattr(self.attributes, 'keep_targetinfo'): # Cache some required values, before releasing # stuff like env, executor and builder... @@ -2848,8 +3056,8 @@ class File(Base): def _rmv_existing(self): self.clear_memoized_values() - if print_duplicate: - print("dup: removing existing target %s"%self) + if SCons.Node.print_duplicate: + print("dup: removing existing target {}".format(self)) e = Unlink(self, [], None) if isinstance(e, SCons.Errors.BuildError): raise e @@ -2874,8 +3082,7 @@ class File(Base): try: self._createDir() except SCons.Errors.StopError as drive: - desc = "No drive `%s' for target `%s'." % (drive, self) - raise SCons.Errors.StopError(desc) + raise SCons.Errors.StopError("No drive `%s' for target `{}'.".format(drive, self)) # # @@ -2884,19 +3091,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: - print("dup: relinking variant '%s' from '%s'"%(self, src)) + if SCons.Node.print_duplicate: + print("dup: relinking variant '{}' from '{}'".format(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) - raise SCons.Errors.StopError(desc) + raise SCons.Errors.StopError("Cannot duplicate `{}' in `{}': {}.".format(src.get_internal_path(), self.dir._path, e.errstr)) self.linked = 1 # The Link() action may or may not have actually # created the file, depending on whether the -n @@ -2904,36 +3110,13 @@ 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 @@ -3010,41 +3193,41 @@ 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. - + Just like for 'release_target_info' we try to release some more target node attributes in order to minimize the overall memory consumption. - + @see: release_target_info """ SCons.Node.Node.built(self) - if (not SCons.Node.interactive and + if (not SCons.Node.interactive and not hasattr(self.attributes, 'keep_targetinfo')): - # Ensure that the build infos get computed and cached... - self.store_info() + # Ensure that the build infos get computed and cached... + 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 - + self.scanner_paths = None def changed(self, node=None, allowcache=False): """ Returns if the node is up-to-date with respect to the BuildInfo - stored last time it was built. - + stored last time it was built. + For File nodes this is basically a wrapper around Node.changed(), but we allow the return value to get cached after the reference to the Executor got released in release_target_info(). - + @see: Node.changed() """ if node is None: @@ -3052,7 +3235,7 @@ class File(Base): return self._memo['changed'] except KeyError: pass - + has_changed = SCons.Node.Node.changed(self, node) if allowcache: self._memo['changed'] = has_changed @@ -3089,16 +3272,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) @@ -3115,8 +3288,8 @@ class File(Base): # ...and they'd like a local copy. e = LocalCopy(self, r, None) if isinstance(e, SCons.Errors.BuildError): - raise - self.store_info() + raise + SCons.Node.store_info_map[self.store_info](self) if T: Trace(' 1\n') return 1 self.changed() @@ -3127,8 +3300,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'] @@ -3198,12 +3370,12 @@ class File(Base): It computes and returns the signature for this node's contents. """ - + try: return self.contentsig except AttributeError: pass - + executor = self.get_executor() result = self.contentsig = SCons.Util.MD5signature(executor.get_contents()) @@ -3223,14 +3395,14 @@ class File(Base): return self.cachesig except AttributeError: pass - + # Collect signatures for all children children = self.children() sigs = [n.get_cachedir_csig() for n in children] # 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 @@ -3246,10 +3418,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 = {} @@ -3291,9 +3459,8 @@ 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] @@ -3331,36 +3498,6 @@ class FileFinder(object): filedir, filename = os.path.split(filename) if filedir: - # More compact code that we can't use until we drop - # support for Python 1.5.2: - # - #def filedir_lookup(p, fd=filedir): - # """ - # A helper function that looks up a directory for a file - # we're trying to find. This only creates the Dir Node - # if it exists on-disk, since if the directory doesn't - # exist we know we won't find any files in it... :-) - # """ - # dir, name = os.path.split(fd) - # if dir: - # p = filedir_lookup(p, dir) - # if not p: - # return None - # norm_name = _my_normcase(name) - # try: - # node = p.entries[norm_name] - # except KeyError: - # return p.dir_on_disk(name) - # if isinstance(node, Dir): - # return node - # if isinstance(node, Entry): - # node.must_be_same(Dir) - # return node - # if isinstance(node, Dir) or isinstance(node, Entry): - # return node - # return None - #paths = [_f for _f in map(filedir_lookup, paths) if _f] - self.default_filedir = filedir paths = [_f for _f in map(self.filedir_lookup, paths) if _f] @@ -3408,7 +3545,7 @@ def invalidate_node_memos(targets): if not SCons.Util.is_List(targets): targets = [targets] - + for entry in targets: # If the target is a Node object, clear the cache. If it is a # filename, look up potentially existing Node object first. @@ -3420,7 +3557,7 @@ def invalidate_node_memos(targets): # do not correspond to an existing Node object. node = get_default_fs().Entry(entry) if node: - node.clear_memoized_values() + node.clear_memoized_values() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 9a8763a..b9c19bc 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") @@ -751,51 +751,45 @@ class FileNodeInfoTestCase(_tempdirTestCase): size = st[stat.ST_SIZE] assert ni.size != size, (ni.size, size) - #fff.clear() - #ni.update(fff) - - #st = os.stat('fff') - - #mtime = st[stat.ST_MTIME] - #assert ni.timestamp == mtime, (ni.timestamp, mtime) - #size = st[stat.ST_SIZE] - #assert ni.size == size, (ni.size, size) - 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() + + self.fs.File('n1') + self.fs.File('n2') + self.fs.File('n3') - s1sig = SCons.Node.FS.FileNodeInfo(self.fs.File('n1')) + 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 +947,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 +1039,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 +1130,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 +1161,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 +1214,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 +1278,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 +1583,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 +1685,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 +1786,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 +1825,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 +1974,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 +2018,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 @@ -2052,6 +2046,32 @@ class DirTestCase(_tempdirTestCase): if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin": assert d.entry_exists_on_disk('case-insensitive') + def test_rentry_exists_on_disk(self): + """Test the Dir.rentry_exists_on_disk() method + """ + test = self.test + + does_not_exist = self.fs.Dir('does_not_exist') + assert not does_not_exist.rentry_exists_on_disk('foo') + + test.subdir('d') + test.write(['d', 'exists'], "d/exists\n") + test.write(['d', 'Case-Insensitive'], "d/Case-Insensitive\n") + + test.subdir('r') + test.write(['r', 'rexists'], "r/rexists\n") + + d = self.fs.Dir('d') + r = self.fs.Dir('r') + d.addRepository(r) + + assert d.rentry_exists_on_disk('exists') + assert d.rentry_exists_on_disk('rexists') + assert not d.rentry_exists_on_disk('does_not_exist') + + if os.path.normcase("TeSt") != os.path.normpath("TeSt") or sys.platform == "cygwin": + assert d.rentry_exists_on_disk('case-insensitive') + def test_srcdir_list(self): """Test the Dir.srcdir_list() method """ @@ -2145,8 +2165,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") @@ -2158,14 +2182,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)) @@ -2219,14 +2243,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') @@ -2406,7 +2430,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 @@ -2415,7 +2439,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) @@ -2509,7 +2533,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): @@ -2880,8 +2904,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') @@ -2905,19 +2935,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 @@ -2927,8 +2957,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') @@ -2952,19 +2988,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 @@ -3218,9 +3254,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 @@ -3334,7 +3374,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() @@ -3347,7 +3387,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' @@ -3526,7 +3566,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..3802f8c 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -121,6 +121,8 @@ class Environment(object): self._dict.update(kw) def __getitem__(self, key): return self._dict[key] + def get(self, key, default = None): + return self._dict.get(key, default) def Dictionary(self, *args): return {} def Override(self, overrides): @@ -132,7 +134,12 @@ class Environment(object): def get_factory(self, factory): return factory or MyNode def get_scanner(self, scanner_key): - return self._dict['SCANNERS'][0] + try: + return self._dict['SCANNERS'][0] + except: + pass + + return [] class Builder(object): def __init__(self, env=None, is_explicit=1): @@ -184,8 +191,8 @@ class Scanner(object): called = None def __call__(self, node): self.called = 1 - return node.found_includes - def path(self, env, dir, target=None, source=None): + return node.GetTag('found_includes') + def path(self, env, dir=None, target=None, source=None, kw={}): return () def select(self, node): return self @@ -200,7 +207,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 +231,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 +251,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 +286,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): @@ -339,8 +358,6 @@ class NodeTestCase(unittest.TestCase): ggg.path = "ggg" fff.sources = ["hhh", "iii"] ggg.sources = ["hhh", "iii"] - # [Charles C. 1/7/2002] Uhhh, why are there no asserts here? - # [SK, 15 May 2003] I dunno, let's add some... built_it = None fff.build() assert built_it @@ -427,6 +444,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 +452,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 +586,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 +697,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,37 +930,37 @@ 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) + deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2], deps # By default, our fake scanner recurses e = MyNode("eee") f = MyNode("fff") g = MyNode("ggg") - d1.found_includes = [e, f] - d2.found_includes = [e, f] - f.found_includes = [g] - deps = node.get_implicit_deps(env, s, target) + d1.Tag('found_includes', [e, f]) + d2.Tag('found_includes', [e, f]) + f.Tag('found_includes', [g]) + deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2, e, f, g], list(map(str, deps)) # Recursive scanning eliminates duplicates - e.found_includes = [f] - deps = node.get_implicit_deps(env, s, target) + e.Tag('found_includes', [f]) + deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2, e, f, g], list(map(str, deps)) # Scanner method can select specific nodes to recurse def no_fff(nodes): return [n for n in nodes if str(n)[0] != 'f'] s.recurse_nodes = no_fff - deps = node.get_implicit_deps(env, s, target) + deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2, e, f], list(map(str, deps)) # Scanner method can short-circuit recursing entirely s.recurse_nodes = lambda nodes: [] - deps = node.get_implicit_deps(env, s, target) + deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2], list(map(str, deps)) def test_get_env_scanner(self): @@ -994,7 +1036,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 +1249,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 +1257,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 +1274,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 f13fd03..85e30c2 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. @@ -563,35 +916,56 @@ class Node(object): """ return [] - def get_implicit_deps(self, env, scanner, path): + def get_implicit_deps(self, env, initial_scanner, path_func, kw = {}): """Return a list of implicit dependencies for this node. This method exists to handle recursive invocation of the scanner on the implicit dependencies returned by the scanner, if the scanner's recursive flag says that we should. """ - if not scanner: - return [] - - # Give the scanner a chance to select a more specific scanner - # for this Node. - #scanner = scanner.select(self) - nodes = [self] seen = {} seen[self] = 1 - deps = [] - while nodes: - n = nodes.pop(0) - d = [x for x in n.get_found_includes(env, scanner, path) if x not in seen] - if d: - deps.extend(d) - for n in d: - seen[n] = 1 - nodes.extend(scanner.recurse_nodes(d)) + dependencies = [] - return deps + root_node_scanner = self._get_scanner(env, initial_scanner, None, kw) + while nodes: + node = nodes.pop(0) + + scanner = node._get_scanner(env, initial_scanner, root_node_scanner, kw) + + if not scanner: + continue + + path = path_func(scanner) + + included_deps = [x for x in node.get_found_includes(env, scanner, path) if x not in seen] + if included_deps: + dependencies.extend(included_deps) + for dep in included_deps: + seen[dep] = 1 + nodes.extend(scanner.recurse_nodes(included_deps)) + + return dependencies + + def _get_scanner(self, env, initial_scanner, root_node_scanner, kw): + if not initial_scanner: + # handle implicit scanner case + scanner = self.get_env_scanner(env, kw) + if scanner: + scanner = scanner.select(self) + else: + # handle explicit scanner case + scanner = initial_scanner.select(self) + + if not scanner: + # no scanner could be found for the given node's scanner key; + # thus, make an attempt at using a default. + scanner = root_node_scanner + + return scanner + def get_env_scanner(self, env, kw={}): return env.get_scanner(self.scanner_key()) @@ -706,7 +1080,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 +1091,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 +1176,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 +1213,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 \ @@ -912,11 +1281,6 @@ class Node(object): def _add_child(self, collection, set, child): """Adds 'child' to 'collection', first checking 'set' to see if it's already present.""" - #if type(child) is not type([]): - # child = [child] - #for c in child: - # if not isinstance(c, Node): - # raise TypeError, c added = None for c in child: if c not in set: @@ -941,11 +1305,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 +1339,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 +1379,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 +1386,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 +1455,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 +1626,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..77e30c4 100644 --- a/src/engine/SCons/PathList.py +++ b/src/engine/SCons/PathList.py @@ -27,7 +27,7 @@ __doc__ = """SCons.PathList A module for handling lists of directory paths (the sort of things that get set as CPPPATH, LIBPATH, etc.) with as much caching of data and -efficiency as we can while still keeping the evaluation delayed so that we +efficiency as we can, while still keeping the evaluation delayed so that we Do the Right Thing (almost) regardless of how the variable is specified. """ @@ -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/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py index 515382a..38ea55a 100644 --- a/src/engine/SCons/Platform/PlatformTests.py +++ b/src/engine/SCons/Platform/PlatformTests.py @@ -26,17 +26,19 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.compat import collections -import sys import unittest import TestUnit import SCons.Errors import SCons.Platform +import SCons.Environment +import SCons.Action class Environment(collections.UserDict): def Detect(self, cmd): return cmd + def AppendENVPath(self, key, value): pass @@ -117,9 +119,83 @@ class PlatformTestCase(unittest.TestCase): SCons.Platform.Platform()(env) assert env != {}, env +class TempFileMungeTestCase(unittest.TestCase): + def test_MAXLINELENGTH(self): + """ Test different values for MAXLINELENGTH with the same + size command string to ensure that the temp file mechanism + kicks in only at MAXLINELENGTH+1, or higher + """ + # Init class with cmd, such that the fully expanded + # string reads "a test command line". + # Note, how we're using a command string here that is + # actually longer than the substituted one. This is to ensure + # that the TempFileMunge class internally really takes the + # length of the expanded string into account. + defined_cmd = "a $VERY $OVERSIMPLIFIED line" + t = SCons.Platform.TempFileMunge(defined_cmd) + env = SCons.Environment.SubstitutionEnvironment(tools=[]) + # Setting the line length high enough... + env['MAXLINELENGTH'] = 1024 + env['VERY'] = 'test' + env['OVERSIMPLIFIED'] = 'command' + expanded_cmd = env.subst(defined_cmd) + # Call the tempfile munger + cmd = t(None,None,env,0) + assert cmd == defined_cmd, cmd + # Let MAXLINELENGTH equal the string's length + env['MAXLINELENGTH'] = len(expanded_cmd) + cmd = t(None,None,env,0) + assert cmd == defined_cmd, cmd + # Finally, let the actual tempfile mechanism kick in + # Disable printing of actions... + old_actions = SCons.Action.print_actions + SCons.Action.print_actions = 0 + env['MAXLINELENGTH'] = len(expanded_cmd)-1 + cmd = t(None,None,env,0) + # ...and restoring its setting. + SCons.Action.print_actions = old_actions + assert cmd != defined_cmd, cmd + + def test_tempfilecreation_once(self): + # Init class with cmd, such that the fully expanded + # string reads "a test command line". + # Note, how we're using a command string here that is + # actually longer than the substituted one. This is to ensure + # that the TempFileMunge class internally really takes the + # length of the expanded string into account. + defined_cmd = "a $VERY $OVERSIMPLIFIED line" + t = SCons.Platform.TempFileMunge(defined_cmd) + env = SCons.Environment.SubstitutionEnvironment(tools=[]) + # Setting the line length high enough... + env['VERY'] = 'test' + env['OVERSIMPLIFIED'] = 'command' + expanded_cmd = env.subst(defined_cmd) + env['MAXLINELENGTH'] = len(expanded_cmd)-1 + # Disable printing of actions... + old_actions = SCons.Action.print_actions + SCons.Action.print_actions = 0 + # Create an instance of object derived class to allow setattrb + class Node(object) : + class Attrs(object): + pass + def __init__(self): + self.attributes = self.Attrs() + target = [Node()] + cmd = t(target, None, env, 0) + # ...and restoring its setting. + SCons.Action.print_actions = old_actions + assert cmd != defined_cmd, cmd + assert cmd == getattr(target[0].attributes, 'tempfile_cmdlist', None) if __name__ == "__main__": - suite = unittest.makeSuite(PlatformTestCase, 'test_') + suite = unittest.TestSuite() + + tclasses = [ PlatformTestCase, + TempFileMungeTestCase ] + for tclass in tclasses: + names = unittest.getTestCaseNames(tclass, 'test_') + suite.addTests(list(map(tclass, names))) + TestUnit.run(suite) # Local Variables: diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py index dba3606..7f4639f 100644 --- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -12,7 +12,7 @@ environment. Consequently, we'll examine both sys.platform and os.name (and anything else that might come in to play) in order to return some specification which is unique enough for our purposes. -Note that because this subsysem just *selects* a callable that can +Note that because this subsystem just *selects* a callable that can modify a construction environment, it's possible for people to define their own "platform specification" in an arbitrary callable function. No one needs to use or tie in to this subsystem in order to roll @@ -21,7 +21,7 @@ their own platform definition. # # __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 @@ -131,7 +131,7 @@ class PlatformSpec(object): def __str__(self): return self.name - + class TempFileMunge(object): """A callable class. You can set an Environment variable to this, then call it with a string argument, then it will perform temporary @@ -140,7 +140,7 @@ class TempFileMunge(object): Example usage: env["TEMPFILE"] = TempFileMunge - env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}" + env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES','$LINKCOMSTR')}" By default, the name of the temporary file used begins with a prefix of '@'. This may be configred for other tool chains by @@ -149,8 +149,9 @@ class TempFileMunge(object): env["TEMPFILEPREFIX"] = '-@' # diab compiler env["TEMPFILEPREFIX"] = '-via' # arm tool chain """ - def __init__(self, cmd): + def __init__(self, cmd, cmdstr = None): self.cmd = cmd + self.cmdstr = cmdstr def __call__(self, target, source, env, for_signature): if for_signature: @@ -174,9 +175,18 @@ class TempFileMunge(object): length = 0 for c in cmd: length += len(c) + length += len(cmd) - 1 if length <= maxline: return self.cmd + # Check if we already created the temporary file for this target + # It should have been previously done by Action.strfunction() call + node = target[0] if SCons.Util.is_List(target) else target + cmdlist = getattr(node.attributes, 'tempfile_cmdlist', None) \ + if node is not None else None + if cmdlist is not None : + return cmdlist + # We do a normpath because mktemp() has what appears to be # a bug in Windows that will use a forward slash as a path # delimiter. Windows's link mistakes that for a command line @@ -188,7 +198,7 @@ class TempFileMunge(object): (fd, tmp) = tempfile.mkstemp('.lnk', text=True) native_tmp = SCons.Util.get_native_path(os.path.normpath(tmp)) - if env['SHELL'] and env['SHELL'] == 'sh': + if env.get('SHELL',None) == 'sh': # The sh shell will try to escape the backslashes in the # path, so unescape them. native_tmp = native_tmp.replace('\\', r'\\\\') @@ -224,10 +234,24 @@ class TempFileMunge(object): # purity get in the way of just being helpful, so we'll # reach into SCons.Action directly. if SCons.Action.print_actions: - print(("Using tempfile "+native_tmp+" for command line:\n"+ - str(cmd[0]) + " " + " ".join(args))) - return [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] - + cmdstr = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, + source) if self.cmdstr is not None else '' + # Print our message only if XXXCOMSTR returns an empty string + if len(cmdstr) == 0 : + print("Using tempfile "+native_tmp+" for command line:\n"+ + str(cmd[0]) + " " + " ".join(args)) + + # Store the temporary file command list into the target Node.attributes + # to avoid creating two temporary files one for print and one for execute. + cmdlist = [ cmd[0], prefix + native_tmp + '\n' + rm, native_tmp ] + if node is not None: + try : + setattr(node.attributes, 'tempfile_cmdlist', cmdlist) + except AttributeError: + pass + return cmdlist + + def Platform(name = platform_default()): """Select a canned Platform specification. """ diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py index e7c8b8a..8b4669c 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/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 1e4fb2a..8ba8218 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -60,21 +60,8 @@ except AttributeError: else: parallel_msg = None - import builtins - builtin_file = getattr(builtins, 'file', None) is not None - - if builtin_file: - _builtin_file = builtins.file - - class _scons_file(_builtin_file): - def __init__(self, *args, **kw): - _builtin_file.__init__(self, *args, **kw) - win32api.SetHandleInformation(msvcrt.get_osfhandle(self.fileno()), - win32con.HANDLE_FLAG_INHERIT, 0) - - builtins.file = _scons_file - - _builtin_open = builtins.open + _builtin_file = file + _builtin_open = open def _scons_open(*args, **kw): fp = _builtin_open(*args, **kw) @@ -83,12 +70,13 @@ else: 0) return fp - builtins.open = _scons_open + file = _scons_file + open = _scons_open try: import threading spawn_lock = threading.Lock() - + # This locked version of spawnve works around a Windows # MSVCRT bug, because its spawnve is not thread-safe. # Without this, python can randomly crash while using -jN. @@ -117,7 +105,7 @@ except ImportError: # simulating a non-existent package. def spawnve(mode, file, args, env): return os.spawnve(mode, file, args, env) - + # The upshot of all this is that, if you are using Python 1.5.2, # you had better have cmd or command.com in your PATH when you run # scons. @@ -267,7 +255,7 @@ def get_program_files_dir(): # A reasonable default if we can't read the registry # (Actually, it's pretty reasonable even if we can :-) val = os.path.join(os.path.dirname(get_system_root()),"Program Files") - + return val @@ -352,7 +340,7 @@ def generate(env): os.path.join(systemroot,'System32') tmp_pathext = '.com;.exe;.bat;.cmd' if 'PATHEXT' in os.environ: - tmp_pathext = os.environ['PATHEXT'] + tmp_pathext = os.environ['PATHEXT'] cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) if not cmd_interp: cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) @@ -362,7 +350,7 @@ def generate(env): if not cmd_interp: cmd_interp = env.Detect('command') - + if 'ENV' not in env: env['ENV'] = {} @@ -407,10 +395,10 @@ def generate(env): env['TEMPFILEPREFIX'] = '@' env['MAXLINELENGTH'] = 2048 env['ESCAPE'] = escape - + env['HOST_OS'] = 'win32' env['HOST_ARCH'] = get_architecture().arch - + # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index 0e6967f..935eef2 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -135,7 +135,7 @@ def CreateConfigHBuilder(env): for k in _ac_config_hs.keys(): env.SConfigHBuilder(k, env.Value(_ac_config_hs[k])) - + class SConfWarning(SCons.Warnings.Warning): pass SCons.Warnings.enableWarningClass(SConfWarning) @@ -176,8 +176,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 @@ -198,10 +201,8 @@ class Streamer(object): try: self.s.write(str) except TypeError as e: - if e.message.startswith('unicode argument expected'): - self.s.write(str.decode()) - else: - raise + # "unicode argument expected" bug in IOStream (python 2.x) + self.s.write(str.decode()) def writelines(self, lines): for l in lines: @@ -217,7 +218,7 @@ class Streamer(object): if self.orig: self.orig.flush() self.s.flush() - + class SConfBuildTask(SCons.Taskmaster.AlwaysTask): """ @@ -254,14 +255,7 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask): else: self.display('Caught exception while building "%s":\n' % self.targets[0]) - try: - excepthook = sys.excepthook - except AttributeError: - # Earlier versions of Python don't have sys.excepthook... - def excepthook(type, value, tb): - traceback.print_tb(tb) - print(type, value) - excepthook(*self.exc_info()) + sys.excepthook(*self.exc_info()) return SCons.Taskmaster.Task.failed(self) def collect_node_states(self): @@ -318,7 +312,7 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask): binfo = self.targets[0].get_stored_info().binfo self.display_cached_string(binfo) raise SCons.Errors.BuildError # will be 'caught' in self.failed - elif is_up_to_date: + elif is_up_to_date: self.display("\"%s\" is up to date." % str(self.targets[0])) binfo = self.targets[0].get_stored_info().binfo self.display_cached_string(binfo) @@ -355,8 +349,8 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask): raise SCons.Errors.ExplicitExit(self.targets[0],exc_value.code) except Exception as e: for t in self.targets: - 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 @@ -373,8 +367,8 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask): raise e else: for t in self.targets: - 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 @@ -395,16 +389,16 @@ class SConfBase(object): tests, be sure to call the Finish() method, which returns the modified environment. Some words about caching: In most cases, it is not necessary to cache - Test results explicitely. Instead, we use the scons dependency checking + Test results explicitly. Instead, we use the scons dependency checking mechanism. For example, if one wants to compile a test program (SConf.TryLink), the compiler is only called, if the program dependencies have changed. However, if the program could not be compiled in a former - SConf run, we need to explicitely cache this error. + SConf run, we need to explicitly cache this error. """ def __init__(self, env, custom_tests = {}, conf_dir='$CONFIGUREDIR', - log_file='$CONFIGURELOG', config_h = None, _depth = 0): - """Constructor. Pass additional tests in the custom_tests-dictinary, + log_file='$CONFIGURELOG', config_h = None, _depth = 0): + """Constructor. Pass additional tests in the custom_tests-dictionary, e.g. custom_tests={'CheckPrivate':MyPrivateTest}, where MyPrivateTest defines a custom test. Note also the conf_dir and log_file arguments (you may want to @@ -440,6 +434,7 @@ class SConfBase(object): 'CheckCXXHeader' : CheckCXXHeader, 'CheckLib' : CheckLib, 'CheckLibWithHeader' : CheckLibWithHeader, + 'CheckProg' : CheckProg, } self.AddTests(default_tests) self.AddTests(custom_tests) @@ -463,7 +458,7 @@ class SConfBase(object): If value is None (default), then #define name is written. If value is not none, then #define name value is written. - + comment is a string which will be put as a C comment in the header, to explain the meaning of the value (appropriate C comments /* and */ will be put automatically.""" @@ -503,7 +498,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 @@ -643,7 +638,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) @@ -687,7 +682,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 @@ -696,7 +690,7 @@ class SConfBase(object): global _ac_config_logs global sconf_global global SConfFS - + self.lastEnvFs = self.env.fs self.env.fs = SConfFS self._createDir(self.confdir) @@ -723,7 +717,7 @@ class SConfBase(object): self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' % (tb[0], tb[1], str(self.confdir)) ) SConfFS.chdir(old_fs_dir) - else: + else: self.logstream = None # we use a special builder to create source files from TEXT action = SCons.Action.Action(_createSource, @@ -920,14 +914,14 @@ def CheckType(context, type_name, includes = "", language = None): def CheckTypeSize(context, type_name, includes = "", language = None, expect = None): res = SCons.Conftest.CheckTypeSize(context, type_name, - header = includes, language = language, + header = includes, language = language, expect = expect) context.did_show_result = 1 return res def CheckDeclaration(context, declaration, includes = "", language = None): res = SCons.Conftest.CheckDeclaration(context, declaration, - includes = includes, + includes = includes, language = language) context.did_show_result = 1 return not res @@ -1011,7 +1005,7 @@ def CheckLib(context, library = None, symbol = "main", if not SCons.Util.is_List(library): library = [library] - + # ToDo: accept path for the library res = SCons.Conftest.CheckLib(context, library, symbol, header = header, language = language, autoadd = autoadd) @@ -1044,6 +1038,14 @@ def CheckLibWithHeader(context, libs, header, language, context.did_show_result = 1 return not res +def CheckProg(context, prog_name): + """Simple check if a program exists in the path. Returns the path + for the application, or None if not found. + """ + res = SCons.Conftest.CheckProg(context, prog_name) + context.did_show_result = 1 + return res + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index 4ce5f5c..15499ed 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -100,11 +100,10 @@ class SConfTestCase(unittest.TestCase): # original builtin functions whenever we have to reset # all of our global state. - import builtins import SCons.Platform.win32 - builtins.file = SCons.Platform.win32._builtin_file - builtins.open = SCons.Platform.win32._builtin_open + file = SCons.Platform.win32._builtin_file + open = SCons.Platform.win32._builtin_open def _baseTryXXX(self, TryFunc): # TryCompile and TryLink are much the same, so we can test them @@ -221,8 +220,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): @@ -613,6 +610,30 @@ int main() { finally: sconf.Finish() + def test_CheckProg(self): + """Test SConf.CheckProg() + """ + self._resetSConfState() + sconf = self.SConf.SConf(self.scons_env, + conf_dir=self.test.workpath('config.tests'), + log_file=self.test.workpath('config.log')) + + try: + if os.name != 'nt': + r = sconf.CheckProg('sh') + assert r, "/bin/sh" + else: + r = sconf.CheckProg('cmd.exe') + self.assertIn('cmd.exe',r) + + + r = sconf.CheckProg('hopefully-not-a-program') + assert r is None + + finally: + sconf.Finish() + + def test_Define(self): """Test SConf.Define() """ diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py index ab8e297..970c35c 100644 --- a/src/engine/SCons/SConsign.py +++ b/src/engine/SCons/SConsign.py @@ -123,16 +123,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 @@ -203,7 +227,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: @@ -218,7 +242,7 @@ class DB(Base): raise except Exception as 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) @@ -245,7 +269,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) @@ -288,7 +312,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') @@ -324,7 +348,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/C.py b/src/engine/SCons/Scanner/C.py index 74b01a4..3b34b88 100644 --- a/src/engine/SCons/Scanner/C.py +++ b/src/engine/SCons/Scanner/C.py @@ -1,6 +1,6 @@ """SCons.Scanner.C -This module implements the depenency scanner for C/C++ code. +This module implements the dependency scanner for C/C++ code. """ 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/IDL.py b/src/engine/SCons/Scanner/IDL.py index 18084e8..86ee006 100644 --- a/src/engine/SCons/Scanner/IDL.py +++ b/src/engine/SCons/Scanner/IDL.py @@ -1,6 +1,6 @@ """SCons.Scanner.IDL -This module implements the depenency scanner for IDL (Interface +This module implements the dependency scanner for IDL (Interface Definition Language) files. """ 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/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py index 49e93a5..6567b3d 100644 --- a/src/engine/SCons/Scanner/Prog.py +++ b/src/engine/SCons/Scanner/Prog.py @@ -38,6 +38,24 @@ def ProgramScanner(**kw): ps = SCons.Scanner.Base(scan, "ProgramScanner", **kw) return ps +def _subst_libs(env, libs): + """ + Substitute environment variables and split into list. + """ + if SCons.Util.is_String(libs): + libs = env.subst(libs) + if SCons.Util.is_String(libs): + libs = libs.split() + elif SCons.Util.is_Sequence(libs): + _libs = [] + for l in libs: + _libs += _subst_libs(env, l) + libs = _libs + else: + # libs is an object (Node, for example) + libs = [libs] + return libs + def scan(node, env, libpath = ()): """ This scanner scans program files for static-library @@ -50,10 +68,8 @@ def scan(node, env, libpath = ()): except KeyError: # There are no LIBS in this environment, so just return a null list: return [] - if SCons.Util.is_String(libs): - libs = libs.split() - else: - libs = SCons.Util.flatten(libs) + + libs = _subst_libs(env, libs) try: prefix = env['LIBPREFIXES'] @@ -83,7 +99,6 @@ def scan(node, env, libpath = ()): adjustixes = SCons.Util.adjustixes for lib in libs: if SCons.Util.is_String(lib): - lib = env.subst(lib) for pref, suf in pairs: l = adjustixes(lib, pref, suf) l = find_file(l, libpath, verbose=print_find_libs) diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py index a5c8956..e7c791c 100644 --- a/src/engine/SCons/Scanner/ProgTests.py +++ b/src/engine/SCons/Scanner/ProgTests.py @@ -32,6 +32,7 @@ import TestUnit import SCons.Node.FS import SCons.Scanner.Prog +import SCons.Subst test = TestCmd.TestCmd(workdir = '') @@ -72,12 +73,7 @@ class DummyEnvironment(object): del self.Dictionary()[key] def subst(self, s, target=None, source=None, conv=None): - try: - if s[0] == '$': - return self._dict[s[1:]] - except IndexError: - return '' - return s + return SCons.Subst.scons_subst(s, self, gvars=self._dict, lvars=self._dict) def subst_path(self, path, target=None, source=None, conv=None): if not isinstance(path, list): @@ -223,6 +219,31 @@ class ProgramScannerTestCase8(unittest.TestCase): deps = s(DummyNode('dummy'), env, path) assert deps == [n1, n2], deps +class ProgramScannerTestCase9(unittest.TestCase): + def runTest(self): + env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ], + LIBS=['foo', '$LIBBAR'], + LIBPREFIXES=['lib'], + LIBSUFFIXES=['.a'], + LIBBAR=['sub/libbar', 'xyz.other']) + s = SCons.Scanner.Prog.ProgramScanner() + path = s.path(env) + deps = s(DummyNode('dummy'), env, path) + assert deps_match(deps, ['dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']), list(map(str, deps)) + +class ProgramScannerTestCase10(unittest.TestCase): + def runTest(self): + env = DummyEnvironment(LIBPATH=[ test.workpath("dir") ], + LIBS=['foo', '$LIBBAR'], + LIBPREFIXES=['lib'], + LIBSUFFIXES=['.a'], + LIBBAR='sub/libbar $LIBBAR2', + LIBBAR2=['xyz.other']) + s = SCons.Scanner.Prog.ProgramScanner() + path = s.path(env) + deps = s(DummyNode('dummy'), env, path) + assert deps_match(deps, ['dir/libfoo.a', 'dir/sub/libbar.a', 'dir/libxyz.other']), list(map(str, deps)) + def suite(): suite = unittest.TestSuite() suite.addTest(ProgramScannerTestCase1()) @@ -232,6 +253,8 @@ def suite(): suite.addTest(ProgramScannerTestCase6()) suite.addTest(ProgramScannerTestCase7()) suite.addTest(ProgramScannerTestCase8()) + suite.addTest(ProgramScannerTestCase9()) + suite.addTest(ProgramScannerTestCase10()) try: unicode except NameError: pass else: diff --git a/src/engine/SCons/Scanner/RC.py b/src/engine/SCons/Scanner/RC.py index 437b861..61393ae 100644 --- a/src/engine/SCons/Scanner/RC.py +++ b/src/engine/SCons/Scanner/RC.py @@ -1,6 +1,6 @@ """SCons.Scanner.RC -This module implements the depenency scanner for RC (Interface +This module implements the dependency scanner for RC (Interface Definition Language) files. """ diff --git a/src/engine/SCons/compat/_scons_io.py b/src/engine/SCons/Scanner/SWIG.py index 538afb7..2650e4b 100644 --- a/src/engine/SCons/compat/_scons_io.py +++ b/src/engine/SCons/Scanner/SWIG.py @@ -1,3 +1,9 @@ +"""SCons.Scanner.SWIG + +This module implements the dependency scanner for SWIG code. + +""" + # # __COPYRIGHT__ # @@ -21,22 +27,16 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__doc__ = """ -io compatibility module for older (pre-2.6) Python versions - -This does not not NOT (repeat, *NOT*) provide complete io -functionality. It only wraps the portions of io functionality used -by SCons, in an interface that looks enough like io for our purposes. -""" - __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -# Use the "imp" module to protect the imports below from fixers. -import imp +import SCons.Scanner + +SWIGSuffixes = [ '.i' ] -_cStringIO = imp.load_module('cStringIO', *imp.find_module('cStringIO')) -StringIO = _cStringIO.StringIO -del _cStringIO +def SWIGScanner(): + expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' + scanner = SCons.Scanner.ClassicCPP("SWIGScanner", ".i", "SWIGPATH", expr) + return scanner # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Script/Interactive.py b/src/engine/SCons/Script/Interactive.py index 0b28f1a..e7a0658 100644 --- a/src/engine/SCons/Script/Interactive.py +++ b/src/engine/SCons/Script/Interactive.py @@ -305,14 +305,8 @@ class SConsInteractiveCmd(cmd.Cmd): return self._strip_initial_spaces(doc) def _strip_initial_spaces(self, s): - #lines = s.split('\n') lines = s.split('\n') spaces = re.match(' *', lines[0]).group(0) - #def strip_spaces(l): - # if l.startswith(spaces): - # l = l[len(spaces):] - # return l - #return '\n'.join([ strip_spaces(l) for l in lines ]) def strip_spaces(l, spaces=spaces): if l[:len(spaces)] == spaces: l = l[len(spaces):] diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 3ef88c7..71d64fc 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -10,11 +10,7 @@ some other module. If it's specific to the "scons" script invocation, it goes here. """ -# Would affect exec()'d site_init.py: -## from __future__ import print_function -from SCons.compat.six import print_ - -unsupported_python_version = (2, 3, 0) +unsupported_python_version = (2, 6, 0) deprecated_python_version = (2, 7, 0) # __COPYRIGHT__ @@ -47,15 +43,6 @@ import sys import time import traceback -# Strip the script directory from sys.path() so on case-insensitive -# (Windows) systems Python doesn't think that the "scons" script is the -# "SCons" package. Replace it with our own version directory so, if -# if they're there, we pick up the right version of the build engine -# modules. -#sys.path = [os.path.join(sys.prefix, -# 'lib', -# 'scons-%d' % SCons.__version__)] + sys.path[1:] - import SCons.CacheDir import SCons.Debug import SCons.Defaults @@ -78,7 +65,7 @@ def fetch_win32_parallel_msg(): # so we don't have to pull it in on all platforms, and so that an # in-line "import" statement in the _main() function below doesn't # cause warnings about local names shadowing use of the 'SCons' - # globl in nest scopes and UnboundLocalErrors and the like in some + # global in nest scopes and UnboundLocalErrors and the like in some # versions (2.1) of Python. import SCons.Platform.win32 return SCons.Platform.win32.parallel_msg @@ -218,7 +205,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) @@ -355,7 +342,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(): @@ -372,8 +359,8 @@ class CleanTask(SCons.Taskmaster.AlwaysTask): # issue, an IOError would indicate something like # the file not existing. In either case, print a # message and keep going to try to remove as many - # targets aa possible. - print(("scons: Could not remove '%s':" % str(t), e.strerror) + # targets as possible. + print("scons: Could not remove '{}':".format(str(t)), e.strerror) else: if removed: display("Removed " + str(t)) @@ -387,7 +374,7 @@ class CleanTask(SCons.Taskmaster.AlwaysTask): # we don't want, like store .sconsign information. executed = SCons.Taskmaster.Task.executed_without_callbacks - # Have the taskmaster arrange to "execute" all of the targets, because + # Have the Taskmaster arrange to "execute" all of the targets, because # we'll figure out ourselves (in remove() or show() above) whether # anything really needs to be done. make_ready = SCons.Taskmaster.Task.make_ready_all @@ -488,7 +475,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): self.stats = [] @@ -676,7 +665,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 = '.' @@ -712,7 +701,6 @@ def _load_site_scons_dir(topdir, site_dir_name=None): site_tools_dir = os.path.join(site_dir, site_tools_dirname) if os.path.exists(site_init_file): import imp, re - # TODO(2.4): turn this into try:-except:-finally: try: try: fp, pathname, description = imp.find_module(site_init_modname, @@ -950,13 +938,21 @@ 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 + # If we're about to start SCons in the interactive mode, + # inform the FS about this right here. Else, the release_target_info + # method could get called on some nodes, like the used "gcc" compiler, + # when using the Configure methods within the SConscripts. + # This would then cause subtle bugs, as already happened in #2971. + if options.interactive: + SCons.Node.interactive = True + # That should cover (most of) the options. Next, set up the variables # that hold command-line arguments, so the SConscript files that we # read and execute have access to them. @@ -1017,7 +1013,7 @@ def _main(parser): # the SConscript file. # # We delay enabling the PythonVersionWarning class until here so that, - # if they explicity disabled it in either in the command line or in + # if they explicitly disabled it in either in the command line or in # $SCONSFLAGS, or in the SConscript file, then the search through # the list of deprecated warning classes will find that disabling # first and not issue the warning. @@ -1086,7 +1082,6 @@ def _main(parser): platform = SCons.Platform.platform_module() if options.interactive: - SCons.Node.interactive = True SCons.Script.Interactive.interact(fs, OptionsParser, options, targets, target_top) @@ -1108,7 +1103,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 @@ -1227,13 +1221,8 @@ def _build_targets(fs, options, targets, target_top): def order(dependencies): """Randomize the dependencies.""" import random - # This is cribbed from the implementation of - # random.shuffle() in Python 2.X. - d = dependencies - for i in range(len(d)-1, 0, -1): - j = int(random.random() * (i+1)) - d[i], d[j] = d[j], d[i] - return d + random.shuffle(dependencies) + return dependencies else: def order(dependencies): """Leave the order of dependencies alone.""" @@ -1311,18 +1300,6 @@ def _exec_main(parser, values): # compat layer imports "cProfile" for us if it's available. from profile import Profile - # Some versions of Python 2.4 shipped a profiler that had the - # wrong 'c_exception' entry in its dispatch table. Make sure - # we have the right one. (This may put an unnecessary entry - # in the table in earlier versions of Python, but its presence - # shouldn't hurt anything). - try: - dispatch = Profile.dispatch - except AttributeError: - pass - else: - dispatch['c_exception'] = Profile.trace_dispatch_return - prof = Profile() try: prof.runcall(_main, parser) diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py index 1250e6b..b2f2858 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) @@ -319,7 +319,13 @@ class SConsOptionParser(optparse.OptionParser): value = option.const elif len(rargs) < nargs: if nargs == 1: - self.error(_("%s option requires an argument") % opt) + if not option.choices: + self.error(_("%s option requires an argument") % opt) + else: + msg = _("%s option requires an argument " % opt) + msg += _("(choose from %s)" + % ', '.join(option.choices)) + self.error(msg) else: self.error(_("%s option requires %d arguments") % (opt, nargs)) @@ -420,7 +426,7 @@ class SConsOptionParser(optparse.OptionParser): result = group.add_option(*args, **kw) if result: - # The option was added succesfully. We now have to add the + # The option was added successfully. We now have to add the # default value to our object that holds the default values # (so that an attempt to fetch the option's attribute will # yield the default value when not overridden) and then @@ -443,11 +449,6 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): "SCons Options." Unfortunately, we have to do this here, because those titles are hard-coded in the optparse calls. """ - if heading == 'options': - # The versions of optparse.py shipped with Pythons 2.3 and - # 2.4 pass this in uncapitalized; override that so we get - # consistent output on all versions. - heading = "Options" if heading == 'Options': heading = "SCons Options" return optparse.IndentedHelpFormatter.format_heading(self, heading) @@ -482,13 +483,7 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): # read data from FILENAME result = [] - try: - opts = self.option_strings[option] - except AttributeError: - # The Python 2.3 version of optparse attaches this to - # to the option argument, not to this object. - opts = option.option_strings - + opts = self.option_strings[option] opt_width = self.help_position - self.current_indent - 2 if len(opts) > opt_width: wrapper = textwrap.TextWrapper(width=self.width, @@ -503,14 +498,7 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): result.append(opts) if option.help: - try: - expand_default = self.expand_default - except AttributeError: - # The HelpFormatter base class in the Python 2.3 version - # of optparse has no expand_default() method. - help_text = option.help - else: - help_text = expand_default(option) + help_text = self.expand_default(option) # SCons: indent every line of the help text but the first. wrapper = textwrap.TextWrapper(width=self.help_width, @@ -524,34 +512,6 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): result.append("\n") return "".join(result) - # For consistent help output across Python versions, we provide a - # subclass copy of format_option_strings() and these two variables. - # This is necessary (?) for Python2.3, which otherwise concatenates - # a short option with its metavar. - _short_opt_fmt = "%s %s" - _long_opt_fmt = "%s=%s" - - def format_option_strings(self, option): - """Return a comma-separated list of option strings & metavariables.""" - if option.takes_value(): - metavar = option.metavar or option.dest.upper() - short_opts = [] - for sopt in option._short_opts: - short_opts.append(self._short_opt_fmt % (sopt, metavar)) - long_opts = [] - for lopt in option._long_opts: - long_opts.append(self._long_opt_fmt % (lopt, metavar)) - else: - short_opts = option._short_opts - long_opts = option._long_opts - - if self.short_first: - opts = short_opts + long_opts - else: - opts = long_opts + short_opts - - return ", ".join(opts) - def Parser(version): """ Returns an options parser object initialized with the standard @@ -645,18 +605,12 @@ def Parser(version): config_options = ["auto", "force" ,"cache"] - def opt_config(option, opt, value, parser, c_options=config_options): - if not value in c_options: - raise OptionValueError(opt_invalid('config', value, c_options)) - setattr(parser.values, option.dest, value) - opt_config_help = "Controls Configure subsystem: %s." \ % ", ".join(config_options) op.add_option('--config', - nargs=1, type="string", + nargs=1, choices=config_options, dest="config", default="auto", - action="callback", callback=opt_config, help = opt_config_help, metavar="MODE") diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index d50951d..ab032be 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -51,16 +51,6 @@ import re import sys import traceback -# The following variables used to live in this module. Some -# SConscript files out there may have referred to them directly as -# SCons.Script.SConscript.*. This is now supported by some special -# handling towards the bottom of the SConscript.__init__.py module. -#Arguments = {} -#ArgList = [] -#BuildTargets = TargetList() -#CommandLineTargets = [] -#DefaultTargets = [] - class SConscriptReturn(Exception): pass @@ -265,7 +255,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 +428,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) @@ -446,7 +436,7 @@ class SConsEnvironment(SCons.Environment.Base): # # Public methods of an SConsEnvironment. These get - # entry points in the global name space so they can be called + # entry points in the global namespace so they can be called # as global functions. # @@ -461,7 +451,8 @@ class SConsEnvironment(SCons.Environment.Base): def EnsureSConsVersion(self, major, minor, revision=0): """Exit abnormally if the SCons version is not late enough.""" - if SCons.__version__ == '__VERSION__': + # split string to avoid replacement during build process + if SCons.__version__ == '__' + 'VERSION__': SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning, "EnsureSConsVersion is ignored for development version") return @@ -498,9 +489,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 e9e8b71..974841c 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,19 @@ 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 = "" + + help_text= help_text + text + # # Will be non-zero if we are reading an SConscript file. @@ -318,6 +327,7 @@ GlobalDefaultEnvironmentFunctions = [ 'Ignore', 'Install', 'InstallAs', + 'InstallVersionedLib', 'Literal', 'Local', 'ParseDepends', diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 43f2e1f..3c9b390 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -344,7 +344,6 @@ _remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') _regex_remove = [ _rm, None, _remove ] def _rm_list(list): - #return [ l for l in list if not l in ('$(', '$)') ] return [l for l in list if not l in ('$(', '$)')] def _remove_list(list): @@ -580,8 +579,6 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={ return result -#Subst_List_Strings = {} - def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. @@ -590,12 +587,6 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv substitutions within strings, so see that function instead if that's what you're looking for. """ -# try: -# Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 -# except KeyError: -# Subst_List_Strings[strSubst] = 1 -# import SCons.Debug -# SCons.Debug.caller_trace(1) class ListSubber(collections.UserList): """A class to construct the results of a scons_subst_list() call. diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py index 2b93e88..8eda845 100644 --- a/src/engine/SCons/SubstTests.py +++ b/src/engine/SCons/SubstTests.py @@ -605,17 +605,6 @@ class scons_subst_TestCase(SubstTestCase): node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars) assert node is n1, node - #def test_subst_function_return(self): - # """Test scons_subst(): returning a function""" - # env = DummyEnv({'FUNCTION' : foo}) - # gvars = env.Dictionary() - # func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars) - # assert func is function_foo, func - # func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars) - # assert func is function_foo, func - # func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars) - # assert func is function_foo, func - def test_subst_overriding_gvars(self): """Test scons_subst(): supplying an overriding gvars dictionary""" env = DummyEnv({'XXX' : 'xxx'}) diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 4a06654..fd1ba64 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -123,7 +123,7 @@ class Task(object): aspects of controlling a build, so any given application *should* be able to do what it wants by sub-classing this class and overriding methods as appropriate. If an application - needs to customze something by sub-classing Taskmaster (or + needs to customize something by sub-classing Taskmaster (or some other build engine class), we should first try to migrate that functionality into this class. @@ -148,7 +148,7 @@ class Task(object): This hook gets called as part of preparing a task for execution (that is, a Node to be built). As part of figuring out what Node - should be built next, the actually target list may be altered, + should be built next, the actual target list may be altered, along with a message describing the alteration. The calling interface can subclass Task and provide a concrete implementation of this method to see those messages. @@ -243,7 +243,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() @@ -661,9 +661,9 @@ class Taskmaster(object): its parent node. A pending child can occur when the Taskmaster completes a loop - through a cycle. For example, lets imagine a graph made of - three node (A, B and C) making a cycle. The evaluation starts - at node A. The taskmaster first consider whether node A's + through a cycle. For example, let's imagine a graph made of + three nodes (A, B and C) making a cycle. The evaluation starts + at node A. The Taskmaster first considers whether node A's child B is up-to-date. Then, recursively, node B needs to check whether node C is up-to-date. This leaves us with a dependency graph looking like: @@ -948,7 +948,7 @@ class Taskmaster(object): task.make_ready() except: # We had a problem just trying to get this task ready (like - # a child couldn't be linked in to a VariantDir when deciding + # a child couldn't be linked to a VariantDir when deciding # whether this node is current). Arrange to raise the # exception when the Task is "executed." self.ready_exc = sys.exc_info() diff --git a/src/engine/SCons/Tool/BitKeeper.py b/src/engine/SCons/Tool/BitKeeper.py index 191879f..44632d7 100644 --- a/src/engine/SCons/Tool/BitKeeper.py +++ b/src/engine/SCons/Tool/BitKeeper.py @@ -49,7 +49,6 @@ def generate(env): act = SCons.Action.Action("$BITKEEPERCOM", "$BITKEEPERCOMSTR") return SCons.Builder.Builder(action = act, env = env) - #setattr(env, 'BitKeeper', BitKeeperFactory) env.BitKeeper = BitKeeperFactory env['BITKEEPER'] = 'bk' diff --git a/src/engine/SCons/Tool/CVS.py b/src/engine/SCons/Tool/CVS.py index a506231..08cf04c 100644 --- a/src/engine/SCons/Tool/CVS.py +++ b/src/engine/SCons/Tool/CVS.py @@ -55,7 +55,6 @@ def generate(env): CVSREPOSITORY = repos, CVSMODULE = module) - #setattr(env, 'CVS', CVSFactory) env.CVS = CVSFactory env['CVS'] = 'cvs' diff --git a/src/engine/SCons/Tool/FortranCommon.py b/src/engine/SCons/Tool/FortranCommon.py index 088c717..e450730 100644 --- a/src/engine/SCons/Tool/FortranCommon.py +++ b/src/engine/SCons/Tool/FortranCommon.py @@ -248,6 +248,21 @@ def add_f03_to_env(env): DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, support_module = 1) +def add_f08_to_env(env): + """Add Builders and construction variables for f08 to an Environment.""" + try: + F08Suffixes = env['F08FILESUFFIXES'] + except KeyError: + F08Suffixes = ['.f08'] + + try: + F08PPSuffixes = env['F08PPFILESUFFIXES'] + except KeyError: + F08PPSuffixes = [] + + DialectAddToEnv(env, "F08", F08Suffixes, F08PPSuffixes, + support_module = 1) + def add_all_to_env(env): """Add builders and construction variables for all supported fortran dialects.""" @@ -256,6 +271,7 @@ def add_all_to_env(env): add_f90_to_env(env) add_f95_to_env(env) add_f03_to_env(env) + add_f08_to_env(env) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/GettextCommon.py b/src/engine/SCons/Tool/GettextCommon.py index ea81f0d..e23a2d7 100644 --- a/src/engine/SCons/Tool/GettextCommon.py +++ b/src/engine/SCons/Tool/GettextCommon.py @@ -251,7 +251,7 @@ class RPaths(object): recently re-created. For such reason, we need a function, which always returns relative paths. This is the purpose of `RPaths` callable object. - The `__call__` method returns paths relative to current woking directory, but + The `__call__` method returns paths relative to current working directory, but we assume, that *xgettext(1)* is run from the directory, where target file is going to be created. @@ -306,23 +306,6 @@ class RPaths(object): - Tuple of strings, which represent paths relative to current working directory (for given environment). """ - # os.path.relpath is available only on python >= 2.6. We use our own - # implementation. It's taken from BareNecessities package: - # http://jimmyg.org/work/code/barenecessities/index.html - from posixpath import curdir - def relpath(path, start=curdir): - import posixpath - """Return a relative version of a path""" - if not path: - raise ValueError("no path specified") - start_list = posixpath.abspath(start).split(posixpath.sep) - path_list = posixpath.abspath(path).split(posixpath.sep) - # Work out how much of the filepath is shared by start and path. - i = len(posixpath.commonprefix([start_list, path_list])) - rel_list = [posixpath.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return posixpath.curdir - return posixpath.join(*rel_list) import os import SCons.Node.FS rpaths = () @@ -330,7 +313,7 @@ class RPaths(object): for node in nodes: rpath = None if isinstance(node, SCons.Node.FS.Base): - rpath = relpath(node.get_abspath(), cwd) + rpath = os.path.relpath(node.get_abspath(), cwd) # FIXME: Other types possible here? if rpath is not None: rpaths += (rpath,) @@ -347,7 +330,7 @@ def _init_po_files(target, source, env): autoinit = False # Well, if everything outside works well, this loop should do single # iteration. Otherwise we are rebuilding all the targets even, if just - # one has changed (but is this out fault?). + # one has changed (but is this our fault?). for tgt in target: if not tgt.exists(): if autoinit: 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/common.py b/src/engine/SCons/Tool/MSCommon/common.py index 4a91310..bcfee2b 100644 --- a/src/engine/SCons/Tool/MSCommon/common.py +++ b/src/engine/SCons/Tool/MSCommon/common.py @@ -85,8 +85,8 @@ def is_win64(): return _is_win64 -def read_reg(value): - return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] +def read_reg(value, hkroot=SCons.Util.HKEY_LOCAL_MACHINE): + return SCons.Util.RegGetValue(hkroot, value)[0] def has_reg(value): """Return True if the given key exists in HKEY_LOCAL_MACHINE, False @@ -94,7 +94,7 @@ def has_reg(value): try: SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) ret = True - except WindowsError: + except SCons.Util.WinError: ret = False return ret @@ -181,7 +181,7 @@ def get_output(vcbat, args = None, env = None): stdout = popen.stdout.read() stderr = popen.stderr.read() - # Extra debug logic, uncomment if necessar + # Extra debug logic, uncomment if necessary # debug('get_output():stdout:%s'%stdout) # debug('get_output():stderr:%s'%stderr) @@ -226,33 +226,6 @@ def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): return dkeep -# TODO(sgk): unused -def output_to_dict(output): - """Given an output string, parse it to find env variables. - - Return a dict where keys are variables names, and values their content""" - envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') - parsedenv = {} - for line in output.splitlines(): - m = envlinem.match(line) - if m: - parsedenv[m.group(1)] = m.group(2) - return parsedenv - -# TODO(sgk): unused -def get_new(l1, l2): - """Given two list l1 and l2, return the items in l2 which are not in l1. - Order is maintained.""" - - # We don't try to be smart: lists are small, and this is not the bottleneck - # is any case - new = [] - for i in l2: - if i not in l1: - new.append(i) - - return new - # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/Tool/MSCommon/netframework.py b/src/engine/SCons/Tool/MSCommon/netframework.py index 1a6478f..787d008 100644 --- a/src/engine/SCons/Tool/MSCommon/netframework.py +++ b/src/engine/SCons/Tool/MSCommon/netframework.py @@ -27,6 +27,7 @@ __doc__ = """ import os import re +import SCons.Util from .common import read_reg, debug @@ -39,13 +40,13 @@ def find_framework_root(): # XXX: find it from environment (FrameworkDir) try: froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) - debug("Found framework install root in registry: %s" % froot) - except WindowsError as e: - debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT) + debug("Found framework install root in registry: {}".format(froot)) + except SCons.Util.WinError as e: + debug("Could not read reg key {}".format(_FRAMEWORKDIR_HKEY_ROOT)) return None if not os.path.exists(froot): - debug("%s not found on fs" % froot) + debug("{} not found on fs".format(froot)) return None return froot diff --git a/src/engine/SCons/Tool/MSCommon/sdk.py b/src/engine/SCons/Tool/MSCommon/sdk.py index f71035b..1845599 100644 --- a/src/engine/SCons/Tool/MSCommon/sdk.py +++ b/src/engine/SCons/Tool/MSCommon/sdk.py @@ -19,7 +19,7 @@ # 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__" @@ -76,23 +76,23 @@ class SDKDefinition(object): return None hkey = self.HKEY_FMT % self.hkey_data - debug('find_sdk_dir(): checking registry:%s'%hkey) + debug('find_sdk_dir(): checking registry:{}'.format(hkey)) try: sdk_dir = common.read_reg(hkey) - except WindowsError as e: - debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey)) + except SCons.Util.WinError as e: + debug('find_sdk_dir(): no SDK registry key {}'.format(repr(hkey))) return None - debug('find_sdk_dir(): Trying SDK Dir: %s'%sdk_dir) + debug('find_sdk_dir(): Trying SDK Dir: {}'.format(sdk_dir)) if not os.path.exists(sdk_dir): - debug('find_sdk_dir(): %s not on file system' % sdk_dir) + debug('find_sdk_dir(): {} not on file system'.format(sdk_dir)) return None ftc = os.path.join(sdk_dir, self.sanity_check_file) if not os.path.exists(ftc): - debug("find_sdk_dir(): sanity check %s not found" % ftc) + debug("find_sdk_dir(): sanity check {} not found".format(ftc)) return None return sdk_dir @@ -105,7 +105,7 @@ class SDKDefinition(object): sdk_dir = self.find_sdk_dir() self._sdk_dir = sdk_dir return sdk_dir - + def get_sdk_vc_script(self,host_arch, target_arch): """ Return the script to initialize the VC compiler installed by SDK """ @@ -113,11 +113,11 @@ class SDKDefinition(object): if (host_arch == 'amd64' and target_arch == 'x86'): # No cross tools needed compiling 32 bits on 64 bit machine host_arch=target_arch - + arch_string=target_arch if (host_arch != target_arch): arch_string='%s_%s'%(host_arch,target_arch) - + debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string, host_arch, target_arch)) @@ -168,10 +168,30 @@ SDK70VCSetupScripts = { 'x86' : r'bin\vcvars32.bat', # # The first SDK found in the list is the one used by default if there # are multiple SDKs installed. Barring good reasons to the contrary, -# this means we should list SDKs with from most recent to oldest. +# this means we should list SDKs from most recent to oldest. # # If you update this list, update the documentation in Tool/mssdk.xml. SupportedSDKList = [ + WindowsSDK('7.1', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK70VCSetupScripts, + ), + WindowsSDK('7.0A', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + vc_setup_scripts = SDK70VCSetupScripts, + ), WindowsSDK('7.0', sanity_check_file=r'bin\SetEnv.Cmd', include_subdir='include', @@ -286,33 +306,9 @@ def set_sdk_by_directory(env, sdk_dir): for variable, directory in env_tuple_list: env.PrependENVPath(variable, directory) - -# TODO(sgk): currently unused; remove? -def get_cur_sdk_dir_from_reg(): - """Try to find the platform sdk directory from the registry. - - Return None if failed or the directory does not exist""" - if not SCons.Util.can_read_reg: - debug('SCons cannot read registry') - return None - - try: - val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT) - debug("Found current sdk dir in registry: %s" % val) - except WindowsError as e: - debug("Did not find current sdk in registry") - return None - - if not os.path.exists(val): - debug("Current sdk dir %s not on fs" % val) - return None - - return val - def get_sdk_by_version(mssdk): if mssdk not in SupportedSDKMap: - msg = "SDK version %s is not supported" % repr(mssdk) - raise SCons.Errors.UserError(msg) + raise SCons.Errors.UserError("SDK version {} is not supported".format(repr(mssdk))) get_installed_sdks() return InstalledSDKMap.get(mssdk) @@ -323,9 +319,6 @@ def get_default_sdk(): return None return InstalledSDKList[0] - - - def mssdk_setup_env(env): debug('sdk.py:mssdk_setup_env()') if 'MSSDK_DIR' in env: @@ -333,14 +326,17 @@ def mssdk_setup_env(env): if sdk_dir is None: return sdk_dir = env.subst(sdk_dir) - debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:%s'%sdk_dir) + debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:{}'.format(sdk_dir)) elif 'MSSDK_VERSION' in env: sdk_version = env['MSSDK_VERSION'] if sdk_version is None: - msg = "SDK version %s is not installed" % repr(mssdk) + msg = "SDK version is specified as None" raise SCons.Errors.UserError(msg) sdk_version = env.subst(sdk_version) mssdk = get_sdk_by_version(sdk_version) + if mssdk is None: + msg = "SDK version %s is not installed" % sdk_version + raise SCons.Errors.UserError(msg) sdk_dir = mssdk.get_sdk_dir() debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir) elif 'MSVS_VERSION' in env: diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index bd7bbd0..f96b8ca 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -35,6 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" __doc__ = """Module for Visual C/C++ detection and configuration. """ import SCons.compat +import SCons.Util import os import platform @@ -134,35 +135,53 @@ 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 = { - '12.0' : [ - r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'], - '12.0Exp' : [ - r'Microsoft\VCExpress\12.0\Setup\VC\ProductDir'], - '11.0': [ - r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'], - '11.0Exp' : [ - r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'], - '10.0': [ - r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'], - '10.0Exp' : [ - r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'], - '9.0': [ - r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir'], - '9.0Exp' : [ - r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'], - '8.0': [ - r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'], - '8.0Exp': [ - r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'], - '7.1': [ - r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'], - '7.0': [ - r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'], - '6.0': [ - r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'] + '14.0' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], + '14.0Exp' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir')], + '12.0' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'), + ], + '12.0Exp' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\12.0\Setup\VC\ProductDir'), + ], + '11.0': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'), + ], + '11.0Exp' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'), + ], + '10.0': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'), + ], + '10.0Exp' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'), + ], + '9.0': [ + (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',), + ], + '9.0Exp' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'), + ], + '8.0': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'), + ], + '8.0Exp': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'), + ], + '7.1': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'), + ], + '7.0': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'), + ], + '6.0': [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'), + ] } def msvc_version_to_maj_min(msvc_version): @@ -212,28 +231,34 @@ def find_vc_pdir(msvc_version): If for some reason the requested version could not be found, an exception which inherits from VisualCException will be raised.""" root = 'Software\\' - if common.is_win64(): - root = root + 'Wow6432Node\\' try: hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] except KeyError: debug("Unknown version of MSVC: %s" % msvc_version) raise UnsupportedVersion("Unknown version %s" % msvc_version) - for key in hkeys: - key = root + key + for hkroot, key in hkeys: try: - comps = common.read_reg(key) - except WindowsError as e: - debug('find_vc_dir(): no VC registry key %s' % repr(key)) + comps = None + if common.is_win64(): + try: + # ordinally at win64, try Wow6432Node first. + comps = common.read_reg(root + 'Wow6432Node\\' + key, hkroot) + except SCons.Util.WinError as e: + # at Microsoft Visual Studio for Python 2.7, value is not in Wow6432Node + pass + if not comps: + # not Win64, or Microsoft Visual Studio for Python 2.7 + comps = common.read_reg(root + key, hkroot) + except SCons.Util.WinError as e: + debug('find_vc_dir(): no VC registry key {}'.format(repr(key))) else: - debug('find_vc_dir(): found VC in registry: %s' % comps) + debug('find_vc_dir(): found VC in registry: {}'.format(comps)) if os.path.exists(comps): return comps else: - debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\ - % comps) - raise MissingConfiguration("registry dir %s not found on the filesystem" % comps) + debug('find_vc_dir(): reg says dir is {}, but it does not exist. (ignoring)'.format(comps)) + raise MissingConfiguration("registry dir {} not found on the filesystem".format(comps)) return None def find_batch_file(env,msvc_version,host_arch,target_arch): @@ -245,7 +270,7 @@ def find_batch_file(env,msvc_version,host_arch,target_arch): if pdir is None: raise NoVersionFound("No version of Visual Studio found") - debug('vc.py: find_batch_file() pdir:%s'%pdir) + debug('vc.py: find_batch_file() pdir:{}'.format(pdir)) # filter out e.g. "Exp" from the version name msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."]) @@ -498,4 +523,3 @@ def msvc_exists(version=None): if version is None: return len(vcs) > 0 return version in vcs - diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py index 68e108a..31197ef 100644 --- a/src/engine/SCons/Tool/MSCommon/vs.py +++ b/src/engine/SCons/Tool/MSCommon/vs.py @@ -52,8 +52,6 @@ class VisualStudio(object): self.__dict__.update(kw) self._cache = {} - # - def find_batch_file(self): vs_dir = self.get_vs_dir() if not vs_dir: @@ -85,10 +83,10 @@ class VisualStudio(object): key = root + key try: comps = read_reg(key) - except WindowsError as e: - debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key)) + except SCons.Util.WinError as e: + debug('find_vs_dir_by_reg(): no VS registry key {}'.format(repr(key))) else: - debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps) + debug('find_vs_dir_by_reg(): found VS in registry: {}'.format(comps)) return comps return None @@ -107,17 +105,15 @@ class VisualStudio(object): def find_executable(self): vs_dir = self.get_vs_dir() if not vs_dir: - debug('find_executable(): no vs_dir (%s)'%vs_dir) + debug('find_executable(): no vs_dir ({})'.format(vs_dir)) return None executable = os.path.join(vs_dir, self.executable_path) executable = os.path.normpath(executable) if not os.path.isfile(executable): - debug('find_executable(): %s not on file system' % executable) + debug('find_executable(): {} not on file system'.format(executable)) return None return executable - # - def get_batch_file(self): try: return self._cache['batch_file'] @@ -203,6 +199,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', @@ -449,7 +467,7 @@ def get_default_version(env): """Returns the default version string to use for MSVS. If no version was requested by the user through the MSVS environment - variable, query all the available the visual studios through + variable, query all the available visual studios through get_installed_visual_studios, and take the highest one. Return diff --git a/src/engine/SCons/Tool/Perforce.py b/src/engine/SCons/Tool/Perforce.py index 15dd83f..c8cf931 100644 --- a/src/engine/SCons/Tool/Perforce.py +++ b/src/engine/SCons/Tool/Perforce.py @@ -38,9 +38,6 @@ import SCons.Builder import SCons.Node.FS import SCons.Util -# This function should maybe be moved to SCons.Util? -from SCons.Tool.PharLapCommon import addPathIfNotExists - # Variables that we want to import from the base OS environment. _import_env = [ 'P4PORT', 'P4CLIENT', 'P4USER', 'USER', 'USERNAME', 'P4PASSWD', @@ -58,7 +55,6 @@ def generate(env): W.warn(W.DeprecatedSourceCodeWarning, """The Perforce() factory is deprecated and there is no replacement.""") return SCons.Builder.Builder(action = PerforceAction, env = env) - #setattr(env, 'Perforce', PerforceFactory) env.Perforce = PerforceFactory env['P4'] = 'p4' @@ -87,7 +83,7 @@ def generate(env): k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE, 'Software\\Perforce\\environment') val, tok = SCons.Util.RegQueryValueEx(k, 'P4INSTROOT') - addPathIfNotExists(environ, 'PATH', val) + SCons.Util.AddPathIfNotExists(environ, 'PATH', val) except SCons.Util.RegError: # Can't detect where Perforce is, hope the user has it set in the # PATH. diff --git a/src/engine/SCons/Tool/PharLapCommon.py b/src/engine/SCons/Tool/PharLapCommon.py index 9f925b9..864a185 100644 --- a/src/engine/SCons/Tool/PharLapCommon.py +++ b/src/engine/SCons/Tool/PharLapCommon.py @@ -85,28 +85,6 @@ def getPharLapVersion(): # Default return for Phar Lap 9.1 return 910 -def addPathIfNotExists(env_dict, key, path, sep=os.pathsep): - """This function will take 'key' out of the dictionary - 'env_dict', then add the path 'path' to that key if it is not - already there. This treats the value of env_dict[key] as if it - has a similar format to the PATH variable...a list of paths - separated by tokens. The 'path' will get added to the list if it - is not already there.""" - try: - is_list = 1 - paths = env_dict[key] - if not SCons.Util.is_List(env_dict[key]): - paths = paths.split(sep) - is_list = 0 - if os.path.normcase(path) not in list(map(os.path.normcase, paths)): - paths = [ path ] + paths - if is_list: - env_dict[key] = paths - else: - env_dict[key] = sep.join(paths) - except KeyError: - env_dict[key] = path - def addPharLapPaths(env): """This function adds the path to the Phar Lap binaries, includes, and libraries, if they are not already there.""" @@ -117,14 +95,14 @@ def addPharLapPaths(env): except KeyError: env_dict = {} env['ENV'] = env_dict - addPathIfNotExists(env_dict, 'PATH', - os.path.join(ph_path, 'bin')) - addPathIfNotExists(env_dict, 'INCLUDE', - os.path.join(ph_path, 'include')) - addPathIfNotExists(env_dict, 'LIB', - os.path.join(ph_path, 'lib')) - addPathIfNotExists(env_dict, 'LIB', - os.path.join(ph_path, os.path.normpath('lib/vclib'))) + SCons.Util.AddPathIfNotExists(env_dict, 'PATH', + os.path.join(ph_path, 'bin')) + SCons.Util.AddPathIfNotExists(env_dict, 'INCLUDE', + os.path.join(ph_path, 'include')) + SCons.Util.AddPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, 'lib')) + SCons.Util.AddPathIfNotExists(env_dict, 'LIB', + os.path.join(ph_path, os.path.normpath('lib/vclib'))) env['PHARLAP_PATH'] = getPharLapPath() env['PHARLAP_VERSION'] = str(getPharLapVersion()) diff --git a/src/engine/SCons/Tool/PharLapCommonTests.py b/src/engine/SCons/Tool/PharLapCommonTests.py deleted file mode 100644 index e67d426..0000000 --- a/src/engine/SCons/Tool/PharLapCommonTests.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# __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 unittest -import os.path -import os -import sys - -import TestUnit - -import SCons.Errors -from SCons.Tool.PharLapCommon import * - -class PharLapCommonTestCase(unittest.TestCase): - def test_addPathIfNotExists(self): - """Test the addPathIfNotExists() function""" - env_dict = { 'FOO' : os.path.normpath('/foo/bar') + os.pathsep + \ - os.path.normpath('/baz/blat'), - 'BAR' : os.path.normpath('/foo/bar') + os.pathsep + \ - os.path.normpath('/baz/blat'), - 'BLAT' : [ os.path.normpath('/foo/bar'), - os.path.normpath('/baz/blat') ] } - addPathIfNotExists(env_dict, 'FOO', os.path.normpath('/foo/bar')) - addPathIfNotExists(env_dict, 'BAR', os.path.normpath('/bar/foo')) - addPathIfNotExists(env_dict, 'BAZ', os.path.normpath('/foo/baz')) - addPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/blat')) - addPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/foo')) - - assert env_dict['FOO'] == os.path.normpath('/foo/bar') + os.pathsep + \ - os.path.normpath('/baz/blat'), env_dict['FOO'] - assert env_dict['BAR'] == os.path.normpath('/bar/foo') + os.pathsep + \ - os.path.normpath('/foo/bar') + os.pathsep + \ - os.path.normpath('/baz/blat'), env_dict['BAR'] - assert env_dict['BAZ'] == os.path.normpath('/foo/baz'), env_dict['BAZ'] - assert env_dict['BLAT'] == [ os.path.normpath('/baz/foo'), - os.path.normpath('/foo/bar'), - os.path.normpath('/baz/blat') ], env_dict['BLAT' ] - -if __name__ == "__main__": - suite = unittest.makeSuite(PharLapCommonTestCase, 'test_') - TestUnit.run(suite) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Tool/RCS.py b/src/engine/SCons/Tool/RCS.py index 6ac1a33..e24b89a 100644 --- a/src/engine/SCons/Tool/RCS.py +++ b/src/engine/SCons/Tool/RCS.py @@ -46,7 +46,6 @@ def generate(env): act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') return SCons.Builder.Builder(action = act, env = env) - #setattr(env, 'RCS', RCSFactory) env.RCS = RCSFactory env['RCS'] = 'rcs' diff --git a/src/engine/SCons/Tool/SCCS.py b/src/engine/SCons/Tool/SCCS.py index 7653468..92ded51 100644 --- a/src/engine/SCons/Tool/SCCS.py +++ b/src/engine/SCons/Tool/SCCS.py @@ -46,7 +46,6 @@ def generate(env): act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') return SCons.Builder.Builder(action = act, env = env) - #setattr(env, 'SCCS', SCCSFactory) env.SCCS = SCCSFactory env['SCCS'] = 'sccs' diff --git a/src/engine/SCons/Tool/Subversion.py b/src/engine/SCons/Tool/Subversion.py index ee70925..f1b3708 100644 --- a/src/engine/SCons/Tool/Subversion.py +++ b/src/engine/SCons/Tool/Subversion.py @@ -54,7 +54,6 @@ def generate(env): SVNREPOSITORY = repos, SVNMODULE = module) - #setattr(env, 'Subversion', SubversionFactory) env.Subversion = SubversionFactory env['SVN'] = 'svn' diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 396807f..9606ac1 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -52,6 +52,7 @@ import SCons.Scanner.C import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog +import SCons.Scanner.SWIG DefaultToolpath=[] @@ -61,6 +62,7 @@ LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner() PDFLaTeXScanner = SCons.Scanner.LaTeX.PDFLaTeXScanner() ProgramScanner = SCons.Scanner.Prog.ProgramScanner() SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner') +SWIGScanner = SCons.Scanner.SWIG.SWIGScanner() CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc", ".h", ".H", ".hxx", ".hpp", ".hh", @@ -74,12 +76,17 @@ IDLSuffixes = [".idl", ".IDL"] LaTeXSuffixes = [".tex", ".ltx", ".latex"] +SWIGSuffixes = ['.i'] + for suffix in CSuffixes: SourceFileScanner.add_scanner(suffix, CScanner) for suffix in DSuffixes: SourceFileScanner.add_scanner(suffix, DScanner) +for suffix in SWIGSuffixes: + SourceFileScanner.add_scanner(suffix, SWIGScanner) + # FIXME: what should be done here? Two scanners scan the same extensions, # but look for different files, e.g., "picture.eps" vs. "picture.pdf". # The builders for DVI and PDF explicitly reference their scanners @@ -102,7 +109,7 @@ class Tool(object): self.options = module.options def _tool_module(self): - # TODO: Interchange zipimport with normal initilization for better error reporting + # TODO: Interchange zipimport with normal initialization for better error reporting oldpythonpath = sys.path sys.path = self.toolpath + sys.path @@ -237,44 +244,31 @@ 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 Verbose: + print('_call_linker_cb: args={:r}'.format(args)) + print('_call_linker_cb: callback={:r}'.format(callback)) - 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) + try: + cbfun = env['LINKCALLBACKS'][callback] + except (KeyError, TypeError): if Verbose: - print("VersionShLibLinkNames: linkname = ",linkname) - linknames.append(linkname) - elif platform == 'posix': - 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'.format(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'.format(callback)) + print('_call_linker_cb: env["LINKCALLBACKS"][{:r}]={:r}'.format(callback, cbfun)) + if(callable(cbfun)): if Verbose: print("VersionShLibLinkNames: linkname ",linkname, ", target ",libname) linknames.append(linkname) @@ -363,15 +357,410 @@ symlinks for the platform we are on""" os.symlink(lib_ver,lastlinkname) if Verbose: print("VerShLib: made sym link of %s -> %s" % (linkname, lib_ver)) + print('_call_linker_cb: env["LINKCALLBACKS"][{:r}] is callable'.format(callback)) + result = cbfun(env, *args) return result -# 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']) +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("_LibPrefixGenerator: input prefix={:r}".format(prefix)) + + version = self.get_lib_version(env, **kw2) + if Verbose: + print("_LibPrefixGenerator: version={:r}".format(version)) + + if version: + prefix = self.generate_versioned_lib_info(env, [prefix, version], prefix, **kw2) + + if Verbose: + print("_LibPrefixGenerator: return prefix={:r}".format(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("_LibSuffixGenerator: input suffix={:r}".format(suffix)) + + version = self.get_lib_version(env, **kw2) + if Verbose: + print("_LibSuffixGenerator: version={:r}".format(version)) + + if version: + suffix = self.generate_versioned_lib_info(env, [suffix, version], suffix, **kw2) + + if Verbose: + print("_LibSuffixGenerator: return suffix={:r}".format(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}".format(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}'.format(version)) + print('_LibSymlinkGenerator: disable={:r}'.format(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}'.format(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}".format(libnode.get_path())) + + version = self.get_lib_version(env, **kw2) + if Verbose: + print('_LibNameGenerator: version={:r}'.format(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}'.format(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}".format(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}".format(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}".format(soname)) + + if Verbose: + print("_LibSonameGenerator: return soname={:r}".format(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})".format(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})".format(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}".format(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}".format(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}".format(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: + linkstr = ', '.join([ "%r->%r" %(k,v) for k,v in StringizeLibSymlinks(symlinks)]) + except (KeyError, ValueError): + pass + else: + cmd += ": %s" % linkstr + return cmd + + +LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun) def createSharedLibBuilder(env): """This is a utility function that creates the SharedLibrary @@ -385,11 +774,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') @@ -409,11 +799,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') @@ -834,4 +1225,3 @@ def tool_list(platform, env): # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: - diff --git a/src/engine/SCons/Tool/__init__.xml b/src/engine/SCons/Tool/__init__.xml index dd06f12..69cc597 100644 --- a/src/engine/SCons/Tool/__init__.xml +++ b/src/engine/SCons/Tool/__init__.xml @@ -200,17 +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> @@ -459,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> @@ -467,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> @@ -487,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/aixf77.py b/src/engine/SCons/Tool/aixf77.py index 21786ee..3e30cd0 100644 --- a/src/engine/SCons/Tool/aixf77.py +++ b/src/engine/SCons/Tool/aixf77.py @@ -41,7 +41,7 @@ from . import f77 # It would be good to look for the AIX F77 package the same way we're now # looking for the C and C++ packages. This should be as easy as supplying # the correct package names in the following list and uncommenting the -# SCons.Platform.aix_get_xlc() call the in the function below. +# SCons.Platform.aix_get_xlc() call in the function below. packages = [] def get_xlf77(env): diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index 1685d7f..2f54a17 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -7,19 +7,28 @@ 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 -from . import gnulink +#MAYBE: from . import gnulink +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: @@ -32,40 +41,144 @@ def shlib_generator(target, source, env, for_signature): ]) else: cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS']) - + 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 library. + 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 +187,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 +197,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 082d5c3..3722936 100644 --- a/src/engine/SCons/Tool/dmd.py +++ b/src/engine/SCons/Tool/dmd.py @@ -82,10 +82,10 @@ def generate(env): env['DC'] = env.Detect(['dmd', 'gdmd']) env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of$TARGET $SOURCES' - env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' - env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' - env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' + env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' + env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' + env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' + env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' env['SHDCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -fPIC -of$TARGET $SOURCES' @@ -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 '' @@ -122,13 +122,13 @@ def generate(env): env['DLIBDIRPREFIX'] = '-L-L' env['DLIBDIRSUFFIX'] = '' - env['_DLIBDIRFLAGS'] = '$( ${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_DLIBDIRFLAGS'] = '${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') - #env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' + #env['_DLIBFLAGS'] = '${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)}' env['DLIBFLAGPREFIX'] = '-' env['DLIBFLAGSUFFIX'] = '' @@ -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 77ed388..2b22e17 100644 --- a/src/engine/SCons/Tool/docbook/__init__.py +++ b/src/engine/SCons/Tool/docbook/__init__.py @@ -10,7 +10,7 @@ selection method. """ # -# Copyright (c) 2001-7,2010 The SCons Foundation +# __COPYRIGHT__ # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -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) @@ -852,30 +852,16 @@ def generate(env): ) _detect(env) - try: - env.AddMethod(DocbookEpub, "DocbookEpub") - env.AddMethod(DocbookHtml, "DocbookHtml") - env.AddMethod(DocbookHtmlChunked, "DocbookHtmlChunked") - env.AddMethod(DocbookHtmlhelp, "DocbookHtmlhelp") - env.AddMethod(DocbookPdf, "DocbookPdf") - env.AddMethod(DocbookMan, "DocbookMan") - env.AddMethod(DocbookSlidesPdf, "DocbookSlidesPdf") - env.AddMethod(DocbookSlidesHtml, "DocbookSlidesHtml") - env.AddMethod(DocbookXInclude, "DocbookXInclude") - env.AddMethod(DocbookXslt, "DocbookXslt") - except AttributeError: - # Looks like we use a pre-0.98 version of SCons... - from SCons.Script.SConscript import SConsEnvironment - SConsEnvironment.DocbookEpub = DocbookEpub - SConsEnvironment.DocbookHtml = DocbookHtml - SConsEnvironment.DocbookHtmlChunked = DocbookHtmlChunked - SConsEnvironment.DocbookHtmlhelp = DocbookHtmlhelp - SConsEnvironment.DocbookPdf = DocbookPdf - SConsEnvironment.DocbookMan = DocbookMan - SConsEnvironment.DocbookSlidesPdf = DocbookSlidesPdf - SConsEnvironment.DocbookSlidesHtml = DocbookSlidesHtml - SConsEnvironment.DocbookXInclude = DocbookXInclude - SConsEnvironment.DocbookXslt = DocbookXslt + env.AddMethod(DocbookEpub, "DocbookEpub") + env.AddMethod(DocbookHtml, "DocbookHtml") + env.AddMethod(DocbookHtmlChunked, "DocbookHtmlChunked") + env.AddMethod(DocbookHtmlhelp, "DocbookHtmlhelp") + env.AddMethod(DocbookPdf, "DocbookPdf") + env.AddMethod(DocbookMan, "DocbookMan") + env.AddMethod(DocbookSlidesPdf, "DocbookSlidesPdf") + env.AddMethod(DocbookSlidesHtml, "DocbookSlidesHtml") + env.AddMethod(DocbookXInclude, "DocbookXInclude") + env.AddMethod(DocbookXslt, "DocbookXslt") def exists(env): diff --git a/src/engine/SCons/Tool/f08.py b/src/engine/SCons/Tool/f08.py new file mode 100644 index 0000000..a45a61d --- /dev/null +++ b/src/engine/SCons/Tool/f08.py @@ -0,0 +1,63 @@ +"""engine.SCons.Tool.f08 + +Tool-specific initialization for the generic Posix f08 Fortran compiler. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import SCons.Defaults +import SCons.Tool +import SCons.Util +import fortran +from SCons.Tool.FortranCommon import add_all_to_env, add_f08_to_env + +compilers = ['f08'] + +def generate(env): + add_all_to_env(env) + add_f08_to_env(env) + + fcomp = env.Detect(compilers) or 'f08' + env['F08'] = fcomp + env['SHF08'] = fcomp + + env['FORTRAN'] = fcomp + env['SHFORTRAN'] = fcomp + + +def exists(env): + return env.Detect(compilers) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Tool/f08.xml b/src/engine/SCons/Tool/f08.xml new file mode 100644 index 0000000..6a49c75 --- /dev/null +++ b/src/engine/SCons/Tool/f08.xml @@ -0,0 +1,311 @@ +<?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="f08"> +<summary> +<para> +Set construction variables for generic POSIX Fortran 08 compilers. +</para> +</summary> +<sets> +<item>F08</item> +<item>F08FLAGS</item> +<item>F08COM</item> +<item>F08PPCOM</item> +<item>SHF08</item> +<item>SHF08FLAGS</item> +<item>SHF08COM</item> +<item>SHF08PPCOM</item> +<item>_F08INCFLAGS</item> +</sets> +<uses> +<item>F08COMSTR</item> +<item>F08PPCOMSTR</item> +<item>SHF08COMSTR</item> +<item>SHF08PPCOMSTR</item> +</uses> +</tool> + +<cvar name="F08"> +<summary> +<para> +The Fortran 08 compiler. +You should normally set the &cv-link-FORTRAN; variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set &cv-link-F08; if you need to use a specific compiler +or compiler version for Fortran 08 files. +</para> +</summary> +</cvar> + +<cvar name="F08COM"> +<summary> +<para> +The command line used to compile a Fortran 08 source file to an object file. +You only need to set &cv-link-F08COM; if you need to use a specific +command line for Fortran 08 files. +You should normally set the &cv-link-FORTRANCOM; variable, +which specifies the default command line +for all Fortran versions. +</para> +</summary> +</cvar> + +<cvar name="F08COMSTR"> +<summary> +<para> +The string displayed when a Fortran 08 source file +is compiled to an object file. +If this is not set, then &cv-link-F08COM; or &cv-link-FORTRANCOM; +(the command line) is displayed. +</para> +</summary> +</cvar> + +<cvar name="F08FILESUFFIXES"> +<summary> +<para> +The list of file extensions for which the F08 dialect will be used. By +default, this is ['.f08'] +</para> +</summary> +</cvar> + +<cvar name="F08PPFILESUFFIXES"> +<summary> +<para> +The list of file extensions for which the compilation + preprocessor pass for +F08 dialect will be used. By default, this is empty +</para> +</summary> +</cvar> + +<cvar name="F08FLAGS"> +<summary> +<para> +General user-specified options that are passed to the Fortran 08 compiler. +Note that this variable does +<emphasis>not</emphasis> +contain +<option>-I</option> +(or similar) include search path options +that scons generates automatically from &cv-link-F08PATH;. +See +&cv-link-_F08INCFLAGS; +below, +for the variable that expands to those options. +You only need to set &cv-link-F08FLAGS; if you need to define specific +user options for Fortran 08 files. +You should normally set the &cv-link-FORTRANFLAGS; variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. +</para> +</summary> +</cvar> + +<cvar name="_F08INCFLAGS"> +<summary> +<para> +An automatically-generated construction variable +containing the Fortran 08 compiler command-line options +for specifying directories to be searched for include files. +The value of &cv-link-_F08INCFLAGS; is created +by appending &cv-link-INCPREFIX; and &cv-link-INCSUFFIX; +to the beginning and end +of each directory in &cv-link-F08PATH;. +</para> +</summary> +</cvar> + +<cvar name="F08PATH"> +<summary> +<para> +The list of directories that the Fortran 08 compiler will search for include +directories. The implicit dependency scanner will search these +directories for include files. Don't explicitly put include directory +arguments in &cv-link-F08FLAGS; because the result will be non-portable +and the directories will not be searched by the dependency scanner. Note: +directory names in &cv-link-F08PATH; will be looked-up relative to the SConscript +directory when they are used in a command. To force +&scons; +to look-up a directory relative to the root of the source tree use #: +You only need to set &cv-link-F08PATH; if you need to define a specific +include path for Fortran 08 files. +You should normally set the &cv-link-FORTRANPATH; variable, +which specifies the include path +for the default Fortran compiler +for all Fortran versions. +</para> + +<example_commands> +env = Environment(F08PATH='#/include') +</example_commands> + +<para> +The directory look-up can also be forced using the +&Dir;() +function: +</para> + +<example_commands> +include = Dir('include') +env = Environment(F08PATH=include) +</example_commands> + +<para> +The directory list will be added to command lines +through the automatically-generated +&cv-link-_F08INCFLAGS; +construction variable, +which is constructed by +appending the values of the +&cv-link-INCPREFIX; and &cv-link-INCSUFFIX; +construction variables +to the beginning and end +of each directory in &cv-link-F08PATH;. +Any command lines you define that need +the F08PATH directory list should +include &cv-link-_F08INCFLAGS;: +</para> + +<example_commands> +env = Environment(F08COM="my_compiler $_F08INCFLAGS -c -o $TARGET $SOURCE") +</example_commands> +</summary> +</cvar> + +<cvar name="F08PPCOM"> +<summary> +<para> +The command line used to compile a Fortran 08 source file to an object file +after first running the file through the C preprocessor. +Any options specified in the &cv-link-F08FLAGS; and &cv-link-CPPFLAGS; construction variables +are included on this command line. +You only need to set &cv-link-F08PPCOM; if you need to use a specific +C-preprocessor command line for Fortran 08 files. +You should normally set the &cv-link-FORTRANPPCOM; variable, +which specifies the default C-preprocessor command line +for all Fortran versions. +</para> +</summary> +</cvar> + +<cvar name="F08PPCOMSTR"> +<summary> +<para> +The string displayed when a Fortran 08 source file +is compiled to an object file +after first running the file through the C preprocessor. +If this is not set, then &cv-link-F08PPCOM; or &cv-link-FORTRANPPCOM; +(the command line) is displayed. +</para> +</summary> +</cvar> + +<cvar name="SHF08"> +<summary> +<para> +The Fortran 08 compiler used for generating shared-library objects. +You should normally set the &cv-link-SHFORTRAN; variable, +which specifies the default Fortran compiler +for all Fortran versions. +You only need to set &cv-link-SHF08; if you need to use a specific compiler +or compiler version for Fortran 08 files. +</para> +</summary> +</cvar> + +<cvar name="SHF08COM"> +<summary> +<para> +The command line used to compile a Fortran 08 source file +to a shared-library object file. +You only need to set &cv-link-SHF08COM; if you need to use a specific +command line for Fortran 08 files. +You should normally set the &cv-link-SHFORTRANCOM; variable, +which specifies the default command line +for all Fortran versions. +</para> +</summary> +</cvar> + +<cvar name="SHF08COMSTR"> +<summary> +<para> +The string displayed when a Fortran 08 source file +is compiled to a shared-library object file. +If this is not set, then &cv-link-SHF08COM; or &cv-link-SHFORTRANCOM; +(the command line) is displayed. +</para> +</summary> +</cvar> + +<cvar name="SHF08FLAGS"> +<summary> +<para> +Options that are passed to the Fortran 08 compiler +to generated shared-library objects. +You only need to set &cv-link-SHF08FLAGS; if you need to define specific +user options for Fortran 08 files. +You should normally set the &cv-link-SHFORTRANFLAGS; variable, +which specifies the user-specified options +passed to the default Fortran compiler +for all Fortran versions. +</para> +</summary> +</cvar> + +<cvar name="SHF08PPCOM"> +<summary> +<para> +The command line used to compile a Fortran 08 source file to a +shared-library object file +after first running the file through the C preprocessor. +Any options specified in the &cv-link-SHF08FLAGS; and &cv-link-CPPFLAGS; construction variables +are included on this command line. +You only need to set &cv-link-SHF08PPCOM; if you need to use a specific +C-preprocessor command line for Fortran 08 files. +You should normally set the &cv-link-SHFORTRANPPCOM; variable, +which specifies the default C-preprocessor command line +for all Fortran versions. +</para> +</summary> +</cvar> + +<cvar name="SHF08PPCOMSTR"> +<summary> +<para> +The string displayed when a Fortran 08 source file +is compiled to a shared-library object file +after first running the file through the C preprocessor. +If this is not set, then &cv-link-SHF08PPCOM; or &cv-link-SHFORTRANPPCOM; +(the command line) is displayed. +</para> +</summary> +</cvar> + +</sconsdoc> diff --git a/src/engine/SCons/Tool/gdc.py b/src/engine/SCons/Tool/gdc.py index 1178b85..32199b3 100644 --- a/src/engine/SCons/Tool/gdc.py +++ b/src/engine/SCons/Tool/gdc.py @@ -65,10 +65,10 @@ def generate(env): env['DC'] = env.Detect('gdc') env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -o $TARGET $SOURCES' - env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' - env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' - env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' + env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' + env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' + env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' + env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' env['SHDCOM'] = '$SHDC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -fPIC -c -o $TARGET $SOURCES' @@ -95,14 +95,14 @@ def generate(env): env['DLINKFLAGS'] = SCons.Util.CLVar('') env['DLINKCOM'] = '$DLINK -o $TARGET $DLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' - env['SHDLINK'] = '$DC' - env['SHDLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared') - env['SHDLINKCOM'] = '$DLINK -o $TARGET $DLINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' + env['DSHLINK'] = '$DC' + env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared') + 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 '') - env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' + env['_DLIBFLAGS'] = '${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)}' env['DLIBFLAGPREFIX'] = '-' env['DLIBFLAGSUFFIX'] = '' @@ -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/gfortran.py b/src/engine/SCons/Tool/gfortran.py index 7b05e68..d33bf52 100644 --- a/src/engine/SCons/Tool/gfortran.py +++ b/src/engine/SCons/Tool/gfortran.py @@ -43,7 +43,7 @@ def generate(env): Environment.""" fortran.generate(env) - for dialect in ['F77', 'F90', 'FORTRAN', 'F95', 'F03']: + for dialect in ['F77', 'F90', 'FORTRAN', 'F95', 'F03', 'F08']: env['%s' % dialect] = 'gfortran' env['SH%s' % dialect] = '$%s' % dialect if env['PLATFORM'] in ['cygwin', 'win32']: diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 6bd9471..b1d5088 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 from . 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-compatibility 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/icl.py b/src/engine/SCons/Tool/icl.py index caa2281..a0bad31 100644 --- a/src/engine/SCons/Tool/icl.py +++ b/src/engine/SCons/Tool/icl.py @@ -35,7 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Tool.intelc -# This has been completely superceded by intelc.py, which can +# This has been completely superseded by intelc.py, which can # handle both Windows and Linux versions. def generate(*args, **kw): diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 0d75765..e16bb5f 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -39,7 +39,8 @@ import shutil import stat import SCons.Action -from SCons.Util import make_path_relative +import SCons.Tool +import SCons.Util # # We keep track of *all* installed files. @@ -48,7 +49,7 @@ _UNIQUE_INSTALLED_FILES = None class CopytreeError(EnvironmentError): pass - + # This is a patched version of shutil.copytree from python 2.5. It # doesn't fail if the dir exists, which regular copytree does # (annoyingly). Note the XXX comment in the docstring. @@ -91,7 +92,7 @@ def scons_copytree(src, dst, symlinks=False): errors.extend(err.args[0]) try: shutil.copystat(src, dst) - except WindowsError: + except SCons.Util.WinError: # can't copy file access times on Windows pass except OSError as why: @@ -142,94 +143,34 @@ 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, env): - """Check if dest is a version shared library name. Return version, libname, & install_dir if it is.""" - Verbose = False - platform = env.subst('$PLATFORM') - if not (platform == 'posix' or platform == 'darwin'): - return (None, None, None) - - libname = os.path.basename(dest) - install_dir = os.path.dirname(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': - # 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] - - 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): +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 - linknames = [] - version, libname, install_dir = versionedLibVersion(dest, 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)) + symlinks = listShlibLinksToInstall(dest, source, env) + if Verbose: + print('installShlibLinks: symlinks={:r}'.format(SCons.Tool.StringizeLibSymlinks(symlinks))) + if symlinks: + SCons.Tool.CreateLibSymlinks(env, symlinks) return def installFunc(target, source, env): @@ -259,7 +200,11 @@ def installFuncVersionedLib(target, source, env): assert len(target)==len(source), \ "Installing source %s into target %s: target and source lists must have same length."%(list(map(str, source)), list(map(str, target))) for t,s in zip(target,source): - if install(t.get_path(),s.get_path(),env): + if hasattr(t.attributes, 'shlibname'): + tpath = os.path.join(t.get_dir(), t.attributes.shlibname) + else: + tpath = t.get_path() + if install(tpath,s.get_path(),env): return 1 return 0 @@ -280,7 +225,7 @@ def stringFunc(target, source, env): # Emitter functions # def add_targets_to_INSTALLED_FILES(target, source, env): - """ an emitter that adds all target files to the list stored in the + """ An emitter that adds all target files to the list stored in the _INSTALLED_FILES global variable. This way all installed files of one scons call will be collected. """ @@ -291,7 +236,7 @@ def add_targets_to_INSTALLED_FILES(target, source, env): return (target, source) def add_versioned_targets_to_INSTALLED_FILES(target, source, env): - """ an emitter that adds all target files to the list stored in the + """ An emitter that adds all target files to the list stored in the _INSTALLED_FILES global variable. This way all installed files of one scons call will be collected. """ @@ -299,28 +244,15 @@ 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].path, 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}".format(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) class DESTDIR_factory(object): - """ a node factory, where all files will be relative to the dir supplied + """ A node factory, where all files will be relative to the dir supplied in the constructor. """ def __init__(self, env, dir): @@ -328,11 +260,11 @@ class DESTDIR_factory(object): self.dir = env.arg2nodes( dir, env.fs.Dir )[0] def Entry(self, name): - name = make_path_relative(name) + name = SCons.Util.make_path_relative(name) return self.dir.Entry(name) def Dir(self, name): - name = make_path_relative(name) + name = SCons.Util.make_path_relative(name) return self.dir.Dir(name) # @@ -370,14 +302,12 @@ def InstallBuilderWrapper(env, target=None, source=None, dir=None, **kw): # '#' on the file name portion as meaning the Node should # be relative to the top-level SConstruct directory. target = env.fs.Entry('.'+os.sep+src.name, dnode) - #tgt.extend(BaseInstallBuilder(env, target, src, **kw)) tgt.extend(BaseInstallBuilder(env, target, src, **kw)) return tgt def InstallAsBuilderWrapper(env, target=None, source=None, **kw): result = [] for src, tgt in map(lambda x, y: (x, y), source, target): - #result.extend(BaseInstallBuilder(env, tgt, src, **kw)) result.extend(BaseInstallBuilder(env, tgt, src, **kw)) return result @@ -440,6 +370,7 @@ def generate(env): source_factory = env.fs.Entry, multi = 1, emitter = [ add_targets_to_INSTALLED_FILES, ], + source_scanner = SCons.Scanner.Base( {}, name = 'Install', recursive = False ), name = 'InstallBuilder') global BaseVersionedInstallBuilder diff --git a/src/engine/SCons/Tool/install.xml b/src/engine/SCons/Tool/install.xml index 0a0ad71..6ae3e30 100644 --- a/src/engine/SCons/Tool/install.xml +++ b/src/engine/SCons/Tool/install.xml @@ -68,23 +68,27 @@ and source arguments list different numbers of files or directories. </para> + +<example_commands> +env.InstallAs(target = '/usr/local/bin/foo', + source = 'foo_debug') +env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], + source = ['libFOO.a', 'libBAR.a']) +</example_commands> + </summary> </builder> <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. -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.InstallAs(target = '/usr/local/bin/foo', - source = 'foo_debug') -env.InstallAs(target = ['../lib/libfoo.a', '../lib/libbar.a'], - source = ['libFOO.a', 'libBAR.a']) +env.InstallVersionedLib(target = '/usr/local/bin/foo', + source = 'libxyz.1.5.2.so') </example_commands> </summary> </builder> diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py index 7cc2376..2b396c4 100644 --- a/src/engine/SCons/Tool/intelc.py +++ b/src/engine/SCons/Tool/intelc.py @@ -62,15 +62,6 @@ class MissingDirError(IntelCError): # dir not found class NoRegistryModuleError(IntelCError): # can't read registry at all pass -def uniquify(s): - """Return a sequence containing only one copy of each unique element from input sequence s. - Does not preserve order. - Input sequence must be hashable (i.e. must be usable as a dictionary key).""" - u = {} - for x in s: - u[x] = 1 - return list(u.keys()) - def linux_ver_normalize(vstr): """Normalize a Linux compiler version number. Intel changed from "80" to "9.0" in 2005, so we assume if the number @@ -191,7 +182,7 @@ def get_intel_registry_value(valuename, version=None, abi=None): except SCons.Util.RegError: raise MissingRegistryError("%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)) - except WindowsError: + except SCons.Util.WinError: raise MissingRegistryError("%s was not found in the registry, for Intel compiler version %s, abi='%s'"%(K, version,abi)) # Get the value: @@ -215,7 +206,7 @@ def get_all_compiler_versions(): try: k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, keyname) - except WindowsError: + except SCons.Util.WinError: # For version 13 or later, check for default instance UUID if is_win64: keyname = 'Software\\WoW6432Node\\Intel\\Suites' @@ -224,7 +215,7 @@ def get_all_compiler_versions(): try: k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, keyname) - except WindowsError: + except SCons.Util.WinError: return [] i = 0 versions = [] @@ -301,7 +292,7 @@ def get_all_compiler_versions(): """Given a dot-separated version string, return a tuple of ints representing it.""" return [int(x) for x in str.split('.')] # split into ints, sort, then remove dups - return sorted(uniquify(versions), key=keyfunc, reverse=True) + return sorted(SCons.Util.unique(versions), key=keyfunc, reverse=True) def get_intel_compiler_top(version, abi): """ diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index 9bae729..8308927 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -97,7 +97,7 @@ def generate(env): env['_JARMANIFEST'] = jarManifest env['_JARSOURCES'] = jarSources env['_JARCOM'] = '$JAR $_JARFLAGS $TARGET $_JARMANIFEST $_JARSOURCES' - env['JARCOM'] = "${TEMPFILE('$_JARCOM')}" + env['JARCOM'] = "${TEMPFILE('$_JARCOM','$JARCOMSTR')}" env['JARSUFFIX'] = '.jar' def exists(env): diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py index 5d0e32a..b26c0b3 100644 --- a/src/engine/SCons/Tool/javac.py +++ b/src/engine/SCons/Tool/javac.py @@ -218,7 +218,7 @@ def generate(env): env['_JAVASOURCEPATH'] = '${_javapathopt("-sourcepath", "JAVASOURCEPATH", "_JAVASOURCEPATHDEFAULT")} ' env['_JAVASOURCEPATHDEFAULT'] = '${TARGET.attributes.java_sourcedir}' env['_JAVACCOM'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES' - env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM')}" + env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM','$JAVACCOMSTR')}" env['JAVACLASSSUFFIX'] = '.class' env['JAVASUFFIX'] = '.java' diff --git a/src/engine/SCons/Tool/ldc.py b/src/engine/SCons/Tool/ldc.py index 6b215e2..ade95db 100644 --- a/src/engine/SCons/Tool/ldc.py +++ b/src/engine/SCons/Tool/ldc.py @@ -70,10 +70,10 @@ def generate(env): env['DC'] = env.Detect('ldc2') env['DCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -of=$TARGET $SOURCES' - env['_DINCFLAGS'] = '$( ${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' - env['_DVERFLAGS'] = '$( ${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)} $)' - env['_DDEBUGFLAGS'] = '$( ${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)} $)' - env['_DFLAGS'] = '$( ${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)} $)' + env['_DINCFLAGS'] = '${_concat(DINCPREFIX, DPATH, DINCSUFFIX, __env__, RDirs, TARGET, SOURCE)}' + env['_DVERFLAGS'] = '${_concat(DVERPREFIX, DVERSIONS, DVERSUFFIX, __env__)}' + env['_DDEBUGFLAGS'] = '${_concat(DDEBUGPREFIX, DDEBUG, DDEBUGSUFFIX, __env__)}' + env['_DFLAGS'] = '${_concat(DFLAGPREFIX, DFLAGS, DFLAGSUFFIX, __env__)}' env['SHDC'] = '$DC' env['SHDCOM'] = '$DC $_DINCFLAGS $_DVERFLAGS $_DDEBUGFLAGS $_DFLAGS -c -relocation-model=pic -of=$TARGET $SOURCES' @@ -101,23 +101,26 @@ def generate(env): env['DLINKCOM'] = '$DLINK -of=$TARGET $DLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' env['DSHLINK'] = '$DC' - env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared') - env['SHDLINKCOM'] = '$DLINK -of=$TARGET $DSHLINKFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' + env['DSHLINKFLAGS'] = SCons.Util.CLVar('$DLINKFLAGS -shared -defaultlib=phobos2-ldc') + # 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 $__DSHLIBVERSIONFLAGS $__DRPATH $SOURCES $_DLIBDIRFLAGS $_DLIBFLAGS' env['DLIBLINKPREFIX'] = '' if env['PLATFORM'] == 'win32' else '-L-l' env['DLIBLINKSUFFIX'] = '.lib' if env['PLATFORM'] == 'win32' else '' - #env['_DLIBFLAGS'] = '$( ${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + #env['_DLIBFLAGS'] = '${_concat(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['_DLIBFLAGS'] = '${_stripixes(DLIBLINKPREFIX, LIBS, DLIBLINKSUFFIX, LIBPREFIXES, LIBSUFFIXES, __env__)}' env['DLIBDIRPREFIX'] = '-L-L' env['DLIBDIRSUFFIX'] = '' - env['_DLIBDIRFLAGS'] = '$( ${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['_DLIBDIRFLAGS'] = '${_concat(DLIBDIRPREFIX, LIBPATH, DLIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)}' env['DLIB'] = 'lib' if env['PLATFORM'] == 'win32' else 'ar cr' env['DLIBCOM'] = '$DLIB $_DLIBFLAGS {0}$TARGET $SOURCES $_DLIBFLAGS'.format('-c ' if env['PLATFORM'] == 'win32' else '') - #env['_DLIBFLAGS'] = '$( ${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)} $)' + #env['_DLIBFLAGS'] = '${_concat(DLIBFLAGPREFIX, DLIBFLAGS, DLIBFLAGSUFFIX, __env__)}' env['DLIBFLAGPREFIX'] = '-' env['DLIBFLAGSUFFIX'] = '' @@ -126,7 +129,18 @@ def generate(env): # platform supports it. env['DRPATHPREFIX'] = '-L-rpath=' env['DRPATHSUFFIX'] = '' - env['_RPATH'] = '${_concat(DRPATHPREFIX, RPATH, DRPATHSUFFIX, __env__)}' + 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 1b03075..ae2c4b8 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -1,3 +1,4 @@ + """SCons.Tool.link Tool-specific initialization for the generic Posix linker. @@ -34,9 +35,10 @@ from __future__ import print_function __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys import re +import os -import SCons.Defaults import SCons.Tool import SCons.Util import SCons.Warnings @@ -72,83 +74,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}".format(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) - # change the name of the target to include the version number - target[0].name = version_names[0] - for name in version_names: - env.SideEffect(name, target[0]) - env.Clean(target[0], name) - if Verbose: - print("shlib_emitter: add side effect - ",name) + symlink_generator = kw['symlink_generator'] except KeyError: - version = None + pass + else: + if Verbose: + print("_lib_emitter: symlink_generator={:r}".format(symlink_generator)) + symlinks = symlink_generator(env, target[0]) + if Verbose: + print("_lib_emitter: symlinks={:r}".format(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': - versionparts = version.split('.') - name = target[0].name - # generate library name with the version number - version_name = target[0].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') - 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') - 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}".format(libnode.get_path())) + print("_versioned_lib_name: version={:r}".format(version)) + print("_versioned_lib_name: prefix={:r}".format(prefix)) + print("_versioned_lib_name: suffix={:r}".format(suffix)) + print("_versioned_lib_name: suffix_generator={:r}".format(suffix_generator)) + + versioned_name = os.path.basename(libnode.get_path()) + if Verbose: + print("_versioned_lib_name: versioned_name={:r}".format(versioned_name)) + + versioned_prefix = prefix_generator(env, **kw) + versioned_suffix = suffix_generator(env, **kw) + if Verbose: + print("_versioned_lib_name: versioned_prefix={:r}".format(versioned_prefix)) + print("_versioned_lib_name: versioned_suffix={:r}".format(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}".format(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}".format(suffix)) + print("_versioned_lib_suffix: version={:r}".format(version)) + if not suffix.endswith(version): + suffix = suffix + '.' + version + if Verbose: + print("_versioned_lib_suffix: return suffix={:r}".format(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}".format(version)) + name = name_func(env, libnode, version, prefix, suffix) + if Verbose: + print("_versioned_lib_soname: name={:r}".format(name)) + major = version.split('.')[0] + soname = name + '.' + major + if Verbose: + print("_versioned_lib_soname: soname={:r}".format(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}".format(libnode.get_path())) + print("_versioned_lib_symlinks: version={:r}".format(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}".format(None)) + return None + + linkdir = libnode.get_dir() + if Verbose: + print("_versioned_lib_symlinks: linkdir={:r}".format(linkdir.get_path())) + + name = name_func(env, libnode, version, prefix, suffix) + if Verbose: + print("_versioned_lib_symlinks: name={:r}".format(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}".format(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.""" @@ -157,7 +281,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]) @@ -182,15 +306,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/mslib.py b/src/engine/SCons/Tool/mslib.py index df8d877..c5a7a32 100644 --- a/src/engine/SCons/Tool/mslib.py +++ b/src/engine/SCons/Tool/mslib.py @@ -50,7 +50,7 @@ def generate(env): env['AR'] = 'lib' env['ARFLAGS'] = SCons.Util.CLVar('/nologo') - env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" + env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES','$ARCOMSTR')}" env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index f30c1d3..55cf33f 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -130,6 +130,14 @@ def _dllEmitter(target, source, env, paramtp): extratargets.append(pdb) target[0].attributes.pdb = pdb + if version_num >= 11.0 and env.get('PCH', 0): + # MSVC 11 and above need the PCH object file to be added to the link line, + # otherwise you get link error LNK2011. + pchobj = SCons.Util.splitext(str(env['PCH']))[0] + '.obj' + # print "prog_emitter, version %s, appending pchobj %s"%(version_num, pchobj) + if pchobj not in extrasources: + extrasources.append(pchobj) + if not no_import_lib and \ not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"): # Append an import library to the list of targets. @@ -209,7 +217,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: @@ -223,7 +231,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: @@ -238,11 +246,11 @@ embedManifestExeCheckAction = SCons.Action.Action(embedManifestExeCheck, None) regServerAction = SCons.Action.Action("$REGSVRCOM", "$REGSVRCOMSTR") regServerCheck = SCons.Action.Action(RegServerFunc, None) -shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}', '$SHLINKCOMSTR') +shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES", "$SHLINKCOMSTR")}', '$SHLINKCOMSTR') compositeShLinkAction = shlibLinkAction + regServerCheck + embedManifestDllCheckAction -ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES")}', '$LDMODULECOMSTR') +ldmodLinkAction = SCons.Action.Action('${TEMPFILE("$LDMODULE $LDMODULEFLAGS $_LDMODULE_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_LDMODULE_SOURCES", "$LDMODULECOMSTR")}', '$LDMODULECOMSTR') compositeLdmodAction = ldmodLinkAction + regServerCheck + embedManifestDllCheckAction -exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows")}', '$LINKCOMSTR') +exeLinkAction = SCons.Action.Action('${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows", "$LINKCOMSTR")}', '$LINKCOMSTR') compositeLinkAction = exeLinkAction + embedManifestExeCheckAction def generate(env): @@ -256,6 +264,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 @@ -297,7 +306,7 @@ def generate(env): # if the manifest actually exists before trying to run mt with it. env['MTEXECOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;1' env['MTSHLIBCOM'] = '-$MT $MTFLAGS -manifest ${TARGET}.manifest $_MANIFEST_SOURCES -outputresource:$TARGET;2' - # Future work garyo 27-Feb-11 + # TODO Future work garyo 27-Feb-11 env['_MANIFEST_SOURCES'] = None # _windowsManifestSources # Set-up ms tools paths diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 0bc296f..f894562 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -224,17 +224,17 @@ def generate(env): env['CC'] = 'cl' env['CCFLAGS'] = SCons.Util.CLVar('/nologo') env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM")}' + env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM","$CCCOMSTR")}' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM")}' + env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCCCOMSTR")}' env['CXX'] = '$CC' env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)') - env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM")}' + env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM","$CXXCOMSTR")}' env['SHCXX'] = '$CXX' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM")}' + env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCXXCOMSTR")}' env['CPPDEFPREFIX'] = '/D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '/I' 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 370af9a..efdc8a1 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -64,6 +64,7 @@ def xmlify(s): s = s.replace("&", "&") # do this first s = s.replace("'", "'") s = s.replace('"', """) + s = s.replace('\n', '
') return s # Process a CPPPATH list in includes, given the env, target and source. @@ -100,42 +101,6 @@ def msvs_parse_version(s): num, suite = version_re.match(s).groups() return float(num), suite -# os.path.relpath has been introduced in Python 2.6 -# We define it locally for earlier versions of Python -def relpath(path, start=os.path.curdir): - """Return a relative version of a path""" - import sys - if not path: - raise ValueError("no path specified") - start_list = os.path.abspath(start).split(os.sep) - path_list = os.path.abspath(path).split(os.sep) - if 'posix' in sys.builtin_module_names: - # Work out how much of the filepath is shared by start and path. - i = len(os.path.commonprefix([start_list, path_list])) - else: - if start_list[0].lower() != path_list[0].lower(): - unc_path, rest = os.path.splitunc(path) - unc_start, rest = os.path.splitunc(start) - if bool(unc_path) ^ bool(unc_start): - raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" - % (path, start)) - else: - raise ValueError("path is on drive %s, start on drive %s" - % (path_list[0], start_list[0])) - # Work out how much of the filepath is shared by start and path. - for i in range(min(len(start_list), len(path_list))): - if start_list[i].lower() != path_list[i].lower(): - break - else: - i += 1 - rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] - if not rel_list: - return os.path.curdir - return os.path.join(*rel_list) - -if not "relpath" in os.path.__all__: - os.path.relpath = relpath - # This is how we re-invoke SCons from inside MSVS Project files. # The problem is that we might have been invoked as either scons.bat # or scons.py. If we were invoked directly as scons.py, then we could @@ -199,6 +164,209 @@ def makeHierarchy(sources): # print 'Warning: failed to decompose path for '+str(file) return hierarchy +class _UserGenerator(object): + ''' + Base class for .dsp.user file generator + ''' + # Default instance values. + # Ok ... a bit defensive, but it does not seem reasonable to crash the + # build for a workspace user file. :-) + usrhead = None + usrdebg = None + usrconf = None + createfile = False + def __init__(self, dspfile, source, env): + # DebugSettings should be a list of debug dictionary sorted in the same order + # as the target list and variants + if 'variant' not in env: + raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVSProject.") + elif SCons.Util.is_String(env['variant']): + variants = [env['variant']] + elif SCons.Util.is_List(env['variant']): + variants = env['variant'] + + if 'DebugSettings' not in env or env['DebugSettings'] == None: + dbg_settings = [] + elif SCons.Util.is_Dict(env['DebugSettings']): + dbg_settings = [env['DebugSettings']] + elif SCons.Util.is_List(env['DebugSettings']): + if len(env['DebugSettings']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'DebugSettings' and 'variant' lists must be the same.") + dbg_settings = [] + for ds in env['DebugSettings']: + if SCons.Util.is_Dict(ds): + dbg_settings.append(ds) + else: + dbg_settings.append({}) + else: + dbg_settings = [] + + if len(dbg_settings) == 1: + dbg_settings = dbg_settings * len(variants) + + self.createfile = self.usrhead and self.usrdebg and self.usrconf and \ + dbg_settings and bool([ds for ds in dbg_settings if ds]) + + if self.createfile: + dbg_settings = dict(zip(variants, dbg_settings)) + for var, src in dbg_settings.items(): + # Update only expected keys + trg = {} + for key in [k for k in self.usrdebg.keys() if k in src]: + trg[key] = str(src[key]) + self.configs[var].debug = trg + + def UserHeader(self): + encoding = self.env.subst('$MSVSENCODING') + versionstr = self.versionstr + self.usrfile.write(self.usrhead % locals()) + + def UserProject(self): + pass + + def Build(self): + if not self.createfile: + return + try: + filename = self.dspabs +'.user' + self.usrfile = open(filename, 'w') + except IOError, detail: + raise SCons.Errors.InternalError('Unable to open "' + filename + '" for writing:' + str(detail)) + else: + self.UserHeader() + self.UserProject() + self.usrfile.close() + +V9UserHeader = """\ +<?xml version="1.0" encoding="%(encoding)s"?> +<VisualStudioUserFile +\tProjectType="Visual C++" +\tVersion="%(versionstr)s" +\tShowAllFiles="false" +\t> +\t<Configurations> +""" + +V9UserConfiguration = """\ +\t\t<Configuration +\t\t\tName="%(variant)s|%(platform)s" +\t\t\t> +\t\t\t<DebugSettings +%(debug_settings)s +\t\t\t/> +\t\t</Configuration> +""" + +V9DebugSettings = { +'Command':'$(TargetPath)', +'WorkingDirectory': None, +'CommandArguments': None, +'Attach':'false', +'DebuggerType':'3', +'Remote':'1', +'RemoteMachine': None, +'RemoteCommand': None, +'HttpUrl': None, +'PDBPath': None, +'SQLDebugging': None, +'Environment': None, +'EnvironmentMerge':'true', +'DebuggerFlavor': None, +'MPIRunCommand': None, +'MPIRunArguments': None, +'MPIRunWorkingDirectory': None, +'ApplicationCommand': None, +'ApplicationArguments': None, +'ShimCommand': None, +'MPIAcceptMode': None, +'MPIAcceptFilter': None, +} + +class _GenerateV7User(_UserGenerator): + """Generates a Project file for MSVS .NET""" + def __init__(self, dspfile, source, env): + if self.version_num >= 9.0: + self.usrhead = V9UserHeader + self.usrconf = V9UserConfiguration + self.usrdebg = V9DebugSettings + _UserGenerator.__init__(self, dspfile, source, env) + + def UserProject(self): + confkeys = sorted(self.configs.keys()) + for kind in confkeys: + variant = self.configs[kind].variant + platform = self.configs[kind].platform + debug = self.configs[kind].debug + if debug: + debug_settings = '\n'.join(['\t\t\t\t%s="%s"' % (key, xmlify(value)) + for key, value in debug.items() + if value is not None]) + self.usrfile.write(self.usrconf % locals()) + self.usrfile.write('\t</Configurations>\n</VisualStudioUserFile>') + +V10UserHeader = """\ +<?xml version="1.0" encoding="%(encoding)s"?> +<Project ToolsVersion="%(versionstr)s" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +""" + +V10UserConfiguration = """\ +\t<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'"> +%(debug_settings)s +\t</PropertyGroup> +""" + +V10DebugSettings = { +'LocalDebuggerCommand': None, +'LocalDebuggerCommandArguments': None, +'LocalDebuggerEnvironment': None, +'DebuggerFlavor': 'WindowsLocalDebugger', +'LocalDebuggerWorkingDirectory': None, +'LocalDebuggerAttach': None, +'LocalDebuggerDebuggerType': None, +'LocalDebuggerMergeEnvironment': None, +'LocalDebuggerSQLDebugging': None, +'RemoteDebuggerCommand': None, +'RemoteDebuggerCommandArguments': None, +'RemoteDebuggerWorkingDirectory': None, +'RemoteDebuggerServerName': None, +'RemoteDebuggerConnection': None, +'RemoteDebuggerDebuggerType': None, +'RemoteDebuggerAttach': None, +'RemoteDebuggerSQLDebugging': None, +'DeploymentDirectory': None, +'AdditionalFiles': None, +'RemoteDebuggerDeployDebugCppRuntime': None, +'WebBrowserDebuggerHttpUrl': None, +'WebBrowserDebuggerDebuggerType': None, +'WebServiceDebuggerHttpUrl': None, +'WebServiceDebuggerDebuggerType': None, +'WebServiceDebuggerSQLDebugging': None, +} + +class _GenerateV10User(_UserGenerator): + """Generates a Project'user file for MSVS 2010""" + + def __init__(self, dspfile, source, env): + self.versionstr = '4.0' + self.usrhead = V10UserHeader + self.usrconf = V10UserConfiguration + self.usrdebg = V10DebugSettings + _UserGenerator.__init__(self, dspfile, source, env) + + def UserProject(self): + confkeys = sorted(self.configs.keys()) + for kind in confkeys: + variant = self.configs[kind].variant + platform = self.configs[kind].platform + debug = self.configs[kind].debug + if debug: + debug_settings = '\n'.join(['\t\t<%s>%s</%s>' % (key, xmlify(value), key) + for key, value in debug.items() + if value is not None]) + self.usrfile.write(self.usrconf % locals()) + self.usrfile.write('</Project>') + class _DSPGenerator(object): """ Base class for DSP generators """ @@ -290,9 +458,17 @@ class _DSPGenerator(object): runfile.append(s) self.sconscript = env['MSVSSCONSCRIPT'] - - cmdargs = env.get('cmdargs', '') - + + if 'cmdargs' not in env or env['cmdargs'] == None: + cmdargs = [''] * len(variants) + elif SCons.Util.is_String(env['cmdargs']): + cmdargs = [env['cmdargs']] * len(variants) + elif SCons.Util.is_List(env['cmdargs']): + if len(env['cmdargs']) != len(variants): + raise SCons.Errors.InternalError("Sizes of 'cmdargs' and 'variant' lists must be the same.") + else: + cmdargs = env['cmdargs'] + self.env = env if 'name' in self.env: @@ -332,9 +508,7 @@ class _DSPGenerator(object): self.sources[t[0]].append(self.env[t[1]]) for n in sourcenames: - #TODO 2.4: compat layer supports sorted(key=) but not sort(key=) - #TODO 2.4: self.sources[n].sort(key=lambda a: a.lower()) - self.sources[n] = sorted(self.sources[n], key=lambda a: a.lower()) + self.sources[n].sort(key=lambda a: a.lower()) def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): config = Config() @@ -355,7 +529,7 @@ class _DSPGenerator(object): print("Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'") for i in range(len(variants)): - AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) + AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs[i]) self.platforms = [] for key in self.configs.keys(): @@ -621,7 +795,7 @@ V8DSPConfiguration = """\ \t\t\t/> \t\t</Configuration> """ -class _GenerateV7DSP(_DSPGenerator): +class _GenerateV7DSP(_DSPGenerator, _GenerateV7User): """Generates a Project file for MSVS .NET""" def __init__(self, dspfile, source, env): @@ -644,6 +818,8 @@ class _GenerateV7DSP(_DSPGenerator): self.dspheader = V7DSPHeader self.dspconfiguration = V7DSPConfiguration self.file = None + + _GenerateV7User.__init__(self, dspfile, source, env) def PrintHeader(self): env = self.env @@ -868,7 +1044,9 @@ class _GenerateV7DSP(_DSPGenerator): self.PrintHeader() self.PrintProject() self.file.close() - + + _GenerateV7User.Build(self) + V10DSPHeader = """\ <?xml version="1.0" encoding="%(encoding)s"?> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> @@ -893,6 +1071,7 @@ V10DSPPropertyGroupCondition = """\ \t<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'" Label="Configuration"> \t\t<ConfigurationType>Makefile</ConfigurationType> \t\t<UseOfMfc>false</UseOfMfc> +\t\t<PlatformToolset>%(toolset)s</PlatformToolset> \t</PropertyGroup> """ @@ -914,15 +1093,16 @@ V10DSPCommandLine = """\ \t\t<NMakeForcedUsingAssemblies Condition="'$(Configuration)|$(Platform)'=='%(variant)s|%(platform)s'">$(NMakeForcedUsingAssemblies)</NMakeForcedUsingAssemblies> """ -class _GenerateV10DSP(_DSPGenerator): +class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): """Generates a Project file for MSVS 2010""" def __init__(self, dspfile, source, env): _DSPGenerator.__init__(self, dspfile, source, env) - self.dspheader = V10DSPHeader self.dspconfiguration = V10DSPProjectConfiguration self.dspglobals = V10DSPGlobals + + _GenerateV10User.__init__(self, dspfile, source, env) def PrintHeader(self): env = self.env @@ -973,6 +1153,10 @@ class _GenerateV10DSP(_DSPGenerator): self.file.write('\t<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\n') + toolset = '' + if 'MSVC_VERSION' in self.env: + version_num, suite = msvs_parse_version(self.env['MSVC_VERSION']) + toolset = 'v%d' % (version_num * 10) for kind in confkeys: variant = self.configs[kind].variant platform = self.configs[kind].platform @@ -1170,6 +1354,8 @@ class _GenerateV10DSP(_DSPGenerator): self.PrintHeader() self.PrintProject() self.file.close() + + _GenerateV10User.Build(self) class _DSWGenerator(object): """ Base class for DSW generators """ @@ -1312,7 +1498,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') @@ -1758,7 +1946,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/msvs.xml b/src/engine/SCons/Tool/msvs.xml index 88a3346..e85b27c 100644 --- a/src/engine/SCons/Tool/msvs.xml +++ b/src/engine/SCons/Tool/msvs.xml @@ -5,202 +5,129 @@ __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'> +<!ENTITY % scons SYSTEM "../../../../doc/scons.mod"> %scons; -<!ENTITY % builders-mod SYSTEM '../../../../doc/generated/builders.mod'> +<!ENTITY % builders-mod SYSTEM "../../../../doc/generated/builders.mod"> %builders-mod; -<!ENTITY % functions-mod SYSTEM '../../../../doc/generated/functions.mod'> +<!ENTITY % functions-mod SYSTEM "../../../../doc/generated/functions.mod"> %functions-mod; -<!ENTITY % tools-mod SYSTEM '../../../../doc/generated/tools.mod'> +<!ENTITY % tools-mod SYSTEM "../../../../doc/generated/tools.mod"> %tools-mod; -<!ENTITY % variables-mod SYSTEM '../../../../doc/generated/variables.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="msvs"> -<summary> -<para> -Sets construction variables for Microsoft Visual Studio. -</para> -</summary> -<sets> -<item>MSVSPROJECTCOM</item> -<item>MSVSSOLUTIONCOM</item> -<item>MSVSSCONSCRIPT</item> -<item>MSVSSCONS</item> -<item>MSVSSCONSFLAGS</item> -<item>MSVSSCONSCOM</item> -<item>MSVSBUILDCOM</item> -<item>MSVSREBUILDCOM</item> -<item>MSVSCLEANCOM</item> -<item>MSVSENCODING</item> -</sets> -<uses> -</uses> -</tool> - -<builder name ="MSVSProject"> -<summary> -<para> -Builds a Microsoft Visual Studio project file, -and by default builds a solution file as well. -</para> - -<para> -This builds a Visual Studio project file, based on the version of -Visual Studio that is configured (either the latest installed version, -or the version specified by -&cv-link-MSVS_VERSION; -in the Environment constructor). -For Visual Studio 6, it will generate a -<filename>.dsp</filename> -file. -For Visual Studio 7 (.NET) and later versions, it will generate a -<filename>.vcproj</filename> -file. -</para> - -<para> -By default, -this also generates a solution file -for the specified project, -a -<filename>.dsw</filename> -file for Visual Studio 6 -or a -<filename>.sln</filename> -file for Visual Studio 7 (.NET). -This behavior may be disabled by specifying -<literal>auto_build_solution=0</literal> -when you call -&b-MSVSProject;, -in which case you presumably want to -build the solution file(s) -by calling the -&b-MSVSSolution; -Builder (see below). -</para> - -<para> -The &b-MSVSProject; builder -takes several lists of filenames -to be placed into the project file. -These are currently limited to -<literal>srcs</literal>, -<literal>incs</literal>, -<literal>localincs</literal>, -<literal>resources</literal>, -and -<literal>misc</literal>. -These are pretty self-explanatory, but it should be noted that these -lists are added to the &cv-link-SOURCES; construction variable as strings, -NOT as SCons File Nodes. This is because they represent file -names to be added to the project file, not the source files used to -build the project file. -</para> - -<para> -The above filename lists are all optional, -although at least one must be specified -for the resulting project file to be non-empty. -</para> - -<para> -In addition to the above lists of values, -the following values may be specified: -</para> - -<para> -<literal>target</literal>: -The name of the target -<filename>.dsp</filename> -or -<filename>.vcproj</filename> -file. -The correct -suffix for the version of Visual Studio must be used, -but the -&cv-link-MSVSPROJECTSUFFIX; -construction variable -will be defined to the correct value (see example below). -</para> - -<para> -<literal>variant</literal>: -The name of this particular variant. -For Visual Studio 7 projects, -this can also be a list of variant names. -These are typically things like "Debug" or "Release", but really -can be anything you want. -For Visual Studio 7 projects, -they may also specify a target platform -separated from the variant name by a -<literal>|</literal> -(vertical pipe) -character: -<literal>Debug|Xbox</literal>. -The default target platform is Win32. -Multiple calls to -&b-MSVSProject; -with different variants are allowed; -all variants will be added to the project file with their appropriate -build targets and sources. -</para> - -<para> -<literal>buildtarget</literal>: -An optional string, node, or list of strings or nodes -(one per build variant), to tell the Visual Studio debugger -what output target to use in what build variant. -The number of -<literal>buildtarget</literal> -entries must match the number of -<literal>variant</literal> -entries. -</para> - -<para> -<literal>runfile</literal>: -The name of the file that Visual Studio 7 and later -will run and debug. -This appears as the value of the -<literal>Output</literal> -field in the resutling Visual Studio project file. -If this is not specified, -the default is the same as the specified -<literal>buildtarget</literal> -value. -</para> - -<para> -Note that because &SCons; always executes its build commands -from the directory in which the &SConstruct; file is located, -if you generate a project file in a different directory -than the &SConstruct; directory, -users will not be able to double-click -on the file name in compilation error messages -displayed in the Visual Studio console output window. -This can be remedied by adding the -Visual C/C++ -<literal>/FC</literal> -compiler option to the &cv-link-CCFLAGS; variable -so that the compiler will print -the full path name of any -files that cause compilation errors. -</para> - -<para> -Example usage: -</para> - -<example_commands> -barsrcs = ['bar.cpp'], +<sconsdoc +xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd" +xmlns="http://www.scons.org/dbxsd/v1.0" +xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +xmlns:xs="http://www.w3.org/2001/XMLSchema" +xmlns:ns="http://www.scons.org/dbxsd/v1.0"><tool name="msvs"> <summary> <para> +Sets construction variables for Microsoft Visual Studio. </para> </summary> +<sets> <item>MSVSPROJECTCOM</item> <item>MSVSSOLUTIONCOM</item> +<item>MSVSSCONSCRIPT</item> <item>MSVSSCONS</item> <item>MSVSSCONSFLAGS</item> +<item>MSVSSCONSCOM</item> <item>MSVSBUILDCOM</item> +<item>MSVSREBUILDCOM</item> <item>MSVSCLEANCOM</item> +<item>MSVSENCODING</item> </sets> <uses> </uses> </tool> <builder +name="MSVSProject"> <summary> <para> Builds a Microsoft Visual Studio project +file, and by default builds a solution file as well. </para> <para> This +builds a Visual Studio project file, based on the version of Visual Studio +that is configured (either the latest installed version, or the version +specified by &cv-link-MSVS_VERSION; in the Environment constructor). For +Visual Studio 6, it will generate a <filename>.dsp</filename> file. For Visual +Studio 7 (.NET) and later versions, it will generate a +<filename>.vcproj</filename> file. </para> <para> By default, this also +generates a solution file for the specified project, a +<filename>.dsw</filename> file for Visual Studio 6 or a +<filename>.sln</filename> file for Visual Studio 7 (.NET). This behavior may +be disabled by specifying <literal>auto_build_solution=0</literal> when you +call &b-MSVSProject;, in which case you presumably want to build the solution +file(s) by calling the &b-MSVSSolution; Builder (see below). </para> <para> +The &b-MSVSProject; builder takes several lists of filenames to be placed into +the project file. These are currently limited to <literal>srcs</literal>, +<literal>incs</literal>, <literal>localincs</literal>, +<literal>resources</literal>, and <literal>misc</literal>. These are pretty +self-explanatory, but it should be noted that these lists are added to the +&cv-link-SOURCES; construction variable as strings, NOT as SCons File Nodes. +This is because they represent file names to be added to the project file, not +the source files used to build the project file. </para> <para> The above +filename lists are all optional, although at least one must be specified for +the resulting project file to be non-empty. </para> <para> In addition to the +above lists of values, the following values may be specified: +</para><variablelist> + <varlistentry> + <term>target</term> + + <listitem> + <para>The name of the target <filename>.dsp</filename> or + <filename>.vcproj</filename> file. The correct suffix for the version + of Visual Studio must be used, but the &cv-link-MSVSPROJECTSUFFIX; + construction variable will be defined to the correct value (see + example below).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>variant</term> + + <listitem> + <para>The name of this particular variant. For Visual Studio 7 + projects, this can also be a list of variant names. These are + typically things like "Debug" or "Release", but really can be anything + you want. For Visual Studio 7 projects, they may also specify a target + platform separated from the variant name by a <literal>|</literal> + (vertical pipe) character: <literal>Debug|Xbox</literal>. The default + target platform is Win32. Multiple calls to &b-MSVSProject; with + different variants are allowed; all variants will be added to the + project file with their appropriate build targets and + sources.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>cmdargs</term> + + <listitem> + <para>Additional command line arguments for the different + variants. The number of <literal>cmdargs</literal> entries must match + the number of <literal>variant</literal> entries, or be empty (not + specified). If you give only one, it will automatically be propagated + to all variants.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>buildtarget</term> + + <listitem> + <para>An optional string, node, or list of strings or nodes (one + per build variant), to tell the Visual Studio debugger what output + target to use in what build variant. The number of + <literal>buildtarget</literal> entries must match the number of + <literal>variant</literal> entries.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>runfile</term> + + <listitem> + <para>The name of the file that Visual Studio 7 and later will + run and debug. This appears as the value of the + <literal>Output</literal> field in the resulting Visual Studio project + file. If this is not specified, the default is the same as the + specified <literal>buildtarget</literal> value.</para> + </listitem> + </varlistentry> + </variablelist><para> Note that because &SCons; always executes its build +commands from the directory in which the &SConstruct; file is located, if you +generate a project file in a different directory than the &SConstruct; +directory, users will not be able to double-click on the file name in +compilation error messages displayed in the Visual Studio console output +window. This can be remedied by adding the Visual C/C++ <literal>/FC</literal> +compiler option to the &cv-link-CCFLAGS; variable so that the compiler will +print the full path name of any files that cause compilation errors. </para> +<para> Example usage: </para> <example_commands>barsrcs = ['bar.cpp'], barincs = ['bar.h'], barlocalincs = ['StdAfx.h'] barresources = ['bar.rc','resource.h'] @@ -218,440 +145,374 @@ env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], buildtarget = dll, variant = 'Release') </example_commands> -</summary> -</builder> - -<builder name ="MSVSSolution"> -<summary> -<para> -Builds a Microsoft Visual Studio solution file. -</para> - -<para> -This builds a Visual Studio solution file, -based on the version of Visual Studio that is configured -(either the latest installed version, -or the version specified by -&cv-link-MSVS_VERSION; -in the construction environment). -For Visual Studio 6, it will generate a -<filename>.dsw</filename> -file. -For Visual Studio 7 (.NET), it will -generate a -<filename>.sln</filename> -file. -</para> - -<para> -The following values must be specified: -</para> - -<para> -<literal>target</literal>: -The name of the target .dsw or .sln file. The correct -suffix for the version of Visual Studio must be used, but the value -&cv-link-MSVSSOLUTIONSUFFIX; -will be defined to the correct value (see example below). -</para> - -<para> -<literal>variant</literal>: -The name of this particular variant, or a list of variant -names (the latter is only supported for MSVS 7 solutions). These are -typically things like "Debug" or "Release", but really can be anything -you want. For MSVS 7 they may also specify target platform, like this -"Debug|Xbox". Default platform is Win32. -</para> - -<para> -<literal>projects</literal>: -A list of project file names, or Project nodes returned by calls to the -&b-MSVSProject; -Builder, -to be placed into the solution file. -It should be noted that these file names are NOT added to the $SOURCES -environment variable in form of files, but rather as strings. This -is because they represent file names to be added to the solution file, -not the source files used to build the solution file. -</para> - -<para> -Example Usage: -</para> - -<example_commands> -env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], - projects = ['bar' + env['MSVSPROJECTSUFFIX']], - variant = 'Release') -</example_commands> -</summary> -</builder> - -<cvar name="MSVS"> -<summary> -<para> -When the Microsoft Visual Studio tools are initialized, they set up -this dictionary with the following keys: -</para> - -<para> -<envar>VERSION</envar>: -the version of MSVS being used (can be set via -&cv-link-MSVS_VERSION;) -</para> - -<para> -<envar>VERSIONS</envar>: -the available versions of MSVS installed -</para> - -<para> -<envar>VCINSTALLDIR</envar>: -installed directory of Visual C++ -</para> - -<para> -<envar>VSINSTALLDIR</envar>: -installed directory of Visual Studio -</para> - -<para> -<envar>FRAMEWORKDIR</envar>: -installed directory of the .NET framework -</para> - -<para> -<envar>FRAMEWORKVERSIONS</envar>: -list of installed versions of the .NET framework, sorted latest to oldest. -</para> - -<para> -<envar>FRAMEWORKVERSION</envar>: -latest installed version of the .NET framework -</para> - -<para> -<envar>FRAMEWORKSDKDIR</envar>: -installed location of the .NET SDK. -</para> - -<para> -<envar>PLATFORMSDKDIR</envar>: -installed location of the Platform SDK. -</para> - -<para> -<envar>PLATFORMSDK_MODULES</envar>: -dictionary of installed Platform SDK modules, -where the dictionary keys are keywords for the various modules, and -the values are 2-tuples where the first is the release date, and the -second is the version number. -</para> - -<para> -If a value isn't set, it wasn't available in the registry. -</para> -</summary> -</cvar> - -<cvar name="MSVS_ARCH"> -<summary> -<para> -Sets the architecture for which the generated project(s) should build. -</para> - -<para> -The default value is <literal>x86</literal>. -<literal>amd64</literal> is also supported -by &SCons; for some Visual Studio versions. -Trying to set &cv-MSVS_ARCH; to an architecture that's not -supported for a given Visual Studio version -will generate an error. -</para> -</summary> -</cvar> - -<cvar name="MSVS_PROJECT_GUID"> -<summary> -<para> -The string -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>ProjectGUID</literal> -attribute. -There is no default value. If not defined, a new GUID is generated. -</para> -</summary> -</cvar> - -<cvar name="MSVS_SCC_AUX_PATH"> -<summary> -<para> -The path name -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>SccAuxPath</literal> -attribute -if the -<envar>MSVS_SCC_PROVIDER</envar> -construction variable is also set. -There is no default value. -</para> -</summary> -</cvar> - -<cvar name="MSVS_SCC_CONNECTION_ROOT"> -<summary> -<para> -The root path of projects in your SCC workspace, i.e the path under which -all project and solution files will be generated. It is used as a -reference path from which the relative paths of the generated -Microsoft Visual Studio project and solution files are computed. -The relative project file path is placed as the value of the -<literal>SccLocalPath</literal> -attribute -of the project file -and as the values of the -<literal>SccProjectFilePathRelativizedFromConnection[i]</literal> -(where [i] ranges from 0 to the number of projects in the solution) -attributes of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -Similarly the relative solution file path is placed as the values of the -<literal>SccLocalPath[i]</literal> -(where [i] ranges from 0 to the number of projects in the solution) -attributes of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -This is used only -if the -<envar>MSVS_SCC_PROVIDER</envar> -construction variable is also set. -The default value is the current working directory. -</para> -</summary> -</cvar> - -<cvar name="MSVS_SCC_PROJECT_NAME"> -<summary> -<para> -The project name -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>SccProjectName</literal> -attribute -if the -<envar>MSVS_SCC_PROVIDER</envar> -construction variable is also set. -In this case the string is also placed in the -<literal>SccProjectName0</literal> -attribute of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -There is no default value. -</para> -</summary> -</cvar> - -<cvar name="MSVS_SCC_PROVIDER"> -<summary> -<para> -The string -placed in a generated Microsoft Visual Studio project file -as the value of the -<literal>SccProvider</literal> -attribute. -The string is also placed in the -<literal>SccProvider0</literal> -attribute of the -<literal>GlobalSection(SourceCodeControl)</literal> -section of the Microsoft Visual Studio solution file. -There is no default value. -</para> -</summary> -</cvar> - -<cvar name="MSVS_VERSION"> -<summary> -<para> -Sets the preferred version of Microsoft Visual Studio to use. -</para> - -<para> -If &cv-MSVS_VERSION; is not set, -&SCons; will (by default) select the latest version -of Visual Studio installed on your system. -So, if you have version 6 and version 7 (MSVS .NET) installed, -it will prefer version 7. -You can override this by -specifying the -<envar>MSVS_VERSION</envar> -variable in the Environment initialization, setting it to the -appropriate version ('6.0' or '7.0', for example). -If the specified version isn't installed, -tool initialization will fail. -</para> - -<para> -This is obsolete: use &cv-MSVC_VERSION; instead. If &cv-MSVS_VERSION; is set and -&cv-MSVC_VERSION; is not, &cv-MSVC_VERSION; will be set automatically to &cv-MSVS_VERSION;. -If both are set to different values, scons will raise an error. -</para> -</summary> -</cvar> - -<cvar name="MSVSBUILDCOM"> -<summary> -<para> -The build command line placed in -a generated Microsoft Visual Studio project file. -The default is to have Visual Studio invoke SCons with any specified -build targets. -</para> -</summary> -</cvar> - -<cvar name="MSVSCLEANCOM"> -<summary> -<para> -The clean command line placed in -a generated Microsoft Visual Studio project file. -The default is to have Visual Studio invoke SCons with the -c option -to remove any specified targets. -</para> -</summary> -</cvar> - -<cvar name="MSVSENCODING"> -<summary> -<para> -The encoding string placed in -a generated Microsoft Visual Studio project file. -The default is encoding -<literal>Windows-1252</literal>. -</para> -</summary> -</cvar> - -<cvar name="MSVSPROJECTCOM"> -<summary> -<para> -The action used to generate Microsoft Visual Studio project files. -</para> -</summary> -</cvar> - -<cvar name="MSVSPROJECTSUFFIX"> -<summary> -<para> -The suffix used for Microsoft Visual Studio project (DSP) files. -The default value is -<filename>.vcproj</filename> -when using Visual Studio version 7.x (.NET) -or later version, -and -<filename>.dsp</filename> -when using earlier versions of Visual Studio. -</para> -</summary> -</cvar> - -<cvar name="MSVSREBUILDCOM"> -<summary> -<para> -The rebuild command line placed in -a generated Microsoft Visual Studio project file. -The default is to have Visual Studio invoke SCons with any specified -rebuild targets. -</para> -</summary> -</cvar> - -<cvar name="MSVSSCONS"> -<summary> -<para> -The SCons used in generated Microsoft Visual Studio project files. -The default is the version of SCons being -used to generate the project file. -</para> -</summary> -</cvar> - -<cvar name="MSVSSCONSFLAGS"> -<summary> -<para> -The SCons flags used in generated Microsoft Visual Studio -project files. -</para> -</summary> -</cvar> - -<cvar name="MSVSSCONSCOM"> -<summary> -<para> -The default SCons command used in generated Microsoft Visual Studio -project files. -</para> -</summary> -</cvar> - -<cvar name="MSVSSCONSCRIPT"> -<summary> -<para> -The sconscript file -(that is, -&SConstruct; -or -&SConscript; -file) -that will be invoked by Visual Studio -project files -(through the -&cv-link-MSVSSCONSCOM; -variable). -The default is the same sconscript file -that contains the call to -&b-MSVSProject; -to build the project file. -</para> -</summary> -</cvar> - -<cvar name="MSVSSOLUTIONCOM"> -<summary> -<para> -The action used to generate Microsoft Visual Studio solution files. -</para> -</summary> -</cvar> - -<cvar name="MSVSSOLUTIONSUFFIX"> -<summary> -<para> -The suffix used for Microsoft Visual Studio solution (DSW) files. -The default value is -<filename>.sln</filename> -when using Visual Studio version 7.x (.NET), -and -<filename>.dsw</filename> -when using earlier versions of Visual Studio. -</para> -</summary> -</cvar> - -<cvar name="SCONS_HOME"> -<summary> -<para> -The (optional) path to the SCons library directory, -initialized from the external environment. -If set, this is used to construct a shorter and more -efficient search path in the -&cv-link-MSVSSCONS; -command line executed -from Microsoft Visual Studio project files. -</para> -</summary> -</cvar> - -</sconsdoc> +<para>Starting with version 2.4 of +SCons it's also possible to specify the optional argument +<parameter>DebugSettings</parameter>, which creates files for debugging under +Visual Studio:</para><variablelist> + <varlistentry> + <term>DebugSettings</term> + + <listitem> + <para>A dictionary of debug settings that get written to the + <filename>.vcproj.user</filename> or the + <filename>.vcxproj.user</filename> file, depending on the version + installed. As it is done for cmdargs (see above), you can specify a + <parameter>DebugSettings</parameter> dictionary per variant. If you + give only one, it will be propagated to all variants.</para> + </listitem> + </varlistentry> + </variablelist><para>Currently, only Visual Studio v9.0 and Visual Studio +version v11 are implemented, for other versions no file is generated. To +generate the user file, you just need to add a +<parameter>DebugSettings</parameter> dictionary to the environment with the +right parameters for your MSVS version. If the dictionary is empty, or does +not contain any good value, no file will be generated.</para><para>Following +is a more contrived example, involving the setup of a project for variants and +DebugSettings:</para><example_commands># Assuming you store your defaults in a file +vars = Variables('variables.py') +msvcver = vars.args.get('vc', '9') + +# Check command args to force one Microsoft Visual Studio version +if msvcver == '9' or msvcver == '11': + env = Environment(MSVC_VERSION=msvcver+'.0', MSVC_BATCH=False) +else: + env = Environment() + +AddOption('--userfile', action='store_true', dest='userfile', default=False, + help="Create Visual Studio Project user file") + +# +# 1. Configure your Debug Setting dictionary with options you want in the list +# of allowed options, for instance if you want to create a user file to launch +# a specific application for testing your dll with Microsoft Visual Studio 2008 (v9): +# +V9DebugSettings = { + 'Command':'c:\\myapp\\using\\thisdll.exe', + 'WorkingDirectory': 'c:\\myapp\\using\\', + 'CommandArguments': '-p password', +# 'Attach':'false', +# 'DebuggerType':'3', +# 'Remote':'1', +# 'RemoteMachine': None, +# 'RemoteCommand': None, +# 'HttpUrl': None, +# 'PDBPath': None, +# 'SQLDebugging': None, +# 'Environment': '', +# 'EnvironmentMerge':'true', +# 'DebuggerFlavor': None, +# 'MPIRunCommand': None, +# 'MPIRunArguments': None, +# 'MPIRunWorkingDirectory': None, +# 'ApplicationCommand': None, +# 'ApplicationArguments': None, +# 'ShimCommand': None, +# 'MPIAcceptMode': None, +# 'MPIAcceptFilter': None, +} + +# +# 2. Because there are a lot of different options depending on the Microsoft +# Visual Studio version, if you use more than one version you have to +# define a dictionary per version, for instance if you want to create a user +# file to launch a specific application for testing your dll with Microsoft +# Visual Studio 2012 (v11): +# +V10DebugSettings = { + 'LocalDebuggerCommand': 'c:\\myapp\\using\\thisdll.exe', + 'LocalDebuggerWorkingDirectory': 'c:\\myapp\\using\\', + 'LocalDebuggerCommandArguments': '-p password', +# 'LocalDebuggerEnvironment': None, +# 'DebuggerFlavor': 'WindowsLocalDebugger', +# 'LocalDebuggerAttach': None, +# 'LocalDebuggerDebuggerType': None, +# 'LocalDebuggerMergeEnvironment': None, +# 'LocalDebuggerSQLDebugging': None, +# 'RemoteDebuggerCommand': None, +# 'RemoteDebuggerCommandArguments': None, +# 'RemoteDebuggerWorkingDirectory': None, +# 'RemoteDebuggerServerName': None, +# 'RemoteDebuggerConnection': None, +# 'RemoteDebuggerDebuggerType': None, +# 'RemoteDebuggerAttach': None, +# 'RemoteDebuggerSQLDebugging': None, +# 'DeploymentDirectory': None, +# 'AdditionalFiles': None, +# 'RemoteDebuggerDeployDebugCppRuntime': None, +# 'WebBrowserDebuggerHttpUrl': None, +# 'WebBrowserDebuggerDebuggerType': None, +# 'WebServiceDebuggerHttpUrl': None, +# 'WebServiceDebuggerDebuggerType': None, +# 'WebServiceDebuggerSQLDebugging': None, +} + +# +# 3. Select the dictionary you want depending on the version of visual Studio +# Files you want to generate. +# +if not env.GetOption('userfile'): + dbgSettings = None +elif env.get('MSVC_VERSION', None) == '9.0': + dbgSettings = V9DebugSettings +elif env.get('MSVC_VERSION', None) == '11.0': + dbgSettings = V10DebugSettings +else: + dbgSettings = None + +# +# 4. Add the dictionary to the DebugSettings keyword. +# +barsrcs = ['bar.cpp', 'dllmain.cpp', 'stdafx.cpp'] +barincs = ['targetver.h'] +barlocalincs = ['StdAfx.h'] +barresources = ['bar.rc','resource.h'] +barmisc = ['ReadMe.txt'] + +dll = env.SharedLibrary(target = 'bar.dll', + source = barsrcs) + +env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], + srcs = barsrcs, + incs = barincs, + localincs = barlocalincs, + resources = barresources, + misc = barmisc, + buildtarget = [dll[0]] * 2, + variant = ('Debug|Win32', 'Release|Win32'), + cmdargs = 'vc=%s' % msvcver, + DebugSettings = (dbgSettings, {})) +</example_commands> </summary> </builder> <builder +name="MSVSSolution"> <summary> <para>Builds a Microsoft Visual Studio solution +file. </para> <para>This builds a Visual Studio solution file, based on the +version of Visual Studio that is configured (either the latest installed +version, or the version specified by &cv-link-MSVS_VERSION; in the +construction environment). For Visual Studio 6, it will generate a +<filename>.dsw</filename> file. For Visual Studio 7 (.NET), it will generate a +<filename>.sln</filename> file. </para> <para> The following values must be +specified: </para><variablelist> + <varlistentry> + <term>target</term> + + <listitem> + <para>The name of the target .dsw or .sln file. The correct + suffix for the version of Visual Studio must be used, but the value + &cv-link-MSVSSOLUTIONSUFFIX; will be defined to the correct value (see + example below).</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>variant</term> + + <listitem> + <para>The name of this particular variant, or a list of variant + names (the latter is only supported for MSVS 7 solutions). These are + typically things like "Debug" or "Release", but really can be anything + you want. For MSVS 7 they may also specify target platform, like this + "Debug|Xbox". Default platform is Win32.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>projects</term> + + <listitem> + <para>A list of project file names, or Project nodes returned by + calls to the &b-MSVSProject; Builder, to be placed into the solution + file. It should be noted that these file names are NOT added to the + $SOURCES environment variable in form of files, but rather as strings. + This is because they represent file names to be added to the solution + file, not the source files used to build the solution + file.</para> + </listitem> + </varlistentry> + </variablelist> <para> Example Usage: </para> <example_commands> +env.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'], projects = ['bar' ++ env['MSVSPROJECTSUFFIX']], variant = 'Release') +</example_commands></summary></builder> <cvar name="MSVS"> <summary> <para> +When the Microsoft Visual Studio tools are initialized, they set up this +dictionary with the following keys: </para><variablelist> + <varlistentry> + <term>VERSION</term> + + <listitem> + <para>the version of MSVS being used (can be set via + &cv-link-MSVS_VERSION;)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>VERSIONS</term> + + <listitem> + <para>the available versions of MSVS installed</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>VCINSTALLDIR</term> + + <listitem> + <para>installed directory of Visual C++</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>VSINSTALLDIR</term> + + <listitem> + <para>installed directory of Visual Studio</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKDIR</term> + + <listitem> + <para>installed directory of the .NET framework</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKVERSIONS</term> + + <listitem> + <para>list of installed versions of the .NET framework, sorted + latest to oldest.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKVERSION</term> + + <listitem> + <para>latest installed version of the .NET + framework</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>FRAMEWORKSDKDIR</term> + + <listitem> + <para>installed location of the .NET SDK.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>PLATFORMSDKDIR</term> + + <listitem> + <para>installed location of the Platform SDK.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>PLATFORMSDK_MODULES</term> + + <listitem> + <para>dictionary of installed Platform SDK modules, where the + dictionary keys are keywords for the various modules, and the values + are 2-tuples where the first is the release date, and the second is + the version number.</para> + </listitem> + </varlistentry> + </variablelist><para>If a value isn't set, it wasn't available in the +registry.</para></summary></cvar> <cvar name="MSVS_ARCH"> <summary> <para>Sets +the architecture for which the generated project(s) should build. </para> +<para>The default value is <literal>x86</literal>. <literal>amd64</literal> is +also supported by &SCons; for some Visual Studio versions. Trying to set +&cv-MSVS_ARCH; to an architecture that's not supported for a given Visual +Studio version will generate an error. </para> </summary> </cvar> <cvar +name="MSVS_PROJECT_GUID"> <summary> <para>The string placed in a generated +Microsoft Visual Studio project file as the value of the +<literal>ProjectGUID</literal> attribute. There is no default value. If not +defined, a new GUID is generated. </para> </summary> </cvar> <cvar +name="MSVS_SCC_AUX_PATH"> <summary> <para>The path name placed in a generated +Microsoft Visual Studio project file as the value of the +<literal>SccAuxPath</literal> attribute if the +<envar>MSVS_SCC_PROVIDER</envar> construction variable is also set. There is +no default value. </para> </summary> </cvar> <cvar +name="MSVS_SCC_CONNECTION_ROOT"> <summary> <para>The root path of projects in +your SCC workspace, i.e the path under which all project and solution files +will be generated. It is used as a reference path from which the relative +paths of the generated Microsoft Visual Studio project and solution files are +computed. The relative project file path is placed as the value of the +<literal>SccLocalPath</literal> attribute of the project file and as the +values of the +<literal>SccProjectFilePathRelativizedFromConnection[i]</literal> (where [i] +ranges from 0 to the number of projects in the solution) attributes of the +<literal>GlobalSection(SourceCodeControl)</literal> section of the Microsoft +Visual Studio solution file. Similarly the relative solution file path is +placed as the values of the <literal>SccLocalPath[i]</literal> (where [i] +ranges from 0 to the number of projects in the solution) attributes of the +<literal>GlobalSection(SourceCodeControl)</literal> section of the Microsoft +Visual Studio solution file. This is used only if the +<envar>MSVS_SCC_PROVIDER</envar> construction variable is also set. The +default value is the current working directory. </para> </summary> </cvar> +<cvar name="MSVS_SCC_PROJECT_NAME"> <summary> <para>The project name placed in +a generated Microsoft Visual Studio project file as the value of the +<literal>SccProjectName</literal> attribute if the +<envar>MSVS_SCC_PROVIDER</envar> construction variable is also set. In this +case the string is also placed in the <literal>SccProjectName0</literal> +attribute of the <literal>GlobalSection(SourceCodeControl)</literal> section +of the Microsoft Visual Studio solution file. There is no default value. +</para> </summary> </cvar> <cvar name="MSVS_SCC_PROVIDER"> <summary> <para>The +string placed in a generated Microsoft Visual Studio project file as the value +of the <literal>SccProvider</literal> attribute. The string is also placed in +the <literal>SccProvider0</literal> attribute of the +<literal>GlobalSection(SourceCodeControl)</literal> section of the Microsoft +Visual Studio solution file. There is no default value. </para> </summary> +</cvar> <cvar name="MSVS_VERSION"> <summary> <para>Sets the preferred version +of Microsoft Visual Studio to use. </para> <para>If &cv-MSVS_VERSION; is not +set, &SCons; will (by default) select the latest version of Visual Studio +installed on your system. So, if you have version 6 and version 7 (MSVS .NET) +installed, it will prefer version 7. You can override this by specifying the +<envar>MSVS_VERSION</envar> variable in the Environment initialization, +setting it to the appropriate version ('6.0' or '7.0', for example). If the +specified version isn't installed, tool initialization will fail. </para> +<para>This is obsolete: use &cv-MSVC_VERSION; instead. If &cv-MSVS_VERSION; is +set and &cv-MSVC_VERSION; is not, &cv-MSVC_VERSION; will be set automatically +to &cv-MSVS_VERSION;. If both are set to different values, scons will raise an +error. </para> </summary> </cvar> <cvar name="MSVSBUILDCOM"> <summary> +<para>The build command line placed in a generated Microsoft Visual Studio +project file. The default is to have Visual Studio invoke SCons with any +specified build targets. </para> </summary> </cvar> <cvar name="MSVSCLEANCOM"> +<summary> <para>The clean command line placed in a generated Microsoft Visual +Studio project file. The default is to have Visual Studio invoke SCons with +the -c option to remove any specified targets. </para> </summary> </cvar> +<cvar name="MSVSENCODING"> <summary> <para>The encoding string placed in a +generated Microsoft Visual Studio project file. The default is encoding +<literal>Windows-1252</literal>. </para> </summary> </cvar> <cvar +name="MSVSPROJECTCOM"> <summary> <para>The action used to generate Microsoft +Visual Studio project files. </para> </summary> </cvar> <cvar +name="MSVSPROJECTSUFFIX"> <summary> <para>The suffix used for Microsoft Visual +Studio project (DSP) files. The default value is <filename>.vcproj</filename> +when using Visual Studio version 7.x (.NET) or later version, and +<filename>.dsp</filename> when using earlier versions of Visual Studio. +</para> </summary> </cvar> <cvar name="MSVSREBUILDCOM"> <summary> <para>The +rebuild command line placed in a generated Microsoft Visual Studio project +file. The default is to have Visual Studio invoke SCons with any specified +rebuild targets. </para> </summary> </cvar> <cvar name="MSVSSCONS"> <summary> +<para>The SCons used in generated Microsoft Visual Studio project files. The +default is the version of SCons being used to generate the project file. +</para> </summary> </cvar> <cvar name="MSVSSCONSFLAGS"> <summary> <para>The +SCons flags used in generated Microsoft Visual Studio project files. </para> +</summary> </cvar> <cvar name="MSVSSCONSCOM"> <summary> <para>The default +SCons command used in generated Microsoft Visual Studio project files. </para> +</summary> </cvar> <cvar name="MSVSSCONSCRIPT"> <summary> <para>The sconscript +file (that is, &SConstruct; or &SConscript; file) that will be invoked by +Visual Studio project files (through the &cv-link-MSVSSCONSCOM; variable). The +default is the same sconscript file that contains the call to &b-MSVSProject; +to build the project file. </para> </summary> </cvar> <cvar +name="MSVSSOLUTIONCOM"> <summary> <para>The action used to generate Microsoft +Visual Studio solution files. </para> </summary> </cvar> <cvar +name="MSVSSOLUTIONSUFFIX"> <summary> <para>The suffix used for Microsoft +Visual Studio solution (DSW) files. The default value is +<filename>.sln</filename> when using Visual Studio version 7.x (.NET), and +<filename>.dsw</filename> when using earlier versions of Visual Studio. +</para> </summary> </cvar> <cvar name="SCONS_HOME"> <summary> <para>The +(optional) path to the SCons library directory, initialized from the external +environment. If set, this is used to construct a shorter and more efficient +search path in the &cv-link-MSVSSCONS; command line executed from Microsoft +Visual Studio project files. </para> </summary> </cvar></sconsdoc> diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py index 9334bac..2bd640f 100644 --- a/src/engine/SCons/Tool/msvsTests.py +++ b/src/engine/SCons/Tool/msvsTests.py @@ -41,6 +41,7 @@ from SCons.Tool.MSCommon.common import debug from SCons.Tool.MSCommon import get_default_version, \ query_versions +from SCons.Tool.msvs import _GenerateV6DSP, _GenerateV7DSP, _GenerateV10DSP regdata_6a = r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio] [HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0] @@ -592,6 +593,84 @@ class msvsTestCase(unittest.TestCase): assert not v1 or str(v1[0]) == self.highest_version, \ (v1, self.highest_version) assert len(v1) == self.number_of_versions, v1 + + def test_config_generation(self): + """Test _DSPGenerator.__init__(...)""" + if not self.highest_version : + return + + # Initialize 'static' variables + version_num, suite = msvs_parse_version(self.highest_version) + if version_num >= 10.0: + function_test = _GenerateV10DSP + elif version_num >= 7.0: + function_test = _GenerateV7DSP + else: + function_test = _GenerateV6DSP + + str_function_test = str(function_test.__init__) + dspfile = 'test.dsp' + source = 'test.cpp' + + # Create the cmdargs test list + list_variant = ['Debug|Win32','Release|Win32', + 'Debug|x64', 'Release|x64'] + list_cmdargs = ['debug=True target_arch=32', + 'debug=False target_arch=32', + 'debug=True target_arch=x64', + 'debug=False target_arch=x64'] + + # Tuple list : (parameter, dictionary of expected result per variant) + tests_cmdargs = [(None, dict.fromkeys(list_variant, '')), + ('', dict.fromkeys(list_variant, '')), + (list_cmdargs[0], dict.fromkeys(list_variant, list_cmdargs[0])), + (list_cmdargs, dict(zip(list_variant, list_cmdargs)))] + + # Run the test for each test case + for param_cmdargs, expected_cmdargs in tests_cmdargs: + debug('Testing %s. with :\n variant = %s \n cmdargs = "%s"' % \ + (str_function_test, list_variant, param_cmdargs)) + param_configs = [] + expected_configs = {} + for platform in ['Win32', 'x64']: + for variant in ['Debug', 'Release']: + variant_platform = '%s|%s' % (variant, platform) + runfile = '%s\\%s\\test.exe' % (platform, variant) + buildtarget = '%s\\%s\\test.exe' % (platform, variant) + outdir = '%s\\%s' % (platform, variant) + + # Create parameter list for this variant_platform + param_configs.append([variant_platform, runfile, buildtarget, outdir]) + + # Create expected dictionary result for this variant_platform + expected_configs[variant_platform] = \ + {'variant': variant, 'platform': platform, + 'runfile': runfile, + 'buildtarget': buildtarget, + 'outdir': outdir, + 'cmdargs': expected_cmdargs[variant_platform]} + + # Create parameter environment with final parameter dictionary + param_dict = dict(zip(('variant', 'runfile', 'buildtarget', 'outdir'), + [list(l) for l in zip(*param_configs)])) + param_dict['cmdargs'] = param_cmdargs + + # Hack to be able to run the test with a 'DummyEnv' + class _DummyEnv(DummyEnv): + def subst(self, string) : + return string + + env = _DummyEnv(param_dict) + env['MSVSSCONSCRIPT'] = '' + env['MSVS_VERSION'] = self.highest_version + + # Call function to test + genDSP = function_test(dspfile, source, env) + + # Check expected result + self.assertListEqual(genDSP.configs.keys(), expected_configs.keys()) + for key in genDSP.configs.keys(): + self.assertDictEqual(genDSP.configs[key].__dict__, expected_configs[key]) class msvs6aTestCase(msvsTestCase): """Test MSVS 6 Registry""" 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 345ec87..1a95abe 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -77,10 +77,9 @@ def Tag(env, target, source, *more_tags, **kw_tags): # differentiate between "normal" object attributes and the # packaging attributes. As the user should not be bothered with # that, the prefix will be added here if missing. - #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. @@ -175,7 +174,7 @@ def Package(env, target=None, source=None, **kw): args=[x for x in args if x not in kw] if len(args)==0: - raise # must be a different error, so reraise + raise # must be a different error, so re-raise elif len(args)==1: raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ % (args[0],packager.__name__) ) @@ -232,12 +231,12 @@ def options(opts): def copy_attr(f1, f2): """ copies the special packaging file attributes from f1 to 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 +261,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()) @@ -276,7 +275,7 @@ def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): return (target, new_source) def stripinstallbuilder(target, source, env): - """ strips the install builder action from the source list and stores + """ Strips the install builder action from the source list and stores the final installation location as the "PACKAGING_INSTALL_LOCATION" of the source of the source file. This effectively removes the final installed files from the source list while remembering the installation location. @@ -301,7 +300,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..c666033 100644 --- a/src/engine/SCons/Tool/packaging/ipk.py +++ b/src/engine/SCons/Tool/packaging/ipk.py @@ -35,7 +35,7 @@ from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, SUMMARY, X_IPK_PRIORITY, X_IPK_SECTION, SOURCE_URL, X_IPK_MAINTAINER, X_IPK_DEPENDS, **kw): - """ this function prepares the packageroot directory for packaging with the + """ This function prepares the packageroot directory for packaging with the ipkg builder. """ SCons.Tool.Tool('ipkg').generate(env) @@ -45,7 +45,7 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, target, source = stripinstallbuilder(target, source, env) target, source = putintopackageroot(target, source, env, PACKAGEROOT) - # This should be overridable from the construction environment, + # This should be overrideable from the construction environment, # which it is by using ARCHITECTURE=. # Guessing based on what os.uname() returns at least allows it # to work for both i386 and x86_64 Linux systems. @@ -61,7 +61,7 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, DESCRIPTION, if 'ARCHITECTURE' in kw: buildarchitecture = kw['ARCHITECTURE'] - # setup the kw to contain the mandatory arguments to this fucntion. + # setup the kw to contain the mandatory arguments to this function. # do this before calling any builder or setup function loc=locals() del loc['kw'] @@ -104,7 +104,7 @@ def gen_ipk_dir(proot, source, env, kw): return proot def build_specfiles(source, target, env): - """ filter the targets for the needed files and use the variables in env + """ Filter the targets for the needed files and use the variables in env to create the specfile. """ # @@ -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 49c28ca..c25f856 100644 --- a/src/engine/SCons/Tool/packaging/msi.py +++ b/src/engine/SCons/Tool/packaging/msi.py @@ -70,7 +70,7 @@ def convert_to_id(s, id_set): try: return id_set[id][s] except KeyError: - # no we did not so initialize with the id + # no we did not, so initialize with the id if id not in id_set: id_set[id] = { s : id } # there is a collision, generate an id which is unique by appending # the collision number @@ -79,7 +79,7 @@ def convert_to_id(s, id_set): return id_set[id][s] def is_dos_short_file_name(file): - """ examine if the given file is in the 8.3 form. + """ Examine if the given file is in the 8.3 form. """ fname, ext = os.path.splitext(file) proper_ext = len(ext) == 0 or (2 <= len(ext) <= 4) # the ext contains the dot @@ -88,7 +88,7 @@ def is_dos_short_file_name(file): return proper_ext and proper_fname def gen_dos_short_file_name(file, filename_set): - """ see http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 + """ See http://support.microsoft.com/default.aspx?scid=kb;en-us;Q142982 These are no complete 8.3 dos short names. The ~ char is missing and replaced with one character from the filename. WiX warns about such @@ -186,10 +186,10 @@ def string_wxsfile(target, source, env): return "building WiX file %s"%( target[0].path ) def build_wxsfile(target, source, env): - """ compiles a .wxs file from the keywords given in env['msi_spec'] and + """ 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 @@ -268,7 +268,7 @@ def create_default_directory_layout(root, NAME, VERSION, VENDOR, filename_set): # mandatory and optional file tags # def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, id_set): - """ builds the Component sections of the wxs file with their included files. + """ Builds the Component sections of the wxs file with their included files. Files need to be specified in 8.3 format and in the long name format, long filenames will be converted automatically. @@ -280,7 +280,7 @@ def build_wxsfile_file_section(root, files, NAME, VERSION, VENDOR, filename_set, factory = Document() def get_directory( node, dir ): - """ returns the node under the given node representing the directory. + """ Returns the node under the given node representing the directory. Returns the component node if dir is None or empty. """ @@ -415,7 +415,7 @@ def build_wxsfile_features_section(root, files, NAME, VERSION, SUMMARY, id_set): root.getElementsByTagName('Product')[0].childNodes.append(Feature) def build_wxsfile_default_gui(root): - """ this function adds a default GUI to the wxs file + """ This function adds a default GUI to the wxs file """ factory = Document() Product = root.getElementsByTagName('Product')[0] @@ -429,7 +429,7 @@ def build_wxsfile_default_gui(root): Product.childNodes.append(UIRef) def build_license_file(directory, spec): - """ creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" + """ Creates a License.rtf file with the content of "X_MSI_LICENSE_TEXT" in the given directory """ name, text = '', '' diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py index 9bd7dbb..a132555 100644 --- a/src/engine/SCons/Tool/packaging/rpm.py +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -72,7 +72,6 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, # if no "SOURCE_URL" tag is given add a default one. if 'SOURCE_URL' not in kw: - #kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') kw['SOURCE_URL']=(str(target[0])+".tar.gz").replace('.rpm', '') # mangle the source and target list for the rpmbuild @@ -86,26 +85,21 @@ def package(env, target, source, PACKAGEROOT, NAME, VERSION, def collectintargz(target, source, env): """ Puts all source files into a tar.gz file. """ - # the rpm tool depends on a source package, until this is chagned + # the rpm tool depends on a source package, until this is changed # this hack needs to be here that tries to pack all sources in. sources = env.FindSourceFiles() # filter out the target we are building the source list for. - #sources = [s for s in sources if not (s in target)] sources = [s for s in sources if s not in target] # find the .spec file for rpm and add it since it is not necessarily found # by the FindSourceFiles function. - #sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) - spec_file = lambda s: str(s).rfind('.spec') != -1 - sources.extend( list(filter(spec_file, source)) ) + sources.extend( [s for s in source if str(s).rfind('.spec')!=-1] ) # as the source contains the url of the source package this rpm package # is built from, we extract the target name - #tarball = (str(target[0])+".tar.gz").replace('.rpm', '') tarball = (str(target[0])+".tar.gz").replace('.rpm', '') try: - #tarball = env['SOURCE_URL'].split('/')[-1] tarball = env['SOURCE_URL'].split('/')[-1] except KeyError as e: raise SCons.Errors.UserError( "Missing PackageTag '%s' for RPM packager" % e.args[0] ) @@ -130,8 +124,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 +162,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', @@ -195,7 +188,7 @@ def build_specfile_sections(spec): return str def build_specfile_header(spec): - """ Builds all section but the %file of a rpm specfile + """ Builds all sections but the %file of a rpm specfile """ str = "" @@ -279,7 +272,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 +282,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 @@ -311,11 +306,10 @@ class SimpleTagCompiler(object): self.mandatory = mandatory def compile(self, values): - """ compiles the tagset and returns a str containing the result + """ Compiles the tagset and returns a str containing the result """ def is_international(tag): - #return tag.endswith('_') - return tag[-1:] == '_' + return tag.endswith('_') def get_country_code(tag): return tag[-2:] @@ -326,7 +320,6 @@ class SimpleTagCompiler(object): replacements = list(self.tagset.items()) str = "" - #domestic = [ (k,v) for k,v in replacements if not is_international(k) ] domestic = [t for t in replacements if not is_international(t[0])] for key, replacement in domestic: try: @@ -335,11 +328,9 @@ class SimpleTagCompiler(object): if self.mandatory: raise e - #international = [ (k,v) for k,v in replacements if is_international(k) ] international = [t for t in replacements if is_international(t[0])] for key, replacement in international: try: - #int_values_for_key = [ (get_country_code(k),v) for k,v in values.items() if strip_country_code(k) == key ] x = [t for t in values.items() if strip_country_code(t[0]) == key] int_values_for_key = [(get_country_code(t[0]),t[1]) for t in x] for v in int_values_for_key: diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index 723c39c..f01fff6 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -321,6 +321,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..ec076c8 100644 --- a/src/engine/SCons/Tool/sunar.py +++ b/src/engine/SCons/Tool/sunar.py @@ -2,7 +2,7 @@ Tool-specific initialization for Solaris (Forte) ar (library archive). If CC exists, static libraries should be built with it, so that template -instantians can be resolved. +instantiations can be resolved. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() @@ -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 af13392..39d9ec2 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 f669f83..9d1965d 100644 --- a/src/engine/SCons/Tool/swig.py +++ b/src/engine/SCons/Tool/swig.py @@ -39,9 +39,13 @@ import subprocess import SCons.Action import SCons.Defaults -import SCons.Scanner import SCons.Tool import SCons.Util +import SCons.Node + +verbose = False + +swigs = [ 'swig', 'swig3.0', 'swig2.0' ] SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') @@ -117,24 +121,33 @@ 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) -def _get_swig_version(env): +def _get_swig_version(env, swig): """Run the SWIG command line tool to get and return the version number""" - pipe = SCons.Action._subproc(env, [env['SWIG'], '-version'], + swig = env.subst(swig) + pipe = SCons.Action._subproc(env, SCons.Util.CLVar(swig) + ['-version'], stdin = 'devnull', stderr = 'devnull', stdout = subprocess.PIPE) if pipe.wait() != 0: return - out = SCons.Util.to_str (pipe.stdout.read()) - match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE) + # MAYBE: out = SCons.Util.to_str (pipe.stdout.read()) + out = pipe.stdout.read() + match = re.search(r'SWIG Version\s+(\S+).*', out, re.MULTILINE) if match: + if verbose: print "Version is:%s"%match.group(1) return match.group(1) + else: + if verbose: print "Unable to detect version: [%s]"%out def generate(env): """Add Builders and construction variables for swig to an Environment.""" @@ -155,8 +168,9 @@ def generate(env): java_file.add_action('.i', SwigAction) java_file.add_emitter('.i', _swigEmitter) - env['SWIG'] = 'swig' - env['SWIGVERSION'] = _get_swig_version(env) + if 'SWIG' not in env: + env['SWIG'] = env.Detect(swigs) or swigs[0] + env['SWIGVERSION'] = _get_swig_version(env, env['SWIG']) env['SWIGFLAGS'] = SCons.Util.CLVar('') env['SWIGDIRECTORSUFFIX'] = '_wrap.h' env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' @@ -168,11 +182,6 @@ def generate(env): env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' - expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' - scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) - - env.Append(SCANNERS = scanner) - def exists(env): swig = env.get('SWIG') or env.Detect(['swig']) return swig 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/tex.py b/src/engine/SCons/Tool/tex.py index 0bf3792..85bd41f 100644 --- a/src/engine/SCons/Tool/tex.py +++ b/src/engine/SCons/Tool/tex.py @@ -701,12 +701,6 @@ def tex_emitter_core(target, source, env, graphics_extensions): content = source[0].get_text_contents() - # These variables are no longer used. - #idx_exists = os.path.isfile(targetbase + '.idx') - #nlo_exists = os.path.isfile(targetbase + '.nlo') - #glo_exists = os.path.isfile(targetbase + '.glo') - #acr_exists = os.path.isfile(targetbase + '.acn') - # set up list with the regular expressions # we use to find features used file_tests_search = [auxfile_re, diff --git a/src/engine/SCons/Tool/textfile.py b/src/engine/SCons/Tool/textfile.py index 693b62b..e79badf 100644 --- a/src/engine/SCons/Tool/textfile.py +++ b/src/engine/SCons/Tool/textfile.py @@ -40,8 +40,8 @@ Textfile/Substfile builder for SCons. expanded (its keys are not expanded). If a value of SUBST_DICT is a python callable function, it is called and the result is expanded as the value. Values are substituted in a "random" order; if any - substitution could be further expanded by another subsitition, it - is unpredictible whether the expansion will occur. + substitution could be further expanded by another substitution, it + is unpredictable whether the expansion will occur. """ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" diff --git a/src/engine/SCons/Tool/xgettext.py b/src/engine/SCons/Tool/xgettext.py index 923fd17..ff45989 100644 --- a/src/engine/SCons/Tool/xgettext.py +++ b/src/engine/SCons/Tool/xgettext.py @@ -77,7 +77,7 @@ def _update_pot_file(target, source, env): nop = lambda target, source, env : 0 # Save scons cwd and os cwd (NOTE: they may be different. After the job, we - # revert ech one to its original state). + # revert each one to its original state). save_cwd = env.fs.getcwd() save_os_cwd = os.getcwd() chdir = target[0].dir @@ -135,7 +135,7 @@ def _update_pot_file(target, source, env): explain = "new file" if needs_update: # Print message employing SCons.Action.Action for that. - msg = "Writting " + repr(str(target[0])) + " (" + explain + ")" + msg = "Writing " + repr(str(target[0])) + " (" + explain + ")" env.Execute(SCons.Action.Action(nop, msg)) f = open(str(target[0]),"w") f.write(new_content) @@ -143,7 +143,7 @@ def _update_pot_file(target, source, env): return 0 else: # Print message employing SCons.Action.Action for that. - msg = "Not writting " + repr(str(target[0])) + " (" + explain + ")" + msg = "Not writing " + repr(str(target[0])) + " (" + explain + ")" env.Execute(SCons.Action.Action(nop, msg)) return 0 ############################################################################# 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/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index 5896cbe..648433b 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -61,7 +61,7 @@ def _yaccEmitter(target, source, env, ysuf, hsuf): base, ext = os.path.splitext(SCons.Util.to_String(source[0])) target.append(base + env.subst("$YACCVCGFILESUFFIX")) - # If -v is specirfied yacc will create the output debug file + # If -v is specified yacc will create the output debug file # which is not really source for any process, but should # be noted and also be cleaned # Bug #2558 @@ -118,14 +118,6 @@ def generate(env): env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' env['YACCHFILESUFFIX'] = '.h' - # Apparently, OS X now creates file.hpp like everybody else - # I have no idea when it changed; it was fixed in 10.4 - #if env['PLATFORM'] == 'darwin': - # # Bison on Mac OS X just appends ".h" to the generated target .cc - # # or .cpp file name. Hooray for delayed expansion of variables. - # env['YACCHXXFILESUFFIX'] = '${TARGET.suffix}.h' - #else: - # env['YACCHXXFILESUFFIX'] = '.hpp' env['YACCHXXFILESUFFIX'] = '.hpp' env['YACCVCGFILESUFFIX'] = '.vcg' diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 3a368cb..a66f92a 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -98,7 +98,7 @@ def splitext(path): def updrive(path): """ Make the drive letter (if any) upper case. - This is useful because Windows is inconsitent on the case + This is useful because Windows is inconsistent on the case of the drive letter, which can cause inconsistencies when calculating command signatures. """ @@ -176,7 +176,7 @@ class DisplayEngine(object): def set_mode(self, mode): self.print_it = mode -def render_tree(root, child_func, prune=0, margin=[0], visited={}): +def render_tree(root, child_func, prune=0, margin=[0], visited=None): """ Render a tree of nodes into an ASCII tree view. root - the root node of the tree @@ -190,6 +190,10 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}): rname = str(root) + # Initialize 'visited' dict, if required + if visited is None: + visited = {} + children = child_func(root) retval = "" for pipe in margin[:-1]: @@ -216,7 +220,7 @@ def render_tree(root, child_func, prune=0, margin=[0], visited={}): IDX = lambda N: N and 1 or 0 -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): +def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited=None): """ Print a tree of nodes. This is like render_tree, except it prints lines directly instead of creating a string representation in memory, @@ -234,6 +238,10 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): rname = str(root) + # Initialize 'visited' dict, if required + if visited is None: + visited = {} + if showtags: if showtags == 2: @@ -300,7 +308,7 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}): # often too slow. # We are using the following trick to speed up these -# functions. Default arguments are used to take a snapshot of the +# functions. Default arguments are used to take a snapshot of # the global functions and constants used by these functions. This # transforms accesses to global variable into local variables # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL). @@ -310,14 +318,14 @@ ListTypes = (list, UserList) SequenceTypes = (list, tuple, UserList) # Note that profiling data shows a speed-up when comparing -# explicitely with str and unicode instead of simply comparing +# explicitly with str and unicode instead of simply comparing # with basestring. (at least on Python 2.5.1) try: StringTypes = (str, unicode, UserString) except NameError: StringTypes = (str, UserString) -# Empirically, it is faster to check explicitely for str and +# Empirically, it is faster to check explicitly for str and # unicode than for basestring. try: BaseStringTypes = (str, unicode) @@ -341,11 +349,11 @@ def is_String(obj, isinstance=isinstance, StringTypes=StringTypes): def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes): # Profiling shows that there is an impressive speed-up of 2x - # when explicitely checking for strings instead of just not + # when explicitly checking for strings instead of just not # sequence when the argument (i.e. obj) is already a string. # But, if obj is a not string then it is twice as fast to # check only for 'not sequence'. The following code therefore - # assumes that the obj argument is a string must of the time. + # assumes that the obj argument is a string most of the time. return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes) def do_flatten(sequence, result, isinstance=isinstance, @@ -446,7 +454,7 @@ def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst, # # A special case is any object that has a __semi_deepcopy__() method, # which we invoke to create the copy. Currently only used by -# BuilderDict to actually prevent the copy operation (as invalid on that object) +# BuilderDict to actually prevent the copy operation (as invalid on that object). # # The dispatch table approach used here is a direct rip-off from the # normal Python copy module. @@ -586,6 +594,19 @@ except ImportError: pass RegError = _NoError +WinError = None +# Make sure we have a definition of WindowsError so we can +# run platform-independent tests of Windows functionality on +# platforms other than Windows. (WindowsError is, in fact, an +# OSError subclass on Windows.) +class PlainWindowsError(OSError): + pass +try: + WinError = WindowsError +except NameError: + WinError = PlainWindowsError + + if can_read_reg: HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE @@ -619,30 +640,16 @@ if can_read_reg: k = RegOpenKeyEx(root, keyp) return RegQueryValueEx(k,val) else: - try: - e = WindowsError - except NameError: - # Make sure we have a definition of WindowsError so we can - # run platform-independent tests of Windows functionality on - # platforms other than Windows. (WindowsError is, in fact, an - # OSError subclass on Windows.) - class WindowsError(OSError): - pass - import builtins - builtins.WindowsError = WindowsError - else: - del e - HKEY_CLASSES_ROOT = None HKEY_LOCAL_MACHINE = None HKEY_CURRENT_USER = None HKEY_USERS = None def RegGetValue(root, key): - raise WindowsError + raise WinError def RegOpenKeyEx(root, key): - raise WindowsError + raise WinError if sys.platform == 'win32': @@ -904,6 +911,28 @@ def AppendPath(oldpath, newpath, sep = os.pathsep, else: return sep.join(paths) +def AddPathIfNotExists(env_dict, key, path, sep=os.pathsep): + """This function will take 'key' out of the dictionary + 'env_dict', then add the path 'path' to that key if it is not + already there. This treats the value of env_dict[key] as if it + has a similar format to the PATH variable...a list of paths + separated by tokens. The 'path' will get added to the list if it + is not already there.""" + try: + is_list = 1 + paths = env_dict[key] + if not is_List(env_dict[key]): + paths = paths.split(sep) + is_list = 0 + if os.path.normcase(path) not in list(map(os.path.normcase, paths)): + paths = [ path ] + paths + if is_list: + env_dict[key] = paths + else: + env_dict[key] = sep.join(paths) + except KeyError: + env_dict[key] = path + if sys.platform == 'cygwin': def get_native_path(path): """Transforms an absolute path into a native path for the system. In @@ -1007,7 +1036,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: @@ -1171,38 +1200,40 @@ def uniquer_hashables(seq): return result +# Recipe 19.11 "Reading Lines with Continuation Characters", +# by Alex Martelli, straight from the Python CookBook (2nd edition). +def logical_lines(physical_lines, joiner=''.join): + logical_line = [] + for line in physical_lines: + stripped = line.rstrip() + if stripped.endswith('\\'): + # a line which continues w/the next physical line + logical_line.append(stripped[:-1]) + else: + # a line which does not continue, end of logical line + logical_line.append(line) + yield joiner(logical_line) + logical_line = [] + if logical_line: + # end of sequence implies end of last logical line + yield joiner(logical_line) + -# Much of the logic here was originally based on recipe 4.9 from the -# Python CookBook, but we had to dumb it way down for Python 1.5.2. class LogicalLines(object): + """ Wrapper class for the logical_lines method. + + Allows us to read all "logical" lines at once from a + given file object. + """ def __init__(self, fileobj): self.fileobj = fileobj - def readline(self): - result = [] - while True: - line = self.fileobj.readline() - if not line: - break - if line[-2:] == '\\\n': - result.append(line[:-2]) - else: - result.append(line) - break - return ''.join(result) - def readlines(self): - result = [] - while True: - line = self.readline() - if not line: - break - result.append(line) + result = [l for l in logical_lines(self.fileobj)] return result - class UniqueList(UserList): def __init__(self, seq = []): UserList.__init__(self, seq) diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index c52c910..1e56dc0 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -125,7 +125,9 @@ class UtilTestCase(unittest.TestCase): def tree_case_2(self, prune=1): """Fixture for the render_tree() and print_tree() tests.""" - stdlib_h = self.Node("stdlib.h") + types_h = self.Node('types.h') + malloc_h = self.Node('malloc.h') + stdlib_h = self.Node('stdlib.h', [types_h, malloc_h]) bar_h = self.Node('bar.h', [stdlib_h]) blat_h = self.Node('blat.h', [stdlib_h]) blat_c = self.Node('blat.c', [blat_h, bar_h]) @@ -136,13 +138,18 @@ class UtilTestCase(unittest.TestCase): +-blat.c +-blat.h | +-stdlib.h + | +-types.h + | +-malloc.h +-bar.h - +-[stdlib.h] """ - - if not prune: - expect = expect.replace('[', '') - expect = expect.replace(']', '') + if prune: + expect += """ +-[stdlib.h] +""" + else: + expect += """ +-stdlib.h + +-types.h + +-malloc.h +""" lines = expect.split('\n')[:-1] lines = ['[E BSPACN ]'+l for l in lines] @@ -163,6 +170,13 @@ class UtilTestCase(unittest.TestCase): actual = render_tree(node, get_children, 1) assert expect == actual, (expect, actual) + # Ensure that we can call render_tree on the same Node + # again. This wasn't possible in version 2.4.1 and earlier + # due to a bug in render_tree (visited was set to {} as default + # parameter) + actual = render_tree(node, get_children, 1) + assert expect == actual, (expect, actual) + def test_print_tree(self): """Test the print_tree() function""" def get_children(node): @@ -183,24 +197,39 @@ class UtilTestCase(unittest.TestCase): actual = sys.stdout.getvalue() assert withtags == actual, (withtags, actual) + # Test that explicitly setting prune to zero works + # the same as the default (see above) node, expect, withtags = self.tree_case_2(prune=0) sys.stdout = io.StringIO() + print_tree(node, get_children, 0) + actual = sys.stdout.getvalue() + assert expect == actual, (expect, actual) + + sys.stdout = io.StringIO() + print_tree(node, get_children, 0, showtags=1) + actual = sys.stdout.getvalue() + assert withtags == actual, (withtags, actual) + + # Test output with prune=1 + node, expect, withtags = self.tree_case_2(prune=1) + + sys.stdout = io.StringIO() print_tree(node, get_children, 1) actual = sys.stdout.getvalue() assert expect == actual, (expect, actual) + # Ensure that we can call print_tree on the same Node + # again. This wasn't possible in version 2.4.1 and earlier + # due to a bug in print_tree (visited was set to {} as default + # parameter) sys.stdout = io.StringIO() - # The following call should work here: - # print_tree(node, get_children, 1, showtags=1) - # For some reason I don't understand, though, *this* - # time that we call print_tree, the visited dictionary - # is still populated with the values from the last call! - # I can't see why this would be, short of a bug in Python, - # and rather than continue banging my head against the - # brick wall for a *test*, we're going to going with - # the cheap, easy workaround: - print_tree(node, get_children, 1, showtags=1, visited={}) + print_tree(node, get_children, 1) + actual = sys.stdout.getvalue() + assert expect == actual, (expect, actual) + + sys.stdout = io.StringIO() + print_tree(node, get_children, 1, showtags=1) actual = sys.stdout.getvalue() assert withtags == actual, (withtags, actual) finally: @@ -488,6 +517,30 @@ class UtilTestCase(unittest.TestCase): p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';') assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') + def test_addPathIfNotExists(self): + """Test the AddPathIfNotExists() function""" + env_dict = { 'FOO' : os.path.normpath('/foo/bar') + os.pathsep + \ + os.path.normpath('/baz/blat'), + 'BAR' : os.path.normpath('/foo/bar') + os.pathsep + \ + os.path.normpath('/baz/blat'), + 'BLAT' : [ os.path.normpath('/foo/bar'), + os.path.normpath('/baz/blat') ] } + AddPathIfNotExists(env_dict, 'FOO', os.path.normpath('/foo/bar')) + AddPathIfNotExists(env_dict, 'BAR', os.path.normpath('/bar/foo')) + AddPathIfNotExists(env_dict, 'BAZ', os.path.normpath('/foo/baz')) + AddPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/blat')) + AddPathIfNotExists(env_dict, 'BLAT', os.path.normpath('/baz/foo')) + + assert env_dict['FOO'] == os.path.normpath('/foo/bar') + os.pathsep + \ + os.path.normpath('/baz/blat'), env_dict['FOO'] + assert env_dict['BAR'] == os.path.normpath('/bar/foo') + os.pathsep + \ + os.path.normpath('/foo/bar') + os.pathsep + \ + os.path.normpath('/baz/blat'), env_dict['BAR'] + assert env_dict['BAZ'] == os.path.normpath('/foo/baz'), env_dict['BAZ'] + assert env_dict['BLAT'] == [ os.path.normpath('/baz/foo'), + os.path.normpath('/foo/bar'), + os.path.normpath('/baz/blat') ], env_dict['BLAT' ] + def test_CLVar(self): """Test the command-line construction variable class""" f = SCons.Util.CLVar('a b') @@ -592,10 +645,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'] @@ -679,12 +733,7 @@ bling \ bling \ bling bling """ - try: - fobj = io.StringIO(content) - except TypeError: - # Python 2.7 and beyond require unicode strings. - fobj = io.StringIO(u(content)) - + fobj = io.StringIO(unicode(content)) lines = LogicalLines(fobj).readlines() assert lines == [ '\n', @@ -696,7 +745,7 @@ bling def test_intern(self): s1 = silent_intern("spam") - # Python 3.x does not have a unicode() global function + # TODO: Python 3.x does not have a unicode() global function if sys.version[0] == '2': s2 = silent_intern(unicode("unicode spam")) s3 = silent_intern(42) @@ -780,6 +829,7 @@ class flattenTestCase(unittest.TestCase): result = flatten('xyz') assert result == ['xyz'], result + if __name__ == "__main__": suite = unittest.TestSuite() tclasses = [ dictifyTestCase, diff --git a/src/engine/SCons/Variables/BoolVariable.py b/src/engine/SCons/Variables/BoolVariable.py index 6bc66bb..c005a62 100644 --- a/src/engine/SCons/Variables/BoolVariable.py +++ b/src/engine/SCons/Variables/BoolVariable.py @@ -51,7 +51,7 @@ def _text2bool(val): will be returned. See '__true_strings' and '__false_strings' for values considered - 'true' or 'false respectivly. + 'true' or 'false respectively. This is usable as 'converter' for SCons' Variables. """ @@ -74,7 +74,7 @@ def _validator(key, val, env): def BoolVariable(key, help, default): """ - The input parameters describe a boolen option, thus they are + The input parameters describe a boolean option, thus they are returned with the correct converter and validator appended. The 'help' text will by appended by '(yes|no) to show the valid valued. The result is usable for input to opts.Add(). diff --git a/src/engine/SCons/Variables/EnumVariable.py b/src/engine/SCons/Variables/EnumVariable.py index 582be27..9448ba9 100644 --- a/src/engine/SCons/Variables/EnumVariable.py +++ b/src/engine/SCons/Variables/EnumVariable.py @@ -52,7 +52,7 @@ def _validator(key, val, env, vals): def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): """ - The input parameters describe a option with only certain values + The input parameters describe an option with only certain values allowed. They are returned with an appropriate converter and validator appended. The result is usable for input to Variables.Add(). @@ -65,7 +65,7 @@ def EnumVariable(key, help, default, allowed_values, map={}, ignorecase=0): for this option. The 'map'-dictionary may be used for converting the input value - into canonical values (eg. for aliases). + into canonical values (e.g. for aliases). 'ignorecase' defines the behaviour of the validator: diff --git a/src/engine/SCons/Variables/ListVariable.py b/src/engine/SCons/Variables/ListVariable.py index 413aacb..f2fdc18 100644 --- a/src/engine/SCons/Variables/ListVariable.py +++ b/src/engine/SCons/Variables/ListVariable.py @@ -4,7 +4,7 @@ This file defines the option type for SCons implementing 'lists'. A 'list' option may either be 'all', 'none' or a list of names separated by comma. After the option has been processed, the option -value holds either the named list elements, all list elemens or no +value holds either the named list elements, all list elements or no list elements at all. Usage example: @@ -48,7 +48,7 @@ Usage example: __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -# Know Bug: This should behave like a Set-Type, but does not really, +# Known Bug: This should behave like a Set-Type, but does not really, # since elements can occur twice. __all__ = ['ListVariable',] @@ -106,14 +106,14 @@ def _converter(val, allowedElems, mapdict): ## def _validator(key, val, env): ## """ ## """ -## # todo: write validater for pgk list +## # todo: write validator for pgk list ## return 1 def ListVariable(key, help, default, names, map={}): """ The input parameters describe a 'package list' option, thus they - are returned with the correct converter and validater appended. The + are returned with the correct converter and validator appended. The result is usable for input to opts.Add() . A 'package list' option may either be 'all', 'none' or a list of diff --git a/src/engine/SCons/Variables/PackageVariable.py b/src/engine/SCons/Variables/PackageVariable.py index 440d0f6..4ab0856 100644 --- a/src/engine/SCons/Variables/PackageVariable.py +++ b/src/engine/SCons/Variables/PackageVariable.py @@ -11,7 +11,7 @@ Usage example: Examples: x11=no (disables X11 support) x11=yes (will search for the package installation dir) - x11=/usr/local/X11 (will check this path for existance) + x11=/usr/local/X11 (will check this path for existence) To replace autoconf's --with-xxx=yyy @@ -70,10 +70,10 @@ def _converter(val): def _validator(key, val, env, searchfunc): - # NB: searchfunc is currenty undocumented and unsupported + # NB: searchfunc is currently undocumented and unsupported """ """ - # todo: write validator, check for path + # TODO write validator, check for path import os if env[key] is True: if searchfunc: @@ -84,14 +84,14 @@ def _validator(key, val, env, searchfunc): def PackageVariable(key, help, default, searchfunc=None): - # NB: searchfunc is currenty undocumented and unsupported + # NB: searchfunc is currently undocumented and unsupported """ The input parameters describe a 'package list' option, thus they are returned with the correct converter and validator appended. The result is usable for input to opts.Add() . A 'package list' option may either be 'all', 'none' or a list of - package names (seperated by space). + package names (separated by space). """ help = '\n '.join( (help, '( yes | no | /path/to/%s )' % key)) diff --git a/src/engine/SCons/Variables/PathVariable.py b/src/engine/SCons/Variables/PathVariable.py index 951fc75..e615a53 100644 --- a/src/engine/SCons/Variables/PathVariable.py +++ b/src/engine/SCons/Variables/PathVariable.py @@ -2,7 +2,7 @@ This file defines an option type for SCons implementing path settings. -To be used whenever a a user-specified path override should be allowed. +To be used whenever a user-specified path override should be allowed. Arguments to PathVariable are: option-name = name of this option on the command line (e.g. "prefix") @@ -22,7 +22,7 @@ Arguments to PathVariable are: is valid. The arguments to the validator function are: (key, val, env). The key is the name of the option, the val is the path specified for the option, - and the env is the env to which the Otions have been + and the env is the env to which the Options have been added. Usage example: @@ -102,7 +102,7 @@ class _PathVariableClass(object): os.makedirs(val) def PathIsFile(self, key, val, env): - """validator to check if Path is a file""" + """Validator to check if Path is a file""" if not os.path.isfile(val): if os.path.isdir(val): m = 'File path for option %s is a directory: %s' @@ -111,13 +111,12 @@ class _PathVariableClass(object): raise SCons.Errors.UserError(m % (key, val)) def PathExists(self, key, val, env): - """validator to check if Path exists""" + """Validator to check if Path exists""" if not os.path.exists(val): m = 'Path for option %s does not exist: %s' raise SCons.Errors.UserError(m % (key, val)) def __call__(self, key, help, default, validator=None): - # NB: searchfunc is currenty undocumented and unsupported """ The input parameters describe a 'path list' option, thus they are returned with the correct converter and validator appended. The diff --git a/src/engine/SCons/Variables/VariablesTests.py b/src/engine/SCons/Variables/VariablesTests.py index 3888d19..7f2a978 100644 --- a/src/engine/SCons/Variables/VariablesTests.py +++ b/src/engine/SCons/Variables/VariablesTests.py @@ -273,6 +273,22 @@ class VariablesTestCase(unittest.TestCase): opts.Update(env, {}) assert 'ANSWER' not in env + def test_noaggregation(self): + """Test that the 'files' and 'args' attributes of the Variables class + don't aggregate entries from one instance to another. + This used to be a bug in SCons version 2.4.1 and earlier. + """ + + opts = SCons.Variables.Variables() + opts.files.append('custom.py') + opts.args['ANSWER'] = 54 + nopts = SCons.Variables.Variables() + + # Ensure that both attributes are initialized to + # an empty list and dict, respectively. + assert len(nopts.files) == 0 + assert len(nopts.args) == 0 + def test_args(self): """Test updating an Environment with arguments overridden""" diff --git a/src/engine/SCons/Variables/__init__.py b/src/engine/SCons/Variables/__init__.py index 1c8d834..b03e5b5 100644 --- a/src/engine/SCons/Variables/__init__.py +++ b/src/engine/SCons/Variables/__init__.py @@ -50,12 +50,17 @@ class Variables(object): Holds all the options, updates the environment with the variables, and renders the help text. """ - def __init__(self, files=[], args={}, is_global=1): + def __init__(self, files=None, args=None, is_global=1): """ files - [optional] List of option configuration files to load (backward compatibility) If a single string is passed it is automatically placed in a file list """ + # initialize arguments + if files is None: + files = [] + if args is None: + args = {} self.options = [] self.args = args if not SCons.Util.is_List(files): diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py index 6f1a7ee..0ddbdd5 100644 --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -35,9 +35,6 @@ the builtins namespace or the global module list so that the rest of our code can use the objects and names imported here regardless of Python version. -Simply enough, things that go in the builtins name space come from -our _scons_builtins module. - The rest of the things here will be in individual compatibility modules that are either: 1) suitably modified copies of the future modules that we want to use; or 2) backwards compatible re-implementations of the @@ -85,149 +82,57 @@ def rename_module(new, old): except ImportError: return False +# TODO: FIXME +# In 3.x, 'pickle' automatically loads the fast version if available. +rename_module('pickle', 'cPickle') -rename_module('builtins', '__builtin__') -from . import _scons_builtins - - -try: - import hashlib -except ImportError: - # Pre-2.5 Python has no hashlib module. - try: - import_as('_scons_hashlib', 'hashlib') - except ImportError: - # If we failed importing our compatibility module, it probably - # means this version of Python has no md5 module. Don't do - # anything and let the higher layer discover this fact, so it - # can fall back to using timestamp. - pass - -try: - set -except NameError: - # Pre-2.4 Python has no native set type - import_as('_scons_sets', 'sets') - import builtins, sets - builtins.set = sets.Set - - -try: - import collections -except ImportError: - # Pre-2.4 Python has no collections module. - import_as('_scons_collections', 'collections') -else: - try: - collections.UserDict - except AttributeError: - exec('from UserDict import UserDict as _UserDict') - collections.UserDict = _UserDict - del _UserDict - try: - collections.UserList - except AttributeError: - exec('from UserList import UserList as _UserList') - collections.UserList = _UserList - del _UserList - try: - collections.UserString - except AttributeError: - exec('from UserString import UserString as _UserString') - collections.UserString = _UserString - del _UserString - - -try: - import io -except ImportError: - # Pre-2.6 Python has no io module. - import_as('_scons_io', 'io') - - -try: - os.devnull -except AttributeError: - # Pre-2.4 Python has no os.devnull attribute - _names = sys.builtin_module_names - if 'posix' in _names: - os.devnull = '/dev/null' - elif 'nt' in _names: - os.devnull = 'nul' - os.path.devnull = os.devnull -try: - os.path.lexists -except AttributeError: - # Pre-2.4 Python has no os.path.lexists function - def lexists(path): - return os.path.exists(path) or os.path.islink(path) - os.path.lexists = lexists - - -# When we're using the '-3' option during regression tests, importing -# cPickle gives a warning no matter how it's done, so always use the -# real profile module, whether it's fast or not. -if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is None: - # Not a regression test with '-3', so try to use faster version. - # In 3.x, 'pickle' automatically loads the fast version if available. - rename_module('pickle', 'cPickle') - - +# TODO: FIXME # In 3.x, 'profile' automatically loads the fast version if available. rename_module('profile', 'cProfile') - +# TODO: FIXME # Before Python 3.0, the 'queue' module was named 'Queue'. rename_module('queue', 'Queue') - +# TODO: FIXME # Before Python 3.0, the 'winreg' module was named '_winreg' rename_module('winreg', '_winreg') +# Python 3 moved builtin intern() to sys package +# To make porting easier, make intern always live +# in sys package (for python 2.7.x) try: - import subprocess -except ImportError: - # Pre-2.4 Python has no subprocess module. - import_as('_scons_subprocess', 'subprocess') + sys.intern +except AttributeError: + # We must be using python 2.7.x so monkey patch + # intern into the sys package + sys.intern = intern + +# Preparing for 3.x. UserDict, UserList, UserString are in +# collections for 3.x, but standalone in 2.7.x +import collections try: - sys.intern + collections.UserDict except AttributeError: - # Pre-2.6 Python has no sys.intern() function. - import builtins - try: - sys.intern = builtins.intern - except AttributeError: - # Pre-2.x Python has no builtin intern() function. - def intern(x): - return x - sys.intern = intern - del intern + exec('from UserDict import UserDict as _UserDict') + collections.UserDict = _UserDict + del _UserDict + try: - sys.maxsize + collections.UserList except AttributeError: - # Pre-2.6 Python has no sys.maxsize attribute - # Wrapping sys in () is silly, but protects it from 2to3 renames fixer - sys.maxsize = (sys).maxint - - -if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: - # We can't apply the 'callable' fixer until the floor is 2.6, but the - # '-3' option to Python 2.6 and 2.7 generates almost ten thousand - # warnings. This hack allows us to run regression tests with the '-3' - # option by replacing the callable() built-in function with a hack - # that performs the same function but doesn't generate the warning. - # Note that this hack is ONLY intended to be used for regression - # testing, and should NEVER be used for real runs. - from types import ClassType - def callable(obj): - if hasattr(obj, '__call__'): return True - if isinstance(obj, (ClassType, type)): return True - return False - import builtins - builtins.callable = callable - del callable + exec('from UserList import UserList as _UserList') + collections.UserList = _UserList + del _UserList + +try: + collections.UserString +except AttributeError: + exec('from UserString import UserString as _UserString') + collections.UserString = _UserString + del _UserString # Local Variables: diff --git a/src/engine/SCons/compat/_scons_builtins.py b/src/engine/SCons/compat/_scons_builtins.py deleted file mode 100644 index 6218cd1..0000000 --- a/src/engine/SCons/compat/_scons_builtins.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# __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. -# - -# Portions of the following are derived from the compat.py file in -# Twisted, under the following copyright: -# -# Copyright (c) 2001-2004 Twisted Matrix Laboratories - -__doc__ = """ -Compatibility idioms for builtins names - -This module adds names to the builtins module for things that we want -to use in SCons but which don't show up until later Python versions than -the earliest ones we support. - -This module checks for the following builtins names: - - all() - any() - memoryview() - -Implementations of functions are *NOT* guaranteed to be fully compliant -with these functions in later versions of Python. We are only concerned -with adding functionality that we actually use in SCons, so be wary -if you lift this code for other uses. (That said, making these more -nearly the same as later, official versions is still a desirable goal, -we just don't need to be obsessive about it.) - -If you're looking at this with pydoc and various names don't show up in -the FUNCTIONS or DATA output, that means those names are already built in -to this version of Python and we don't need to add them from this module. -""" - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -import builtins - -try: - all -except NameError: - # Pre-2.5 Python has no all() function. - def all(iterable): - """ - Returns True if all elements of the iterable are true. - """ - for element in iterable: - if not element: - return False - return True - builtins.all = all - all = all - -try: - any -except NameError: - # Pre-2.5 Python has no any() function. - def any(iterable): - """ - Returns True if any element of the iterable is true. - """ - for element in iterable: - if element: - return True - return False - builtins.any = any - any = any - -try: - memoryview -except NameError: - # Pre-2.7 doesn't have the memoryview() built-in. - class memoryview(object): - def __init__(self, obj): - # wrapping buffer in () keeps the fixer from changing it - self.obj = (buffer)(obj) - def __getitem__(self, indx): - if isinstance(indx, slice): - return self.obj[indx.start:indx.stop] - else: - return self.obj[indx] - builtins.memoryview = memoryview - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/compat/_scons_sets.py b/src/engine/SCons/compat/_scons_sets.py deleted file mode 100644 index 0fde994..0000000 --- a/src/engine/SCons/compat/_scons_sets.py +++ /dev/null @@ -1,563 +0,0 @@ -"""Classes to represent arbitrary sets (including sets of sets). - -This module implements sets using dictionaries whose values are -ignored. The usual operations (union, intersection, deletion, etc.) -are provided as both methods and operators. - -Important: sets are not sequences! While they support 'x in s', -'len(s)', and 'for x in s', none of those operations are unique for -sequences; for example, mappings support all three as well. The -characteristic operation for sequences is subscripting with small -integers: s[i], for i in range(len(s)). Sets don't support -subscripting at all. Also, sequences allow multiple occurrences and -their elements have a definite order; sets on the other hand don't -record multiple occurrences and don't remember the order of element -insertion (which is why they don't support s[i]). - -The following classes are provided: - -BaseSet -- All the operations common to both mutable and immutable - sets. This is an abstract class, not meant to be directly - instantiated. - -Set -- Mutable sets, subclass of BaseSet; not hashable. - -ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. - An iterable argument is mandatory to create an ImmutableSet. - -_TemporarilyImmutableSet -- A wrapper around a Set, hashable, - giving the same hash value as the immutable set equivalent - would have. Do not use this class directly. - -Only hashable objects can be added to a Set. In particular, you cannot -really add a Set as an element to another Set; if you try, what is -actually added is an ImmutableSet built from it (it compares equal to -the one you tried adding). - -When you ask if `x in y' where x is a Set and y is a Set or -ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and -what's tested is actually `z in y'. - -""" - -# Code history: -# -# - Greg V. Wilson wrote the first version, using a different approach -# to the mutable/immutable problem, and inheriting from dict. -# -# - Alex Martelli modified Greg's version to implement the current -# Set/ImmutableSet approach, and make the data an attribute. -# -# - Guido van Rossum rewrote much of the code, made some API changes, -# and cleaned up the docstrings. -# -# - Raymond Hettinger added a number of speedups and other -# improvements. - -# protect this import from the fixers... -exec('from itertools import ifilterfalse as filterfalse') - -__all__ = ['BaseSet', 'Set', 'ImmutableSet'] - -class BaseSet(object): - """Common base class for mutable and immutable sets.""" - - __slots__ = ['_data'] - - # Constructor - - def __init__(self): - """This is an abstract class.""" - # Don't call this from a concrete subclass! - if self.__class__ is BaseSet: - raise TypeError("BaseSet is an abstract class. " - "Use Set or ImmutableSet.") - - # Standard protocols: __len__, __repr__, __str__, __iter__ - - def __len__(self): - """Return the number of elements of a set.""" - return len(self._data) - - def __repr__(self): - """Return string representation of a set. - - This looks like 'Set([<list of elements>])'. - """ - return self._repr() - - # __str__ is the same as __repr__ - __str__ = __repr__ - - def _repr(self, sort_them=False): - elements = list(self._data.keys()) - if sort_them: - elements.sort() - return '%s(%r)' % (self.__class__.__name__, elements) - - def __iter__(self): - """Return an iterator over the elements or a set. - - This is the keys iterator for the underlying dict. - """ - # Wrapping name in () prevents fixer from "fixing" this - return (self._data.iterkeys)() - - # Three-way comparison is not supported. However, because __eq__ is - # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and - # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this - # case). - - def __cmp__(self, other): - raise TypeError("can't compare sets using cmp()") - - # Equality comparisons using the underlying dicts. Mixed-type comparisons - # are allowed here, where Set == z for non-Set z always returns False, - # and Set != z always True. This allows expressions like "x in y" to - # give the expected result when y is a sequence of mixed types, not - # raising a pointless TypeError just because y contains a Set, or x is - # a Set and y contain's a non-set ("in" invokes only __eq__). - # Subtle: it would be nicer if __eq__ and __ne__ could return - # NotImplemented instead of True or False. Then the other comparand - # would get a chance to determine the result, and if the other comparand - # also returned NotImplemented then it would fall back to object address - # comparison (which would always return False for __eq__ and always - # True for __ne__). However, that doesn't work, because this type - # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, - # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. - - def __eq__(self, other): - if isinstance(other, BaseSet): - return self._data == other._data - else: - return False - - def __ne__(self, other): - if isinstance(other, BaseSet): - return self._data != other._data - else: - return True - - # Copying operations - - def copy(self): - """Return a shallow copy of a set.""" - result = self.__class__() - result._data.update(self._data) - return result - - __copy__ = copy # For the copy module - - def __deepcopy__(self, memo): - """Return a deep copy of a set; used by copy module.""" - # This pre-creates the result and inserts it in the memo - # early, in case the deep copy recurses into another reference - # to this same set. A set can't be an element of itself, but - # it can certainly contain an object that has a reference to - # itself. - from copy import deepcopy - result = self.__class__() - memo[id(self)] = result - data = result._data - value = True - for elt in self: - data[deepcopy(elt, memo)] = value - return result - - # Standard set operations: union, intersection, both differences. - # Each has an operator version (e.g. __or__, invoked with |) and a - # method version (e.g. union). - # Subtle: Each pair requires distinct code so that the outcome is - # correct when the type of other isn't suitable. For example, if - # we did "union = __or__" instead, then Set().union(3) would return - # NotImplemented instead of raising TypeError (albeit that *why* it - # raises TypeError as-is is also a bit subtle). - - def __or__(self, other): - """Return the union of two sets as a new set. - - (I.e. all elements that are in either set.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.union(other) - - def union(self, other): - """Return the union of two sets as a new set. - - (I.e. all elements that are in either set.) - """ - result = self.__class__(self) - result._update(other) - return result - - def __and__(self, other): - """Return the intersection of two sets as a new set. - - (I.e. all elements that are in both sets.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.intersection(other) - - def intersection(self, other): - """Return the intersection of two sets as a new set. - - (I.e. all elements that are in both sets.) - """ - if not isinstance(other, BaseSet): - other = Set(other) - if len(self) <= len(other): - little, big = self, other - else: - little, big = other, self - common = iter(filter(big._data.has_key, little)) - return self.__class__(common) - - def __xor__(self, other): - """Return the symmetric difference of two sets as a new set. - - (I.e. all elements that are in exactly one of the sets.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.symmetric_difference(other) - - def symmetric_difference(self, other): - """Return the symmetric difference of two sets as a new set. - - (I.e. all elements that are in exactly one of the sets.) - """ - result = self.__class__() - data = result._data - value = True - selfdata = self._data - try: - otherdata = other._data - except AttributeError: - otherdata = Set(other)._data - for elt in filterfalse(otherdata.has_key, selfdata): - data[elt] = value - for elt in filterfalse(selfdata.has_key, otherdata): - data[elt] = value - return result - - def __sub__(self, other): - """Return the difference of two sets as a new Set. - - (I.e. all elements that are in this set and not in the other.) - """ - if not isinstance(other, BaseSet): - return NotImplemented - return self.difference(other) - - def difference(self, other): - """Return the difference of two sets as a new Set. - - (I.e. all elements that are in this set and not in the other.) - """ - result = self.__class__() - data = result._data - try: - otherdata = other._data - except AttributeError: - otherdata = Set(other)._data - value = True - for elt in filterfalse(otherdata.has_key, self): - data[elt] = value - return result - - # Membership test - - def __contains__(self, element): - """Report whether an element is a member of a set. - - (Called in response to the expression `element in self'.) - """ - try: - return element in self._data - except TypeError: - transform = getattr(element, "__as_temporarily_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - return transform() in self._data - - # Subset and superset test - - def issubset(self, other): - """Report whether another set contains this set.""" - self._binary_sanity_check(other) - if len(self) > len(other): # Fast check for obvious cases - return False - for elt in filterfalse(other._data.has_key, self): - return False - return True - - def issuperset(self, other): - """Report whether this set contains another set.""" - self._binary_sanity_check(other) - if len(self) < len(other): # Fast check for obvious cases - return False - for elt in filterfalse(self._data.has_key, other): - return False - return True - - # Inequality comparisons using the is-subset relation. - __le__ = issubset - __ge__ = issuperset - - def __lt__(self, other): - self._binary_sanity_check(other) - return len(self) < len(other) and self.issubset(other) - - def __gt__(self, other): - self._binary_sanity_check(other) - return len(self) > len(other) and self.issuperset(other) - - # Assorted helpers - - def _binary_sanity_check(self, other): - # Check that the other argument to a binary operation is also - # a set, raising a TypeError otherwise. - if not isinstance(other, BaseSet): - raise TypeError("Binary operation only permitted between sets") - - def _compute_hash(self): - # Calculate hash code for a set by xor'ing the hash codes of - # the elements. This ensures that the hash code does not depend - # on the order in which elements are added to the set. This is - # not called __hash__ because a BaseSet should not be hashable; - # only an ImmutableSet is hashable. - result = 0 - for elt in self: - result ^= hash(elt) - return result - - def _update(self, iterable): - # The main loop for update() and the subclass __init__() methods. - data = self._data - - # Use the fast update() method when a dictionary is available. - if isinstance(iterable, BaseSet): - data.update(iterable._data) - return - - value = True - - if type(iterable) in (list, tuple, xrange): - # Optimized: we know that __iter__() and next() can't - # raise TypeError, so we can move 'try:' out of the loop. - it = iter(iterable) - while True: - try: - for element in it: - data[element] = value - return - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - data[transform()] = value - else: - # Safe: only catch TypeError where intended - for element in iterable: - try: - data[element] = value - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - data[transform()] = value - - -class ImmutableSet(BaseSet): - """Immutable set class.""" - - __slots__ = ['_hashcode'] - - # BaseSet + hashing - - def __init__(self, iterable=None): - """Construct an immutable set from an optional iterable.""" - self._hashcode = None - self._data = {} - if iterable is not None: - self._update(iterable) - - def __hash__(self): - if self._hashcode is None: - self._hashcode = self._compute_hash() - return self._hashcode - - def __getstate__(self): - return self._data, self._hashcode - - def __setstate__(self, state): - self._data, self._hashcode = state - -class Set(BaseSet): - """ Mutable set class.""" - - __slots__ = [] - - # BaseSet + operations requiring mutability; no hashing - - def __init__(self, iterable=None): - """Construct a set from an optional iterable.""" - self._data = {} - if iterable is not None: - self._update(iterable) - - def __getstate__(self): - # getstate's results are ignored if it is not - return self._data, - - def __setstate__(self, data): - self._data, = data - - def __hash__(self): - """A Set cannot be hashed.""" - # We inherit object.__hash__, so we must deny this explicitly - raise TypeError("Can't hash a Set, only an ImmutableSet.") - - # In-place union, intersection, differences. - # Subtle: The xyz_update() functions deliberately return None, - # as do all mutating operations on built-in container types. - # The __xyz__ spellings have to return self, though. - - def __ior__(self, other): - """Update a set with the union of itself and another.""" - self._binary_sanity_check(other) - self._data.update(other._data) - return self - - def union_update(self, other): - """Update a set with the union of itself and another.""" - self._update(other) - - def __iand__(self, other): - """Update a set with the intersection of itself and another.""" - self._binary_sanity_check(other) - self._data = (self & other)._data - return self - - def intersection_update(self, other): - """Update a set with the intersection of itself and another.""" - if isinstance(other, BaseSet): - self &= other - else: - self._data = (self.intersection(other))._data - - def __ixor__(self, other): - """Update a set with the symmetric difference of itself and another.""" - self._binary_sanity_check(other) - self.symmetric_difference_update(other) - return self - - def symmetric_difference_update(self, other): - """Update a set with the symmetric difference of itself and another.""" - data = self._data - value = True - if not isinstance(other, BaseSet): - other = Set(other) - if self is other: - self.clear() - for elt in other: - if elt in data: - del data[elt] - else: - data[elt] = value - - def __isub__(self, other): - """Remove all elements of another set from this set.""" - self._binary_sanity_check(other) - self.difference_update(other) - return self - - def difference_update(self, other): - """Remove all elements of another set from this set.""" - data = self._data - if not isinstance(other, BaseSet): - other = Set(other) - if self is other: - self.clear() - for elt in filter(data.has_key, other): - del data[elt] - - # Python dict-like mass mutations: update, clear - - def update(self, iterable): - """Add all values from an iterable (such as a list or file).""" - self._update(iterable) - - def clear(self): - """Remove all elements from this set.""" - self._data.clear() - - # Single-element mutations: add, remove, discard - - def add(self, element): - """Add an element to a set. - - This has no effect if the element is already present. - """ - try: - self._data[element] = True - except TypeError: - transform = getattr(element, "__as_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - self._data[transform()] = True - - def remove(self, element): - """Remove an element from a set; it must be a member. - - If the element is not a member, raise a KeyError. - """ - try: - del self._data[element] - except TypeError: - transform = getattr(element, "__as_temporarily_immutable__", None) - if transform is None: - raise # re-raise the TypeError exception we caught - del self._data[transform()] - - def discard(self, element): - """Remove an element from a set if it is a member. - - If the element is not a member, do nothing. - """ - try: - self.remove(element) - except KeyError: - pass - - def pop(self): - """Remove and return an arbitrary set element.""" - return self._data.popitem()[0] - - def __as_immutable__(self): - # Return a copy of self as an immutable set - return ImmutableSet(self) - - def __as_temporarily_immutable__(self): - # Return self wrapped in a temporarily immutable set - return _TemporarilyImmutableSet(self) - - -class _TemporarilyImmutableSet(BaseSet): - # Wrap a mutable set as if it was temporarily immutable. - # This only supplies hashing and equality comparisons. - - def __init__(self, set): - self._set = set - self._data = set._data # Needed by ImmutableSet.__eq__() - - def __hash__(self): - return self._set._compute_hash() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/compat/_scons_subprocess.py b/src/engine/SCons/compat/_scons_subprocess.py deleted file mode 100644 index a58539d..0000000 --- a/src/engine/SCons/compat/_scons_subprocess.py +++ /dev/null @@ -1,1283 +0,0 @@ -# subprocess - Subprocesses with accessible I/O streams -# -# For more information about this module, see PEP 324. -# -# This module should remain compatible with Python 2.2, see PEP 291. -# -# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se> -# -# Licensed to PSF under a Contributor Agreement. -# See http://www.python.org/2.4/license for licensing details. - -r"""subprocess - Subprocesses with accessible I/O streams - -This module allows you to spawn processes, connect to their -input/output/error pipes, and obtain their return codes. This module -intends to replace several other, older modules and functions, like: - -os.system -os.spawn* -os.popen* -popen2.* -commands.* - -Information about how the subprocess module can be used to replace these -modules and functions can be found below. - - - -Using the subprocess module -=========================== -This module defines one class called Popen: - -class Popen(args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - - -Arguments are: - -args should be a string, or a sequence of program arguments. The -program to execute is normally the first item in the args sequence or -string, but can be explicitly set by using the executable argument. - -On UNIX, with shell=False (default): In this case, the Popen class -uses os.execvp() to execute the child program. args should normally -be a sequence. A string will be treated as a sequence with the string -as the only item (the program to execute). - -On UNIX, with shell=True: If args is a string, it specifies the -command string to execute through the shell. If args is a sequence, -the first item specifies the command string, and any additional items -will be treated as additional shell arguments. - -On Windows: the Popen class uses CreateProcess() to execute the child -program, which operates on strings. If args is a sequence, it will be -converted to a string using the list2cmdline method. Please note that -not all MS Windows applications interpret the command line the same -way: The list2cmdline is designed for applications using the same -rules as the MS C runtime. - -bufsize, if given, has the same meaning as the corresponding argument -to the built-in open() function: 0 means unbuffered, 1 means line -buffered, any other positive value means use a buffer of -(approximately) that size. A negative bufsize means to use the system -default, which usually means fully buffered. The default value for -bufsize is 0 (unbuffered). - -stdin, stdout and stderr specify the executed programs' standard -input, standard output and standard error file handles, respectively. -Valid values are PIPE, an existing file descriptor (a positive -integer), an existing file object, and None. PIPE indicates that a -new pipe to the child should be created. With None, no redirection -will occur; the child's file handles will be inherited from the -parent. Additionally, stderr can be STDOUT, which indicates that the -stderr data from the applications should be captured into the same -file handle as for stdout. - -If preexec_fn is set to a callable object, this object will be called -in the child process just before the child is executed. - -If close_fds is true, all file descriptors except 0, 1 and 2 will be -closed before the child process is executed. - -if shell is true, the specified command will be executed through the -shell. - -If cwd is not None, the current directory will be changed to cwd -before the child is executed. - -If env is not None, it defines the environment variables for the new -process. - -If universal_newlines is true, the file objects stdout and stderr are -opened as a text files, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Note: This feature is only -available if Python is built with universal newline support (the -default). Also, the newlines attribute of the file objects stdout, -stdin and stderr are not updated by the communicate() method. - -The startupinfo and creationflags, if given, will be passed to the -underlying CreateProcess() function. They can specify things such as -appearance of the main window and priority for the new process. -(Windows only) - - -This module also defines two shortcut functions: - -call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - -check_call(*popenargs, **kwargs): - Run command with arguments. Wait for command to complete. If the - exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - -Exceptions ----------- -Exceptions raised in the child process, before the new program has -started to execute, will be re-raised in the parent. Additionally, -the exception object will have one extra attribute called -'child_traceback', which is a string containing traceback information -from the childs point of view. - -The most common exception raised is OSError. This occurs, for -example, when trying to execute a non-existent file. Applications -should prepare for OSErrors. - -A ValueError will be raised if Popen is called with invalid arguments. - -check_call() will raise CalledProcessError, if the called process -returns a non-zero return code. - - -Security --------- -Unlike some other popen functions, this implementation will never call -/bin/sh implicitly. This means that all characters, including shell -metacharacters, can safely be passed to child processes. - - -Popen objects -============= -Instances of the Popen class have the following methods: - -poll() - Check if child process has terminated. Returns returncode - attribute. - -wait() - Wait for child process to terminate. Returns returncode attribute. - -communicate(input=None) - Interact with process: Send data to stdin. Read data from stdout - and stderr, until end-of-file is reached. Wait for process to - terminate. The optional stdin argument should be a string to be - sent to the child process, or None, if no data should be sent to - the child. - - communicate() returns a tuple (stdout, stderr). - - Note: The data read is buffered in memory, so do not use this - method if the data size is large or unlimited. - -The following attributes are also available: - -stdin - If the stdin argument is PIPE, this attribute is a file object - that provides input to the child process. Otherwise, it is None. - -stdout - If the stdout argument is PIPE, this attribute is a file object - that provides output from the child process. Otherwise, it is - None. - -stderr - If the stderr argument is PIPE, this attribute is file object that - provides error output from the child process. Otherwise, it is - None. - -pid - The process ID of the child process. - -returncode - The child return code. A None value indicates that the process - hasn't terminated yet. A negative value -N indicates that the - child was terminated by signal N (UNIX only). - - -Replacing older functions with the subprocess module -==================================================== -In this section, "a ==> b" means that b can be used as a replacement -for a. - -Note: All functions in this section fail (more or less) silently if -the executed program cannot be found; this module raises an OSError -exception. - -In the following examples, we assume that the subprocess module is -imported with "from subprocess import *". - - -Replacing /bin/sh shell backquote ---------------------------------- -output=`mycmd myarg` -==> -output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] - - -Replacing shell pipe line -------------------------- -output=`dmesg | grep hda` -==> -p1 = Popen(["dmesg"], stdout=PIPE) -p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) -output = p2.communicate()[0] - - -Replacing os.system() ---------------------- -sts = os.system("mycmd" + " myarg") -==> -p = Popen("mycmd" + " myarg", shell=True) -pid, sts = os.waitpid(p.pid, 0) - -Note: - -* Calling the program through the shell is usually not required. - -* It's easier to look at the returncode attribute than the - exitstatus. - -A more real-world example would look like this: - -try: - retcode = call("mycmd" + " myarg", shell=True) - if retcode < 0: - print >>sys.stderr, "Child was terminated by signal", -retcode - else: - print >>sys.stderr, "Child returned", retcode -except OSError, e: - print >>sys.stderr, "Execution failed:", e - - -Replacing os.spawn* -------------------- -P_NOWAIT example: - -pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") -==> -pid = Popen(["/bin/mycmd", "myarg"]).pid - - -P_WAIT example: - -retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") -==> -retcode = call(["/bin/mycmd", "myarg"]) - - -Vector example: - -os.spawnvp(os.P_NOWAIT, path, args) -==> -Popen([path] + args[1:]) - - -Environment example: - -os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) -==> -Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) - - -Replacing os.popen* -------------------- -pipe = os.popen(cmd, mode='r', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout - -pipe = os.popen(cmd, mode='w', bufsize) -==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin - - -(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdin, child_stdout) = (p.stdin, p.stdout) - - -(child_stdin, - child_stdout, - child_stderr) = os.popen3(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) -(child_stdin, - child_stdout, - child_stderr) = (p.stdin, p.stdout, p.stderr) - - -(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) -==> -p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) -(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) - - -Replacing popen2.* ------------------- -Note: If the cmd argument to popen2 functions is a string, the command -is executed through /bin/sh. If it is a list, the command is directly -executed. - -(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) -==> -p = Popen(["somestring"], shell=True, bufsize=bufsize - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - - -(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) -==> -p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) -(child_stdout, child_stdin) = (p.stdout, p.stdin) - -The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen, -except that: - -* subprocess.Popen raises an exception if the execution fails -* the capturestderr argument is replaced with the stderr argument. -* stdin=PIPE and stdout=PIPE must be specified. -* popen2 closes all filedescriptors by default, but you have to specify - close_fds=True with subprocess.Popen. - - -""" -from __future__ import print_function -from SCons.compat.six import integer_types - -import sys -mswindows = (sys.platform == "win32") - -import os -import types -import traceback - -# Exception classes used by this module. -class CalledProcessError(Exception): - """This exception is raised when a process run by check_call() returns - a non-zero exit status. The exit status will be stored in the - returncode attribute.""" - def __init__(self, returncode, cmd): - self.returncode = returncode - self.cmd = cmd - def __str__(self): - return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) - - -if mswindows: - try: - import threading - except ImportError: - # SCons: the threading module is only used by the communicate() - # method, which we don't actually use, so don't worry if we - # can't import it. - pass - import msvcrt - try: - # Try to get _subprocess - from _subprocess import * - class STARTUPINFO(object): - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - wShowWindow = 0 - class pywintypes(object): - error = IOError - except ImportError: - # If not there, then drop back to requiring pywin32 - # TODO: Should this be wrapped in try as well? To notify user to install - # pywin32 ? With URL to it? - import pywintypes - from win32api import GetStdHandle, STD_INPUT_HANDLE, \ - STD_OUTPUT_HANDLE, STD_ERROR_HANDLE - from win32api import GetCurrentProcess, DuplicateHandle, \ - GetModuleFileName, GetVersion - from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE - from win32pipe import CreatePipe - from win32process import CreateProcess, STARTUPINFO, \ - GetExitCodeProcess, STARTF_USESTDHANDLES, \ - STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE - from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 - - -else: - import select - import errno - import fcntl - import pickle - - try: - fcntl.F_GETFD - except AttributeError: - fcntl.F_GETFD = 1 - - try: - fcntl.F_SETFD - except AttributeError: - fcntl.F_SETFD = 2 - -__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] - -try: - MAXFD = os.sysconf("SC_OPEN_MAX") -except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts -except: - MAXFD = 256 - -try: - isinstance(1, int) -except TypeError: - def is_int(obj): - return isinstance(obj, type(1)) - def is_int_or_long(obj): - return type(obj) in (type(1), type(1)) -else: - def is_int(obj): - return isinstance(obj, int) - def is_int_or_long(obj): - return isinstance(obj, integer_types) - -try: - types.StringTypes -except AttributeError: - try: - types.StringTypes = (str, unicode) - except NameError: - types.StringTypes = (str,) -def is_string(obj): - return isinstance(obj, str) - -_active = [] - -def _cleanup(): - for inst in _active[:]: - if inst.poll(_deadstate=sys.maxsize) >= 0: - try: - _active.remove(inst) - except ValueError: - # This can happen if two threads create a new Popen instance. - # It's harmless that it was already removed, so ignore. - pass - -PIPE = -1 -STDOUT = -2 - - -def call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete, then - return the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - retcode = call(["ls", "-l"]) - """ - return apply(Popen, popenargs, kwargs).wait() - - -def check_call(*popenargs, **kwargs): - """Run command with arguments. Wait for command to complete. If - the exit code was zero then return, otherwise raise - CalledProcessError. The CalledProcessError object will have the - return code in the returncode attribute. - - The arguments are the same as for the Popen constructor. Example: - - check_call(["ls", "-l"]) - """ - retcode = call(*popenargs, **kwargs) - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - if retcode: - raise CalledProcessError(retcode, cmd) - return retcode - - -def list2cmdline(seq): - """ - Translate a sequence of arguments into a command line - string, using the same rules as the MS C runtime: - - 1) Arguments are delimited by white space, which is either a - space or a tab. - - 2) A string surrounded by double quotation marks is - interpreted as a single argument, regardless of white space - contained within. A quoted string can be embedded in an - argument. - - 3) A double quotation mark preceded by a backslash is - interpreted as a literal double quotation mark. - - 4) Backslashes are interpreted literally, unless they - immediately precede a double quotation mark. - - 5) If backslashes immediately precede a double quotation mark, - every pair of backslashes is interpreted as a literal - backslash. If the number of backslashes is odd, the last - backslash escapes the next double quotation mark as - described in rule 3. - """ - - # See - # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp - result = [] - needquote = False - for arg in seq: - bs_buf = [] - - # Add a space to separate this argument from the others - if result: - result.append(' ') - - needquote = (" " in arg) or ("\t" in arg) - if needquote: - result.append('"') - - for c in arg: - if c == '\\': - # Don't know if we need to double yet. - bs_buf.append(c) - elif c == '"': - # Double backspaces. - result.append('\\' * len(bs_buf)*2) - bs_buf = [] - result.append('\\"') - else: - # Normal char - if bs_buf: - result.extend(bs_buf) - bs_buf = [] - result.append(c) - - # Add remaining backspaces, if any. - if bs_buf: - result.extend(bs_buf) - - if needquote: - result.extend(bs_buf) - result.append('"') - - return ''.join(result) - -class Popen(object): - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - """Create new Popen instance.""" - _cleanup() - - self._child_created = False - if not is_int_or_long(bufsize): - raise TypeError("bufsize must be an integer") - - if mswindows: - if preexec_fn is not None: - raise ValueError("preexec_fn is not supported on Windows " - "platforms") - if close_fds: - raise ValueError("close_fds is not supported on Windows " - "platforms") - else: - # POSIX - if startupinfo is not None: - raise ValueError("startupinfo is only supported on Windows " - "platforms") - if creationflags != 0: - raise ValueError("creationflags is only supported on Windows " - "platforms") - - self.stdin = None - self.stdout = None - self.stderr = None - self.pid = None - self.returncode = None - self.universal_newlines = universal_newlines - - # Input and output objects. The general principle is like - # this: - # - # Parent Child - # ------ ----- - # p2cwrite ---stdin---> p2cread - # c2pread <--stdout--- c2pwrite - # errread <--stderr--- errwrite - # - # On POSIX, the child objects are file descriptors. On - # Windows, these are Windows file handles. The parent objects - # are file descriptors on both platforms. The parent objects - # are None when not using PIPEs. The child objects are None - # when not redirecting. - - (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) = self._get_handles(stdin, stdout, stderr) - - self._execute_child(args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - if p2cwrite: - self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) - if c2pread: - if universal_newlines: - self.stdout = os.fdopen(c2pread, 'rU', bufsize) - else: - self.stdout = os.fdopen(c2pread, 'rb', bufsize) - if errread: - if universal_newlines: - self.stderr = os.fdopen(errread, 'rU', bufsize) - else: - self.stderr = os.fdopen(errread, 'rb', bufsize) - - - def _translate_newlines(self, data): - data = data.replace("\r\n", "\n") - data = data.replace("\r", "\n") - return data - - - def __del__(self): - if not self._child_created: - # We didn't get to successfully create a child process. - return - # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxsize) - if self.returncode is None and _active is not None: - # Child is still running, keep us alive until we can wait on it. - _active.append(self) - - - def communicate(self, input=None): - """Interact with process: Send data to stdin. Read data from - stdout and stderr, until end-of-file is reached. Wait for - process to terminate. The optional input argument should be a - string to be sent to the child process, or None, if no data - should be sent to the child. - - communicate() returns a tuple (stdout, stderr).""" - - # Optimization: If we are only using one pipe, or no pipe at - # all, using select() or threads is unnecessary. - if [self.stdin, self.stdout, self.stderr].count(None) >= 2: - stdout = None - stderr = None - if self.stdin: - if input: - self.stdin.write(input) - self.stdin.close() - elif self.stdout: - stdout = self.stdout.read() - elif self.stderr: - stderr = self.stderr.read() - self.wait() - return (stdout, stderr) - - return self._communicate(input) - - - if mswindows: - # - # Windows methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - if stdin is None and stdout is None and stderr is None: - return (None, None, None, None, None, None) - - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - p2cread = GetStdHandle(STD_INPUT_HANDLE) - elif stdin == PIPE: - p2cread, p2cwrite = CreatePipe(None, 0) - # Detach and turn into fd - p2cwrite = p2cwrite.Detach() - p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) - elif is_int(stdin): - p2cread = msvcrt.get_osfhandle(stdin) - else: - # Assuming file-like object - p2cread = msvcrt.get_osfhandle(stdin.fileno()) - p2cread = self._make_inheritable(p2cread) - - if stdout is None: - c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) - elif stdout == PIPE: - c2pread, c2pwrite = CreatePipe(None, 0) - # Detach and turn into fd - c2pread = c2pread.Detach() - c2pread = msvcrt.open_osfhandle(c2pread, 0) - elif is_int(stdout): - c2pwrite = msvcrt.get_osfhandle(stdout) - else: - # Assuming file-like object - c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) - c2pwrite = self._make_inheritable(c2pwrite) - - if stderr is None: - errwrite = GetStdHandle(STD_ERROR_HANDLE) - elif stderr == PIPE: - errread, errwrite = CreatePipe(None, 0) - # Detach and turn into fd - errread = errread.Detach() - errread = msvcrt.open_osfhandle(errread, 0) - elif stderr == STDOUT: - errwrite = c2pwrite - elif is_int(stderr): - errwrite = msvcrt.get_osfhandle(stderr) - else: - # Assuming file-like object - errwrite = msvcrt.get_osfhandle(stderr.fileno()) - errwrite = self._make_inheritable(errwrite) - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _make_inheritable(self, handle): - """Return a duplicate of handle, which is inheritable""" - return DuplicateHandle(GetCurrentProcess(), handle, - GetCurrentProcess(), 0, 1, - DUPLICATE_SAME_ACCESS) - - - def _find_w9xpopen(self): - """Find and return absolut path to w9xpopen.exe""" - w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - # Eeek - file-not-found - possibly an embedding - # situation - see if we can locate it in sys.exec_prefix - w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), - "w9xpopen.exe") - if not os.path.exists(w9xpopen): - raise RuntimeError("Cannot locate w9xpopen.exe, which is " - "needed for Popen to work with your " - "shell or platform.") - return w9xpopen - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (MS Windows version)""" - - if not isinstance(args, types.StringTypes): - args = list2cmdline(args) - - # Process startup details - if startupinfo is None: - startupinfo = STARTUPINFO() - if None not in (p2cread, c2pwrite, errwrite): - startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESTDHANDLES - startupinfo.hStdInput = p2cread - startupinfo.hStdOutput = c2pwrite - startupinfo.hStdError = errwrite - - if shell: - startupinfo.dwFlags = startupinfo.dwFlags | STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_HIDE - comspec = os.environ.get("COMSPEC", "cmd.exe") - args = comspec + " /c " + args - if (GetVersion() >= 0x80000000 or - os.path.basename(comspec).lower() == "command.com"): - # Win9x, or using command.com on NT. We need to - # use the w9xpopen intermediate program. For more - # information, see KB Q150956 - # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp) - w9xpopen = self._find_w9xpopen() - args = '"%s" %s' % (w9xpopen, args) - # Not passing CREATE_NEW_CONSOLE has been known to - # cause random failures on win9x. Specifically a - # dialog: "Your program accessed mem currently in - # use at xxx" and a hopeful warning about the - # stability of your system. Cost is Ctrl+C wont - # kill children. - creationflags = creationflags | CREATE_NEW_CONSOLE - - # Start the process - try: - hp, ht, pid, tid = CreateProcess(executable, args, - # no special security - None, None, - # must inherit handles to pass std - # handles - 1, - creationflags, - env, - cwd, - startupinfo) - except pywintypes.error as e: - # Translate pywintypes.error to WindowsError, which is - # a subclass of OSError. FIXME: We should really - # translate errno using _sys_errlist (or simliar), but - # how can this be done from Python? - raise WindowsError(*e.args) - - # Retain the process handle, but close the thread handle - self._child_created = True - self._handle = hp - self.pid = pid - ht.Close() - - # Child is launched. Close the parent's copy of those pipe - # handles that only the child should have open. You need - # to make sure that no handles to the write end of the - # output pipe are maintained in this process or else the - # pipe will not close when the child process exits and the - # ReadFile will hang. - if p2cread is not None: - p2cread.Close() - if c2pwrite is not None: - c2pwrite.Close() - if errwrite is not None: - errwrite.Close() - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - obj = WaitForSingleObject(self._handle, INFINITE) - self.returncode = GetExitCodeProcess(self._handle) - return self.returncode - - - def _readerthread(self, fh, buffer): - buffer.append(fh.read()) - - - def _communicate(self, input): - stdout = None # Return - stderr = None # Return - - if self.stdout: - stdout = [] - stdout_thread = threading.Thread(target=self._readerthread, - args=(self.stdout, stdout)) - stdout_thread.setDaemon(True) - stdout_thread.start() - if self.stderr: - stderr = [] - stderr_thread = threading.Thread(target=self._readerthread, - args=(self.stderr, stderr)) - stderr_thread.setDaemon(True) - stderr_thread.start() - - if self.stdin: - if input is not None: - self.stdin.write(input) - self.stdin.close() - - if self.stdout: - stdout_thread.join() - if self.stderr: - stderr_thread.join() - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = stdout[0] - if stderr is not None: - stderr = stderr[0] - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - else: - # - # POSIX methods - # - def _get_handles(self, stdin, stdout, stderr): - """Construct and return tupel with IO objects: - p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite - """ - p2cread, p2cwrite = None, None - c2pread, c2pwrite = None, None - errread, errwrite = None, None - - if stdin is None: - pass - elif stdin == PIPE: - p2cread, p2cwrite = os.pipe() - elif is_int(stdin): - p2cread = stdin - else: - # Assuming file-like object - p2cread = stdin.fileno() - - if stdout is None: - pass - elif stdout == PIPE: - c2pread, c2pwrite = os.pipe() - elif is_int(stdout): - c2pwrite = stdout - else: - # Assuming file-like object - c2pwrite = stdout.fileno() - - if stderr is None: - pass - elif stderr == PIPE: - errread, errwrite = os.pipe() - elif stderr == STDOUT: - errwrite = c2pwrite - elif is_int(stderr): - errwrite = stderr - else: - # Assuming file-like object - errwrite = stderr.fileno() - - return (p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite) - - - def _set_cloexec_flag(self, fd): - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - - old = fcntl.fcntl(fd, fcntl.F_GETFD) - fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) - - - def _close_fds(self, but): - for i in range(3, MAXFD): - if i == but: - continue - try: - os.close(i) - except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts - except: - pass - - - def _execute_child(self, args, executable, preexec_fn, close_fds, - cwd, env, universal_newlines, - startupinfo, creationflags, shell, - p2cread, p2cwrite, - c2pread, c2pwrite, - errread, errwrite): - """Execute program (POSIX version)""" - - if is_string(args): - args = [args] - - if shell: - args = ["/bin/sh", "-c"] + args - - if executable is None: - executable = args[0] - - # For transferring possible exec failure from child to parent - # The first char specifies the exception type: 0 means - # OSError, 1 means some other error. - errpipe_read, errpipe_write = os.pipe() - self._set_cloexec_flag(errpipe_write) - - self.pid = os.fork() - self._child_created = True - if self.pid == 0: - # Child - try: - # Close parent's pipe ends - if p2cwrite: - os.close(p2cwrite) - if c2pread: - os.close(c2pread) - if errread: - os.close(errread) - os.close(errpipe_read) - - # Dup fds for child - if p2cread: - os.dup2(p2cread, 0) - if c2pwrite: - os.dup2(c2pwrite, 1) - if errwrite: - os.dup2(errwrite, 2) - - # Close pipe fds. Make sure we don't close the same - # fd more than once, or standard fds. - try: - set - except NameError: - # Fall-back for earlier Python versions, so epydoc - # can use this module directly to execute things. - if p2cread: - os.close(p2cread) - if c2pwrite and c2pwrite not in (p2cread,): - os.close(c2pwrite) - if errwrite and errwrite not in (p2cread, c2pwrite): - os.close(errwrite) - else: - for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)): - if fd: os.close(fd) - - # Close all other fds, if asked for - if close_fds: - self._close_fds(but=errpipe_write) - - if cwd is not None: - os.chdir(cwd) - - if preexec_fn: - apply(preexec_fn) - - if env is None: - os.execvp(executable, args) - else: - os.execvpe(executable, args, env) - - except KeyboardInterrupt: - raise # SCons: don't swallow keyboard interrupts - - except: - exc_type, exc_value, tb = sys.exc_info() - # Save the traceback and attach it to the exception object - exc_lines = traceback.format_exception(exc_type, - exc_value, - tb) - exc_value.child_traceback = ''.join(exc_lines) - os.write(errpipe_write, pickle.dumps(exc_value)) - - # This exitcode won't be reported to applications, so it - # really doesn't matter what we return. - os._exit(255) - - # Parent - os.close(errpipe_write) - if p2cread and p2cwrite: - os.close(p2cread) - if c2pwrite and c2pread: - os.close(c2pwrite) - if errwrite and errread: - os.close(errwrite) - - # Wait for exec to fail or succeed; possibly raising exception - data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB - os.close(errpipe_read) - if data != "": - os.waitpid(self.pid, 0) - child_exception = pickle.loads(data) - raise child_exception - - - def _handle_exitstatus(self, sts): - if os.WIFSIGNALED(sts): - self.returncode = -os.WTERMSIG(sts) - elif os.WIFEXITED(sts): - self.returncode = os.WEXITSTATUS(sts) - else: - # Should never happen - raise RuntimeError("Unknown child exit status!") - - - def poll(self, _deadstate=None): - """Check if child process has terminated. Returns returncode - attribute.""" - if self.returncode is None: - try: - pid, sts = os.waitpid(self.pid, os.WNOHANG) - if pid == self.pid: - self._handle_exitstatus(sts) - except os.error: - if _deadstate is not None: - self.returncode = _deadstate - return self.returncode - - - def wait(self): - """Wait for child process to terminate. Returns returncode - attribute.""" - if self.returncode is None: - pid, sts = os.waitpid(self.pid, 0) - self._handle_exitstatus(sts) - return self.returncode - - - def _communicate(self, input): - read_set = [] - write_set = [] - stdout = None # Return - stderr = None # Return - - if self.stdin: - # Flush stdio buffer. This might block, if the user has - # been writing to .stdin in an uncontrolled fashion. - self.stdin.flush() - if input: - write_set.append(self.stdin) - else: - self.stdin.close() - if self.stdout: - read_set.append(self.stdout) - stdout = [] - if self.stderr: - read_set.append(self.stderr) - stderr = [] - - input_offset = 0 - while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) - - if self.stdin in wlist: - # When select has indicated that the file is writable, - # we can write up to PIPE_BUF bytes without risk - # blocking. POSIX defines PIPE_BUF >= 512 - m = memoryview(input)[input_offset:input_offset+512] - bytes_written = os.write(self.stdin.fileno(), m) - input_offset = input_offset + bytes_written - if input_offset >= len(input): - self.stdin.close() - write_set.remove(self.stdin) - - if self.stdout in rlist: - data = os.read(self.stdout.fileno(), 1024) - if data == "": - self.stdout.close() - read_set.remove(self.stdout) - stdout.append(data) - - if self.stderr in rlist: - data = os.read(self.stderr.fileno(), 1024) - if data == "": - self.stderr.close() - read_set.remove(self.stderr) - stderr.append(data) - - # All data exchanged. Translate lists into strings. - if stdout is not None: - stdout = ''.join(stdout) - if stderr is not None: - stderr = ''.join(stderr) - - # Translate newlines, if requested. We cannot let the file - # object do the translation: It is based on stdio, which is - # impossible to combine with select (unless forcing no - # buffering). - if self.universal_newlines and hasattr(file, 'newlines'): - if stdout: - stdout = self._translate_newlines(stdout) - if stderr: - stderr = self._translate_newlines(stderr) - - self.wait() - return (stdout, stderr) - - -def _demo_posix(): - # - # Example 1: Simple redirection: Get process list - # - plist = Popen(["ps"], stdout=PIPE).communicate()[0] - print("Process list:") - print(plist) - - # - # Example 2: Change uid before executing child - # - if os.getuid() == 0: - p = Popen(["id"], preexec_fn=lambda: os.setuid(100)) - p.wait() - - # - # Example 3: Connecting several subprocesses - # - print("Looking for 'hda'...") - p1 = Popen(["dmesg"], stdout=PIPE) - p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) - print(repr(p2.communicate()[0])) - - # - # Example 4: Catch execution error - # - print() - print("Trying a weird file...") - try: - print(Popen(["/this/path/does/not/exist"]).communicate()) - except OSError as e: - if e.errno == errno.ENOENT: - print("The file didn't exist. I thought so...") - print("Child traceback:") - print(e.child_traceback) - else: - print("Error", e.errno) - else: - sys.stderr.write( "Gosh. No error.\n" ) - - -def _demo_windows(): - # - # Example 1: Connecting several subprocesses - # - print("Looking for 'PROMPT' in set output...") - p1 = Popen("set", stdout=PIPE, shell=True) - p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE) - print(repr(p2.communicate()[0])) - - # - # Example 2: Simple execution of program - # - print("Executing calc...") - p = Popen("calc") - p.wait() - - -if __name__ == "__main__": - if mswindows: - _demo_windows() - else: - _demo_posix() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/cppTests.py b/src/engine/SCons/cppTests.py index 37b4aae..9f4b875 100644 --- a/src/engine/SCons/cppTests.py +++ b/src/engine/SCons/cppTests.py @@ -668,15 +668,12 @@ class fileTestCase(unittest.TestCase): _Cleanup.remove(self.tempdir) def strip_initial_spaces(self, s): - #lines = s.split('\n') lines = s.split('\n') spaces = re.match(' *', lines[0]).group(0) def strip_spaces(l, spaces=spaces): - #if l.startswith(spaces): if l[:len(spaces)] == spaces: l = l[len(spaces):] return l - #return '\n'.join([ strip_spaces(l) for l in lines ]) return '\n'.join(map(strip_spaces, lines)) def write(self, file, contents): diff --git a/src/engine/SCons/dblite.py b/src/engine/SCons/dblite.py index dbbd65d..3be6cb2 100644 --- a/src/engine/SCons/dblite.py +++ b/src/engine/SCons/dblite.py @@ -4,7 +4,6 @@ from __future__ import print_function import SCons.compat -import builtins import os # compat layer imports "cPickle" for us if it's available. import pickle @@ -48,7 +47,7 @@ class dblite(object): # See the discussion at: # http://mail.python.org/pipermail/python-bugs-list/2003-March/016877.html - _open = builtins.open + _open = open _pickle_dump = staticmethod(pickle.dump) _os_chmod = os.chmod try: @@ -103,7 +102,10 @@ class dblite(object): if (len(p) > 0): try: self._dict = pickle.loads(p) - except (pickle.UnpicklingError, EOFError): + except (pickle.UnpicklingError, EOFError, KeyError): + # Note how we catch KeyErrors too here, which might happen + # when we don't have cPickle available (default pickle + # throws it). if (ignore_corrupt_dbfiles == 0): raise if (ignore_corrupt_dbfiles == 1): corruption_warning(self._file_name) diff --git a/src/script/scons-time.py b/src/script/scons-time.py index 9737dfe..ebdaf08 100644 --- a/src/script/scons-time.py +++ b/src/script/scons-time.py @@ -42,53 +42,10 @@ import sys import tempfile import time -try: - sorted -except NameError: - # Pre-2.4 Python has no sorted() function. - # - # The pre-2.4 Python list.sort() method does not support - # list.sort(key=) nor list.sort(reverse=) keyword arguments, so - # we must implement the functionality of those keyword arguments - # by hand instead of passing them to list.sort(). - def sorted(iterable, cmp=None, key=None, reverse=False): - if key is not None: - result = [(key(x), x) for x in iterable] - else: - result = iterable[:] - if cmp is None: - # Pre-2.3 Python does not support list.sort(None). - result.sort() - else: - result.sort(cmp) - if key is not None: - result = [t1 for t0,t1 in result] - if reverse: - result.reverse() - return result - -if os.environ.get('SCONS_HORRIBLE_REGRESSION_TEST_HACK') is not None: - # We can't apply the 'callable' fixer until the floor is 2.6, but the - # '-3' option to Python 2.6 and 2.7 generates almost ten thousand - # warnings. This hack allows us to run regression tests with the '-3' - # option by replacing the callable() built-in function with a hack - # that performs the same function but doesn't generate the warning. - # Note that this hack is ONLY intended to be used for regression - # testing, and should NEVER be used for real runs. - from types import ClassType - def callable(obj): - if hasattr(obj, '__call__'): return True - if isinstance(obj, (ClassType, type)): return True - return False - def make_temp_file(**kw): try: result = tempfile.mktemp(**kw) - try: - result = os.path.realpath(result) - except AttributeError: - # Python 2.1 has no os.path.realpath() method. - pass + result = os.path.realpath(result) except TypeError: try: save_template = tempfile.template diff --git a/src/script/scons.py b/src/script/scons.py index 25ae644..b5476b9 100644 --- a/src/script/scons.py +++ b/src/script/scons.py @@ -98,7 +98,7 @@ try: except ImportError: pass else: - # when running from an egg add the egg's directory + # when running from an egg add the egg's directory try: d = pkg_resources.get_distribution('scons') except pkg_resources.DistributionNotFound: @@ -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(" {}".format(path)) raise # this does all the work, and calls sys.exit diff --git a/src/script/sconsign.py b/src/script/sconsign.py index 9f75e3a..1a4caf7 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -57,6 +57,14 @@ import sys # followed by generic) so we pick up the right version of the build # engine modules if they're in either directory. + +if sys.version_info >= (3,0,0): + msg = "sconsign: *** Version %s does not run under Python version %s.\n\ +Python 3 is not yet supported.\n" + sys.stderr.write(msg % (__version__, sys.version.split()[0])) + sys.exit(1) + + script_dir = sys.path[0] if script_dir in sys.path: @@ -67,6 +75,11 @@ libs = [] if "SCONS_LIB_DIR" in os.environ: libs.append(os.environ["SCONS_LIB_DIR"]) +# - running from source takes priority (since 2.3.2), excluding SCONS_LIB_DIR settings +script_path = os.path.abspath(os.path.dirname(__file__)) +source_path = os.path.join(script_path, '..', 'engine') +libs.append(source_path) + local_version = 'scons-local-' + __version__ local = 'scons-local' if script_dir: @@ -286,7 +299,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: @@ -471,12 +484,22 @@ for o, a in opts: elif o in ('-e', '--entry'): Print_Entries.append(a) elif o in ('-f', '--format'): + # Try to map the given DB format to a known module + # name, that we can then try to import... Module_Map = {'dblite' : 'SCons.dblite', 'sconsign' : None} dbm_name = Module_Map.get(a, a) if dbm_name: try: - dbm = my_import(dbm_name) + if dbm_name != "SCons.dblite": + dbm = my_import(dbm_name) + else: + import SCons.dblite + dbm = SCons.dblite + # Ensure that we don't ignore corrupt DB files, + # this was handled by calling my_import('SCons.dblite') + # again in earlier versions... + SCons.dblite.ignore_corrupt_dbfiles = 0 except: sys.stderr.write("sconsign: illegal file format `%s'\n" % a) print(helpstr) @@ -508,7 +531,15 @@ else: dbm_name = dbm.whichdb(a) if dbm_name: Map_Module = {'SCons.dblite' : 'dblite'} - dbm = my_import(dbm_name) + if dbm_name != "SCons.dblite": + dbm = my_import(dbm_name) + else: + import SCons.dblite + dbm = SCons.dblite + # Ensure that we don't ignore corrupt DB files, + # this was handled by calling my_import('SCons.dblite') + # again in earlier versions... + SCons.dblite.ignore_corrupt_dbfiles = 0 Do_SConsignDB(Map_Module.get(dbm_name, dbm_name), dbm)(a) else: Do_SConsignDir(a) diff --git a/src/setup.py b/src/setup.py index 2c75c60..c036fb0 100644 --- a/src/setup.py +++ b/src/setup.py @@ -73,6 +73,16 @@ import distutils.command.install_data import distutils.command.install_lib import distutils.command.install_scripts import distutils.command.build_scripts +import distutils.msvccompiler + +def get_build_version(): + """ monkey patch distutils msvc version if we're not on windows. + We need to use vc version 9 for python 2.7.x and it defaults to 6 + for non-windows platforms and there is no way to override it besides + monkey patching""" + return 9 + +distutils.msvccompiler.get_build_version = get_build_version _install = distutils.command.install.install _install_data = distutils.command.install_data.install_data @@ -325,7 +335,7 @@ class install_scripts(_install_scripts): self.copy_scons(src, scons_version_bat) # --- distutils copy/paste --- - if os.name == 'posix': + if hasattr(os, 'chmod') and hasattr(os,'stat'): # Set the executable bits (owner, group, and world) on # all the scripts we just installed. for file in self.get_outputs(): 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/compat/any.py b/test/Builder/different-actions.py index 3c03807..33a1363 100644 --- a/test/compat/any.py +++ b/test/Builder/different-actions.py @@ -25,34 +25,27 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ -Verify that we can use the any() function (in any supported Python -version we happen to be testing). - -This test can be retired at some point in the distant future when Python -2.5 becomes the minimum version supported by SCons. +Verify that two builders in two environments with different +actions generate an error. """ import TestSCons -test = TestSCons.TestSCons() +test = TestSCons.TestSCons(match=TestSCons.match_re) test.write('SConstruct', """\ -print any([True, False]) and "YES" or "NO" -print any([1]) and "YES" or "NO" -SConscript('SConscript') -""") +e1 = Environment() +e2 = Environment() -test.write('SConscript', """\ -print any([0, False]) and "YES" or "NO" +e1.Command('out.txt', [], 'echo 1 > $TARGET') +e2.Command('out.txt', [], 'echo 2 > $TARGET') """) -expect = """\ -YES -YES -NO -""" +expect = TestSCons.re_escape(""" +scons: *** Two environments with different actions were specified for the same target: out.txt +""") + TestSCons.file_expr -test.run(arguments = '-Q -q', stdout = expect) +test.run(arguments='out.txt', status=2, stderr=expect) test.pass_test() diff --git a/test/CC/CCVERSION.py b/test/CC/CCVERSION.py index ac28e38..f785ddc 100644 --- a/test/CC/CCVERSION.py +++ b/test/CC/CCVERSION.py @@ -33,7 +33,8 @@ _exe = TestSCons._exe test = TestSCons.TestSCons() - +if sys.platform == 'win32': + test.skip_test('CCVERSION not set with MSVC, skipping test.') test.write("versioned.py", """import os diff --git a/test/CPPDEFINES/pkg-config.py b/test/CPPDEFINES/pkg-config.py index 0656900..1b308d3 100644 --- a/test/CPPDEFINES/pkg-config.py +++ b/test/CPPDEFINES/pkg-config.py @@ -32,7 +32,8 @@ import TestSCons test = TestSCons.TestSCons() -if not test.where_is('pkg-config'): +pkg_config_path = test.where_is('pkg-config') +if not pkg_config_path: test.skip_test("Could not find 'pkg-config' in system PATH, skipping test.\n") test.write('bug.pc', """\ @@ -58,7 +59,7 @@ test.write('SConstruct', """\ # http://scons.tigris.org/issues/show_bug.cgi?id=2671 # Passing test cases env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) -env_1.ParseConfig('PKG_CONFIG_PATH=. pkg-config --cflags bug') +env_1.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags bug') print env_1.subst('$_CPPDEFFLAGS') env_2 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) @@ -67,7 +68,7 @@ print env_2.subst('$_CPPDEFFLAGS') # Failing test cases env_3 = Environment(CPPDEFINES={'DEBUG':1, 'TEST':None}) -env_3.ParseConfig('PKG_CONFIG_PATH=. pkg-config --cflags bug') +env_3.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags bug') print env_3.subst('$_CPPDEFFLAGS') env_4 = Environment(CPPDEFINES={'DEBUG':1, 'TEST':None}) @@ -76,10 +77,10 @@ print env_4.subst('$_CPPDEFFLAGS') # http://scons.tigris.org/issues/show_bug.cgi?id=1738 env_1738_1 = Environment(tools=['default']) -env_1738_1.ParseConfig('PKG_CONFIG_PATH=. pkg-config --cflags --libs bug') +env_1738_1.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags --libs bug') env_1738_1.Append(CPPDEFINES={'value' : '1'}) print env_1738_1.subst('$_CPPDEFFLAGS') -""") +"""%locals() ) expect_print_output="""\ -DDEBUG=1 -DTEST -DSOMETHING -DVARIABLE=2 diff --git a/test/CXX/CXXVERSION.py b/test/CXX/CXXVERSION.py index 8433aa6..6017001 100644 --- a/test/CXX/CXXVERSION.py +++ b/test/CXX/CXXVERSION.py @@ -33,6 +33,8 @@ _exe = TestSCons._exe test = TestSCons.TestSCons() +if sys.platform == 'win32': + test.skip_test('CXXVERSION not set with MSVC, skipping test.') test.write("versioned.py", 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/Configure/custom-tests.py b/test/Configure/custom-tests.py index f79ea0d..687ba48 100644 --- a/test/Configure/custom-tests.py +++ b/test/Configure/custom-tests.py @@ -180,7 +180,7 @@ scons: Configure: Display of random string ... scons: Configure: \(cached\) a random string scons: Configure: Display of empty string ... -scons: Configure: \(cached\). +scons: Configure: \(cached\) * scons: Configure: Display of dictionary ... scons: Configure: \(cached\) yes diff --git a/test/Copy-Symlinks.py b/test/Copy-Symlinks.py index f8f92d7..2b8b824 100644 --- a/test/Copy-Symlinks.py +++ b/test/Copy-Symlinks.py @@ -38,6 +38,9 @@ SCons.Defaults.DefaultEnvironment( tools = [] ) test = TestSCons.TestSCons() +if not hasattr(os, 'symlink'): + test.skip_test('No os.symlink() method, no symlinks to test.\n') + filelinkToCopy = 'filelinkToCopy' fileToLink = 'file.in' fileContents = 'stuff n things\n' @@ -47,21 +50,24 @@ treeToLink = 'tree' treelinkToCopy = 'treelinkToCopy' badToLink = 'None' # do not write this item badlinkToCopy = 'badlinkToCopy' +relToLink = os.path.join( treeToLink, fileToLink ) +rellinkToCopy = 'relLinkToCopy' -try: - test.symlink( fileToLink, filelinkToCopy ) - test.symlink( dirToLink, dirlinkToCopy ) - test.symlink( treeToLink, treelinkToCopy ) - test.symlink( badToLink, badlinkToCopy ) -except: - test.no_result() +test.symlink( fileToLink, filelinkToCopy ) +test.symlink( dirToLink, dirlinkToCopy ) +test.symlink( treeToLink, treelinkToCopy ) +test.symlink( badToLink, badlinkToCopy ) +test.symlink( relToLink, rellinkToCopy ) test.write( fileToLink, fileContents ) test.subdir( dirToLink ) test.subdir( treeToLink ) -test.write( os.path.join( treeToLink, fileToLink ), fileContents ) +test.write( relToLink, fileContents ) + +sconstructPath = 'SConstruct' +sconscriptPath = os.path.join( treeToLink, 'SConscript' ) -test.write('SConstruct', +test.write( sconstructPath, """\ import SCons.Defaults SCons.Defaults.DefaultEnvironment( tools = [] ) @@ -81,17 +87,32 @@ Execute( Copy( 'L6', '%(treelinkToCopy)s', True ) ) Execute( Copy( 'Fails', '%(badlinkToCopy)s', False ) ) Execute( Copy( 'L7', '%(badlinkToCopy)s' ) ) Execute( Copy( 'L8', '%(badlinkToCopy)s', True ) ) + +SConscript( '%(sconscriptPath)s' ) """ % locals() ) -test.must_exist( 'SConstruct' ) +relLinkCopyPath = os.path.join( '..', rellinkToCopy ) + +test.write( sconscriptPath, +"""\ +Execute( Copy( 'F2', '%(relLinkCopyPath)s', False ) ) +Execute( Copy( 'L9', '%(relLinkCopyPath)s' ) ) +Execute( Copy( 'L10', '%(relLinkCopyPath)s', True ) ) +""" +% locals() +) + +test.must_exist( sconstructPath ) +test.must_exist( sconscriptPath ) test.must_exist( fileToLink ) test.must_exist( filelinkToCopy ) test.must_exist( dirlinkToCopy ) test.must_exist( treelinkToCopy ) test.must_not_exist( badToLink ) test.must_exist( badlinkToCopy ) +test.must_exist( rellinkToCopy ) expectStdout = test.wrap_stdout( read_str = @@ -105,9 +126,12 @@ Copy("L4", "%(dirlinkToCopy)s") Copy("T1", "%(treelinkToCopy)s") Copy("L5", "%(treelinkToCopy)s") Copy("L6", "%(treelinkToCopy)s") -Copy("Fails", "badlinkToCopy") +Copy("Fails", "%(badlinkToCopy)s") Copy("L7", "%(badlinkToCopy)s") Copy("L8", "%(badlinkToCopy)s") +Copy("F2", "%(relLinkCopyPath)s") +Copy("L9", "%(relLinkCopyPath)s") +Copy("L10", "%(relLinkCopyPath)s") ''' % locals(), build_str = '''\ @@ -117,13 +141,18 @@ scons: `.' is up to date. expectStderr = \ '''\ -scons: *** None: No such file or directory -''' +scons: *** %s: No such file or directory +''' % os.path.join( os.getcwd(), badToLink ) test.run( stdout = expectStdout, stderr = expectStderr, status = None ) +F2 = os.path.join( treeToLink, 'F2' ) +L9 = os.path.join( treeToLink, 'L9' ) +L10 = os.path.join( treeToLink, 'L10' ) + test.must_exist('D1') test.must_exist('F1') +test.must_exist( F2 ) test.must_exist('L2') test.must_exist('L3') test.must_exist('L4') @@ -131,17 +160,21 @@ test.must_exist('L5') test.must_exist('L6') test.must_exist('L7') test.must_exist('L8') +test.must_exist( L9 ) +test.must_exist( L10 ) test.must_exist('T1') test.must_not_exist( 'Fails' ) test.must_match( fileToLink, fileContents ) test.must_match( 'F1', fileContents ) +test.must_match( F2 , fileContents ) test.must_match( 'L1', fileContents ) test.must_match( 'L2', fileContents ) test.must_match( os.path.join( treeToLink, fileToLink ), fileContents ) test.fail_test( condition=os.path.islink('D1') ) test.fail_test( condition=os.path.islink('F1') ) +test.fail_test( condition=os.path.islink( F2 ) ) test.fail_test( condition=os.path.islink('T1') ) test.fail_test( condition=(not os.path.isdir('D1')) ) test.fail_test( condition=(not os.path.isfile('F1')) ) @@ -154,8 +187,12 @@ test.fail_test( condition=(not os.path.islink('L5')) ) test.fail_test( condition=(not os.path.islink('L6')) ) test.fail_test( condition=(not os.path.islink('L7')) ) test.fail_test( condition=(not os.path.islink('L8')) ) +test.fail_test( condition=(not os.path.islink( L9 )) ) +test.fail_test( condition=(not os.path.islink( L10 )) ) test.fail_test( condition=(os.path.exists('L7')) ) test.fail_test( condition=(os.path.exists('L8')) ) +test.fail_test( condition=(os.path.exists( L9 )) ) +test.fail_test( condition=(os.path.exists( L10 )) ) test.fail_test( condition=(os.readlink(filelinkToCopy) != os.readlink('L1')) ) test.fail_test( condition=(os.readlink(filelinkToCopy) != os.readlink('L2')) ) test.fail_test( condition=(os.readlink(dirlinkToCopy) != os.readlink('L3')) ) @@ -164,6 +201,8 @@ test.fail_test( condition=(os.readlink(treelinkToCopy) != os.readlink('L5')) ) test.fail_test( condition=(os.readlink(treelinkToCopy) != os.readlink('L6')) ) test.fail_test( condition=(os.readlink(badlinkToCopy) != os.readlink('L7')) ) test.fail_test( condition=(os.readlink(badlinkToCopy) != os.readlink('L8')) ) +test.fail_test( condition=(os.readlink(rellinkToCopy) != os.readlink( L9 )) ) +test.fail_test( condition=(os.readlink(rellinkToCopy) != os.readlink( L10 )) ) test.pass_test() diff --git a/test/D/CoreScanner/Common/common.py b/test/D/CoreScanner/Common/common.py index 657e83e..1d2fde0 100644 --- a/test/D/CoreScanner/Common/common.py +++ b/test/D/CoreScanner/Common/common.py @@ -44,7 +44,7 @@ def testForTool(tool): _obj = TestSCons._obj if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('Image') test.write('SConstruct', open('SConstruct_template', 'r').read().format(tool)) diff --git a/test/D/CoreScanner/Image/SConstruct_template b/test/D/CoreScanner/Image/SConstruct_template index a128c67..e91343b 100644 --- a/test/D/CoreScanner/Image/SConstruct_template +++ b/test/D/CoreScanner/Image/SConstruct_template @@ -3,7 +3,6 @@ import os environment = Environment( - ENV=os.environ, tools=['link', '{}']) environment.Program('test1.d') environment.Program('test2.d') diff --git a/test/D/DMD.py b/test/D/DMD.py index 1bde380..2d9333a 100644 --- a/test/D/DMD.py +++ b/test/D/DMD.py @@ -28,16 +28,20 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import TestSCons +import sys +from os.path import abspath, dirname, join +sys.path.append(join(dirname(abspath(__file__)), 'Support')) +from executablesSearch import isExecutableOfToolAvailable + _exe = TestSCons._exe test = TestSCons.TestSCons() -dmd = test.where_is('dmd') -if not dmd: +if not isExecutableOfToolAvailable(test, 'dmd'): test.skip_test("Could not find 'dmd'; skipping test.\n") test.write('SConstruct', """\ import os -env = Environment(ENV=os.environ) +env = Environment() if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/DMD2.py b/test/D/DMD2.py index cc8ab93..84ceb51 100644 --- a/test/D/DMD2.py +++ b/test/D/DMD2.py @@ -28,15 +28,21 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import TestSCons +import sys +from os.path import abspath, dirname, join +sys.path.append(join(dirname(abspath(__file__)), 'Support')) +from executablesSearch import isExecutableOfToolAvailable + _exe = TestSCons._exe test = TestSCons.TestSCons() -if not test.where_is('dmd') and not test.where_is('gdmd'): - test.skip_test("Could not find 'dmd' or 'gdmd', skipping test.\n") +if not isExecutableOfToolAvailable(test, 'dmd'): + test.skip_test("Could not find 'dmd'; skipping test.\n") test.write('SConstruct', """\ import os -env = Environment(tools=['link', 'dmd'], ENV=os.environ) +env = Environment(tools=['link', 'dmd']) +env['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/DMD2_Alt.py b/test/D/DMD2_Alt.py index fbe2f2b..3bd58b4 100644 --- a/test/D/DMD2_Alt.py +++ b/test/D/DMD2_Alt.py @@ -28,15 +28,21 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import TestSCons +import sys +from os.path import abspath, dirname, join +sys.path.append(join(dirname(abspath(__file__)), 'Support')) +from executablesSearch import isExecutableOfToolAvailable + _exe = TestSCons._exe test = TestSCons.TestSCons() -if not test.where_is('dmd') and not test.where_is('gdmd'): - test.skip_test("Could not find 'dmd' or 'gdmd', skipping test.\n") +if not isExecutableOfToolAvailable(test, 'dmd'): + test.skip_test("Could not find 'dmd'; skipping test.\n") test.write('SConstruct', """\ import os -env = Environment(tools=['dmd', 'link'], ENV=os.environ) +env = Environment(tools=['dmd', 'link']) +env['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/GDC.py b/test/D/GDC.py index e24ec43..b548b94 100644 --- a/test/D/GDC.py +++ b/test/D/GDC.py @@ -36,7 +36,7 @@ if not test.where_is('gdc'): test.write('SConstruct', """\ import os -env = Environment(tools=['link', 'gdc'], ENV=os.environ) +env = Environment(tools=['link', 'gdc']) if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/GDC_Alt.py b/test/D/GDC_Alt.py index cac7949..27159ee 100644 --- a/test/D/GDC_Alt.py +++ b/test/D/GDC_Alt.py @@ -36,7 +36,7 @@ if not test.where_is('gdc'): test.write('SConstruct', """\ import os -env = Environment(tools=['gdc', 'link'], ENV=os.environ) +env = Environment(tools=['gdc', 'link']) if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/HSTeoh/ArLibIssue/SConstruct_template b/test/D/HSTeoh/ArLibIssue/SConstruct_template index 81f81f5..b17847a 100644 --- a/test/D/HSTeoh/ArLibIssue/SConstruct_template +++ b/test/D/HSTeoh/ArLibIssue/SConstruct_template @@ -1,3 +1,6 @@ env = Environment({}) +import os +env['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd + env.StaticLibrary('mylib', ['a.d', 'b.d']) diff --git a/test/D/HSTeoh/Common/arLibIssue.py b/test/D/HSTeoh/Common/arLibIssue.py index fe5902b..9bca3d8 100644 --- a/test/D/HSTeoh/Common/arLibIssue.py +++ b/test/D/HSTeoh/Common/arLibIssue.py @@ -43,10 +43,10 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('ArLibIssue') - test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{}", "ar"]'.format(tool))) + test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{0}", "ar"]'.format(tool))) test.run() diff --git a/test/D/HSTeoh/Common/libCompileOptions.py b/test/D/HSTeoh/Common/libCompileOptions.py index dd95fc8..4a21c45 100644 --- a/test/D/HSTeoh/Common/libCompileOptions.py +++ b/test/D/HSTeoh/Common/libCompileOptions.py @@ -43,10 +43,10 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('LibCompileOptions') - test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{}", "link", "ar"]'.format(tool))) + test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{0}", "link", "ar"]'.format(tool))) test.run() diff --git a/test/D/HSTeoh/Common/linkingProblem.py b/test/D/HSTeoh/Common/linkingProblem.py index 59b409f..b526ad7 100644 --- a/test/D/HSTeoh/Common/linkingProblem.py +++ b/test/D/HSTeoh/Common/linkingProblem.py @@ -29,7 +29,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import TestSCons -from os.path import abspath, dirname +from os.path import abspath, dirname, exists import sys sys.path.insert(1, abspath(dirname(__file__) + '/../../Support')) @@ -41,7 +41,10 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) + + if not exists('/usr/include/ncurses.h'): + test.skip_test("ncurses not apparently installed, skip this test.") test.dir_fixture('LinkingProblem') test.write('SConstruct', open('SConstruct_template', 'r').read().format(tool)) diff --git a/test/D/HSTeoh/Common/singleStringCannotBeMultipleOptions.py b/test/D/HSTeoh/Common/singleStringCannotBeMultipleOptions.py index 4dabf7b..4716f1c 100644 --- a/test/D/HSTeoh/Common/singleStringCannotBeMultipleOptions.py +++ b/test/D/HSTeoh/Common/singleStringCannotBeMultipleOptions.py @@ -42,7 +42,7 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('SingleStringCannotBeMultipleOptions') test.write('SConstruct', open('SConstruct_template', 'r').read().format(tool)) @@ -55,6 +55,10 @@ def testForTool(tool): 'ldc': ".*Unknown command line argument '-m64 -O'.*", }[tool] + from SCons.Environment import Base + if tool == 'dmd' and Base()['DC'] == 'gdmd': + result = ".*unrecognized command line option '-m64 -O'.*" + test.fail_test(not test.match_re_dotall(test.stderr(), result)) test.pass_test() diff --git a/test/D/HSTeoh/LibCompileOptions/SConstruct_template b/test/D/HSTeoh/LibCompileOptions/SConstruct_template index 7031f5c..1489624 100644 --- a/test/D/HSTeoh/LibCompileOptions/SConstruct_template +++ b/test/D/HSTeoh/LibCompileOptions/SConstruct_template @@ -1,5 +1,8 @@ env = Environment({}) +import os +env['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd + env.Library('mylib', 'mylib.d') prog_env = env.Clone( diff --git a/test/D/HSTeoh/LinkingProblem/SConstruct_template b/test/D/HSTeoh/LinkingProblem/SConstruct_template index 6815cdf..2c53b54 100644 --- a/test/D/HSTeoh/LinkingProblem/SConstruct_template +++ b/test/D/HSTeoh/LinkingProblem/SConstruct_template @@ -3,10 +3,11 @@ import os environment = Environment( - ENV=os.environ, - tools = ['cc', 'link' , '{}'], + tools = ['cc', '{}', 'link'], LIBS = ['ncurses']) +environment['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd + environment.Object('ncurs_impl.o', 'ncurs_impl.c') environment.Program('prog', Split(""" diff --git a/test/D/HSTeoh/SingleStringCannotBeMultipleOptions/SConstruct_template b/test/D/HSTeoh/SingleStringCannotBeMultipleOptions/SConstruct_template index 89c603b..118a7b2 100644 --- a/test/D/HSTeoh/SingleStringCannotBeMultipleOptions/SConstruct_template +++ b/test/D/HSTeoh/SingleStringCannotBeMultipleOptions/SConstruct_template @@ -3,7 +3,6 @@ import os environment = Environment( - ENV=os.environ, tools=['link', '{}'], # It might be thought that a single string can contain multiple options space separated. Actually this # is deemed to be a single option, so leads to an error. diff --git a/test/D/HelloWorld/CompileAndLinkOneStep/Common/common.py b/test/D/HelloWorld/CompileAndLinkOneStep/Common/common.py index 618041b..e757d47 100644 --- a/test/D/HelloWorld/CompileAndLinkOneStep/Common/common.py +++ b/test/D/HelloWorld/CompileAndLinkOneStep/Common/common.py @@ -41,7 +41,7 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('Image') test.write('SConstruct', open('SConstruct_template', 'r').read().format(tool)) diff --git a/test/D/HelloWorld/CompileAndLinkOneStep/Image/SConstruct_template b/test/D/HelloWorld/CompileAndLinkOneStep/Image/SConstruct_template index c688ab7..e2e7439 100644 --- a/test/D/HelloWorld/CompileAndLinkOneStep/Image/SConstruct_template +++ b/test/D/HelloWorld/CompileAndLinkOneStep/Image/SConstruct_template @@ -3,7 +3,6 @@ import os environment = Environment( - ENV=os.environ, tools=['link', '{}']) environment.Program('helloWorld.d') diff --git a/test/D/HelloWorld/CompileThenLinkTwoSteps/Common/common.py b/test/D/HelloWorld/CompileThenLinkTwoSteps/Common/common.py index 618041b..e757d47 100644 --- a/test/D/HelloWorld/CompileThenLinkTwoSteps/Common/common.py +++ b/test/D/HelloWorld/CompileThenLinkTwoSteps/Common/common.py @@ -41,7 +41,7 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('Image') test.write('SConstruct', open('SConstruct_template', 'r').read().format(tool)) diff --git a/test/D/HelloWorld/CompileThenLinkTwoSteps/Image/SConstruct_template b/test/D/HelloWorld/CompileThenLinkTwoSteps/Image/SConstruct_template index 425970a..b38a9f0 100644 --- a/test/D/HelloWorld/CompileThenLinkTwoSteps/Image/SConstruct_template +++ b/test/D/HelloWorld/CompileThenLinkTwoSteps/Image/SConstruct_template @@ -3,7 +3,6 @@ import os environment = Environment( - ENV=os.environ, tools=['link', '{}']) objects = environment.Object('helloWorld.d') diff --git a/test/D/Issues/2939_Ariovistus/Common/correctLinkOptions.py b/test/D/Issues/2939_Ariovistus/Common/correctLinkOptions.py index 3b178b9..1b42580 100644 --- a/test/D/Issues/2939_Ariovistus/Common/correctLinkOptions.py +++ b/test/D/Issues/2939_Ariovistus/Common/correctLinkOptions.py @@ -43,14 +43,28 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('Project') - test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{}", "link"]'.format(tool))) + test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{0}", "link"]'.format(tool))) test.run() - for f in ('libstuff.so', 'stuff.os', 'test1', 'test1.o', 'test2', 'test2.o'): + platform = Base()['PLATFORM'] + + if platform == 'posix': + libraryname = 'libstuff.so' + filename = 'stuff.os' + elif platform == 'darwin': + libraryname = 'libstuff.dylib' + filename = 'stuff.os' + elif platform == 'win32': + libraryname = 'stuff.dll' + filename = 'stuff.obj' + else: + test.fail_test('No information about platform: ' + platform) + + for f in (libraryname, filename, 'test1', 'test1.o', 'test2', 'test2.o'): test.must_exist(test.workpath(join('test', 'test1', f))) test.pass_test() diff --git a/test/D/Issues/2939_Ariovistus/Project/SConstruct_template b/test/D/Issues/2939_Ariovistus/Project/SConstruct_template index 55f02aa..c78ba96 100644 --- a/test/D/Issues/2939_Ariovistus/Project/SConstruct_template +++ b/test/D/Issues/2939_Ariovistus/Project/SConstruct_template @@ -2,6 +2,9 @@ from os.path import join environment = Environment({}) +import os +environment['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd + Export('environment') environment.SConscript([ diff --git a/test/D/Issues/2940_Ariovistus/Common/correctLinkOptions.py b/test/D/Issues/2940_Ariovistus/Common/correctLinkOptions.py index 3b178b9..8060add 100644 --- a/test/D/Issues/2940_Ariovistus/Common/correctLinkOptions.py +++ b/test/D/Issues/2940_Ariovistus/Common/correctLinkOptions.py @@ -43,14 +43,30 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) + + platform = Base()['PLATFORM'] + if platform == 'posix': + libraryname = 'libstuff.so' + filename = 'stuff.os' + elif platform == 'darwin': + libraryname = 'libstuff.dylib' + filename = 'stuff.os' + # As at 2014-09-14, DMD 2.066, LDC master head, and GDC 4.9.1 do not support + # shared libraries on OSX. + test.skip_test('Dynamic libraries not yet supported on OSX.\n') + elif platform == 'win32': + libraryname = 'stuff.dll' + filename = 'stuff.obj' + else: + test.fail_test('No information about platform: ' + platform) test.dir_fixture('Project') - test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{}", "link"]'.format(tool))) + test.write('SConstruct', open('SConstruct_template', 'r').read().format('tools=["{0}", "link"]'.format(tool))) test.run() - for f in ('libstuff.so', 'stuff.os', 'test1', 'test1.o', 'test2', 'test2.o'): + for f in (libraryname, filename, 'test1', 'test1.o', 'test2', 'test2.o'): test.must_exist(test.workpath(join('test', 'test1', f))) test.pass_test() diff --git a/test/D/Issues/2940_Ariovistus/Project/SConstruct_template b/test/D/Issues/2940_Ariovistus/Project/SConstruct_template index 55f02aa..c78ba96 100644 --- a/test/D/Issues/2940_Ariovistus/Project/SConstruct_template +++ b/test/D/Issues/2940_Ariovistus/Project/SConstruct_template @@ -2,6 +2,9 @@ from os.path import join environment = Environment({}) +import os +environment['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd + Export('environment') environment.SConscript([ diff --git a/test/D/Issues/2944/D_changed_DFLAGS_not_rebuilding.py b/test/D/Issues/2944/D_changed_DFLAGS_not_rebuilding.py new file mode 100644 index 0000000..1d9854f --- /dev/null +++ b/test/D/Issues/2944/D_changed_DFLAGS_not_rebuilding.py @@ -0,0 +1,23 @@ +# Test to check for issue reported in tigris bug 2994 +# http://scons.tigris.org/issues/show_bug.cgi?id=2994 +# + +import TestSCons + +test = TestSCons.TestSCons() + +dmd_present = test.detect_tool('dmd', prog='dmd') +ldc_present = test.detect_tool('ldc',prog='ldc2') +gdc_present = test.detect_tool('gdc',prog='gdc') + +if not (dmd_present or ldc_present or gdc_present): + test.skip_test("Could not load dmd ldc or gdc Tool; skipping test(s).\n") + + +test.dir_fixture('image') +test.run() +test.fail_test('main.o' not in test.stdout()) +test.run(arguments='change=1') +test.fail_test('is up to date' in test.stdout()) + +test.pass_test() diff --git a/test/D/Issues/2944/image/SConstruct b/test/D/Issues/2944/image/SConstruct new file mode 100644 index 0000000..2c7deee --- /dev/null +++ b/test/D/Issues/2944/image/SConstruct @@ -0,0 +1,11 @@ +# -*- codig:utf-8; -*- + +env=Environment() + +change = ARGUMENTS.get('change', 0) +if int(change): + env.Append(DFLAGS = '-d') + +env.Program('proj', ['main.d']) + + diff --git a/test/D/Issues/2944/image/main.d b/test/D/Issues/2944/image/main.d new file mode 100644 index 0000000..f0aa23a --- /dev/null +++ b/test/D/Issues/2944/image/main.d @@ -0,0 +1,11 @@ +/* This program prints a + hello world message + to the console. */ + +import std.stdio; + +void main() +{ + writeln("Hello, World!"); +} + diff --git a/test/D/LDC.py b/test/D/LDC.py index 94acf1c..19070a5 100644 --- a/test/D/LDC.py +++ b/test/D/LDC.py @@ -43,7 +43,7 @@ if not isExecutableOfToolAvailable(test, 'ldc'): test.write('SConstruct', """\ import os -env = Environment(tools=['link', 'ldc'], ENV=os.environ) +env = Environment(tools=['link', 'ldc']) if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/LDC_Alt.py b/test/D/LDC_Alt.py index 571b8f0..bca7dd6 100644 --- a/test/D/LDC_Alt.py +++ b/test/D/LDC_Alt.py @@ -43,7 +43,7 @@ if not isExecutableOfToolAvailable(test, 'ldc'): test.write('SConstruct', """\ import os -env = Environment(tools=['ldc', 'link'], ENV=os.environ) +env = Environment(tools=['ldc', 'link']) if env['PLATFORM'] == 'cygwin': env['OBJSUFFIX'] = '.obj' # trick DMD env.Program('foo', 'foo.d') """) diff --git a/test/D/MixedDAndC/Common/common.py b/test/D/MixedDAndC/Common/common.py index 66c738f..852e2e2 100644 --- a/test/D/MixedDAndC/Common/common.py +++ b/test/D/MixedDAndC/Common/common.py @@ -30,6 +30,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import TestSCons from os.path import abspath, dirname +from platform import architecture import sys sys.path.insert(1, abspath(dirname(__file__) + '/../../Support')) @@ -41,10 +42,20 @@ def testForTool(tool): test = TestSCons.TestSCons() if not isExecutableOfToolAvailable(test, tool) : - test.skip_test("Required executable for tool '{}' not found, skipping test.\n".format(tool)) + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) test.dir_fixture('Image') +# There was an issue with Russel mentioned but couldn't remember the details +# Which drove him to add the following logic. For now removing until +# we can determine what that issue is and if there's not a better +# way to handle the corner case +# if architecture()[0] == '32bit': +# test.run(status=2) +# test.fail_test('64-bit mode not compiled in' not in test.stderr()) +# else: +# test.run() + test.run() test.pass_test() diff --git a/test/D/MixedDAndC/Image/SConstruct b/test/D/MixedDAndC/Image/SConstruct index 47870d7..176c4d3 100644 --- a/test/D/MixedDAndC/Image/SConstruct +++ b/test/D/MixedDAndC/Image/SConstruct @@ -3,8 +3,10 @@ import os environment = Environment( - ENV=os.environ, - DFLAGS=['-m64', '-O']) +) +# CFLAGS=['-m64'], +# DLINKFLAGS=['-m64'], +# DFLAGS=['-m64', '-O']) environment.Program('proj', [ 'proj.d', diff --git a/test/D/Scanner.py b/test/D/Scanner.py index 48af057..b005adf 100644 --- a/test/D/Scanner.py +++ b/test/D/Scanner.py @@ -31,12 +31,16 @@ a single statement. import TestSCons +import sys +from os.path import abspath, dirname, join +sys.path.append(join(dirname(abspath(__file__)), 'Support')) +from executablesSearch import isExecutableOfToolAvailable + test = TestSCons.TestSCons() _obj = TestSCons._obj -dmd = test.where_is('dmd') -if not dmd: +if not isExecutableOfToolAvailable(test, 'dmd'): test.skip_test("Could not find 'dmd'; skipping test.\n") test.subdir(['p']) diff --git a/test/D/SharedObjects/Common/__init__.py b/test/D/SharedObjects/Common/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/D/SharedObjects/Common/__init__.py diff --git a/test/D/SharedObjects/Common/common.py b/test/D/SharedObjects/Common/common.py new file mode 100644 index 0000000..e81cf5b --- /dev/null +++ b/test/D/SharedObjects/Common/common.py @@ -0,0 +1,89 @@ +""" +Support functions for all the tests. +""" + +# +# __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 TestSCons + +from SCons.Environment import Base + +from os.path import abspath, dirname + +import sys +sys.path.insert(1, abspath(dirname(__file__) + '/../../Support')) + +from executablesSearch import isExecutableOfToolAvailable + +def testForTool(tool): + + test = TestSCons.TestSCons() + + if not isExecutableOfToolAvailable(test, tool) : + test.skip_test("Required executable for tool '{0}' not found, skipping test.\n".format(tool)) + + if tool == 'gdc': + test.skip_test('gdc does not, as at version 4.9.1, support shared libraries.\n') + + if tool == 'dmd' and Base()['DC'] == 'gdmd': + test.skip_test('gdmd does not recognize the -shared option so cannot support linking of shared objects.\n') + + platform = Base()['PLATFORM'] + if platform == 'posix': + filename = 'code.o' + libraryname = 'libanswer.so' + elif platform == 'darwin': + filename = 'code.o' + libraryname = 'libanswer.dylib' + # As at 2014-09-14, DMD 2.066, LDC master head, and GDC 4.9.1 do not support + # shared libraries on OSX. + test.skip_test('Dynamic libraries not yet supported on OSX.\n') + elif platform == 'win32': + filename = 'code.obj' + libraryname = 'answer.dll' + else: + test.fail_test() + + test.dir_fixture('Image') + test.write('SConstruct', open('SConstruct_template', 'r').read().format(tool)) + + if tool == 'dmd': + # The gdmd executable in Debian Unstable as at 2012-05-12, version 4.6.3 puts out messages on stderr + # that cause inappropriate failure of the tests, so simply ignore them. + test.run(stderr=None) + else: + test.run() + + test.must_exist(test.workpath(filename)) + test.must_exist(test.workpath(libraryname)) + + test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/D/SharedObjects/Common/sconstest.skip b/test/D/SharedObjects/Common/sconstest.skip new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/D/SharedObjects/Common/sconstest.skip diff --git a/test/D/SharedObjects/Image/SConstruct_template b/test/D/SharedObjects/Image/SConstruct_template new file mode 100644 index 0000000..cae8713 --- /dev/null +++ b/test/D/SharedObjects/Image/SConstruct_template @@ -0,0 +1,10 @@ +# -*- mode:python; coding:utf-8; -*- + +import os + +environment = Environment( + tools=['{}', 'link']) + +environment['ENV']['HOME'] = os.environ['HOME'] # Hack for gdmd + +environment.SharedLibrary('answer', 'code.d') diff --git a/test/D/SharedObjects/Image/code.d b/test/D/SharedObjects/Image/code.d new file mode 100644 index 0000000..0d9d1d7 --- /dev/null +++ b/test/D/SharedObjects/Image/code.d @@ -0,0 +1,3 @@ +int returnTheAnswer() { + return 42; +} diff --git a/src/engine/SCons/compat/_scons_collections.py b/test/D/SharedObjects/sconstest-dmd.py index 1591b2e..df6ddeb 100644 --- a/src/engine/SCons/compat/_scons_collections.py +++ b/test/D/SharedObjects/sconstest-dmd.py @@ -1,3 +1,7 @@ +""" +Test compiling and executing using the dmd tool. +""" + # # __COPYRIGHT__ # @@ -21,22 +25,10 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__doc__ = """ -collections compatibility module for older (pre-2.4) Python versions - -This does not not NOT (repeat, *NOT*) provide complete collections -functionality. It only wraps the portions of collections functionality -used by SCons, in an interface that looks enough like collections for -our purposes. -""" - __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -# Use exec to hide old names from fixers. -exec("""if True: - from UserDict import UserDict - from UserList import UserList - from UserString import UserString""") +from Common.common import testForTool +testForTool('dmd') # Local Variables: # tab-width:4 diff --git a/test/D/SharedObjects/sconstest-gdc.py b/test/D/SharedObjects/sconstest-gdc.py new file mode 100644 index 0000000..43bb8eb --- /dev/null +++ b/test/D/SharedObjects/sconstest-gdc.py @@ -0,0 +1,37 @@ +""" +Test compiling and executing using the gcd tool. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from Common.common import testForTool +testForTool('gdc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/D/SharedObjects/sconstest-ldc.py b/test/D/SharedObjects/sconstest-ldc.py new file mode 100644 index 0000000..f61efbc --- /dev/null +++ b/test/D/SharedObjects/sconstest-ldc.py @@ -0,0 +1,37 @@ +""" +Test compiling and executing using the ldc tool. +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +from Common.common import testForTool +testForTool('ldc') + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/D/Support/executablesSearch.py b/test/D/Support/executablesSearch.py index e0487f6..17d9990 100755 --- a/test/D/Support/executablesSearch.py +++ b/test/D/Support/executablesSearch.py @@ -29,39 +29,60 @@ Support functions for all the tests. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +if __name__ == '__main__': + import sys + import os.path + sys.path.append(os.path.abspath('../../../src/engine')) + +from SCons.Environment import Base + +path = Base()['ENV']['PATH'] + def isExecutableOfToolAvailable(test, tool): for executable in { 'dmd': ['dmd', 'gdmd'], 'gdc': ['gdc'], - 'ldc': ['ldc2', 'ldc']}[tool]: - if test.where_is(executable): + 'ldc': ['ldc2', 'ldc'], + }[tool]: + if test.where_is(executable, path): return True return False + if __name__ == '__main__': import unittest - import sys - import os.path sys.path.append(os.path.abspath('../../../QMTest')) - sys.path.append(os.path.abspath('../../../src/engine')) + import TestSCons class VariousTests(unittest.TestCase): + ''' + These tests are somewhat self referential in that + isExecutableOfToolAvailable uses where_is to do most of it's + work and we use the same function in the tests. + ''' def setUp(self): self.test = TestSCons.TestSCons() + + def tearDown(self): + self.test = None + def test_None_tool(self): self.assertRaises(KeyError, isExecutableOfToolAvailable, self.test, None) + def test_dmd_tool(self): self.assertEqual( - self.test.where_is('dmd') is not None or self.test.where_is('gdmd') is not None, + self.test.where_is('dmd', path) is not None or self.test.where_is('gdmd', path) is not None, isExecutableOfToolAvailable(self.test, 'dmd')) + def test_gdc_tool(self): self.assertEqual( - self.test.where_is('gdc') is not None, + self.test.where_is('gdc', path) is not None, isExecutableOfToolAvailable(self.test, 'gdc')) + def test_ldc_tool(self): self.assertEqual( - self.test.where_is('ldc2') is not None or self.test.where_is('ldc') is not None, + self.test.where_is('ldc2', path) is not None or self.test.where_is('ldc', path) is not None, isExecutableOfToolAvailable(self.test, 'ldc')) unittest.main() diff --git a/test/Docbook/basedir/htmlchunked/htmlchunked.py b/test/Docbook/basedir/htmlchunked/htmlchunked.py index cf5f3d1..f97555b 100644 --- a/test/Docbook/basedir/htmlchunked/htmlchunked.py +++ b/test/Docbook/basedir/htmlchunked/htmlchunked.py @@ -38,6 +38,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basedir/htmlhelp/htmlhelp.py b/test/Docbook/basedir/htmlhelp/htmlhelp.py index 22bbd72..17aba09 100644 --- a/test/Docbook/basedir/htmlhelp/htmlhelp.py +++ b/test/Docbook/basedir/htmlhelp/htmlhelp.py @@ -38,6 +38,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basedir/slideshtml/slideshtml.py b/test/Docbook/basedir/slideshtml/slideshtml.py index a2375e4..a89edcd 100644 --- a/test/Docbook/basedir/slideshtml/slideshtml.py +++ b/test/Docbook/basedir/slideshtml/slideshtml.py @@ -39,6 +39,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/epub/epub.py b/test/Docbook/basic/epub/epub.py index 19e08a0..0a317b6 100644 --- a/test/Docbook/basic/epub/epub.py +++ b/test/Docbook/basic/epub/epub.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/html/html.py b/test/Docbook/basic/html/html.py index 0cb4fb6..acf38a3 100644 --- a/test/Docbook/basic/html/html.py +++ b/test/Docbook/basic/html/html.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/htmlchunked/htmlchunked.py b/test/Docbook/basic/htmlchunked/htmlchunked.py index 46cc8dc..74b8c7a 100644 --- a/test/Docbook/basic/htmlchunked/htmlchunked.py +++ b/test/Docbook/basic/htmlchunked/htmlchunked.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/htmlhelp/htmlhelp.py b/test/Docbook/basic/htmlhelp/htmlhelp.py index 9b00bbd..080ec60 100644 --- a/test/Docbook/basic/htmlhelp/htmlhelp.py +++ b/test/Docbook/basic/htmlhelp/htmlhelp.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/man/man.py b/test/Docbook/basic/man/man.py index c1f164d..d9b16b3 100644 --- a/test/Docbook/basic/man/man.py +++ b/test/Docbook/basic/man/man.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/slideshtml/slideshtml.py b/test/Docbook/basic/slideshtml/slideshtml.py index 37c2be0..8251b3e 100644 --- a/test/Docbook/basic/slideshtml/slideshtml.py +++ b/test/Docbook/basic/slideshtml/slideshtml.py @@ -39,6 +39,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/basic/xinclude/xinclude.py b/test/Docbook/basic/xinclude/xinclude.py index 2d87713..302c777 100644 --- a/test/Docbook/basic/xinclude/xinclude.py +++ b/test/Docbook/basic/xinclude/xinclude.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/dependencies/xinclude/xinclude.py b/test/Docbook/dependencies/xinclude/xinclude.py index 84d9ce7..115163c 100644 --- a/test/Docbook/dependencies/xinclude/xinclude.py +++ b/test/Docbook/dependencies/xinclude/xinclude.py @@ -32,6 +32,7 @@ test = TestSCons.TestSCons() try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/rootname/htmlchunked/htmlchunked.py b/test/Docbook/rootname/htmlchunked/htmlchunked.py index 8ab91d2..65b50ef 100644 --- a/test/Docbook/rootname/htmlchunked/htmlchunked.py +++ b/test/Docbook/rootname/htmlchunked/htmlchunked.py @@ -38,6 +38,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/rootname/htmlhelp/htmlhelp.py b/test/Docbook/rootname/htmlhelp/htmlhelp.py index ee37e1a..9d0b076 100644 --- a/test/Docbook/rootname/htmlhelp/htmlhelp.py +++ b/test/Docbook/rootname/htmlhelp/htmlhelp.py @@ -38,6 +38,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/Docbook/rootname/slideshtml/slideshtml.py b/test/Docbook/rootname/slideshtml/slideshtml.py index 4d55035..399764b 100644 --- a/test/Docbook/rootname/slideshtml/slideshtml.py +++ b/test/Docbook/rootname/slideshtml/slideshtml.py @@ -39,6 +39,7 @@ if not (sys.platform.startswith('linux') and try: import libxml2 + import libxslt except: try: import lxml diff --git a/test/FindSourceFiles.py b/test/FindSourceFiles.py index 3ba542b..88b9d7e 100644 --- a/test/FindSourceFiles.py +++ b/test/FindSourceFiles.py @@ -28,12 +28,13 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" Test Environment's FindSourceFiles method. """ +import sys import TestSCons test = TestSCons.TestSCons() package_format = "src_tarbz2" -if not test.where_is('tar'): +if not test.where_is('tar') or sys.platform == 'win32': if not test.where_is('zip'): test.skip_test("neither 'tar' nor 'zip' found; skipping test\n") package_format = "src_zip" diff --git a/test/Fortran/F08.py b/test/Fortran/F08.py new file mode 100644 index 0000000..35df37c --- /dev/null +++ b/test/Fortran/F08.py @@ -0,0 +1,151 @@ +#!/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 TestSCons + +from common import write_fake_link + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +write_fake_link(test) + +test.write('myfortran.py', r""" +import getopt +import sys +comment = '#' + sys.argv[1] +length = len(comment) +opts, args = getopt.getopt(sys.argv[2:], 'co:') +for opt, arg in opts: + if opt == '-o': out = arg +infile = open(args[0], 'rb') +outfile = open(out, 'wb') +for l in infile.readlines(): + if l[:length] != comment: + outfile.write(l) +sys.exit(0) +""") + +test.write('SConstruct', """ +env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + F08 = r'%(_python_)s myfortran.py f08', + FORTRAN = r'%(_python_)s myfortran.py fortran') +env.Program(target = 'test01', source = 'test01.f') +env.Program(target = 'test02', source = 'test02.F') +env.Program(target = 'test03', source = 'test03.for') +env.Program(target = 'test04', source = 'test04.FOR') +env.Program(target = 'test05', source = 'test05.ftn') +env.Program(target = 'test06', source = 'test06.FTN') +env.Program(target = 'test07', source = 'test07.fpp') +env.Program(target = 'test08', source = 'test08.FPP') +env.Program(target = 'test09', source = 'test09.f08') +env.Program(target = 'test10', source = 'test10.F08') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") +test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortran\n") +test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") +test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortran\n") +test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") +test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") +test.write('test09.f08', "This is a .f08 file.\n#link\n#f08\n") +test.write('test10.F08', "This is a .F08 file.\n#link\n#f08\n") + +test.run(arguments = '.', stderr = None) + +test.must_match('test01' + _exe, "This is a .f file.\n") +test.must_match('test02' + _exe, "This is a .F file.\n") +test.must_match('test03' + _exe, "This is a .for file.\n") +test.must_match('test04' + _exe, "This is a .FOR file.\n") +test.must_match('test05' + _exe, "This is a .ftn file.\n") +test.must_match('test06' + _exe, "This is a .FTN file.\n") +test.must_match('test07' + _exe, "This is a .fpp file.\n") +test.must_match('test08' + _exe, "This is a .FPP file.\n") +test.must_match('test09' + _exe, "This is a .f08 file.\n") +test.must_match('test10' + _exe, "This is a .F08 file.\n") + + +fc = 'f08' +g08 = test.detect_tool(fc) + +if g08: + + test.write("wrapper.py", +"""import os +import sys +open('%s', 'wb').write("wrapper.py\\n") +os.system(" ".join(sys.argv[1:])) +""" % test.workpath('wrapper.out').replace('\\', '\\\\')) + + test.write('SConstruct', """ +foo = Environment(F08 = '%(fc)s') +f08 = foo.Dictionary('F08') +bar = foo.Clone(F08 = r'%(_python_)s wrapper.py ' + f08) +foo.Program(target = 'foo', source = 'foo.f08') +bar.Program(target = 'bar', source = 'bar.f08') +""" % locals()) + + test.write('foo.f08', r""" + PROGRAM FOO + PRINT *,'foo.f08' + ENDPROGRAM FOO +""") + + test.write('bar.f08', r""" + PROGRAM BAR + PRINT *,'bar.f08' + ENDPROGRAM BAR +""") + + + test.run(arguments = 'foo' + _exe, stderr = None) + + test.run(program = test.workpath('foo'), stdout = " foo.f08\n") + + test.must_not_exist('wrapper.out') + + import sys + if sys.platform[:5] == 'sunos': + test.run(arguments = 'bar' + _exe, stderr = None) + else: + test.run(arguments = 'bar' + _exe) + + test.run(program = test.workpath('bar'), stdout = " bar.f08\n") + + test.must_match('wrapper.out', "wrapper.py\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/Fortran/F08COM.py b/test/Fortran/F08COM.py new file mode 100644 index 0000000..783a163 --- /dev/null +++ b/test/Fortran/F08COM.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__" + +import TestSCons + +from common import write_fake_link + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +write_fake_link(test) + +test.write('myfortran.py', r""" +import sys +comment = '#' + sys.argv[1] +outfile = open(sys.argv[2], 'wb') +infile = open(sys.argv[3], 'rb') +for l in infile.readlines(): + if l[:len(comment)] != comment: + outfile.write(l) +sys.exit(0) +""") + +test.write('SConstruct', """ +env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + F08COM = r'%(_python_)s myfortran.py f08 $TARGET $SOURCES', + F08PPCOM = r'%(_python_)s myfortran.py f08pp $TARGET $SOURCES', + FORTRANCOM = r'%(_python_)s myfortran.py fortran $TARGET $SOURCES', + FORTRANPPCOM = r'%(_python_)s myfortran.py fortranpp $TARGET $SOURCES') +env.Program(target = 'test01', source = 'test01.f') +env.Program(target = 'test02', source = 'test02.F') +env.Program(target = 'test03', source = 'test03.for') +env.Program(target = 'test04', source = 'test04.FOR') +env.Program(target = 'test05', source = 'test05.ftn') +env.Program(target = 'test06', source = 'test06.FTN') +env.Program(target = 'test07', source = 'test07.fpp') +env.Program(target = 'test08', source = 'test08.FPP') +env.Program(target = 'test09', source = 'test09.f08') +env.Program(target = 'test10', source = 'test10.F08') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortranpp\n") +test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortranpp\n") +test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") +test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortranpp\n") +test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortranpp\n") +test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortranpp\n") +test.write('test09.f08', "This is a .f08 file.\n#link\n#f08\n") +test.write('test10.F08', "This is a .F08 file.\n#link\n#f08pp\n") + +test.run(arguments = '.', stderr = None) + +test.must_match('test01' + _exe, "This is a .f file.\n") +test.must_match('test02' + _exe, "This is a .F file.\n") +test.must_match('test03' + _exe, "This is a .for file.\n") +test.must_match('test04' + _exe, "This is a .FOR file.\n") +test.must_match('test05' + _exe, "This is a .ftn file.\n") +test.must_match('test06' + _exe, "This is a .FTN file.\n") +test.must_match('test07' + _exe, "This is a .fpp file.\n") +test.must_match('test08' + _exe, "This is a .FPP file.\n") +test.must_match('test09' + _exe, "This is a .f08 file.\n") +test.must_match('test10' + _exe, "This is a .F08 file.\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/compat/_scons_hashlib.py b/test/Fortran/F08COMSTR.py index 04d31fa..65bf32c 100644 --- a/src/engine/SCons/compat/_scons_hashlib.py +++ b/test/Fortran/F08COMSTR.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # __COPYRIGHT__ # @@ -21,53 +22,54 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__doc__ = """ -hashlib backwards-compatibility module for older (pre-2.5) Python versions +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -This does not not NOT (repeat, *NOT*) provide complete hashlib -functionality. It only wraps the portions of MD5 functionality used -by SCons, in an interface that looks like hashlib (or enough for our -purposes, anyway). In fact, this module will raise an ImportError if -the underlying md5 module isn't available. -""" +import TestSCons -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +_python_ = TestSCons._python_ -import md5 -from string import hexdigits +test = TestSCons.TestSCons() -class md5obj(object): - md5_module = md5 - def __init__(self, name, string=''): - if not name in ('MD5', 'md5'): - raise ValueError("unsupported hash type") - self.name = 'md5' - self.m = self.md5_module.md5() +test.write('myfc.py', r""" +import sys +fline = '#'+sys.argv[1]+'\n' +outfile = open(sys.argv[2], 'wb') +infile = open(sys.argv[3], 'rb') +for l in [l for l in infile.readlines() if l != fline]: + outfile.write(l) +sys.exit(0) +""") - def __repr__(self): - return '<%s HASH object @ %#x>' % (self.name, id(self)) +if not TestSCons.case_sensitive_suffixes('.f','.F'): + f08pp = 'f08' +else: + f08pp = 'f08pp' - def copy(self): - import copy - result = copy.copy(self) - result.m = self.m.copy() - return result - def digest(self): - return self.m.digest() +test.write('SConstruct', """ +env = Environment(F08COM = r'%(_python_)s myfc.py f08 $TARGET $SOURCES', + F08COMSTR = 'Building f08 $TARGET from $SOURCES', + F08PPCOM = r'%(_python_)s myfc.py f08pp $TARGET $SOURCES', + F08PPCOMSTR = 'Building f08pp $TARGET from $SOURCES', + OBJSUFFIX='.obj') +env.Object(source = 'test01.f08') +env.Object(source = 'test02.F08') +""" % locals()) - def update(self, arg): - return self.m.update(arg) +test.write('test01.f08', "A .f08 file.\n#f08\n") +test.write('test02.F08', "A .F08 file.\n#%s\n" % f08pp) - def hexdigest(self): - return self.m.hexdigest() +test.run(stdout = test.wrap_stdout("""\ +Building f08 test01.obj from test01.f08 +Building %(f08pp)s test02.obj from test02.F08 +""" % locals())) -new = md5obj +test.must_match('test01.obj', "A .f08 file.\n") +test.must_match('test02.obj', "A .F08 file.\n") -def md5(string=''): - return md5obj('md5', string) +test.pass_test() # Local Variables: # tab-width:4 diff --git a/test/Fortran/F08FILESUFFIXES.py b/test/Fortran/F08FILESUFFIXES.py new file mode 100644 index 0000000..8463403 --- /dev/null +++ b/test/Fortran/F08FILESUFFIXES.py @@ -0,0 +1,101 @@ +#!/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 TestSCons + +from common import write_fake_link + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +write_fake_link(test) + +test.write('myfortran.py', r""" +import getopt +import sys +comment = '#' + sys.argv[1] +opts, args = getopt.getopt(sys.argv[2:], 'co:') +for opt, arg in opts: + if opt == '-o': out = arg +infile = open(args[0], 'rb') +outfile = open(out, 'wb') +for l in infile.readlines(): + if l[:len(comment)] != comment: + outfile.write(l) +sys.exit(0) +""") + +# Test default file suffix: .f90/.F90 for F90 +test.write('SConstruct', """ +env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + F08 = r'%(_python_)s myfortran.py f08', + FORTRAN = r'%(_python_)s myfortran.py fortran') +env.Program(target = 'test01', source = 'test01.f') +env.Program(target = 'test02', source = 'test02.F') +env.Program(target = 'test03', source = 'test03.for') +env.Program(target = 'test04', source = 'test04.FOR') +env.Program(target = 'test05', source = 'test05.ftn') +env.Program(target = 'test06', source = 'test06.FTN') +env.Program(target = 'test07', source = 'test07.fpp') +env.Program(target = 'test08', source = 'test08.FPP') +env.Program(target = 'test09', source = 'test09.f08') +env.Program(target = 'test10', source = 'test10.F08') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortranpp\n") +test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortranpp\n") +test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortranpp\n") +test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortranpp\n") +test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortranpp\n") +test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortranpp\n") +test.write('test09.f08', "This is a .f08 file.\n#link\n#f08\n") +test.write('test10.F08', "This is a .F08 file.\n#link\n#f08pp\n") + +test.run(arguments = '.', stderr = None) + +test.must_match('test01' + _exe, "This is a .f file.\n") +test.must_match('test02' + _exe, "This is a .F file.\n") +test.must_match('test03' + _exe, "This is a .for file.\n") +test.must_match('test04' + _exe, "This is a .FOR file.\n") +test.must_match('test05' + _exe, "This is a .ftn file.\n") +test.must_match('test06' + _exe, "This is a .FTN file.\n") +test.must_match('test07' + _exe, "This is a .fpp file.\n") +test.must_match('test08' + _exe, "This is a .FPP file.\n") +test.must_match('test09' + _exe, "This is a .f08 file.\n") +test.must_match('test10' + _exe, "This is a .F08 file.\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/Fortran/F08FILESUFFIXES2.py b/test/Fortran/F08FILESUFFIXES2.py new file mode 100644 index 0000000..39bba44 --- /dev/null +++ b/test/Fortran/F08FILESUFFIXES2.py @@ -0,0 +1,91 @@ +#!/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 TestSCons + +from common import write_fake_link + +_python_ = TestSCons._python_ +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +write_fake_link(test) + +test.write('myfortran.py', r""" +import getopt +import sys +comment = '#' + sys.argv[1] +opts, args = getopt.getopt(sys.argv[2:], 'co:') +for opt, arg in opts: + if opt == '-o': out = arg +infile = open(args[0], 'rb') +outfile = open(out, 'wb') +for l in infile.readlines(): + if l[:len(comment)] != comment: + outfile.write(l) +sys.exit(0) +""") + +# Test non-default file suffix: .f/.F for F08 +test.write('SConstruct', """ +env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + F77 = r'%(_python_)s myfortran.py f77', + F08 = r'%(_python_)s myfortran.py f08', + F08FILESUFFIXES = ['.f', '.F', '.f08', '.F08'], + tools = ['default', 'f08']) +env.Program(target = 'test01', source = 'test01.f') +env.Program(target = 'test02', source = 'test02.F') +env.Program(target = 'test03', source = 'test03.f08') +env.Program(target = 'test04', source = 'test04.F08') +env.Program(target = 'test05', source = 'test05.f77') +env.Program(target = 'test06', source = 'test06.F77') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#link\n#f08\n") +test.write('test02.F', "This is a .F file.\n#link\n#f08\n") +test.write('test03.f08', "This is a .f08 file.\n#link\n#f08\n") +test.write('test04.F08', "This is a .F08 file.\n#link\n#f08\n") +test.write('test05.f77', "This is a .f77 file.\n#link\n#f77\n") +test.write('test06.F77', "This is a .F77 file.\n#link\n#f77\n") + +test.run(arguments = '.', stderr = None) + +test.must_match('test01' + _exe, "This is a .f file.\n") +test.must_match('test02' + _exe, "This is a .F file.\n") +test.must_match('test03' + _exe, "This is a .f08 file.\n") +test.must_match('test04' + _exe, "This is a .F08 file.\n") +test.must_match('test05' + _exe, "This is a .f77 file.\n") +test.must_match('test06' + _exe, "This is a .F77 file.\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/Fortran/F08FLAGS.py b/test/Fortran/F08FLAGS.py new file mode 100644 index 0000000..866ea2c --- /dev/null +++ b/test/Fortran/F08FLAGS.py @@ -0,0 +1,158 @@ +#!/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 TestSCons + +from common import write_fake_link + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() +_exe = TestSCons._exe + +write_fake_link(test) + +test.write('myfortran.py', r""" +import getopt +import sys +comment = '#' + sys.argv[1] +opts, args = getopt.getopt(sys.argv[2:], 'co:xy') +optstring = '' +for opt, arg in opts: + if opt == '-o': out = arg + else: optstring = optstring + ' ' + opt +infile = open(args[0], 'rb') +outfile = open(out, 'wb') +outfile.write(optstring + "\n") +for l in infile.readlines(): + if l[:len(comment)] != comment: + outfile.write(l) +sys.exit(0) +""") + + + +test.write('SConstruct', """ +env = Environment(LINK = r'%(_python_)s mylink.py', + LINKFLAGS = [], + F08 = r'%(_python_)s myfortran.py g08', + F08FLAGS = '-x', + FORTRAN = r'%(_python_)s myfortran.py fortran', + FORTRANFLAGS = '-y') +env.Program(target = 'test01', source = 'test01.f') +env.Program(target = 'test02', source = 'test02.F') +env.Program(target = 'test03', source = 'test03.for') +env.Program(target = 'test04', source = 'test04.FOR') +env.Program(target = 'test05', source = 'test05.ftn') +env.Program(target = 'test06', source = 'test06.FTN') +env.Program(target = 'test07', source = 'test07.fpp') +env.Program(target = 'test08', source = 'test08.FPP') +env.Program(target = 'test09', source = 'test09.f08') +env.Program(target = 'test10', source = 'test10.F08') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#link\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#link\n#fortran\n") +test.write('test03.for', "This is a .for file.\n#link\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#link\n#fortran\n") +test.write('test05.ftn', "This is a .ftn file.\n#link\n#fortran\n") +test.write('test06.FTN', "This is a .FTN file.\n#link\n#fortran\n") +test.write('test07.fpp', "This is a .fpp file.\n#link\n#fortran\n") +test.write('test08.FPP', "This is a .FPP file.\n#link\n#fortran\n") +test.write('test09.f08', "This is a .f08 file.\n#link\n#g08\n") +test.write('test10.F08', "This is a .F08 file.\n#link\n#g08\n") + +test.run(arguments = '.', stderr = None) + +test.must_match('test01' + _exe, " -c -y\nThis is a .f file.\n") +test.must_match('test02' + _exe, " -c -y\nThis is a .F file.\n") +test.must_match('test03' + _exe, " -c -y\nThis is a .for file.\n") +test.must_match('test04' + _exe, " -c -y\nThis is a .FOR file.\n") +test.must_match('test05' + _exe, " -c -y\nThis is a .ftn file.\n") +test.must_match('test06' + _exe, " -c -y\nThis is a .FTN file.\n") +test.must_match('test07' + _exe, " -c -y\nThis is a .fpp file.\n") +test.must_match('test08' + _exe, " -c -y\nThis is a .FPP file.\n") +test.must_match('test09' + _exe, " -c -x\nThis is a .f08 file.\n") +test.must_match('test10' + _exe, " -c -x\nThis is a .F08 file.\n") + + +fc = 'f08' +g08 = test.detect_tool(fc) + + +if g08: + + test.write("wrapper.py", +"""import os +import sys +open('%s', 'wb').write("wrapper.py\\n") +os.system(" ".join(sys.argv[1:])) +""" % test.workpath('wrapper.out').replace('\\', '\\\\')) + + test.write('SConstruct', """ +foo = Environment(F08 = '%(fc)s') +f08 = foo.Dictionary('F08') +bar = foo.Clone(F08 = r'%(_python_)s wrapper.py ' + f08, F08FLAGS = '-Ix') +foo.Program(target = 'foo', source = 'foo.f08') +bar.Program(target = 'bar', source = 'bar.f08') +""" % locals()) + + test.write('foo.f08', r""" + PROGRAM FOO + PRINT *,'foo.f08' + ENDPROGRAM FOO +""") + + test.write('bar.f08', r""" + PROGRAM BAR + PRINT *,'bar.f08' + ENDPROGRAM FOO +""") + + + test.run(arguments = 'foo' + _exe, stderr = None) + + test.run(program = test.workpath('foo'), stdout = " foo.f08\n") + + test.must_not_exist('wrapper.out') + + import sys + if sys.platform[:5] == 'sunos': + test.run(arguments = 'bar' + _exe, stderr = None) + else: + test.run(arguments = 'bar' + _exe) + + test.run(program = test.workpath('bar'), stdout = " bar.f08\n") + + test.must_match('wrapper.out', "wrapper.py\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/Fortran/FORTRANSUFFIXES.py b/test/Fortran/FORTRANSUFFIXES.py index 583b71b..9673e6f 100644 --- a/test/Fortran/FORTRANSUFFIXES.py +++ b/test/Fortran/FORTRANSUFFIXES.py @@ -56,51 +56,51 @@ env = Environment(FORTRANPATH = ['.'], env.Append(FORTRANSUFFIXES = ['.x']) env.Object(target = 'test1', source = 'test1.f') env.InstallAs('test1_f', 'test1.f') -env.InstallAs('test1_h', 'test1.h') env.InstallAs('test1_x', 'test1.x') +env.InstallAs('test2_f', 'test2.f') """ % locals()) test.write('test1.f', """\ test1.f 1 - INCLUDE 'test1.h' + INCLUDE 'test2.f' INCLUDE 'test1.x' """) -test.write('test1.h', """\ - test1.h 1 - INCLUDE 'foo.h' +test.write('test2.f', """\ + test2.f 1 + INCLUDE 'foo.f' """) test.write('test1.x', """\ test1.x 1 - INCLUDE 'foo.h' + INCLUDE 'foo.f' """) -test.write('foo.h', """\ - foo.h 1 +test.write('foo.f', """\ + foo.f 1 """) expect = test.wrap_stdout("""\ %(_python_)s myfc.py test1.o test1.f Install file: "test1.f" as "test1_f" -Install file: "test1.h" as "test1_h" Install file: "test1.x" as "test1_x" +Install file: "test2.f" as "test2_f" """ % locals()) test.run(arguments='.', stdout=expect) test.must_match('test1.o', """\ test1.f 1 - test1.h 1 - foo.h 1 + test2.f 1 + foo.f 1 test1.x 1 - foo.h 1 + foo.f 1 """) test.up_to_date(arguments='.') -test.write('foo.h', """\ - foo.h 2 +test.write('foo.f', """\ + foo.f 2 """) expect = test.wrap_stdout("""\ @@ -111,17 +111,17 @@ test.run(arguments='.', stdout=expect) test.must_match('test1.o', """\ test1.f 1 - test1.h 1 - foo.h 2 + test2.f 1 + foo.f 2 test1.x 1 - foo.h 2 + foo.f 2 """) test.up_to_date(arguments='.') test.write('test1.x', """\ test1.x 2 - INCLUDE 'foo.h' + INCLUDE 'foo.f' """) expect = test.wrap_stdout("""\ @@ -133,32 +133,32 @@ test.run(arguments='.', stdout=expect) test.must_match('test1.o', """\ test1.f 1 - test1.h 1 - foo.h 2 + test2.f 1 + foo.f 2 test1.x 2 - foo.h 2 + foo.f 2 """) test.up_to_date(arguments='.') -test.write('test1.h', """\ - test1.h 2 - INCLUDE 'foo.h' +test.write('test2.f', """\ + test2.f 2 + INCLUDE 'foo.f' """) expect = test.wrap_stdout("""\ %(_python_)s myfc.py test1.o test1.f -Install file: "test1.h" as "test1_h" +Install file: "test2.f" as "test2_f" """ % locals()) test.run(arguments='.', stdout=expect) test.must_match('test1.o', """\ test1.f 1 - test1.h 2 - foo.h 2 + test2.f 2 + foo.f 2 test1.x 2 - foo.h 2 + foo.f 2 """) test.up_to_date(arguments='.') diff --git a/test/Fortran/SHF08.py b/test/Fortran/SHF08.py new file mode 100644 index 0000000..85f2bcd --- /dev/null +++ b/test/Fortran/SHF08.py @@ -0,0 +1,144 @@ +#!/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 TestSCons + +_python_ = TestSCons._python_ +_obj = TestSCons._shobj +obj_ = TestSCons.shobj_ + +test = TestSCons.TestSCons() + + + +test.write('myfortran.py', r""" +import getopt +import sys +comment = '#' + sys.argv[1] +opts, args = getopt.getopt(sys.argv[2:], 'cf:o:K:') +for opt, arg in opts: + if opt == '-o': out = arg +infile = open(args[0], 'rb') +outfile = open(out, 'wb') +for l in infile.readlines(): + if l[:len(comment)] != comment: + outfile.write(l) +sys.exit(0) +""") + + + +test.write('SConstruct', """ +env = Environment(SHF08 = r'%(_python_)s myfortran.py g08', + SHFORTRAN = r'%(_python_)s myfortran.py fortran') +env.SharedObject(target = 'test01', source = 'test01.f') +env.SharedObject(target = 'test02', source = 'test02.F') +env.SharedObject(target = 'test03', source = 'test03.for') +env.SharedObject(target = 'test04', source = 'test04.FOR') +env.SharedObject(target = 'test05', source = 'test05.ftn') +env.SharedObject(target = 'test06', source = 'test06.FTN') +env.SharedObject(target = 'test07', source = 'test07.fpp') +env.SharedObject(target = 'test08', source = 'test08.FPP') +env.SharedObject(target = 'test09', source = 'test09.f08') +env.SharedObject(target = 'test10', source = 'test10.F08') +""" % locals()) + +test.write('test01.f', "This is a .f file.\n#fortran\n") +test.write('test02.F', "This is a .F file.\n#fortran\n") +test.write('test03.for', "This is a .for file.\n#fortran\n") +test.write('test04.FOR', "This is a .FOR file.\n#fortran\n") +test.write('test05.ftn', "This is a .ftn file.\n#fortran\n") +test.write('test06.FTN', "This is a .FTN file.\n#fortran\n") +test.write('test07.fpp', "This is a .fpp file.\n#fortran\n") +test.write('test08.FPP', "This is a .FPP file.\n#fortran\n") +test.write('test09.f08', "This is a .f08 file.\n#g08\n") +test.write('test10.F08', "This is a .F08 file.\n#g08\n") + +test.run(arguments = '.', stderr = None) + +test.must_match(obj_ + 'test01' + _obj, "This is a .f file.\n") +test.must_match(obj_ + 'test02' + _obj, "This is a .F file.\n") +test.must_match(obj_ + 'test03' + _obj, "This is a .for file.\n") +test.must_match(obj_ + 'test04' + _obj, "This is a .FOR file.\n") +test.must_match(obj_ + 'test05' + _obj, "This is a .ftn file.\n") +test.must_match(obj_ + 'test06' + _obj, "This is a .FTN file.\n") +test.must_match(obj_ + 'test07' + _obj, "This is a .fpp file.\n") +test.must_match(obj_ + 'test08' + _obj, "This is a .FPP file.\n") +test.must_match(obj_ + 'test09' + _obj, "This is a .f08 file.\n") +test.must_match(obj_ + 'test10' + _obj, "This is a .F08 file.\n") + +fc = 'f08' +g08 = test.detect_tool(fc) + +if g08: + + test.write("wrapper.py", +"""import os +import sys +open('%s', 'wb').write("wrapper.py\\n") +os.system(" ".join(sys.argv[1:])) +""" % test.workpath('wrapper.out').replace('\\', '\\\\')) + + test.write('SConstruct', """ +foo = Environment(SHF08 = '%(fc)s') +shf08 = foo.Dictionary('SHF08') +bar = foo.Clone(SHF08 = r'%(_python_)s wrapper.py ' + shf08) +foo.SharedObject(target = 'foo/foo', source = 'foo.f08') +bar.SharedObject(target = 'bar/bar', source = 'bar.f08') +""" % locals()) + + test.write('foo.f08', r""" + PROGRAM FOO + PRINT *,'foo.f08' + ENDPROGRAM FOO +""") + + test.write('bar.f08', r""" + PROGRAM BAR + PRINT *,'bar.f08' + ENDPROGRAM BAR +""") + + + test.run(arguments = 'foo', stderr = None) + + test.must_not_exist('wrapper.out') + + import sys + if sys.platform[:5] == 'sunos': + test.run(arguments = 'bar', stderr = None) + else: + test.run(arguments = 'bar') + + test.must_match('wrapper.out', "wrapper.py\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/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 new file mode 100644 index 0000000..fe93b82 --- /dev/null +++ b/test/Glob/exclude.py @@ -0,0 +1,86 @@ +#!/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__" + +""" +Verify the "exclude" parameter usage of the Glob() function. + - with or without subdir + - with or without returning strings + - a file in a subdir is not excluded by a pattern without this subdir +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +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('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.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))) +""") + +test.write('f1.in', "f1.in\n") +test.write('f2.in', "f2.in\n") +test.write('f3.in', "f3.in\n") +test.write('f4.in', "f4.in\n") +test.write('f5.in', "f5.in\n") + +test.subdir('d1', 'd2') +test.write(['d1', 'f1.in'], "d1/f1.in\n") +test.write(['d1', 'f2.in'], "d1/f2.in\n") +test.write(['d1', 'f3.in'], "d1/f3.in\n") +test.write(['d2', 'f1.in'], "d2/f1.in\n") +test.write(['d2', 'f2.in'], "d2/f2.in\n") +test.write(['d2', 'f3.in'], "d2/f3.in\n") + +test.run(arguments = '.') + +test.must_match('fa.out', "f1.in\nf3.in\nf5.in\n") +test.must_match('fb.out', "f1.in\nf3.in\nf5.in\n") +test.must_match('fc.out', "d1/f2.in\nd1/f3.in\nd2/f2.in\nd2/f3.in\n") +test.must_match('fd.out', "d1/f2.in\nd1/f3.in\nd2/f2.in\nd2/f3.in\n") +test.must_match('fe.out', "f2.in\nf3.in\nf4.in\nf5.in\n") +test.must_match('ff.out', "f1.in\nf2.in\nf3.in\nf4.in\nf5.in\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/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/IDL/IDLSUFFIXES.py b/test/IDL/IDLSUFFIXES.py index f71ceba..0a9a50c 100644 --- a/test/IDL/IDLSUFFIXES.py +++ b/test/IDL/IDLSUFFIXES.py @@ -60,11 +60,6 @@ test.up_to_date(arguments='.') test.write('foo.h', "foo.h 2\n") -test.run(arguments='.', stdout=test.wrap_stdout("""\ -Install file: "foo.idl" as "foo_idl" -Install file: "foo.x" as "foo_x" -""")) - test.up_to_date(arguments='.') test.pass_test() 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/configure.py b/test/Interactive/configure.py new file mode 100644 index 0000000..1a92f6c --- /dev/null +++ b/test/Interactive/configure.py @@ -0,0 +1,127 @@ +#!/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__" + +""" +Verify basic operation of the --interactive command line option to build +a target, while using a Configure context within the environment. + +Also tests that "b" can be used as a synonym for "build". +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('mycc.py', r""" +import sys +outfile = open(sys.argv[1], 'wb') +infile = open(sys.argv[2], 'rb') +for l in [l for l in infile.readlines() if l[:7] != '/*c++*/']: + outfile.write(l) +sys.exit(0) +""") + +test.write('SConstruct', """\ +env = Environment(CXXCOM = r'%(_python_)s mycc.py $TARGET $SOURCE', + OBJSUFFIX='.obj') + +# Ensure that our 'compiler' works... +def CheckMyCC(context): + context.Message('Checking for MyCC compiler...') + result = context.TryBuild(context.env.Object, + 'int main(void) {return 0;}', + '.cpp') + context.Result(result) + return result + +conf = Configure(env, + custom_tests = {'CheckMyCC' : CheckMyCC}) + +if conf.CheckMyCC(): + pass # build succeeded +else: + Exit(1) +conf.Finish() + +env.Object('foo.obj','foo.cpp') +""" % locals()) + +test.write('foo.cpp', """\ +#include <stdio.h> + +int main (int argc, char *argv[]) +{ + printf("Hello, World!"); + return 0; +} +""") + + +scons = test.start(arguments = '-Q --interactive') +# Initial build +scons.send("build foo.obj\n") + +test.wait_for(test.workpath('foo.obj')) +# Update without any changes -> no action +scons.send("build foo.obj\n") +# Changing the source file +test.write('foo.cpp', """\ +#include <stdio.h> + +void foo() +{ + ; +} + +int main (int argc, char *argv[]) +{ + printf("Hello, World!"); + return 0; +} +""") + +# Verify that "b" can be used as a synonym for the "build" command. +scons.send("b foo.obj\n") + +scons.send("build foo.obj\n") + +expect_stdout = r"""scons>>> .*foo\.cpp.* +scons>>> .*foo\.cpp.* +scons>>> scons: `foo.obj' is up to date. +scons>>> scons: `foo.obj' is up to date. +scons>>>\s* +""" + +test.finish(scons, stdout = expect_stdout, match=TestSCons.match_re) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: 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/Java/DerivedSourceTest.py b/test/Java/DerivedSourceTest.py index c749cf3..7f80595 100644 --- a/test/Java/DerivedSourceTest.py +++ b/test/Java/DerivedSourceTest.py @@ -45,14 +45,14 @@ SCons.Defaults.DefaultEnvironment(tools = []) test = TestSCons.TestSCons() -# No result if tools not available -test.no_result( condition=(test.where_is( 'javac' ) is None) ) -test.no_result( condition=(test.where_is( 'jar' ) is None) ) - # This test is known to fail as of July 2014; see Tigris issue 1771 and issue 2931. # Once the underlying issue is corrected, this test should be re-enabled. test.skip_test('Skipping derived-source test until issue 1771 is fixed.\n') +# No result if tools not available +test.no_result( condition=(test.where_is( 'javac' ) is None) ) +test.no_result( condition=(test.where_is( 'jar' ) is None) ) + test.write( ['Sample.java'], """ diff --git a/test/LEX/live.py b/test/LEX/live.py index f50b06f..e4b4dfb 100644 --- a/test/LEX/live.py +++ b/test/LEX/live.py @@ -68,6 +68,7 @@ yywrap() return 1; } +int main() { yylex(); 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/compat/all.py b/test/MSVC/pch-basics.py index ac7a6ea..45735ed 100644 --- a/test/compat/all.py +++ b/test/MSVC/pch-basics.py @@ -21,43 +21,57 @@ # 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__" - + """ -Verify that we can use the any() function (in any supported Python -version we happen to be testing). - -This test can be retired at some point in the distant future when Python -2.5 becomes the minimum version supported by SCons. +Verify PCH works to build a simple exe and a simple dll. """ - + +import time + import TestSCons - -test = TestSCons.TestSCons() - -test.write('SConstruct', """\ -print all([True, 1]) and "YES" or "NO" -print all([0]) and "YES" or "NO" -SConscript('SConscript') + +test = TestSCons.TestSCons(match = TestSCons.match_re) + +test.skip_if_not_msvc() + +test.write('Main.cpp', """\ +#include "Precompiled.h" + +int main() +{ + return testf(); +} """) - -test.write('SConscript', """\ -print all([1, False]) and "YES" or "NO" -print all([True, None]) and "YES" or "NO" + +test.write('Precompiled.cpp', """\ +#include "Precompiled.h" """) - -expect = """\ -YES -NO -NO -NO -""" - -test.run(arguments = '-Q -q', stdout = expect) - + +test.write('Precompiled.h', """\ +#pragma once + +static int testf() +{ + return 0; +} +""") + +test.write('SConstruct', """\ +env = Environment() + +env['PCHSTOP'] = 'Precompiled.h' +env['PCH'] = env.PCH('Precompiled.cpp')[0] + +env.SharedLibrary('pch_dll', 'Main.cpp') +env.Program('pch_exe', 'Main.cpp') +""") + +test.run(arguments='.', stderr=None) + test.pass_test() - + # Local Variables: # tab-width:4 # indent-tabs-mode:nil 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/QT/manual.py b/test/QT/manual.py index ff38f32..d911fb3 100644 --- a/test/QT/manual.py +++ b/test/QT/manual.py @@ -46,13 +46,15 @@ sources = ['aaa.cpp', 'bbb.cpp', 'ddd.cpp', 'eee.cpp', 'main.cpp'] # normal invocation sources.append(env.Moc('include/aaa.h')) -env.Moc('bbb.cpp') +moc = env.Moc('bbb.cpp') +env.Ignore( moc, moc ) sources.extend(env.Uic('ui/ccc.ui')[1:]) # manual target specification sources.append(env.Moc('moc-ddd.cpp', 'include/ddd.h', QT_MOCHPREFIX='')) # Watch out ! -env.Moc('moc_eee.cpp', 'eee.cpp') +moc = env.Moc('moc_eee.cpp', 'eee.cpp') +env.Ignore( moc, moc ) sources.extend(env.Uic(['include/uic_fff.hpp', 'fff.cpp', 'fff.moc.cpp'], 'ui/fff.ui')[1:]) diff --git a/test/RANLIB/RANLIB.py b/test/RANLIB/RANLIB.py index c9fc10f..fbfe36c 100644 --- a/test/RANLIB/RANLIB.py +++ b/test/RANLIB/RANLIB.py @@ -81,6 +81,10 @@ library_function(void) test.write('main.c', r""" #include <stdlib.h> + +void +library_function(void); + int main(int argc, char *argv[]) { diff --git a/test/RANLIB/RANLIBFLAGS.py b/test/RANLIB/RANLIBFLAGS.py index 636059b..86e5283 100644 --- a/test/RANLIB/RANLIBFLAGS.py +++ b/test/RANLIB/RANLIBFLAGS.py @@ -82,6 +82,10 @@ library_function(void) test.write('main.c', r""" #include <stdlib.h> +void +library_function(void); + + int main(int argc, char *argv[]) { diff --git a/test/SWIG/SWIG.py b/test/SWIG/SWIG.py index 96e00e7..d53fa49 100644 --- a/test/SWIG/SWIG.py +++ b/test/SWIG/SWIG.py @@ -43,10 +43,20 @@ if not python: test.write('myswig.py', r""" import getopt import sys -opts, args = getopt.getopt(sys.argv[1:], 'c:o:') +opts, args = getopt.getopt(sys.argv[1:], 'c:o:v:') for opt, arg in opts: if opt == '-c': pass elif opt == '-o': out = arg + elif opt == '-v' and arg == 'ersion': + print "" + print "SWIG Version 0.1.2" + print "" + print "Compiled with g++ [x86_64-pc-linux-gnu]" + print "" + print "Configured options: +pcre" + print "" + print "Please see http://www.swig.org for reporting bugs and further information" + sys.exit(0) infile = open(args[0], 'rb') outfile = open(out, 'wb') for l in infile.readlines(): @@ -58,6 +68,7 @@ sys.exit(0) test.write('SConstruct', """ env = Environment(tools=['default', 'swig'], SWIG = [r'%(python)s', 'myswig.py']) +print env.subst("Using SWIG $SWIGVERSION") env.Program(target = 'test1', source = 'test1.i') env.CFile(target = 'test2', source = 'test2.i') env.Clone(SWIGFLAGS = '-c++').Program(target = 'test3', source = 'test3.i') @@ -91,7 +102,7 @@ main(int argc, char *argv[]) { swig """) -test.run(arguments = '.', stderr = None) +test.run(arguments = '.', stderr = None, stdout = r'.*Using SWIG 0.1.2.*', match = TestSCons.match_re_dotall) test.run(program = test.workpath('test1' + _exe), stdout = "test1.i\n") test.must_exist(test.workpath('test1_wrap.c')) diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py new file mode 100644 index 0000000..364bd73 --- /dev/null +++ b/test/SWIG/recursive-includes-cpp.py @@ -0,0 +1,123 @@ +#!/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__" + +""" +Verify that SWIG include directives produce the correct dependencies +in cases of recursive inclusion. +""" + +import os +import TestSCons +from SCons.Defaults import DefaultEnvironment + +DefaultEnvironment( tools = [ 'swig' ] ) + +test = TestSCons.TestSCons() + +# Check for prerequisites of this test. +for pre_req in ['swig', 'python']: + if not test.where_is(pre_req): + test.skip_test('Can not find installed "' + pre_req + '", skipping test.%s' % os.linesep) + +test.write("recursive.h", """\ +/* An empty header file. */ +""") + +test.write("main.h", """\ +#include "recursive.h" +""") + +test.write("main.c", """\ +#include "main.h" +""") + +test.write("mod.i", """\ +%module mod + +%include "main.h" + +#include "main.h" +""") + +test.write('SConstruct', """\ +import distutils.sysconfig + +DefaultEnvironment( tools = [ 'swig' ] ) + +env = Environment( + SWIGFLAGS = [ + '-python' + ], + CPPPATH = [ + distutils.sysconfig.get_python_inc() + ], + SHLIBPREFIX = "" +) + +env.SharedLibrary( + 'mod.so', + [ + "mod.i", + "main.c", + ] +) +""") + +expectMain = """\ ++-main.os + +-main.c + +-main.h + +-recursive.h""" + +expectMod = """\ ++-mod_wrap.os + +-mod_wrap.c + | +-mod.i + | +-main.h + | +-recursive.h""" + +# Validate that the recursive dependencies are found with SWIG scanning first. +test.run( arguments = '--tree=all mod_wrap.os main.os' ) + +test.must_contain_all( test.stdout(), expectMain ) +test.must_contain_all( test.stdout(), expectMod ) + +# Validate that the recursive dependencies are found consistently. +test.run( arguments = '--tree=all main.os mod_wrap.os' ) + +test.must_contain_all( test.stdout(), expectMain ) +test.must_contain_all( test.stdout(), expectMod ) + +test.run() +test.up_to_date() + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/CrossLanguageNoExtension.py b/test/Scanner/CrossLanguageNoExtension.py new file mode 100644 index 0000000..5bf205f --- /dev/null +++ b/test/Scanner/CrossLanguageNoExtension.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__" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +# Test behavior of Scanners when evaluating implicit dependencies +# for nodes that do not have mappings from their scanner_key +# to a scanner instance + +test.write('SConstruct', """ +import re + +include_re = re.compile(r'^include\s+(\S+)$', re.M) + +def scan(node, env, scanpaths, arg): + contents = node.get_text_contents() + includes = include_re.findall(contents) + return includes + +def kfile_scan(node, env, scanpaths, arg): + print 'kscan: ' + str(node) + return scan(node, env, scanpaths, arg) + +def k2file_scan(node, env, scanpaths, arg): + print 'k2scan: ' + str(node) + return scan(node, env, scanpaths, arg) + +kscan = Scanner(name = 'kfile', + function = kfile_scan, + argument = None, + skeys = ['.k'], + recursive = True) + +k2scan = Scanner(name = 'k2', + function = k2file_scan, + argument = None, + skeys = ['.k2']) + +k2scan2 = Scanner(name = 'k2', + function = k2file_scan, + argument = None, + skeys = ['']) + +env1 = Environment() +env1.Append(SCANNERS = [ kscan, k2scan ] ) +env1.Command( 'k', 'foo.k', Copy( '$TARGET', '$SOURCE' ) ) + +env2 = env1.Clone() +env2.Append(SCANNERS = [ k2scan2 ] ) +env2.Command( 'k2', 'foo.k', Copy( '$TARGET', '$SOURCE' ) ) +""") + +test.write('foo.k', +"""foo.k 1 line 1 +include xxx.k +include yyy +foo.k 1 line 4 +""") + +test.write('xxx.k', "xxx.k 1\n") +test.write('yyy', "yyy 1\n") +test.write('yyy.k2', "yyy.k2 1\n") + +expected_stdout = test.wrap_stdout("""\ +kscan: foo.k +kscan: xxx.k +kscan: yyy +Copy("k", "foo.k") +kscan: foo.k +kscan: xxx.k +k2scan: yyy +Copy("k2", "foo.k") +""") + +test.run(arguments='k k2', stdout=expected_stdout) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: 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/Scanner/generated.py b/test/Scanner/generated.py index 82206c2..7246cbe 100644 --- a/test/Scanner/generated.py +++ b/test/Scanner/generated.py @@ -338,6 +338,7 @@ class CScannerCounter(object): import SCons.Tool MyCScanner = CScannerCounter(SCons.Script.CScanner) SCons.Tool.SourceFileScanner.add_scanner('.c', MyCScanner) +SCons.Tool.SourceFileScanner.add_scanner('.h', MyCScanner) env = Environment(CPPPATH = ".") l = env.StaticLibrary("g", Split("libg_1.c libg_2.c libg_3.c")) diff --git a/test/Script-import.py b/test/Script-import.py index 98ae271..504de0e 100644 --- a/test/Script-import.py +++ b/test/Script-import.py @@ -62,7 +62,6 @@ SCons.Script.CleanTask SCons.Script.QuestionTask old_SCons_Script_variables = [ - 'PrintHelp', 'OptParser', 'keep_going_on_error', 'print_explanations', diff --git a/test/TEX/bibliography.py b/test/TEX/bibliography.py index 5e26f6e..c26b010 100644 --- a/test/TEX/bibliography.py +++ b/test/TEX/bibliography.py @@ -118,9 +118,6 @@ test.must_not_exist(test.workpath('simple.blg')) test.pass_test() - -# FUTURE: - test.write('SConstruct', """\ env = Environment(tools = ['tex', 'latex', 'dvips']) env.PostScript('d00', 'd00.tex') 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/basic.py b/test/explain/basic.py index 5e31cfd..1072ac4 100644 --- a/test/explain/basic.py +++ b/test/explain/basic.py @@ -169,10 +169,18 @@ test.write(['src', 'file6.in'], "file6.in 1\n") test.write(['src', 'subdir', 'file7.in'], "subdir/file7.in 1\n") -args = '--debug=explain .' +args = '--debug=explain ..' expect = test.wrap_stdout("""\ +scons: building `%(inc_aaa)s' because it doesn't exist +Install file: "aaa" as "%(inc_aaa)s" +scons: building `%(inc_bbb_k)s' because it doesn't exist +Install file: "bbb.k" as "%(inc_bbb_k)s" +scons: building `%(inc_ddd)s' because it doesn't exist +Install file: "ddd" as "%(inc_ddd)s" +scons: building `%(inc_eee)s' because it doesn't exist +Install file: "eee.in" as "%(inc_eee)s" scons: building `file1' because it doesn't exist %(_python_)s %(cat_py)s file1 file1.in scons: building `file2' because it doesn't exist @@ -181,14 +189,6 @@ scons: building `file3' because it doesn't exist %(_python_)s %(cat_py)s file3 xxx yyy zzz scons: building `file4' because it doesn't exist %(_python_)s %(cat_py)s file4 - file4.in -scons: building `%(inc_aaa)s' because it doesn't exist -Install file: "aaa" as "%(inc_aaa)s" -scons: building `%(inc_ddd)s' because it doesn't exist -Install file: "ddd" as "%(inc_ddd)s" -scons: building `%(inc_eee)s' because it doesn't exist -Install file: "eee.in" as "%(inc_eee)s" -scons: building `%(inc_bbb_k)s' because it doesn't exist -Install file: "bbb.k" as "%(inc_bbb_k)s" scons: building `file5' because it doesn't exist %(_python_)s %(cat_py)s file5 file5.k scons: building `file6' because it doesn't exist @@ -236,6 +236,8 @@ test_value = '"second"' WriteInitialTest( locals() ) expect = test.wrap_stdout("""\ +scons: rebuilding `%(inc_bbb_k)s' because `bbb.k' changed +Install file: "bbb.k" as "%(inc_bbb_k)s" scons: rebuilding `file1' because `file1.in' changed %(_python_)s %(cat_py)s file1 file1.in scons: rebuilding `file2' because `yyy' changed @@ -244,11 +246,6 @@ scons: rebuilding `file3' because: `yyy' changed `zzz' changed %(_python_)s %(cat_py)s file3 xxx yyy zzz -scons: rebuilding `%(inc_bbb_k)s' because: - `%(inc_ddd)s' is no longer a dependency - `%(inc_eee)s' is no longer a dependency - `bbb.k' changed -Install file: "bbb.k" as "%(inc_bbb_k)s" scons: rebuilding `file5' because `%(inc_bbb_k)s' changed %(_python_)s %(cat_py)s file5 file5.k scons: rebuilding `file6' because AlwaysBuild() is specified 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/explain/save-info.py b/test/explain/save-info.py index d2ffc7d..af4c3f5 100644 --- a/test/explain/save-info.py +++ b/test/explain/save-info.py @@ -141,7 +141,7 @@ file5.k 1 line 4 test.write(['src', 'subdir', 'file6.in'], "subdir/file6.in 1\n") # -test.run(chdir='src', arguments='.') +test.run(chdir='src', arguments='..') test.must_match(['src', 'file1'], "file1.in 1\n") test.must_match(['src', 'file2'], """\ @@ -176,10 +176,7 @@ scons: rebuilding `file3' because: `yyy' changed `zzz' changed %(_python_)s %(cat_py)s file3 xxx yyy zzz -scons: rebuilding `%(inc_bbb_k)s' because: - `%(inc_ddd)s' is no longer a dependency - `%(inc_eee)s' is no longer a dependency - `bbb.k' changed +scons: rebuilding `%(inc_bbb_k)s' because `bbb.k' changed Install file: "bbb.k" as "%(inc_bbb_k)s" scons: rebuilding `file5' because `%(inc_bbb_k)s' changed %(_python_)s %(cat_py)s file5 file5.k 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-j.py b/test/option-j.py index 3eb7bd3..69ef414 100644 --- a/test/option-j.py +++ b/test/option-j.py @@ -30,9 +30,11 @@ SConscript settable option. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path +import sys import TestSCons + _python_ = TestSCons._python_ try: @@ -120,42 +122,41 @@ test.fail_test(start2 < finish1) # succeeds. test.run(arguments='-j 2 out') +if sys.platform != 'win32': + # Test breaks on win32 when using real subprocess is not the only + # package to import threading + # + # Test that we fall back and warn properly if there's no threading.py + # module (simulated), which is the case if this version of Python wasn't + # built with threading support. -# Test that we fall back and warn properly if there's no threading.py -# module (simulated), which is the case if this version of Python wasn't -# built with threading support. + test.subdir('pythonlib') -test.subdir('pythonlib') + test.write(['pythonlib', 'threading.py'], "raise ImportError\n") -test.write(['pythonlib', 'threading.py'], """\ -raise ImportError -""") + save_pythonpath = os.environ.get('PYTHONPATH', '') + os.environ['PYTHONPATH'] = test.workpath('pythonlib') -save_pythonpath = os.environ.get('PYTHONPATH', '') -os.environ['PYTHONPATH'] = test.workpath('pythonlib') + #start2, finish1 = RunTest('-j 2 f1, f2', "fifth") -#start2, finish1 = RunTest('-j 2 f1, f2', "fifth") + test.write('f1.in', 'f1.in pythonlib\n') + test.write('f2.in', 'f2.in pythonlib\n') -test.write('f1.in', 'f1.in pythonlib\n') -test.write('f2.in', 'f2.in pythonlib\n') + test.run(arguments = "-j 2 f1 f2", stderr=None) -test.run(arguments = "-j 2 f1 f2", stderr=None) - -warn = \ -"""scons: warning: parallel builds are unsupported by this version of Python; -\tignoring -j or num_jobs option. -""" -test.must_contain_all_lines(test.stderr(), [warn]) + warn = """scons: warning: parallel builds are unsupported by this version of Python; +\tignoring -j or num_jobs option.""" + test.must_contain_all_lines(test.stderr(), [warn]) -str = test.read("f1") -start1,finish1 = list(map(float, str.split("\n"))) + str = test.read("f1") + start1,finish1 = list(map(float, str.split("\n"))) -str = test.read("f2") -start2,finish2 = list(map(float, str.split("\n"))) + str = test.read("f2") + start2,finish2 = list(map(float, str.split("\n"))) -test.fail_test(start2 < finish1) + test.fail_test(start2 < finish1) -os.environ['PYTHONPATH'] = save_pythonpath + os.environ['PYTHONPATH'] = save_pythonpath # Test SetJobs() with no -j: diff --git a/test/option/debug-count.py b/test/option/debug-count.py index ce79086..b82a434 100644 --- a/test/option/debug-count.py +++ b/test/option/debug-count.py @@ -29,6 +29,7 @@ Test that the --debug=count option works. """ import re +import sys import TestSCons @@ -86,11 +87,13 @@ scons: warning: --debug=count is not supported when running SCons \twith the python -O option or optimized \\(.pyo\\) modules. """ + TestSCons.file_expr -test.run(arguments = '--debug=count -h', - interpreter = ['python', '-O'], - stderr = expect_warning, - match = TestSCons.match_re) - +test.run( + arguments = '--debug=count -h', + # Test against current interpreter vs default path option. + interpreter = [ sys.executable, '-O' ], + stderr = expect_warning, + match = TestSCons.match_re +) test.pass_test() 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/option/profile.py b/test/option/profile.py index f408136..93dad91 100644 --- a/test/option/profile.py +++ b/test/option/profile.py @@ -26,19 +26,14 @@ from SCons.compat.six import u __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import sys -try: - import io - _StringIO = io.StringIO -except (ImportError, AttributeError): - # Pre-2.6 Python has no "io" module. - exec('from cStringIO import StringIO') -else: - # TODO(2.6): In 2.6 and beyond, the io.StringIO.write() method - # requires unicode strings. This subclass can probably be removed - # when we drop support for Python 2.6. - class StringIO(_StringIO): - def write(self, s): - _StringIO.write(self, u(s)) +import io +_StringIO = io.StringIO +# TODO(2.6): In 2.6 and beyond, the io.StringIO.write() method +# requires unicode strings. This subclass can probably be removed +# when we drop support for Python 2.6. +class StringIO(_StringIO): + def write(self, s): + _StringIO.write(self, unicode(s)) import TestSCons diff --git a/test/option/tree-lib.py b/test/option/tree-lib.py index 2721344..2dc5fb0 100644 --- a/test/option/tree-lib.py +++ b/test/option/tree-lib.py @@ -52,7 +52,7 @@ test.write('main.c', """\ #include <stdlib.h> #include <stdio.h> int -main(int argc, char *argv) +main(int argc, char **argv) { printf("#define FOO_H \\"foo.h\\"\\n"); return (0); 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/Configure.py b/test/sconsign/script/Configure.py index 3b43def..679f084 100644 --- a/test/sconsign/script/Configure.py +++ b/test/sconsign/script/Configure.py @@ -58,9 +58,8 @@ CC_file = re.escape(CC_file) _sconf_temp_conftest_0_c = '.sconf_temp/conftest_0.c' test.write('SConstruct', """ -env = Environment() import os -env.AppendENVPath('PATH', os.environ['PATH']) +env = Environment(ENV={'PATH' : os.environ.get('PATH','')}) conf = Configure(env) r1 = conf.CheckCHeader( 'math.h' ) env = conf.Finish() diff --git a/test/sconsign/script/SConsignFile.py b/test/sconsign/script/SConsignFile.py index 71a7e6d..dc45cc1 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/bad.py b/test/sconsign/script/bad.py index fc21577..a384748 100644 --- a/test/sconsign/script/bad.py +++ b/test/sconsign/script/bad.py @@ -47,16 +47,16 @@ test.run_sconsign(arguments = "-f dblite bad1.dblite", stderr = "sconsign: \[Errno 2\] No such file or directory: 'bad1.dblite'\n") test.run_sconsign(arguments = "-f dblite bad2", - stderr = "sconsign: ignoring invalid `dblite' file `bad2'\n") + stderr = "sconsign: ignoring invalid `dblite' file `bad2'.*\n") test.run_sconsign(arguments = "-f dblite bad2.dblite", - stderr = "sconsign: ignoring invalid `dblite' file `bad2.dblite'\n") + stderr = "sconsign: ignoring invalid `dblite' file `bad2.dblite'.*\n") test.run_sconsign(arguments = "-f sconsign no_sconsign", stderr = "sconsign: \[Errno 2\] No such file or directory: 'no_sconsign'\n") test.run_sconsign(arguments = "-f sconsign bad3", - stderr = "sconsign: ignoring invalid .sconsign file `bad3'\n") + stderr = "sconsign: ignoring invalid .sconsign file `bad3'.*\n") test.pass_test() diff --git a/test/sconsign/script/no-SConsignFile.py b/test/sconsign/script/no-SConsignFile.py index 70b18c8..4e9915b 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/override.py b/test/site_scons/override.py index ad03eae..e33bb88 100644 --- a/test/site_scons/override.py +++ b/test/site_scons/override.py @@ -48,7 +48,7 @@ def exists(env): """) test.write('SConstruct', """ -e=Environment() +e=Environment(tools=['m4']) print e.subst('M4 is $M4, M4_MINE is $M4_MINE') """) test.run(arguments = '-Q .', diff --git a/test/site_scons/sysdirs.py b/test/site_scons/sysdirs.py index 87f448d..663700b 100644 --- a/test/site_scons/sysdirs.py +++ b/test/site_scons/sysdirs.py @@ -21,7 +21,6 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -from __future__ import print_function __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" @@ -40,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 .') @@ -55,10 +54,10 @@ else: dir_to_check_for='.scons' if 'Loading site dir' not in test.stdout(): - print(test.stdout()) + print test.stdout() test.fail_test() if dir_to_check_for not in test.stdout(): - print(test.stdout()) + print test.stdout() test.fail_test() test.pass_test() diff --git a/test/update-release-info/update-release-info.py b/test/update-release-info/update-release-info.py index e44aa99..970bcce 100644 --- a/test/update-release-info/update-release-info.py +++ b/test/update-release-info/update-release-info.py @@ -60,10 +60,10 @@ combo_strings = [ """version_tuple = (2, 0, 0, 'bad', 0) """, # Index 1: Python version tuple -"""unsupported_python_version = (2, 3) +"""unsupported_python_version = (2, 6) """, # Index 2: Python version tuple -"""deprecated_python_version = (2, 4) +"""deprecated_python_version = (2, 7) """, # Index 3: alpha version tuple """version_tuple = (2, 0, 0, 'alpha', 0) @@ -223,14 +223,14 @@ These files are a part of 33.22.11: test.must_match(TestSCons, """ copyright_years = '%s' default_version = '2.0.0.alpha.yyyymmdd' -python_version_unsupported = (2, 3) -python_version_deprecated = (2, 4) +python_version_unsupported = (2, 6) +python_version_deprecated = (2, 7) """%years, mode = 'r') # should get Python floors from TestSCons module. test.must_match(Main, """ -unsupported_python_version = (2, 3) -deprecated_python_version = (2, 4) +unsupported_python_version = (2, 6) +deprecated_python_version = (2, 7) """, mode = 'r') #TODO: Release option diff --git a/testing/framework/TestUnit/taprunner.py b/testing/framework/TestUnit/taprunner.py index 01e0e81..5c2e87c 100644 --- a/testing/framework/TestUnit/taprunner.py +++ b/testing/framework/TestUnit/taprunner.py @@ -5,12 +5,22 @@ http://testanything.org/tap-version-13-specification.html Public domain work by: anatoly techtonik <techtonik@gmail.com> +Changes: + 0.3 - fixed used imports that failed on Python 2.6 + 0.2 - removed unused import that failed on Python 2.6 + 0.1 - initial release """ -from unittest import suite -from unittest.runner import TextTestRunner, TextTestResult +__version__ = "0.3" + + +from unittest import TextTestRunner +try: + from unittest import TextTestResult +except ImportError: + # Python 2.6 + from unittest import _TextTestResult as TextTestResult -__version__ = "0.1" class TAPTestResult(TextTestResult): |