summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Deegan <bill@baddogconsulting.com>2023-01-29 00:52:07 (GMT)
committerWilliam Deegan <bill@baddogconsulting.com>2023-01-29 00:52:07 (GMT)
commita220b3f5989db0696d9d03a0988cbcff60e90e59 (patch)
treef8cce0c05888743c2337bbc1a7a066c056165e4b
parente980c5bc26892ce4db1e45516e9983bbc6a2bd33 (diff)
parent04bc3eaab31c5437764b10ee942b19740b5139c9 (diff)
downloadSCons-a220b3f5989db0696d9d03a0988cbcff60e90e59.zip
SCons-a220b3f5989db0696d9d03a0988cbcff60e90e59.tar.gz
SCons-a220b3f5989db0696d9d03a0988cbcff60e90e59.tar.bz2
Merge remote-tracking branch 'upstream/master' into fix_configure_marking_up_to_date
-rw-r--r--.appveyor.yml63
-rw-r--r--.appveyor/exclude_tests.ps1 (renamed from .appveyor/disable_msvc_10.ps1)3
-rw-r--r--.appveyor/install-cov.bat2
-rw-r--r--.appveyor/install.bat13
-rw-r--r--.flake822
-rw-r--r--.git-blame-ignore-revs2
-rw-r--r--.github/workflows/experimental_tests.yml16
-rw-r--r--.github/workflows/framework_tests.yml42
-rw-r--r--.github/workflows/runtest.yml18
-rw-r--r--.github/workflows/scons-package.yml14
-rw-r--r--.travis.yml2
-rw-r--r--[-rwxr-xr-x]CHANGES.txt274
-rw-r--r--CONTRIBUTING.rst205
-rwxr-xr-xREADME.rst10
-rw-r--r--[-rwxr-xr-x]RELEASE.txt234
-rwxr-xr-xReleaseConfig2
-rw-r--r--SCons/Action.py90
-rw-r--r--SCons/Action.xml138
-rw-r--r--SCons/ActionTests.py366
-rw-r--r--SCons/CacheDir.py40
-rw-r--r--SCons/Defaults.py125
-rw-r--r--SCons/Environment.py25
-rw-r--r--SCons/Environment.xml378
-rw-r--r--SCons/EnvironmentTests.py10
-rw-r--r--SCons/Errors.py77
-rw-r--r--SCons/Executor.py12
-rw-r--r--SCons/Node/FS.py116
-rw-r--r--SCons/Node/FSTests.py2
-rw-r--r--SCons/Node/Python.py15
-rw-r--r--SCons/Node/__init__.py10
-rw-r--r--SCons/Platform/PlatformTests.py4
-rw-r--r--SCons/Platform/__init__.py52
-rw-r--r--SCons/Platform/darwin.py3
-rw-r--r--SCons/SConf.py24
-rw-r--r--SCons/SConfTests.py17
-rw-r--r--SCons/Scanner/Java.py72
-rw-r--r--SCons/Scanner/JavaTests.py107
-rw-r--r--SCons/Scanner/ScannerTests.py4
-rw-r--r--SCons/Script/Main.py40
-rw-r--r--SCons/Script/Main.xml83
-rw-r--r--SCons/Script/SConsOptions.py46
-rw-r--r--SCons/Script/SConscript.py36
-rw-r--r--SCons/Script/SConscript.xml102
-rw-r--r--SCons/Script/__init__.py24
-rw-r--r--SCons/Subst.py13
-rw-r--r--SCons/SubstTests.py15
-rw-r--r--SCons/Taskmaster/Job.py (renamed from SCons/Job.py)327
-rw-r--r--SCons/Taskmaster/JobTests.py (renamed from SCons/JobTests.py)89
-rw-r--r--SCons/Taskmaster/TaskmasterTests.py (renamed from SCons/TaskmasterTests.py)153
-rw-r--r--SCons/Taskmaster/__init__.py (renamed from SCons/Taskmaster.py)181
-rw-r--r--SCons/Tool/FortranCommon.py177
-rw-r--r--SCons/Tool/MSCommon/MSVC/Config.py331
-rw-r--r--SCons/Tool/MSCommon/MSVC/ConfigTests.py88
-rw-r--r--SCons/Tool/MSCommon/MSVC/Dispatcher.py84
-rw-r--r--SCons/Tool/MSCommon/MSVC/DispatcherTests.py119
-rw-r--r--SCons/Tool/MSCommon/MSVC/Exceptions.py56
-rw-r--r--SCons/Tool/MSCommon/MSVC/Policy.py301
-rw-r--r--SCons/Tool/MSCommon/MSVC/PolicyTests.py169
-rw-r--r--SCons/Tool/MSCommon/MSVC/Registry.py118
-rw-r--r--SCons/Tool/MSCommon/MSVC/RegistryTests.py83
-rw-r--r--SCons/Tool/MSCommon/MSVC/ScriptArguments.py1031
-rw-r--r--SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py591
-rw-r--r--SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py233
-rw-r--r--SCons/Tool/MSCommon/MSVC/Util.py366
-rw-r--r--SCons/Tool/MSCommon/MSVC/UtilTests.py209
-rw-r--r--SCons/Tool/MSCommon/MSVC/Warnings.py35
-rw-r--r--SCons/Tool/MSCommon/MSVC/WinSDK.py264
-rw-r--r--SCons/Tool/MSCommon/MSVC/WinSDKTests.py132
-rw-r--r--SCons/Tool/MSCommon/MSVC/__init__.py55
-rw-r--r--SCons/Tool/MSCommon/README107
-rw-r--r--SCons/Tool/MSCommon/README.rst501
-rw-r--r--SCons/Tool/MSCommon/__init__.py49
-rw-r--r--SCons/Tool/MSCommon/common.py33
-rw-r--r--SCons/Tool/MSCommon/vc.py482
-rw-r--r--SCons/Tool/MSCommon/vcTests.py325
-rw-r--r--SCons/Tool/ToolTests.py17
-rw-r--r--SCons/Tool/__init__.py154
-rw-r--r--SCons/Tool/compilation_db.py10
-rw-r--r--SCons/Tool/docbook/__init__.py12
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/extensions/docbook.py6
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/fo/highlight.xsl154
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/c-hl.xml200
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/common.xsl240
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/cpp-hl.xml298
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/csharp-hl.xml372
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/delphi-hl.xml398
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ini-hl.xml88
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/java-hl.xml232
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/javascript-hl.xml292
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/m2-hl.xml178
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/myxml-hl.xml230
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/perl-hl.xml238
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/php-hl.xml296
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/python-hl.xml198
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ruby-hl.xml216
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/xslthl-config.xml90
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/html/highlight.xsl172
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/params/bibliography.style.xml70
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/params/highlight.source.xml164
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.async.js142
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.css168
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/content/search/nwSearchFnt.js1024
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.async.js142
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.css168
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/en-us.props88
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/es-es.props356
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/nwSearchFnt.js1024
-rw-r--r--SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/punctuation.props62
-rw-r--r--SCons/Tool/docbook/docs/html.xsl110
-rw-r--r--SCons/Tool/docbook/docs/pdf.xsl124
-rw-r--r--SCons/Tool/f03.xml5
-rw-r--r--SCons/Tool/f08.xml5
-rw-r--r--SCons/Tool/f77.xml7
-rw-r--r--SCons/Tool/f90.xml5
-rw-r--r--SCons/Tool/f95.xml5
-rw-r--r--SCons/Tool/fortran.py2
-rw-r--r--SCons/Tool/fortran.xml62
-rw-r--r--SCons/Tool/g77.py24
-rw-r--r--SCons/Tool/g77.xml27
-rw-r--r--SCons/Tool/gfortran.py52
-rw-r--r--SCons/Tool/gfortran.xml5
-rw-r--r--SCons/Tool/ifl.py21
-rw-r--r--SCons/Tool/ifort.py36
-rw-r--r--SCons/Tool/jar.xml4
-rw-r--r--SCons/Tool/javac.py4
-rw-r--r--SCons/Tool/javac.xml178
-rw-r--r--SCons/Tool/javah.xml4
-rw-r--r--SCons/Tool/lex.py106
-rw-r--r--SCons/Tool/lex.xml66
-rw-r--r--SCons/Tool/linkCommon/__init__.py13
-rw-r--r--SCons/Tool/midl.py50
-rw-r--r--SCons/Tool/mslib.py8
-rw-r--r--SCons/Tool/mslink.py8
-rw-r--r--SCons/Tool/msvc.py8
-rw-r--r--SCons/Tool/msvc.xml1114
-rw-r--r--SCons/Tool/msvs.py8
-rw-r--r--SCons/Tool/msvs.xml574
-rw-r--r--SCons/Tool/msvsTests.py4
-rw-r--r--SCons/Tool/ninja/Globals.py2
-rw-r--r--SCons/Tool/ninja/Methods.py4
-rw-r--r--SCons/Tool/ninja/NinjaState.py202
-rw-r--r--SCons/Tool/ninja/Overrides.py2
-rw-r--r--SCons/Tool/ninja/Utils.py48
-rw-r--r--SCons/Tool/ninja/__init__.py74
-rw-r--r--SCons/Tool/ninja/ninja.xml19
-rw-r--r--SCons/Tool/ninja/ninja_daemon_build.py25
-rw-r--r--SCons/Tool/ninja/ninja_run_daemon.py3
-rw-r--r--SCons/Tool/ninja/ninja_scons_daemon.py116
-rw-r--r--SCons/Tool/packaging/__init__.py3
-rw-r--r--SCons/Tool/rmic.xml28
-rw-r--r--SCons/Tool/tex.py25
-rw-r--r--SCons/Tool/yacc.py96
-rw-r--r--SCons/Tool/yacc.xml102
-rw-r--r--SCons/Util/__init__.py (renamed from SCons/Util.py)1134
-rw-r--r--SCons/Util/envs.py325
-rw-r--r--SCons/Util/hashes.py401
-rw-r--r--SCons/Util/types.py310
-rw-r--r--SCons/UtilTests.py10
-rw-r--r--SCons/Utilities/sconsign.py72
-rw-r--r--SCons/Variables/BoolVariable.py24
-rw-r--r--SCons/Variables/BoolVariableTests.py27
-rw-r--r--SCons/Warnings.py3
-rw-r--r--SCons/__init__.py14
-rw-r--r--SCons/cpp.py3
-rw-r--r--SCons/cppTests.py12
-rw-r--r--SConstruct2
-rwxr-xr-xbin/update_doc_files.sh5
-rw-r--r--doc/design/chtml.xsl114
-rw-r--r--doc/design/html.xsl302
-rw-r--r--doc/design/pdf.xsl146
-rw-r--r--doc/generated/builders.gen27
-rw-r--r--doc/generated/examples/caching_ex-random_1.xml6
-rw-r--r--doc/generated/examples/depends_include_SConstruct2
-rw-r--r--doc/generated/examples/depends_mixing_1.xml2
-rw-r--r--doc/generated/examples/mergeflags_MergeFlags1_1.xml2
-rw-r--r--doc/generated/examples/mergeflags_MergeFlags2_1.xml2
-rw-r--r--doc/generated/examples/mergeflags_MergeFlags3_1.xml4
-rw-r--r--doc/generated/examples/troubleshoot_Dump_1.xml2
-rw-r--r--doc/generated/examples/troubleshoot_Dump_2.xml6
-rw-r--r--doc/generated/examples/troubleshoot_explain1_3.xml2
-rw-r--r--doc/generated/examples/troubleshoot_stacktrace_2.xml2
-rw-r--r--doc/generated/functions.gen510
-rw-r--r--doc/generated/functions.mod4
-rw-r--r--doc/generated/tools.gen41
-rw-r--r--doc/generated/variables.gen1539
-rw-r--r--doc/generated/variables.mod42
-rw-r--r--doc/man/scons.xml1276
-rw-r--r--doc/reference/chtml.xsl114
-rw-r--r--doc/reference/html.xsl302
-rw-r--r--doc/reference/pdf.xsl146
-rw-r--r--doc/scons.mod2
-rw-r--r--doc/sphinx/SCons.Scanner.rst8
-rw-r--r--doc/sphinx/SCons.Taskmaster.rst21
-rw-r--r--doc/sphinx/SCons.rst19
-rw-r--r--doc/sphinx/conf.py30
-rw-r--r--doc/sphinx/index.rst29
-rw-r--r--doc/user/build-install.xml428
-rw-r--r--doc/user/command-line.xml4
-rw-r--r--doc/user/external.xml165
-rw-r--r--doc/user/factories.xml4
-rw-r--r--doc/user/hierarchy.xml90
-rw-r--r--doc/user/java.xml49
-rw-r--r--doc/user/less-simple.xml18
-rw-r--r--doc/user/main.xml4
-rw-r--r--doc/user/misc.xml4
-rw-r--r--doc/user/repositories.xml4
-rw-r--r--doc/user/separate.xml284
-rw-r--r--doc/user/variants.xml34
-rwxr-xr-xpackaging/debian/changelog (renamed from debian/changelog)7
-rw-r--r--packaging/debian/compat (renamed from debian/compat)0
-rw-r--r--packaging/debian/control (renamed from debian/control)0
-rw-r--r--packaging/debian/copyright (renamed from debian/copyright)0
-rw-r--r--packaging/debian/dirs (renamed from debian/dirs)0
-rw-r--r--packaging/debian/docs (renamed from debian/docs)0
-rw-r--r--packaging/debian/postinst (renamed from debian/postinst)0
-rw-r--r--packaging/debian/prerm (renamed from debian/prerm)0
-rw-r--r--packaging/debian/rules (renamed from debian/rules)0
-rw-r--r--packaging/etc/README.txt10
-rwxr-xr-xpackaging/etc/reproducible_install.sh17
-rw-r--r--packaging/etc/reproducible_site_init.py24
-rw-r--r--packaging/rpm/scons.spec.in (renamed from rpm/scons.spec.in)0
-rw-r--r--pyproject.toml13
-rw-r--r--requirements-dev.txt14
-rw-r--r--requirements-pkg.txt13
-rw-r--r--requirements.txt18
-rwxr-xr-xruntest.py19
-rw-r--r--scripts/scons-configure-cache.py8
-rw-r--r--scripts/scons.bat74
-rwxr-xr-xscripts/scons.py3
-rw-r--r--scripts/sconsign.py6
-rw-r--r--setup.cfg2
-rw-r--r--site_scons/scons_local_package.py102
-rw-r--r--site_scons/zip_utils.py147
-rwxr-xr-xtemplate/RELEASE.txt15
-rw-r--r--test/Actions/actions.py13
-rw-r--r--test/Actions/subst_shell_env-fixture/SConstruct24
-rw-r--r--test/Actions/subst_shell_env.py6
-rw-r--r--test/Alias/Alias.py9
-rw-r--r--test/Alias/Depends.py8
-rw-r--r--test/Alias/action.py8
-rw-r--r--test/Batch/changed_sources_main.cpp14
-rw-r--r--test/CPPPATH/CPPPATH.py9
-rw-r--r--test/CacheDir/option--cs.py4
-rw-r--r--test/CacheDir/timestamp-match.py25
-rw-r--r--test/CacheDir/timestamp-newer.py24
-rw-r--r--test/Chmod.py63
-rw-r--r--test/CompilationDatabase/TEMPFILE.py66
-rw-r--r--test/CompilationDatabase/basic.py14
-rw-r--r--test/CompilationDatabase/fixture/SConstruct2
-rw-r--r--test/CompilationDatabase/fixture/SConstruct_tempfile29
-rw-r--r--test/CompilationDatabase/variant_dir.py18
-rw-r--r--test/Configure/issue-3469/fixture/SConstruct6
-rw-r--r--test/Copy-Action.py81
-rw-r--r--test/Copy-Option.py25
-rw-r--r--test/Decider/MD5-timestamp-Repository.py20
-rw-r--r--test/Decider/MD5-timestamp.py21
-rw-r--r--test/Decider/timestamp.py23
-rw-r--r--test/Default.py20
-rw-r--r--test/Dir/PyPackageDir/PyPackageDir.py108
-rw-r--r--test/Dir/PyPackageDir/image/SConstruct62
-rw-r--r--test/Dir/source.py85
-rw-r--r--test/Errors/Exception.py9
-rw-r--r--test/Errors/InternalError.py9
-rw-r--r--test/Fortran/F77PATH.py81
-rw-r--r--test/Fortran/F90PATH.py68
-rw-r--r--test/Fortran/F95FLAGS.py72
-rw-r--r--test/Fortran/FORTRANCOMMONFLAGS.py132
-rw-r--r--test/Fortran/FORTRANFILESUFFIXES2.py43
-rw-r--r--test/Fortran/FORTRANPATH.py64
-rw-r--r--test/Fortran/SHF95FLAGS.py69
-rw-r--r--test/Fortran/fixture/myfortran.py17
-rw-r--r--test/Fortran/fixture/myfortran_flags.py17
-rw-r--r--test/Fortran/link-with-cxx.py57
-rw-r--r--test/GetBuildFailures/serial.py9
-rw-r--r--test/Interactive/taskmastertrace.py11
-rw-r--r--test/Java/JAVAPROCESSORPATH.py95
-rw-r--r--test/Java/inner-cacheable-live.py77
-rw-r--r--test/LEX/FLEXFLAGS.py133
-rw-r--r--test/LEX/LEX.py1
-rw-r--r--test/LEX/LEXCOM.py20
-rw-r--r--test/LEX/LEXCOMSTR.py22
-rw-r--r--test/LEX/LEXFLAGS.py10
-rw-r--r--test/LEX/lex_headerfile.py (renamed from test/print_statement.py)30
-rw-r--r--test/LEX/lex_headerfile/spaced path/SConstruct2
-rw-r--r--test/LEX/lex_headerfile/spaced path/src/SConscript10
-rw-r--r--test/LEX/lex_headerfile/spaced path/src/lexer.l1
-rw-r--r--test/LEX/lex_headerfile/spaced path/src/lexer2.l1
-rw-r--r--test/LEX/live.py28
-rw-r--r--test/LEX/live_mingw.py4
-rw-r--r--test/LEX/no_lex.py12
-rw-r--r--test/Libs/LIBPATH.py75
-rw-r--r--test/Libs/LIBS.py4
-rw-r--r--test/Libs/SharedLibraryIxes.py8
-rw-r--r--test/MSVC/MSVC_NOTFOUND_POLICY.py126
-rw-r--r--test/MSVC/MSVC_SCRIPTERROR_POLICY.py141
-rw-r--r--test/MSVC/MSVC_SDK_VERSION.py241
-rw-r--r--test/MSVC/MSVC_SPECTRE_LIBS.py152
-rw-r--r--test/MSVC/MSVC_TOOLSET_VERSION.py233
-rw-r--r--test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct3
-rw-r--r--test/MSVC/MSVC_UWP_APP.py310
-rw-r--r--test/MSVC/PCH-source.py204
-rw-r--r--test/MSVC/embed-manifest.py188
-rw-r--r--test/MSVC/msvc_badversion.py82
-rw-r--r--test/MSVC/msvc_cache_force_defaults.py80
-rw-r--r--test/MSVC/no_msvc.py37
-rw-r--r--test/MSVS/CPPPATH-Dirs.py188
-rw-r--r--test/NoClean.py194
-rw-r--r--test/NodeOps.py1
-rw-r--r--test/Parallel/multiple-parents.py93
-rw-r--r--test/PharLap.py19
-rw-r--r--test/Program.py88
-rw-r--r--test/Repository/Java.py8
-rw-r--r--test/Repository/JavaH.py1
-rw-r--r--test/Repository/Program.py4
-rw-r--r--test/Repository/RMIC.py9
-rw-r--r--test/Repository/StaticLibrary.py9
-rw-r--r--test/Repository/VariantDir.py8
-rw-r--r--test/Repository/include.py1
-rw-r--r--test/Repository/no-SConsignFile.py14
-rw-r--r--test/Repository/variants.py213
-rw-r--r--test/TAR/TAR.py9
-rw-r--r--test/TARGETS.py1
-rw-r--r--test/TEX/biber_biblatex2.py17
-rw-r--r--test/TEX/bibtex-latex-rerun.py16
-rw-r--r--test/Touch.py35
-rw-r--r--test/ValidateOptions.py68
-rw-r--r--test/Value.py7
-rw-r--r--test/Variables/BoolVariable.py21
-rw-r--r--test/Variables/ListVariable.py19
-rw-r--r--test/Variables/PathVariable.py16
-rw-r--r--test/Variables/Variables.py6
-rw-r--r--test/Variables/help.py74
-rw-r--r--test/VariantDir/VariantDir.py12
-rw-r--r--test/YACC/BISONFLAGS.py130
-rw-r--r--test/YACC/YACC-fixture/myyacc.py12
-rw-r--r--test/YACC/YACC.py36
-rw-r--r--test/YACC/YACCCOM.py20
-rw-r--r--test/YACC/YACCCOMSTR.py22
-rw-r--r--test/YACC/YACCFLAGS-fixture/myyacc.py78
-rw-r--r--test/YACC/YACCFLAGS.py31
-rw-r--r--test/YACC/YACCHFILESUFFIX.py33
-rw-r--r--test/YACC/YACCHXXFILESUFFIX.py33
-rw-r--r--test/YACC/YACCVCGFILESUFFIX.py33
-rw-r--r--test/YACC/live-check-output-cleaned.py13
-rw-r--r--test/YACC/live.py21
-rw-r--r--test/builderrors.py15
-rw-r--r--test/chained-build.py62
-rw-r--r--test/diskcheck.py17
-rw-r--r--test/exceptions.py49
-rw-r--r--test/fixture/SConstruct-check-valid-options21
-rw-r--r--test/fixture/mylex.py31
-rw-r--r--test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py15
-rw-r--r--test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py15
-rw-r--r--test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py15
-rw-r--r--test/fixture/no_msvc/no_msvcs_sconstruct_tools.py14
-rw-r--r--test/fixture/no_msvc/no_msvcs_sconstruct_version.py19
-rw-r--r--test/ninja/build_libraries.py6
-rw-r--r--test/ninja/command_line_targets.py94
-rw-r--r--test/ninja/default_targets.py83
-rw-r--r--test/ninja/force_scons_callback.py2
-rw-r--r--test/ninja/generate_and_build.py7
-rw-r--r--test/ninja/ninja_file_deterministic.py110
-rw-r--r--test/ninja/ninja_test_sconscripts/sconstruct_default_targets14
-rw-r--r--test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism19
-rw-r--r--test/ninja/shell_command.py6
-rw-r--r--test/ninja/shutdown_scons_daemon.py84
-rw-r--r--test/no-global-dependencies.py7
-rw-r--r--test/option/debug-stacktrace.py52
-rw-r--r--test/option/fixture/SConstruct__taskmastertrace9
-rw-r--r--test/option/fixture/taskmaster_expected_file_1.txt74
-rw-r--r--test/option/fixture/taskmaster_expected_new_parallel.txt90
-rw-r--r--test/option/fixture/taskmaster_expected_stdout_1.txt76
-rw-r--r--test/option/option--Y.py1
-rw-r--r--test/option/option--experimental.py7
-rw-r--r--test/option/option-n.py8
-rw-r--r--test/option/stack-size.py9
-rw-r--r--test/option/taskmastertrace.py189
-rw-r--r--test/rebuild-generated.py2
-rw-r--r--test/runtest/python.py10
-rw-r--r--test/sconsign/script/Configure.py10
-rw-r--r--test/sconsign/script/Signatures.py26
-rw-r--r--test/sconsign/script/dblite.py45
-rw-r--r--test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool1.py8
-rw-r--r--test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool2/__init__.py8
-rw-r--r--test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_1.py8
-rw-r--r--test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_2/__init__.py8
-rw-r--r--test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_1.py8
-rw-r--r--test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py8
-rw-r--r--test/toolpath/nested/image/SConstruct138
-rw-r--r--test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool1.py8
-rw-r--r--test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool2/__init__.py8
-rw-r--r--test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_1.py8
-rw-r--r--test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_2/__init__.py8
-rw-r--r--test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_1.py8
-rw-r--r--test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py8
-rw-r--r--test/toolpath/relative_import/image/SConstruct20
-rw-r--r--test/toolpath/relative_import/image/tools/TestTool1/TestTool1_1.py8
-rw-r--r--test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_1.py8
-rw-r--r--test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_2/__init__.py8
-rw-r--r--test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/__init__.py22
-rw-r--r--test/toolpath/relative_import/image/tools/TestTool1/__init__.py18
-rw-r--r--test/toolpath/relative_import/relative_import.py104
-rw-r--r--testing/framework/TestCmd.py211
-rw-r--r--testing/framework/TestCmdTests.py570
-rw-r--r--testing/framework/TestCommon.py105
-rw-r--r--testing/framework/TestCommonTests.py158
-rw-r--r--testing/framework/TestRuntest.py6
-rw-r--r--testing/framework/TestSCons.py175
-rw-r--r--testing/framework/TestSConsMSVS.py12
-rw-r--r--testing/framework/TestSCons_time.py16
410 files changed, 25187 insertions, 12357 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 30d6439..a8db5e9 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -17,68 +17,76 @@ cache:
- C:\ProgramData\chocolatey\lib -> appveyor.yml
install:
- # add python and python user-base to path for pip installs
+ # direct choco install supposed to work, but not? still doing in install.bat
+ #- cinst: dmd ldc swig vswhere ixsltproc winflexbison3
- cmd: .\.appveyor\install.bat
+ - cmd: if %COVERAGE% equ 1 .\.appveyor\install-cov.bat
-# build matrix will be number of images multiplied by each '-' below,
-# less any exclusions.
-# split builds into sets of four jobs due to appveyor per-job time limit
+# Build matrix will be number of images multiplied by #entries in matrix:,
+# less any excludes.
+#
+# "Build" is kind of a misnomer - we are actually running the test suite,
+# and this is slow on Windows, so keep the matrix as small as possible.
+# Leaving the Coverage build on VS2017 for build-time reasons (1hr time limit).
+# maybe move coverage to github in future to restore some flexibility?
environment:
+ COVERAGE: 0
+ SCONS_CACHE_MSVC_CONFIG: "true"
matrix:
- - WINPYTHON: "Python36"
- COVERAGE: 1
+ # Test oldest and newest supported Pythons, and a subset in between.
+ # Skipping 3.7 and 3.9 at this time
+ - WINPYTHON: "Python311"
- - WINPYTHON: "Python37"
- COVERAGE: 0
+ - WINPYTHON: "Python310"
- WINPYTHON: "Python38"
- COVERAGE: 0
-
- - WINPYTHON: "Python310"
- COVERAGE: 0
+ - WINPYTHON: "Python36"
+ COVERAGE: 1
# remove sets of build jobs based on criteria below
# to fine tune the number and platforms tested
matrix:
exclude:
- # test python 3.8 on Visual Studio 2017 image
+ # test python 3.6 on Visual Studio 2017 image
- image: Visual Studio 2017
- WINPYTHON: "Python310"
+ WINPYTHON: "Python311"
- image: Visual Studio 2017
- WINPYTHON: "Python37"
+ WINPYTHON: "Python310"
- image: Visual Studio 2017
WINPYTHON: "Python38"
- # test python 3.7 on Visual Studio 2019 image
+ # test python 3.8 on Visual Studio 2019 image
+ - image: Visual Studio 2019
+ WINPYTHON: "Python311"
- image: Visual Studio 2019
WINPYTHON: "Python310"
- image: Visual Studio 2019
WINPYTHON: "Python36"
- # test python 3.10 on Visual Studio 2022 image
+ # test python 3.10 and 3.11 on Visual Studio 2022 image
- image: Visual Studio 2022
WINPYTHON: "Python36"
- image: Visual Studio 2022
- WINPYTHON: "Python37"
- - image: Visual Studio 2022
WINPYTHON: "Python38"
-# remove some binaries we don't want to be found
+# Remove some binaries we don't want to be found
+# Note this is no longer needed, git-windows bin/ is quite minimal now.
before_build:
- ps: .\.appveyor\ignore_git_bins.ps1
build: off
build_script:
-
- # exclude VS 10.0 because it hangs the testing until this is resolved:
- # https://help.appveyor.com/discussions/problems/19283-visual-studio-2010-trial-license-has-expired
- - ps: .\.appveyor\disable_msvc_10.ps1
+ # Image version-based excludes:
+ # No excludes at the moment, but the exclude script generates the
+ # (possibly empty) exclude_list.txt which is used in the following step,
+ # so leave the scheme in place in case we need to put back excludes later.
+ - ps: .\.appveyor\exclude_tests.ps1
# setup coverage by creating the coverage config file, and adding coverage
# to the sitecustomize so that all python processes start with coverage
- - ps: .\.appveyor\coverage_setup.ps1
+ - ps: if ($env:COVERAGE -eq 1) { .\.appveyor\coverage_setup.ps1 }
# NOTE: running powershell from cmd is intended because
# it formats the output correctly
@@ -86,8 +94,7 @@ build_script:
# run coverage even if there was a test failure
on_finish:
- - ps: .\.appveyor\coverage_report.ps1
- # running codecov in powershell causes an error so running in platform
- # shells
+ - ps: if ($env:COVERAGE -eq 1) { .\.appveyor\coverage_report.ps1 }
+ # running codecov in powershell causes an error so running in cmd
- cmd: if %COVERAGE% equ 1 codecov -X gcov --file coverage_xml.xml
diff --git a/.appveyor/disable_msvc_10.ps1 b/.appveyor/exclude_tests.ps1
index 086f1e4..6006a5c 100644
--- a/.appveyor/disable_msvc_10.ps1
+++ b/.appveyor/exclude_tests.ps1
@@ -1,4 +1,7 @@
New-Item -Name exclude_list.txt -ItemType File;
+
+# exclude VS 10.0 because it hangs the testing until this is resolved:
+# https://help.appveyor.com/discussions/problems/19283-visual-studio-2010-trial-license-has-expired
$workaround_image = "Visual Studio 2015";
if ($env:APPVEYOR_BUILD_WORKER_IMAGE -eq $workaround_image) {
Add-Content -Path 'exclude_list.txt' -Value 'test\MSVS\vs-10.0-exec.py';
diff --git a/.appveyor/install-cov.bat b/.appveyor/install-cov.bat
new file mode 100644
index 0000000..7dbc945
--- /dev/null
+++ b/.appveyor/install-cov.bat
@@ -0,0 +1,2 @@
+for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -c "import sys; print(sys.path[-1])"') do (set PYSITEDIR=%%g)
+C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off coverage codecov
diff --git a/.appveyor/install.bat b/.appveyor/install.bat
index d009e54..95f5115 100644
--- a/.appveyor/install.bat
+++ b/.appveyor/install.bat
@@ -1,12 +1,13 @@
C:\\%WINPYTHON%\\python.exe --version
for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -c "import sys; print(sys.path[-1])"') do (set PYSITEDIR=%%g)
REM use mingw 32 bit until #3291 is resolved
+REM add python and python user-base to path for pip installs
set PATH=C:\\%WINPYTHON%;C:\\%WINPYTHON%\\Scripts;C:\\ProgramData\\chocolatey\\bin;C:\\MinGW\\bin;C:\\MinGW\\msys\\1.0\\bin;C:\\cygwin\\bin;C:\\msys64\\usr\\bin;C:\\msys64\\mingw64\\bin;%PATH%
C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pip setuptools wheel
-C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off coverage codecov
-set STATIC_DEPS=true & C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off lxml
-C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off -r requirements.txt
-REM install 3rd party tools to test with
-choco install --allow-empty-checksums dmd ldc swig vswhere xsltproc winflexbison
-set SCONS_CACHE_MSVC_CONFIG=true
+
+REM requirements-dev.txt will skip installing lxml for windows and py 3.11+, where there's
+REM no current binary wheel
+C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off -r requirements-dev.txt
+
+choco install --allow-empty-checksums dmd ldc swig vswhere xsltproc winflexbison3
set
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..d10d72d
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,22 @@
+[flake8]
+show-source = True
+# don't complain about work black has done
+max-line-length = 88
+extend-exclude =
+ bin,
+ bench,
+ doc,
+ src,
+ template,
+ testing,
+ test,
+ timings,
+ SCons/Tool/docbook/docbook-xsl-1.76.1,
+ bootstrap.py,
+ runtest.py
+extend-ignore =
+ E302,
+ E305
+per-file-ignores =
+ # module symbols made available for compat - ignore "unused" warns
+ SCons/Util/__init__.py: F401
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..538cb23
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+# files reformatted from DOS line-endings
+1277d8e5ab6457ed18d291100539f31d1bdb2d7c
diff --git a/.github/workflows/experimental_tests.yml b/.github/workflows/experimental_tests.yml
index 3672144..8717b14 100644
--- a/.github/workflows/experimental_tests.yml
+++ b/.github/workflows/experimental_tests.yml
@@ -19,8 +19,11 @@ jobs:
build:
strategy:
+ fail-fast: false
matrix:
- os: ['ubuntu-latest', 'windows-latest']
+ # note: in the 2nd half of 2022 the setup-mingw was often failing on
+ # windows-latest. revisit someday (perhaps when there's an @v3)
+ os: ['ubuntu-latest', 'windows-2019', 'macos-latest']
# The type of runner that the job will run on
runs-on: ${{ matrix.os }}
@@ -32,20 +35,21 @@ jobs:
- name: Set up MinGW
uses: egor-tensin/setup-mingw@v2
- if: matrix.os == 'windows-latest'
+ if: matrix.os == 'windows-2019'
with:
platform: x64
- - name: Set up Python 3.8 ${{ matrix.os }}
+ - name: Set up Python 3.11 ${{ matrix.os }}
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: '3.11'
+
- name: Install dependencies including ninja ${{ matrix.os }}
run: |
python -m pip install --upgrade pip setuptools wheel
- python -m pip install ninja
- # if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ python -m pip install ninja psutil
# sudo apt-get update
+
- name: Test experimental packages ${{ matrix.os }}
run: |
python runtest.py test/import.py test/ninja
diff --git a/.github/workflows/framework_tests.yml b/.github/workflows/framework_tests.yml
new file mode 100644
index 0000000..1c4754b
--- /dev/null
+++ b/.github/workflows/framework_tests.yml
@@ -0,0 +1,42 @@
+name: Test Framework Tests
+
+on:
+ # PR events only on master
+ push:
+ branches:
+ - 'master'
+ paths:
+ - 'testing/framework/*'
+
+ pull_request:
+ branches:
+ - 'master'
+ paths:
+ - 'testing/framework/*'
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ fwtest:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: ['ubuntu-latest', 'windows-latest']
+
+ # The type of runner that the job will run on
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ # Checkouut repository under $GITHUB_WORKSPACE
+ - uses: actions/checkout@v2
+
+ - name: Set up Python 3.11 ${{ matrix.os }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.11'
+
+ - name: Test test framework ${{ matrix.os }}
+ run: |
+ python runtest.py testing/framework
+
diff --git a/.github/workflows/runtest.yml b/.github/workflows/runtest.yml
index cfc585e..02be7c1 100644
--- a/.github/workflows/runtest.yml
+++ b/.github/workflows/runtest.yml
@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
- os: ['ubuntu-latest']
+ os: ['ubuntu-22.04']
# The type of runner that the job will run on
runs-on: ${{ matrix.os }}
@@ -30,22 +30,24 @@ jobs:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- - name: Set up Python 3.8 ${{ matrix.os }}
+ - name: Set up Python 3.10 ${{ matrix.os }}
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: '3.10'
+
- name: Install dependencies including ninja ${{ matrix.os }}
run: |
python -m pip install --upgrade pip setuptools wheel
- python -m pip install ninja
- # if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ python -m pip install -r requirements-dev.txt
# sudo apt-get update
+
- name: runtest ${{ matrix.os }}
run: |
- python runtest.py -a -j 2
- - name: Archive Failed tests
+ python runtest.py --all --time --jobs=2
+
+ - name: Archive Failed tests ${{ matrix.os }}
uses: actions/upload-artifact@v2
with:
- name: linux-failed-tests
+ name: ${{ matrix.os }}-failed-tests
path: |
failed_tests.log
diff --git a/.github/workflows/scons-package.yml b/.github/workflows/scons-package.yml
index b124592..4c0a148 100644
--- a/.github/workflows/scons-package.yml
+++ b/.github/workflows/scons-package.yml
@@ -15,19 +15,22 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up Python 3.8
+
+ - name: Set up Python 3.10
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: '3.10'
+
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
#python -m pip install flake8 pytest
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ if [ -f requirements-pkg.txt ]; then pip install -r requirements-pkg.txt; elif [ -f requirements.txt ]; then pip install -r requirements.txt; fi
sudo apt-get update
sudo apt-get -y install docbook-xml docbook-xsl xsltproc fop docbook-xsl-doc-pdf
- # try to keeo the texlive install as small as we can to save some time/space
+ # try to keep the texlive install as small as we can to save some time/space
sudo apt-get -y --no-install-recommends install texlive biber texmaker ghostscript texlive-latex-base texlive-latex-extra texlive-bibtex-extra texlive-font-utils latexmk
+
# This is disabled until the run can be configured to only
# check the code that matters, else we fail on non-essentials
# like the bench/ stuff.
@@ -37,14 +40,17 @@ jobs:
# flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
# flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+
- name: Update Doc sources (some parts are generated)
run: |
python bin/docs-update-generated.py
python bin/docs-validate.py
python bin/docs-create-example-outputs.py
+
- name: Build SCons packages
run: |
python scripts/scons.py
+
- name: Verify package
run: |
ls -l build/dist
diff --git a/.travis.yml b/.travis.yml
index 048fb3c..bae69bc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,7 @@ os:
install:
# needed for Docbook tests, must be in virtualenv context
- - pip install -r requirements.txt
+ - pip install -r requirements-dev.txt
# do the rest of the image setup
- ./.travis/install.sh
diff --git a/CHANGES.txt b/CHANGES.txt
index d3879d0..ce91690 100755..100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -9,6 +9,137 @@ NOTE: 4.3.0 now requires Python 3.6.0 and above. Python 3.5.x is no longer suppo
RELEASE VERSION/DATE TO BE FILLED IN LATER
+ From Anatoli Babenia:
+ - Do not initialize DefaultEnvironment when calling EnsureSConsVersion(),
+ EnsurePythonVersion(), Exit(), GetLaunchDir() and SConscriptChdir().
+ - Remove unused private method SConsEnvironment._exceeds_version().
+
+ From William Deegan:
+ - Added ValidateOptions() which will check that all command line options are in either
+ those specified by SCons itself, or by AddOption() in SConstruct/SConscript. It should
+ not be called until all AddOption() calls are completed. Resolves Issue #4187
+ - Refactored SCons/Taskmaster into a package. Moved SCons/Jobs.py into that package.
+ NOTE: If you hook into SCons.Jobs, you'll have to change that to use SCons.Taskmaster.Jobs
+ - Changed the Taskmaster trace logic to use python's logging module. The output formatting
+ should remain (mostly) the same. Minor update to unittest for this to adjust for 1 less newline.
+ - Migrated logging logic for --taskmastertrace to use Python's logging module. Added logging
+ to NewParallel Job class (Andrew Morrow's new parallel job implementation)
+ - Ninja: Fix execution environment sanitation for launching ninja. Previously if you set an
+ execution environment variable set to a python list it would crash. Now it
+ will create a string joining the list with os.pathsep
+ - Move execution environment sanitation from Action._subproc() to
+ SCons.Util.sanitize_shell_env(ENV)
+ - Moved rpm and debian directories under packaging
+ - Added logic to help packagers enable reproducible builds into packaging/etc/. Please
+ read packaging/etc/README.txt if you are interested.
+ - Added --experimental=tm_v2, which enables Andrew Morrow's new NewParallel Job implementation.
+ This should scale much better for highly parallel builds. You can also enable this via SetOption().
+ - Fixed command line argument --diskcheck: previously a value of 'none' was ignored.
+ SetOption('diskcheck','none') is unaffected, as it did not have the problem.
+ - Added overrides argument to SCons.Subst.scons_subst(), subst_list(), subst(), and Action's process(),
+ strfunction(). This allows passing a dictionary of envvars to override when evaluating subst()'d strings/lists
+ - Fixed Issue #4275 - when outputting compilation db and TEMPFILE was in use, the compilation db would have
+ command lines using the generated tempfile for long command lines, instead of the full command line for
+ the compilation step for the source/target pair.
+
+ From David H:
+ - Added JAVAPROCESSORPATH construction variable which populates -processorpath.
+ - Updated JavaScanner to scan JAVAPROCESSORPATH.
+
+ From Dan Mezhiborsky:
+ - Add newline to end of compilation db (compile_commands.json).
+
+ From Daniel Moody:
+ - Added error message to handle the case when SCons attempts to retrieve all the targets
+ for a specified builder from the CacheDir, fails to do so, and then runs into an error
+ when deleting the files which were retrieved. Previously if this happened there was no
+ errors or warnings.
+ - Fix for issue #2757, configure checks now clear node info for non conftest nodes, so they
+ will be re-evaluated for the real taskmaster run when the build commences. Fix issue where
+ a Configure() checker uses a source file in their processing, which then
+ causes SCons to ignore any changes to those source files during the regular build. This
+ would then prevent SCons from rebuilding any files which depend on those source files from
+ rebuilding.
+ - Fix issue #2757, where Configure checks that perform a check which reads a modified source
+ (including program, source or header file(s)) would incorrectly mark that file "up to date" so the
+ actual build would not see the file as modified. Leading to incorrect incremental builds.
+ Now configure checks now clear node info for non conftest nodes, so they will be re-evaluated for
+ the real taskmaster run when the build commences.
+
+
+ From Andrew Morrow
+ - Avoid returning UniqueList for `children` and other `Executor` APIs. This type
+ iterates more slowly than the builtin types. Also simplify uniquer_hashables to
+ use an faster implementation under the assumption of ordered dictionaries.
+
+ From Ryan Saunders:
+ - Fixed runtest.py failure on Windows caused by excessive escaping of the path to python.exe.
+
+ From Lukas Schrangl:
+ - Run LaTeX after biber/bibtex only if necessary
+
+ From Flaviu Tamas:
+ - Added -fsanitize support to ParseFlags(). This will propagate to CCFLAGS and LINKFLAGS.
+
+ From Mats Wichmann:
+ - A list argument as the source to the Copy() action function is now
+ correctly handled by converting elements to string. Copy errors out
+ if asked to copy a list to an existing non-directory destination.
+ Both the implementation and the strfunction which prints the progress
+ message were adjusted. Fixes #3009.
+ - doc: EnsureSConsVersion, EnsurePythonVersion, Exit, GetLaunchDir and
+ SConscriptChdir are now listed as Global functions only; the
+ Environment versions still work but are not documented.
+ - The Java scanner processing of JAVACLASSPATH for dependencies was
+ changed to split on os.pathsep instead of space, to match usage of
+ passing a path string like "xxx:yyy:zzz". This is not portable -
+ passing a POSIX-style path string (with ':') won't work on Windows
+ (';') - which is now documented with a hint to use a list instead
+ to be portable. Splitting on space broke paths with embedded spaces.
+ Fixes #4243.
+ - Cleanup: make sure BoolVariable usage in tests and examples uses Python
+ boolean values instead of 0/1.
+ - Stop telling people to run "python setup.py install" in the User Guide.
+ Adds new content on using virtualenvs to be able to have multiple
+ different SCons versions available on one system.
+ - Added the "DefaultEnvironment(tools=[])" stanza to a number of tests
+ that are known to be particularly slow. It's still just a tiny
+ speedup, but the Windows CI had been occasionally timing out,
+ so maybe this helps a bit.
+ - Remove an extra existence check in one ninja test that caused it
+ to be skipped on some otherwise-valid Windows installations.
+ - test framework tests now pass on Linux and Windows (the latter can
+ still run into problems on some configurations), and automated
+ tests are now run on changes in this area so future problems can
+ be spotted.
+ - The single-file Util module was split into a package with a few
+ functional areas getting their own files - Util.py had grown to
+ over 2100 lines.
+ - Add a zipapp package of scons-local: can use SCons from a local
+ file which does not need unpacking.
+ - Additional explanations for MSVSProject and MSVSSolution builders.
+ - Fix a problem (present in 4.4.0 only) where a Java inner class could
+ not be cached because the emitted filename contained a '$' and when
+ looked up through a node ended up generating a Python SyntaxError
+ because it was passed through scons_subst().
+ - Have the gfortran tool do a better job of honoring user preferences
+ for the dialect tools (F77, F90, F03 and F09, as well as the shared-library
+ equivalents SHF77, SHF90, SHF03, SHF09). Previously these were
+ unconditionally overwritten to 'gfortran'; the change should be more
+ in line with expectations of how these variables should work.
+ Also cleaned a few Fortran tests - test behavior does not change.
+ - Updated MSVC documentation - adds "version added" annotations on recently
+ added construction variables and provides a version-mapping table.
+ - Add Python 3.12 support, and indicate 3.11/3.12 support in package.
+ 3.12 is in alpha for this SCons release, the bytecode sequences
+ embedded in SCons/ActionTests.py may need to change later, but
+ based on what is known now, 3.12 itself should work with this release.
+ - Add "append" keyword argument to Configure context's CheckLib and
+ CheckLibWithHeader to control whether to append or prepend (issue #2767)
+
+
+RELEASE 4.4.0 - Sat, 30 Jul 2022 14:08:29 -0700
+
From Joseph Brill:
- Verify that a user specified msvc script (via MSVC_USE_SCRIPT) exists and raise an exception
when the user specified msvc script does not exist.
@@ -31,8 +162,30 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
determination when configuring the build environment. This could lead to build failures when
only an MSVC Express instance is installed and the MSVC version is not explicitly specified
(issue #2668 and issue #2697).
- - Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
+ - Added MSVC_USE_SETTINGS construction variable to pass a dictionary to configure the msvc compiler
system environment as an alternative to bypassing Visual Studio autodetection entirely.
+ - Added MSVC_SDK_VERSION construction variable which allows building with a specific Microsoft
+ SDK version. This variable is used with the msvc batch file determined via autodetection subject
+ to validation constraints. Refer to the documentation for additional requirements and validation
+ details.
+ - Added MSVC_TOOLSET_VERSION construction variable which allows building with a specific toolset
+ version. This variable is used with the msvc batch file determined via autodetection subject to
+ validation constraints. This variable does not affect the autodetection and selection of msvc
+ instances. The toolset version is applied after an msvc instance is selected. This could be the
+ default version of msvc. Refer to the documentation for additional requirements and validation
+ details. Addresses issue #3265, issue #3664, and pull request #4149.
+ - Added MSVC_SPECTRE_LIBS construction variable which allows building with spectre-mitigated
+ Visual C++ libraries. This variable is used with the msvc batch file determined via autodetection
+ subject to validation constraints. Refer to the documentation for additional requirements and
+ validation details.
+ - Added MSVC_SCRIPT_ARGS construction variable which specifies command line arguments that are
+ passed to the msvc batch file determined via autodetection subject to validation constraints.
+ Refer to the documentation for additional requirements and validation details. Addresses
+ enhancement issue #4106.
+ - An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier.
+ Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013
+ and earlier. Refer to the documentation for additional requirements and validation details.
+ MSVC_UWP_APP was extended to accept True, False, and None in addition to '1' and '0'.
- The imported system environment variable names for MSVC 7.0 and 6.0 have been changed to the
names set by their respective installers. Prior to this change, bypassing MSVC detection by
specifying the MSVC 7.0 batch file directly would fail due to using an erroneous environment
@@ -48,6 +201,52 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
require delayed expansion to be enabled which is currently not supported and is
typically not enabled by default on the host system. The batch files may also require
environment variables that are not included by default in the msvc environment.
+ - Suppress issuing a warning when there are no installed Visual Studio instances for the default
+ tools configuration (issue #2813). When msvc is the default compiler because there are no
+ compilers installed, a build may fail due to the cl.exe command not being recognized. At
+ present, there is no easy way to detect during msvc initialization if the default environment
+ will be used later to build a program and/or library. There is no error/warning issued for the
+ default tools as there are legitimate SCons uses that do not require a c compiler.
+ - Added a global policy setting and an environment construction variable for specifying the
+ action to be taken when an msvc request cannot be satisfied. The available options are "error",
+ "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be
+ set and retrieved via the functions msvc_set_notfound_policy and msvc_get_notfound_policy,
+ respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment
+ construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction
+ variable overrides the global policy setting for a given environment. When the active policy
+ is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy
+ is "warning" or "warn", a VisualCMissingWarning warning is issued and the constructed
+ environment is likely incomplete. When the active policy is "ignore" or "suppress", no action
+ is taken and the constructed environment is likely incomplete. As implemented, the default
+ global policy is "warning". The ability to set the global policy via an SCons command-line
+ option may be added in a future enhancement.
+ - Added a global policy setting and an environment construction variable for specifying the
+ action to be taken when msvc script errors are detected. The available options are "error",
+ "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be
+ set and retrieved via the functions msvc_set_scripterror_policy and msvc_get_scripterror_policy,
+ respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment
+ construction variable is MSVC_SCRIPTERROR_POLICY. When defined, the environment construction
+ variable overrides the global policy setting for a given environment. When the active policy
+ is "error" or "exception", an MSVCScriptExecutionError exception is raised when msvc batch file
+ errors are detected. When the active policy is "warning" or "warn", an MSVCScriptExecutionWarning
+ warning is issued when msvc batch file errors are detected. When the active policy is "ignore" or
+ "suppress", msvc batch error messages are suppressed. As implemented, the default global policy
+ is "ignore". The ability to set the global policy via an SCons command-line option may be added
+ in a future enhancement.
+ - Added experimental function msvc_query_version_toolset to SCons.Tool.MSCommon. Given a version
+ specification, this function will return an msvc version and an msvc toolset version. The msvc
+ toolset version may be None. The msvc version and msvc toolset version can be used in the
+ environment construction variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The
+ version specification may be an msvc version or an msvc toolset version. This is a proxy for
+ using an msvc toolset version to select an msvc instance. This function may be removed when an
+ msvc toolset version is used during msvc instance selection.
+ - Modify the MSCommon logger configuration to be independent of the root logger. This fixes an issue
+ when multiple loggers are created and the MSCommon logger added computed fields to the root logger
+ that are not present in other logging instances.
+ - Modify the MSVC_USE_SCRIPT_ARGS test fixture to disable the msvc cache. This fixes an issue where
+ the MSVC_USE_SCRIPT_ARGS test for success relied on a debug log message that was not produced when
+ the msvc cache file exists and the test keys are already in the cache as the msvc script invocation
+ was bypassed.
From William Deegan:
- Fix check for unsupported Python version. It was broken. Also now the error message
@@ -81,8 +280,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
only used for ninja)
- Ninja: Fix issue where Configure files weren't being properly processed when build run
via ninja.
- - Added ninja mingw support and improved ninja CommandGeneratorAction support.
- - Update ninja file generation to only create response files for build commands
+ - Ninja: Added ninja mingw support and improved ninja CommandGeneratorAction support.
+ - Ninja: Update ninja file generation to only create response files for build commands
which exceed MAXLINELENGTH
- Ninja: Added NINJA_GENERATED_SOURCE_ALIAS_NAME which allows user to specify an
Alias() which the ninja tool can use to determine which files are generated sources.
@@ -93,20 +292,40 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
required by other build targets. Code contributed by MongoDB
The downstream commit is here:
https://github.com/mongodb/mongo/commit/2fef432fa6e7cf3fd4f22ba3b193222c2887f14f
- - Added special case for ninja scons daemon to work in win32 python3.6 environments.
+ - Ninja: Added special case for ninja scons daemon to work in win32 python3.6 environments.
This particular environment does a bad job managing popen standard file handles, so
some special workarounds are needed.
- - Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
+ - Ninja:Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
format. Compiler tools will also configure the variable automatically.
- - Added SHELL_ENV_GENERATOR construction variables. This variable allows the user to Define
- a function which will be called to generate or alter the execution environment which will
- be used in the shell command of some Action.
- - Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
- - Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
+ - Ninja: Made ninja tool force the ninja file as the only target.
+ - Ninja: Improved the default targets setup and made sure there is always a default target for
+ the ninja file, which excludes targets that start and stop the daemon.
+ - Ninja: Update ninja tool so targets passed to SCons are propagated to ninja when scons
+ automatically executes ninja.
+ - Small refactor of scons daemons using a shared StateInfo class for communication
+ between the scons interactive thread and the http server thread. Added error handling
+ for scons interactive failing to startup.
+ - Ninja: Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
+ - Ninja: Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
+ - Added SHELL_ENV_GENERATORS construction variable. This variable should be set to a list
+ (or an iterable) which contains functions to be called in order
+ when constructing the execution environment (Generally this is the shell environment
+ variables). This allows the user to customize how (for example) PATH is constructed.
+ Note that these are called for every build command run by SCons. It could have considerable
+ performance impact if not used carefully.
to connect to the server during start up.
- - Fix for issue #2757, configure checks now clear node info for non conftest nodes, so they
- will be re-evaluated for the real taskmaster run when the build commences.
+ - lex: Fixed an issue with the lex tool where file arguments specified to either "--header-file="
+ or "--tables-file=" which included a space in the path to the file would be processed incorrectly
+ - Ninja: added option "--skip-ninja-regen" to enable skipping regeneration of the ninja file
+ if scons can determine the ninja file doesnot need to be regenerated, which will also
+ skip restarting the scons daemon. Note this option is could result in incorrect rebuilds
+ if scons Glob or scons generated files are used in ninja build target's command lines.
+ - Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon.
+ Also added cleanup to test framework to kill ninja scons daemons and clean ip daemon logs.
+ NOTE: Test for this requires python psutil module. It will be skipped if not present.
+ - Ninja: Added command line variable NINJA_CMD_ARGS that allows to pass through ninja command line args.
+ This can also be set in your Environment().
From Mats Wichmann:
- Tweak the way default site_scons paths on Windows are expressed to
@@ -151,11 +370,41 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Restore the ability of the content-timestamp decider to see that a
a source which is a symlink has changed if the file-system target of
that link has been modified (issue #3880)
+ - Modernize a few tests that use now-deprecated unittest.getTestCaseNames
+ and unittest.makeSuite - Python itself suggests the replacements.
+ - SCons.Tool.find_program_path now takes an optional add_path argument
+ to add a path to the execution environment if it was discovered in
+ default_paths. Previously, the routine, called by many tool modules,
+ never altered the execution environment, leaving it to the tools.
+ - A new construction variable FORTRANCOMMONFLAGS is added which is
+ applied to all Fortran dialects, in case someone needs to set some
+ flags globally. FORTRANFLAGS looked like it was intended for that,
+ but was not applied to other dialects, and e2e tests explicitly checked
+ that FORTRANFLAGS did not propagate outside the FORTRAN dialect,
+ so the conclusion is that behavior is intentional (issue #2257)
+ - SCons programmatic importing (tool modules and platform modules)
+ no longer uses the deprecated (since Py 3.10) importlib.load_module
+ routine, shifting to the preferred exec_module. Old Python 2 compatible
+ import fallback (using the imp module) in tool module loading is dropped.
+ Tool module loading no longer special-cases Jython, which is a dead
+ project as far as SCons (no timeline in sight for Python 3 support).
+ - Improvements to lex and yacc tools: better documentation of
+ extra-file options, add test for extra-file behavior.
+ - Two new construction variables are introduced for lex (LEX_HEADER_FILE
+ and LEX_TABLES_FILE) as the preferred way of specifying these extra-file
+ options.
+ - Two new construction variables are introduced for yacc
+ (YACC_HEADER_FILE and YACC_GRAPH_FILE) as the preferred way of
+ specifying these extra-file options.
+
From Zhichang Yu:
- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
- Added Configure.CheckMember() checker to check if struct/class has the specified member.
+ From Ivan Kravets, PlatformIO:
+ - Conditional C/C++ Preprocessor: Strip shell's backslashes from the computed include (-DFOO_H=\"foo.h\")
+
RELEASE 4.3.0 - Tue, 16 Nov 2021 18:12:46 -0700
From Jacob Cassagnol:
@@ -834,7 +1083,6 @@ RELEASE 3.1.1 - Mon, 07 Aug 2019 20:09:12 -0500
- JSON encoding errors for CacheDir config
- JSON decoding errors for CacheDir config
-
RELEASE 3.1.0 - Mon, 20 Jul 2019 16:59:23 -0700
From Joseph Brill:
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index c179678..8702711 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -2,11 +2,11 @@ Contributing to SCons
#####################
Introduction
-===========
+============
-Thanks for taking the time to contribute!
+Thanks for taking the time to contribute to SCons!
-This page will talk a bit about the development process,
+This will give a brief overview of the development process,
and about the SCons tree (right here, if you're reading this
in Github, or in a cloned repository).
@@ -24,7 +24,7 @@ there is a web interface.
Resources
=========
-Here are some important pointers:
+Here are some important pointers to other resources:
* `SCons Home Page <https://scons.org>`_
* `Github Project Page <https://github.com/scons/scons>`_
@@ -38,9 +38,9 @@ Reporting Bugs
One of the easiest ways to contribute is by filing bugs.
The SCons project welcomes bug reports and feature requests,
-but we have do a preference for having talked about it first -
-we request you send an email to the
-`SCons Users Mailing List <https://two.pairlist.net/mailman/listinfo/scons-users>`_
+but we *do* have a preference for having talked about them first -
+we request you send an email to the
+`SCons Users Mailing List <https://pairlist4.pair.net/mailman/listinfo/scons-users>`_
or hop on the Discord channel (see link above), and if so
instructed, then proceed to an issue report.
@@ -54,23 +54,29 @@ workarounds for the problem you've run into::
About the Development Tree
==========================
-This tree has more logic than just the SCons code itself -
-there's quite a bit to do with packaging it in a couple
+This tree contains a lot more than just the SCons engine itself.
+Some of it has to do with packaging it in a couple
of forms: a Python-installable package (source distribution
-and installable wheel file), a portable zip (or tar) distribution
+and installable wheel file, which get uploaded to the Python
+Package Index), a portable zip (or tar) distribution
called "scons-local", and a full source bundle. You usually
-don't need to worry about the packaging methods when working
-on a source or doc contribution.
-
-To the extent that this tree is about building SCons packages, the *full*
-development cycle is not just to test the code directly, but to package SCons,
-unpack the package, "install" SCons in a test subdirectory, and then to run
-the tests against the unpacked and installed software. This helps eliminate
-problems caused by, for example, failure to update the list of files to be
-packaged.
+don't need to worry about the packaging parts when working
+on a source or doc contribution - unless you're adding an entirely
+new file, then the packaging bits may need to know about it. The
+project maintainers can usually help with that part.
+There are also tests and tools in the tree.
+
+The *full* development cycle is not just to test code changes directly,
+but also to package SCons, unpack the package, install SCons in a test
+location, and then run the tests against the unpacked and installed
+software. This helps eliminate problems caused by the development
+tree being different than the tree anyone else would see (files
+not checked in to git, files not added to package manifest, etc.)
+Most of that is handled by the CI setup driven through GitHub,
+which runs checks for each update to a Pull Request.
For just working on making an individual change to the SCons source, however,
-you don't actually need to build or install SCons; you *can* actually edit and
+you don't actually need to build or install SCons; you just edit and
execute SCons in-place. See the following sections below for more
information:
@@ -91,16 +97,16 @@ information:
Executing SCons Without Installing
==================================
-Being a project written in Python, you don't have to "build"
-SCons to run it, "build" actually refers to the steps taken to prepare
+Since SCons is written entirely in Python, you don't have to "build"
+SCons to run it. "Building" refers to the steps taken to prepare
for constructing packages, the most time-consuing of which is
-building the documentation.
+building the documentation.
Documentation is written in a markup language which is a
light extension of Docbook-XML, and the doc build consists
of translating to pure docbook, then using standard tools to
generate HTML and PDF outputs from that. There's lots more
-on the documentation process at the Documentation Toolchain page::
+on the documentation process at the Documentation Toolchain page:
https://github.com/SCons/scons/blob/master/doc/overview.rst
@@ -119,11 +125,45 @@ unless you are doing development that relates to the packaging.
Often when doing SCons development, you have a sample project that
isn't working right, and it's useful to be able to build that
-project from place you're working on changes to SCons. You
+project from the place you're working on changes to SCons. You
can do that with the ``C`` (change directory) option::
$ python scripts/scons.py -C /some/other/location [arguments]
+There is another approach that kind of reverses that order:
+construct a Python virtualenv and install the development tree in it.
+If you're not familiar with virtualenvs, there's an example here:
+
+ https://scons-cookbook.readthedocs.io/en/latest/#setting-up-a-python-virtualenv-for-scons
+
+Follow the initial steps, except omit installing ``scons`` as a package.
+When the virtualenv is contructed and activated, go to your working SCons
+git tree and perform a development install instead::
+
+ (myvenv) $ pip install --editable .
+
+Now while this virtualenv is activated, the command ``scons`` will refer
+to this editable version, and you don't have to be "in" this tree
+to run it.
+
+You can also arrange to execute ``scons.py`` from the command line
+by adding it to the ``PATH``, like::
+
+ # on Linux/Mac
+ $ export PATH=$PATH:`pwd`/scripts
+
+ # on Windows
+ C:\> set PATH="%PATH%;C:\path\to\scripts"
+
+Be careful on Windows, the path has a limit of 1024 characters which
+is pretty easy to exceed, and it will just truncate.
+
+You may first need to make ``scons.py`` executable (it should be
+by default, but sometimes things happen)::
+
+ $ chmod +x scripts/scons.py
+
+
Other Required Software
=======================
@@ -132,7 +172,22 @@ of Python. The tools which will be used to to actually construct the
project, such as compilers, documentation production tools, etc.
should of course be installed by the appropriate means. In order
to develop SCons and run its test suite, there are some dependencies,
-listed in the ``requirements.txt`` file.
+listed in the ``requirements-dev.txt`` file. Install these with::
+
+ $ python -m pip install -r requirements-dev.txt
+
+For building the SCons packages and documentation there are some further
+requirements, you can get these with::
+
+ $ python -m pip install -r requirements-pkg.txt
+
+The requirements are inclusive so you only need the latter to get
+everything installed.
+
+There are other, non-Python requirements to do a doc build. These
+are system-specific. See bin/scons_dev_master.py for the set up that
+works for Ubuntu systems.
+
Making Changes
==============
@@ -140,23 +195,15 @@ Making Changes
Virtually all of the SCons functionality exists in the "build engine," the
``SCons`` subdirectory hierarchy that contains all of the modules that
make up SCons. The ``scripts/scons.py`` wrapper script exists mainly to find
-the appropriate build engine library and then execute it.
+the appropriate build engine module and execute it.
In order to make your own changes locally and test them by hand, simply edit
modules in the local ``SCons`` subdirectory tree and then run
-(see the section above about `Executing SCons Without Installing`_)::
+(see the section `Executing SCons Without Installing`_)::
$ python scripts/scons.py [arguments]
-If you want to be able to just execute your modified version of SCons from the
-command line, you can make it executable and add its directory to your $PATH
-like so::
-
- $ chmod 755 scripts/scons.py
- $ export PATH=$PATH:`pwd`/scripts
-
-You should then be able to run this version of SCons by just typing ``scons.py``
-at your UNIX or Linux command line.
+Or, if using the virtualenv/editable approach: ``scons [arguments]``
Note that the regular SCons development process makes heavy use of automated
testing. See the `Testing`_ and `Typical Development Workflow`_ sections below for more
@@ -170,7 +217,7 @@ Debugging
Python comes with a good interactive debugger. When debugging changes by hand
(i.e., when not using the automated tests), you can invoke SCons under control
-of the Python debugger by specifying the --debug=pdb option::
+of the Python debugger by specifying the ``--debug=pdb`` option::
$ scons --debug=pdb [arguments]
> /home/knight/scons/SCons/Script/Main.py(927)_main()
@@ -191,20 +238,21 @@ the debugger at that point.
The debugger supports single stepping, stepping into functions, printing
variables, etc.
-Trying to debug problems found by running the automated tests (see the
-`Testing`_ section, below) is more difficult, because the test automation
-harness re-invokes SCons and captures output. Consequently, there isn't an
-easy way to invoke the Python debugger in a useful way on any particular SCons
-call within a test script.
-
-The most effective technique for debugging problems that occur during an
-automated test is to use the good old tried-and-true technique of adding
-statements to print tracing information. But note that you can't just use
-the ``print`` function, or even ``sys.stdout.write()`` because those change the
-SCons output, and the automated tests usually look for matches of specific
-output strings to decide if a given SCons invocation passes the test -
-so these additions may cause apparent failures different than the one you
-are trying to debug.
+When debugging unexpected behavior when running the test suite
+(see the `Testing`_ section, below), it can get a bit more complicated.
+For the Unit Tests, you will be running in-process, and so the
+``runtest.py`` script's debug option is helpful in getting things set up.
+
+Trying to debug problems found by running the end-to-end tests is
+more difficult, because the test automation harness re-invokes SCons and
+captures output - essentially, an instance of SCons is being runs as
+a "black box", and so it is considerably harder to interact with it
+effectively. The way forward is usually to add statements to trace progress.
+You can't just use the ``print`` function directly, or even ``sys.stdout.write()``
+because those change the SCons output, and the end-to-end tests usually
+look for matches of specific output strings to decide if a given SCons
+invocation has behaved as expected - so interleaving your trace information
+would cause lots of mismatches, and often obscure what you are trying to debug.
To deal with this, SCons supports a ``Trace()`` function that (by default) will
print messages to your console screen (``/dev/tty`` on UNIX or Linux, ``con`` on
@@ -287,7 +335,8 @@ subdirectory, and do not require that a build be performed first.
Typical Development Workflow
============================
- Caveat: The point of this section isn't to describe one dogmatic workflow.
+.. hint::
+ The point of this section is not to describe one dogmatic workflow.
Just running the test suite can be time-consuming, and getting a patch to
pass all of the tests can be more so. If you're genuinely blocked, it may
make more sense to submit a patch with a note about which tests still
@@ -324,7 +373,9 @@ platform, Windows users can translate as appropriate)):
$ python runtest.py -a -o test.log
- Be patient, there are more than 1100 test scripts in the whole suite!
+ Be patient, there are more than 1100 test scripts in the whole suite
+ (using a ``-j`` option pretty much always helps. For example, if you have
+ an 8-core processor, try ``runtest.py -j 8```).
If any test scripts fail, they will be listed in a summary at the end of the
log file. Some test scripts may also report NO RESULT because (for example)
@@ -359,11 +410,21 @@ example, if you're making a change to just the Java support, you might start
looking for regressions by just running the ``test/Java/\*.py`` tests instead of
running all tests with ``runtest.py -a``.
+- To actually submit the fix and any test work as a Pull Request,
+ there will be some version control steps. For example::
+
+ $ git checkout -b fix-1387
+ $ git modified # check that this reports your expected list
+ $ git add `git modified`
+ $ git commit -s # fill in a good description of your changes
+
+ And proceed to push the change as a PR.
+
Building Packages
=================
-We use SCons (version 3.1.2 or later) to build its own packages. If you
+We use SCons (version 3.1.2 or newer) 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::
@@ -378,12 +439,12 @@ you can run the build directly from the source tree
Those are full builds: depending on the utilities installed on your system,
any or all of the following packages will be built::
- SCons-4.3.0-py3-none-any.whl
- SCons-4.3.0ayyyymmdd.tar.gz
- SCons-4.3.0ayyyymmdd.zip
- scons-doc-4.3.0ayyyymmdd.tar.gz
- scons-local-4.3.0ayyyymmdd.tar.gz
- scons-local-4.3.0ayyyymmdd.zip
+ SCons-4.4.0-py3-none-any.whl
+ SCons-4.4.0ayyyymmdd.tar.gz
+ SCons-4.4.0ayyyymmdd.zip
+ scons-doc-4.4.0ayyyymmdd.tar.gz
+ scons-local-4.4.0ayyyymmdd.tar.gz
+ scons-local-4.4.0ayyyymmdd.zip
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.
@@ -432,17 +493,17 @@ bootstrap.py
Obsolete packaging logic - ignore this.
debian/
- Files needed to construct a Debian package. The contents of this directory
- are dictated by the
+ Files needed to construct a Debian package.
+ The contents of this directory are dictated by the
`Debian Policy Manual <https://www.debian.org/doc/debian-policy>`).
The package will not be accepted into the Debian distribution unless
the contents of this directory satisfy the relevant Debian policies.
- At this point, this is a sample; SCons is packaged in the Debian
- project (and thus inherited by projects which derive from it, if
- they haven't made their own packages). See:
+ At this point, this is a sample; SCons is packaged for Debian by the
+ Debian project itself (and thus inherited by projects which derive from it,
+ if they haven't made their own packages). See:
- - <https://packages.debian.org/bullseye/scons>_
- - <https://packages.ubuntu.com/impish/scons>_
+ - `Debian scons packages <https://packages.debian.org/search?keywords=scons&searchon=names&suite=all&section=all>`_
+ - `Ubuntu scons packages <https://packages.ubuntu.com/search?keywords=scons&searchon=names&suite=all&section=all>`_
doc/
SCons documentation. A variety of things here, in various stages of
@@ -491,12 +552,12 @@ SConstruct
software.)
SCons/
- Where the actual source code is kept, of course.
+ This is the source code of the engine, plus unit tests and
+ documentation stubs kept together with pieces of the engine.
test/
- End-to-end tests of the SCons utility itself. These are separate from the
- individual module unit tests, which live side-by-side with the modules
- under SCons.
+ End-to-end tests of the SCons utility itself.
+ These are separate from the individual module unit tests.
testing/
SCons testing framework.
diff --git a/README.rst b/README.rst
index 1cc390d..6cc89bd 100755
--- a/README.rst
+++ b/README.rst
@@ -117,7 +117,7 @@ Installation Requirements
=========================
SCons has no installation dependencies beyond a compatible version
-of Python. The tools which will be used to to actually construct the
+of Python. The tools which will be used to actually construct the
project, such as compilers, documentation production tools, etc.
should of course be installed by the appropriate means.
@@ -258,6 +258,14 @@ software, or hardware) to support continued work on the project. Information
is available at https://www.scons.org/donate.html
or the GitHub Sponsors button on https://github.com/scons/scons.
+Reproducible Builds
+===================
+In order to suppor those users who which to produce reproducible builds
+(https://reproducible-builds.org/specs/source-date-epoch/) we're now including
+logic to force SCons to propagate SOURCE_DATE_EPOCH from your shell environment for
+all SCons builds to support reproducible builds we're now providing an example
+site_init.py and a script to install it in your ~/.scons. See packaging/etc/README.txt
+for more info
For More Information
====================
diff --git a/RELEASE.txt b/RELEASE.txt
index 3f7d198..f182c86 100755..100644
--- a/RELEASE.txt
+++ b/RELEASE.txt
@@ -1,10 +1,17 @@
-A new SCons release, 4.3.1, is now available
-on the SCons download page:
+If you are reading this in the git repository, the contents
+refer to *unreleased* changes since the last SCons release.
+Past official release announcements appear at:
- https://scons.org/pages/download.html
+ https://scons.org/tag/releases.html
+==================================================================
-Here is a summary of the changes since 4.3.1:
+A new SCons release, 4.4.1, is now available on the SCons download page:
+
+ https://scons.org/pages/download.html
+
+
+Here is a summary of the changes since 4.4.0:
NOTE: If you build with Python 3.10.0 and then rebuild with 3.10.1 (or higher), you may
see unexpected rebuilds. This is due to Python internals changing which changed
@@ -14,13 +21,11 @@ NOTE: If you build with Python 3.10.0 and then rebuild with 3.10.1 (or higher),
NEW FUNCTIONALITY
-----------------
-- Added MSVC_USE_SCRIPT_ARGS variable to pass arguments to MSVC_USE_SCRIPT.
-- Added Configure.CheckMember() checker to check if struct/class has the specified member.
-- Added SHELL_ENV_GENERATOR construction variables. This variable allows the user to Define
- a function which will be called to generate or alter the execution environment which will
- be used in the shell command of some Action.
-- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler
- system environment as an alternative to bypassing Visual Studio autodetection entirely.
+- Added ValidateOptions() which will check that all command line options are in either
+ those specified by SCons itself, or by AddOption() in SConstruct/SConscript. It should
+ not be called until all AddOption() calls are completed. Resolves Issue #4187
+- Added --experimental=tm_v2, which enables Andrew Morrow's NewParallel Job implementation.
+ This should scale much better for highly parallel builds. You can also enable this via SetOption().
DEPRECATED FUNCTIONALITY
@@ -31,139 +36,126 @@ DEPRECATED FUNCTIONALITY
CHANGED/ENHANCED EXISTING FUNCTIONALITY
---------------------------------------
-- On Windows, %AllUsersProfile%\scons\site_scons is now the default "system"
- location for a site_scons directory.
- %AllUsersProfile%\Application Data\scons\site_scons will continue to work.
- There does not seem to be any existing convention to use an
- "Application Data" subdirectory here.
-- Action._subproc() can now be used as a python context manager to ensure that the
- POpen object is properly closed.
-- SCons help (-H) no longer prints the "ignored for compatibility" options,
- which are still listed in the manpage.
-- Help is now sensitive to the size of the terminal window: the width of the
- help text will scale to wider (or narrower) terminals than 80 characters.
-- Ninja: Changed generated build.ninja file to run SCons only build Actions via
- a SCons Deamon. Added logic for starting and connecting to SCons daemon (currently
- only used for ninja)
-- The change to "content" and "content-timestamp" Decider names is reflected
- in the User Guide as well, since the hash function may be other than md5
- (tidying up from earlier change)
-- If the (experimental) SCONS_CACHE_MSVC_CONFIG feature is used, it will now
- attempt a sanity check for the cached compiler information, and regenerate
- it if needed. Previously, this cache would fail if a compiler upgrade caused
- a change to internal paths (e.g. upgrading from 17.1 to 17.2 causes
- a necessary path component in some of the cached vars to need to 14.32.31326
- instead of 14.31.31103), and the cache file needed to be manually removed.
- The default cachefile name is now "scons_msvc_cache.json" rather than
- ".scons_msvc_cache" so there should be no transition problem if using the
- default; if using a custom cache file name, the cache should still be
- manually removed if there are problems to transition to the new style.
-- Update ninja file generation to only create response files for build commands
- which exceed MAXLINELENGTH
-- Update the debug output written to stdout for MSVC initialization which is enabled
- by setting SCONS_MSCOMMON_DEBUG=- to use the logging module. Also changed the debug
- output format written to stdout to include more information about the source for each
- message of MSVC initialization debugging output. A single space was added before the
- message for all debugging output records written to stdout and to files.
-- Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool.
-- The build argument (i.e., x86) is no longer passed to the MSVC 6.0 to 7.1 batch
- files. This may improve the effectiveness of the internal msvc cache when using
- MSVC detection and when bypassing MSVC detection as the MSVC 6.0 to 7.1 batch files
- do not expect any arguments.
-- Propagate the OS and windir environment variables from the system environment to the msvc
- environment. The OS and windir environment variables are used in the MSVC 6.0 batch file
- and the SDK 6.0-7.1 SetEnv.cmd batch files. Inclusion of the OS and windir environment
- variables eliminates some partial paths and warnings generated by the MSVC 6.0 and SDK
- 6.0-7.1 batch files when the variables are not defined.
- Note: Attempting to run the SDK 6.0-7.1 batch files directly via MSVC_USE_SCRIPT can lead to
- build failures and/or incomplete build environments. The SDK 6.0-7.1 batch files
- require delayed expansion to be enabled which is currently not supported and is
- typically not enabled by default on the host system. The batch files may also require
- environment variables that are not included by default in the msvc environment.
+- Added -fsanitize support to ParseFlags(). This will propagate to CCFLAGS and LINKFLAGS.
+- Calling EnsureSConsVersion() and EnsurePythonVersion() won't initialize
+ DefaultEnvironment anymore.
+- The console message from the Chmod() action function now displays
+ octal modes using the modern Python syntax (0o755 rather than 0755).
+- Migrated logging logic for --taskmastertrace to use Python's logging module. Added logging
+ to NewParallel Job class (Andrew Morrow's new parallel job implementation)
+- Preliminary support for Python 3.12.
+- Run LaTeX after biber/bibtex only if necessary
+- Configure context methods CheckLib and CheckLibWithHeader now expose
+ an additional keyword argument 'append' which controls whether to append
+ (the default) or prepend discovered libraries to $LIBS. The functionality
+ was always present but prepending could not be requested via the offical API.
+
FIXES
-----
-- Fix a number of Python ResourceWarnings which are issued when running SCons and/or it's tests
- with python 3.9 (or higher)
-- Ninja: Fix issue where Configure files weren't being properly processed when build run
- via ninja.
-- Fixed crash in C scanner's dictify_CPPDEFINES() function which happens if
- AppendUnique is called on CPPPATH. (Issue #4108).
-- Added default values for source and target arguments to _defines() function. This
- is used to expand CPPDEFINES (and others). Previous change added those arguments
- with no defaults, so old usage where _defines() was called without source and target
- arguments would yield an exception. This issue was found via qt4 and qt5 tools in
- scons-contrib https://github.com/SCons/scons-contrib/issues/45
-- Fix issue where if you only had mingw installed on a Windows system and no MSVC compiler, and
- did not explicitly request the mingw tool, mingw tool initialization would fail and set the
- default compiler to MSVC which wasn't installed, yielding broken build.
- Updated mingw tool so that the generate and exists methods use the same mingw search paths
- (issue #4134).
-- Ninja: Added NINJA_GENERATED_SOURCE_ALIAS_NAME which allows user to specify an
- Alias() which the ninja tool can use to determine which files are generated sources.
- If this is not set by the user then the ninja tool will still dynamically determine
- which files are generated sources based on NINJA_GENERATED_SOURCE_SUFFIXES, and create
- a phony target _ninja_generated_sources. Generated sources will be built first by
- ninja. This is needed because ninja cannot determine which generated sources are
- required by other build targets. Code contributed by MongoDB.
-- Added special case for ninja scons daemon to work in win32 python3.6 environments.
- This particular environment does a bad job managing popen standard file handles, so
- some special workarounds are needed.
-- Added user configurable setting of ninja depfile format via NINJA_DEPFILE_PARSE_FORMAT.
- Now setting NINJA_DEPFILE_PARSE_FORMAT to [msvc,gcc,clang] can force the ninja expected
- format. Compiler tools will also configure the variable automatically.
-- Fix issue where Express versions of the MSVC compiler were not detected due to differences
- in initial msvc detection and msvc batch file determination when configuring the build
- environment. This could lead to build failures when only an MSVC Express instance is installed
- and the MSVC version is not explicitly specified (issue #2668 and issue #2697).
-- Restore the ability of the content-timestamp decider to see that a
- a source which is a symlink has changed if the file-system target of
- that link has been modified (issue #3880)
-- Fix typo in ninja scons daemon startup which causes ConnectionRefusedError to not retry
- to connect to the server during start up.
-- Fix incorrect Java classpath generation when a NodeList is used as part of any JAVA*PATH variables.
-- The system environment variable names imported for MSVC 7.0 and 6.0 were updated to be
- consistent with the variables names defined by their respective installers. This fixes an
- error caused when bypassing MSVC detection by specifying the MSVC 7.0 batch file directly.
-- Fix for issue #2757, configure checks now clear node info for non conftest nodes, so they
- will be re-evaluated for the real taskmaster run when the build commences.
+- Added missing newline to generated compilation database (compile_commands.json)
+- A list argument as the source to the Copy() action function is now handled.
+ Both the implementation and the strfunction which prints the progress
+ message were adjusted.
+- The Java Scanner processing of JAVACLASSPATH for dependencies (behavior
+ that was introduced in SCons 4.4.0) is adjusted to split on the system's
+ search path separator instead of on a space. The previous behavior meant
+ that a path containing spaces (e.g. r"C:\somepath\My Classes") would
+ lead to unexpected errors. If the split-on-space behavior is desired,
+ pre-split the value: instead of: env["JAVACLASSPATH"] = "foo bar baz"
+ use: env["JAVACLASSPATH"] = env.Split("foo bar baz")
+ There is no change in how JAVACLASSPATH gets turned into the -classpath
+ argument passed to the JDK tools.
+- Ninja: Fix execution environment sanitation for launching ninja. Previously if you set an
+ execution environment variable set to a python list it would crash. Now it
+ will create a string joining the list with os.pathsep
+- Fixed command line argument --diskcheck: previously a value of 'none' was ignored.
+ SetOption('diskcheck','none') is unaffected, as it did not have the problem.
+- Fixed Issue #4275 - when outputting compilation db and TEMPFILE was in use, the compilation db would have
+ command lines using the generated tempfile for long command lines, instead of the full command line for
+ the compilation step for the source/target pair.
+- A refactor in the caching logic for version 4.4.0 left Java inner classes
+ failing with an exception when a CacheDir was enabled. This is now corrected.
+- When using the gfortran tool (the default on most platforms as long as a GNU
+ toolchain is installed), the user setting of the "dialect" compilers
+ (F77, F90, F03 and F09, as well as the shared-library equivalents SHF77,
+ SHF90, SHF03, SHF09) is now honored; previously the tool overwrote the
+ settings to 'gfortran', which made it difficult reference a cross-compile
+ version for dialects.
+- Fix issue #2757, where Configure checks that perform a check which reads a modified source
+ (including program, source or header file(s)) would incorrectly mark that file "up to date" so the
+ actual build would not see the file as modified. Leading to incorrect incremental builds.
+ Now configure checks now clear node info for non conftest nodes, so they will be re-evaluated for
+ the real taskmaster run when the build commences.
+
IMPROVEMENTS
------------
-- Verify that a user specified msvc script (via MSVC_USE_SCRIPT) exists and raise an
- exception immediately when the user specified msvc script does not exist.
+- Changed the Taskmaster trace logic to use python's logging module.
- Add cache-debug messages for push failures.
-- Added ninja mingw support and improved ninja CommandGeneratorAction support.
-- Command-line help is now sensitive to the size of the terminal window: the
- width of the help text will scale for terminals other than 80 chars wide.
-- Refactor the msvc code so that the same data structures are used during initial msvc detection
- and msvc batch file determination when configuring the build environment. Simplify the msvc
- code by eliminating special case handling primarily due to the differences between the full
- versions and express versions of visual studio.
-- Updated ninja scons daemon scripts to output errors to stderr as well as the daemon log.
+- Added error message to handle the case when SCons attempts to retrieve all the targets
+ for a specified builder from the CacheDir, fails to do so, and then runs into an error
+ when deleting the files which were retrieved. Previously if this happened there were no
+ errors or warnings.
PACKAGING
---------
-- Added project_url for mailing lists and Discord
-- Updated setup.cfg to remove Python 3.5 and add Python 3.10
+- SCons now has three requirements files: requirements.txt describes
+ requirements to run scons; requirements-dev.txt requirements to
+ develop it - mainly things needed to run the testsuite;
+ requirements_pkg.txt are the requirements to do a full build
+ (including docs build) with an intent to create the packages.
+- Moved rpm and debian directories under packaging
+- Added logic to help packagers enable reproducible builds into packaging/etc/.
+ Please read packaging/etc/README.txt if you are interested.
+- A zipapp of scons-local is now also built.
+
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)
+- Updated the --hash-format manpage entry.
+- EnsureSConsVersion, EnsurePythonVersion, Exit, GetLaunchDir and
+ SConscriptChdir are now listed as Global functions only.
+- Updated the docs for Glob.
+- Updated SHELL_ENV_GENERATORS description and added versionadded indicator.
+- JAVABOOTCLASSPATH, JAVACLASSPATH and JAVASOURCEPATH better document the
+ acceptable syntax for values, and how they will be interpreted,
+ including that JAVACLASSPATH will be scanned for dependencies.
+ Added note on the possibly surprising feature that SCons always passes
+ -sourcepath when calling javac, which affects how the class path is
+ used when finding sources.
+- Updated the User Guide chapter on installation: modernized the notes
+ on Python installs, SCons installs, and having multiple SCons versions
+ present on a single system.
+- Updated the User Guide chapter on variant directories with more
+ explanation, and the introduction of terms like "out of tree" that
+ may help in forming a mental model.
+- Additional explanations for MSVSProject and MSVSSolution builders.
+- Updated MSVC documentation - adds "version added" annotations on recently
+ added construction variables and provides a version-mapping table.
DEVELOPMENT
-----------
-- List visible changes in the way SCons is developed
+- Refactored SCons/Taskmaster into a package. Moved SCons/Jobs.py into that package.
+ NOTE: If you hook into SCons.Jobs, you'll have to change that to use SCons.Taskmaster.Jobs
+- Refactored SCons.Util, which had grown quite large, to be a package,
+ not a single-file module. Change should be transparent: the same import
+ of SCons.Util and public symbols from it will continue to work, however
+ code which reaches directly in to grab private symbols (starting with
+ underscore) which moved to a submodule, that code will have to be adjusted,
+ as those are not imported to the package level (new SCons.Util.hashes has
+ some of these, which are used by existing unit tests).
+- Added overrides argument to SCons.Subst.scons_subst(), subst_list(), subst(), and Action's process(),
+ strfunction(). This allows passing a dictionary of envvars to override when evaluating subst()'d strings/lists
+
Thanks to the following contributors listed below for their contributions to this release.
==========================================================================================
.. code-block:: text
- git shortlog --no-merges -ns 4.3.0..HEAD
+ git shortlog --no-merges -ns 4.4.0..HEAD
diff --git a/ReleaseConfig b/ReleaseConfig
index 6e5531f..98a3785 100755
--- a/ReleaseConfig
+++ b/ReleaseConfig
@@ -31,7 +31,7 @@
# '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 = (4, 3, 0, 'a', 0)
+version_tuple = (4, 4, 1, 'a', 0)
# Python versions prior to unsupported_python_version cause a fatal error
# when that version is used. Python versions prior to deprecate_python_version
diff --git a/SCons/Action.py b/SCons/Action.py
index 0849178..1f5e548 100644
--- a/SCons/Action.py
+++ b/SCons/Action.py
@@ -110,6 +110,7 @@ import inspect
from collections import OrderedDict
import SCons.Debug
+import SCons.Util
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Util
@@ -732,29 +733,60 @@ def _string_from_cmd_list(cmd_list):
default_ENV = None
-def get_default_ENV(env, target=None, source=None):
- """
- A fiddlin' little function that has an 'import SCons.Environment' which
- can't be moved to the top level without creating an import loop. Since
- this import creates a local variable named 'SCons', it blocks access to
- the global variable, so we move it here to prevent complaints about local
- variables being used uninitialized.
+def get_default_ENV(env):
+ """Returns an execution environment.
+
+ If there is one in *env*, just use it, else return the Default
+ Environment, insantiated if necessary.
+
+ A fiddlin' little function that has an ``import SCons.Environment``
+ which cannot be moved to the top level without creating an import
+ loop. Since this import creates a local variable named ``SCons``,
+ it blocks access to the global variable, so we move it here to
+ prevent complaints about local variables being used uninitialized.
"""
global default_ENV
+
try:
return env['ENV']
except KeyError:
if not default_ENV:
import SCons.Environment
- # This is a hideously expensive way to get a default shell
+ # This is a hideously expensive way to get a default execution
# environment. What it really should do is run the platform
# setup to get the default ENV. Fortunately, it's incredibly
- # rare for an Environment not to have a shell environment, so
- # we're not going to worry about it overmuch.
+ # rare for an Environment not to have an execution environment,
+ # so we're not going to worry about it overmuch.
default_ENV = SCons.Environment.Environment()['ENV']
return default_ENV
+def _resolve_shell_env(env, target, source):
+ """Returns a resolved execution environment.
+
+ First get the execution environment. Then if ``SHELL_ENV_GENERATORS``
+ is set and is iterable, call each function to allow it to alter the
+ created execution environment, passing each the returned execution
+ environment from the previous call.
+
+ .. versionadded:: 4.4
+ """
+ ENV = get_default_ENV(env)
+ shell_gen = env.get('SHELL_ENV_GENERATORS')
+ if shell_gen:
+ try:
+ shell_gens = iter(shell_gen)
+ except TypeError:
+ raise SCons.Errors.UserError("SHELL_ENV_GENERATORS must be iteratable.")
+ else:
+ ENV = ENV.copy()
+ for generator in shell_gens:
+ ENV = generator(env, target, source, ENV)
+ if not isinstance(ENV, dict):
+ raise SCons.Errors.UserError(f"SHELL_ENV_GENERATORS function: {generator} must return a dict.")
+ return ENV
+
+
def _subproc(scons_env, cmd, error='ignore', **kw):
"""Wrapper for subprocess which pulls from construction env.
@@ -770,28 +802,11 @@ def _subproc(scons_env, cmd, error='ignore', **kw):
if is_String(io) and io == 'devnull':
kw[stream] = DEVNULL
- # Figure out what shell environment to use
+ # Figure out what execution environment to use
ENV = kw.get('env', None)
if ENV is None: ENV = get_default_ENV(scons_env)
- # Ensure that the ENV values are all strings:
- new_env = {}
- for key, value in ENV.items():
- if is_List(value):
- # If the value is a list, then we assume it is a path list,
- # because that's a pretty common list-like value to stick
- # in an environment variable:
- value = SCons.Util.flatten_sequence(value)
- new_env[key] = os.pathsep.join(map(str, value))
- else:
- # It's either a string or something else. If it's a string,
- # we still want to call str() because it might be a *Unicode*
- # string, which makes subprocess.Popen() gag. If it isn't a
- # string or a list, then we just coerce it to a string, which
- # is the proper way to handle Dir and File instances and will
- # produce something reasonable for just about everything else:
- new_env[key] = str(value)
- kw['env'] = new_env
+ kw['env'] = SCons.Util.sanitize_shell_env(ENV)
try:
pobj = subprocess.Popen(cmd, **kw)
@@ -860,11 +875,11 @@ class CommandAction(_ActionAction):
return ' '.join(map(str, self.cmd_list))
return str(self.cmd_list)
- def process(self, target, source, env, executor=None):
+ def process(self, target, source, env, executor=None, overrides=False):
if executor:
- result = env.subst_list(self.cmd_list, 0, executor=executor)
+ result = env.subst_list(self.cmd_list, 0, executor=executor, overrides=overrides)
else:
- result = env.subst_list(self.cmd_list, 0, target, source)
+ result = env.subst_list(self.cmd_list, 0, target, source, overrides=overrides)
silent = None
ignore = None
while True:
@@ -881,18 +896,18 @@ class CommandAction(_ActionAction):
pass
return result, ignore, silent
- def strfunction(self, target, source, env, executor=None):
+ def strfunction(self, target, source, env, executor=None, overrides=False):
if self.cmdstr is None:
return None
if self.cmdstr is not _null:
from SCons.Subst import SUBST_RAW
if executor:
- c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
+ c = env.subst(self.cmdstr, SUBST_RAW, executor=executor, overrides=overrides)
else:
- c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source, overrides=overrides)
if c:
return c
- cmd_list, ignore, silent = self.process(target, source, env, executor)
+ cmd_list, ignore, silent = self.process(target, source, env, executor, overrides=overrides)
if silent:
return ''
return _string_from_cmd_list(cmd_list[0])
@@ -924,10 +939,9 @@ class CommandAction(_ActionAction):
escape = env.get('ESCAPE', lambda x: x)
- ENV = env.get('SHELL_ENV_GENERATOR', get_default_ENV)(env, target, source)
+ ENV = _resolve_shell_env(env, target, source)
# Ensure that the ENV values are all strings:
-
for key, value in ENV.items():
if not is_String(value):
if is_List(value):
diff --git a/SCons/Action.xml b/SCons/Action.xml
index 2c18d55..becd30f 100644
--- a/SCons/Action.xml
+++ b/SCons/Action.xml
@@ -26,14 +26,13 @@ See its __doc__ string for a discussion of the format.
<cvar name="IMPLICIT_COMMAND_DEPENDENCIES">
<summary>
<para>
-Controls whether or not SCons will
+Controls whether or not &SCons; will
add implicit dependencies for the commands
executed to build targets.
</para>
<para>
-By default, SCons will add
-to each target
+By default, &SCons; will add to each target
an implicit dependency on the command
represented by the first argument of any
command line it executes (which is typically
@@ -130,44 +129,65 @@ A Python function used to print the command lines as they are executed
or
<option>-s</option>
options or their equivalents).
-The function should take four arguments:
+The function must accept four arguments:
<varname>s</varname>,
-the command being executed (a string),
<varname>target</varname>,
-the target being built (file node, list, or string name(s)),
+<varname>source</varname> and
+<varname>env</varname>.
+<varname>s</varname>
+is a string showing the command being executed,
+<varname>target</varname>,
+is the target being built (file node, list, or string name(s)),
<varname>source</varname>,
-the source(s) used (file node, list, or string name(s)), and
-<varname>env</varname>,
-the environment being used.
+is the source(s) used (file node, list, or string name(s)),
+and <varname>env</varname>
+is the environment being used.
</para>
<para>
-The function must do the printing itself. The default implementation,
-used if this variable is not set or is None, is:
+The function must do the printing itself.
+The default implementation,
+used if this variable is not set or is <constant>None</constant>,
+is to just print the string, as in:
</para>
<example_commands>
def print_cmd_line(s, target, source, env):
- sys.stdout.write(s + "\n")
+ sys.stdout.write(s + "\n")
</example_commands>
<para>
-Here's an example of a more interesting function:
+Here is an example of a more interesting function:
</para>
<example_commands>
def print_cmd_line(s, target, source, env):
- sys.stdout.write("Building %s -> %s...\n" %
- (' and '.join([str(x) for x in source]),
- ' and '.join([str(x) for x in target])))
-env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
-env.Program('foo', 'foo.c')
+ sys.stdout.write(
+ "Building %s -> %s...\n"
+ % (
+ ' and '.join([str(x) for x in source]),
+ ' and '.join([str(x) for x in target]),
+ )
+ )
+
+env = Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
+env.Program('foo', ['foo.c', 'bar.c'])
</example_commands>
<para>
-This just prints "Building <varname>targetname</varname> from <varname>sourcename</varname>..." instead
-of the actual commands.
-Such a function could also log the actual commands to a log file,
-for example.
+This prints:
+</para>
+
+<screen>
+...
+scons: Building targets ...
+Building bar.c -> bar.o...
+Building foo.c -> foo.o...
+Building foo.o and bar.o -> foo...
+scons: done building targets.
+</screen>
+
+<para>
+Another example could be a function that logs the actual commands to a file.
</para>
</summary>
</cvar>
@@ -176,7 +196,7 @@ for example.
<summary>
<para>
A command interpreter function that will be called to execute command line
-strings. The function must expect the following arguments:
+strings. The function must accept five arguments:
</para>
<example_commands>
@@ -184,49 +204,69 @@ def spawn(shell, escape, cmd, args, env):
</example_commands>
<para>
-<varname>sh</varname>
-is a string naming the shell program to use.
+<varname>shell</varname>
+is a string naming the shell program to use,
<varname>escape</varname>
is a function that can be called to escape shell special characters in
-the command line.
+the command line,
<varname>cmd</varname>
-is the path to the command to be executed.
+is the path to the command to be executed,
<varname>args</varname>
-is the arguments to the command.
+holds the arguments to the command and
<varname>env</varname>
-is a dictionary of the environment variables
-in which the command should be executed.
+is a dictionary of environment variables
+defining the execution environment in which the command should be executed.
</para>
</summary>
</cvar>
-<cvar name="SHELL_ENV_GENERATOR">
+<cvar name="SHELL_ENV_GENERATORS">
<summary>
<para>
-A function to generate or alter the environment dictionary which will be used
-when executing the &cv-link-SPAWN; function. This primarily give the
-user a chance to customize the execution environment for particular Actions.
-It must return a dictionary containing the environment variables as
-keys and the values as values.
+A hook allowing the execution environment to be modified prior
+to the actual execution of a command line from an action
+via the spawner function defined by &cv-link-SPAWN;.
+Allows substitution based on targets and sources,
+as well as values from the &consenv;,
+adding extra environment variables, etc.
</para>
- <example_commands>
-def custom_shell_env(env, target, source):
- </example_commands>
-
<para>
- <varname>env</varname>
-The SCons construction environment from which the
-execution environment can be derived from.
+The value must be a list (or other iterable)
+of functions which each generate or
+alter the execution environment dictionary.
+The first function will be passed a copy of the initial execution environment
+(&cv-link-ENV; in the current &consenv;);
+the dictionary returned by that function is passed to the next,
+until the iterable is exhausted and the result returned
+for use by the command spawner.
+The original execution environment is not modified.
</para>
+
<para>
- <varname>target</varname>
-The list of targets associated with this action.
- </para>
- <para>
- <varname>source</varname>
-The list of sources associated with this action.
+Each function provided in &cv-SHELL_ENV_GENERATORS; must accept four
+arguments and return a dictionary:
+<varname>env</varname> is the &consenv; for this action;
+<varname>target</varname> is the list of targets associated with this action;
+<varname>source</varname> is the list of sources associated with this action;
+and <varname>shell_env</varname> is the current dictionary after iterating
+any previous &cv-SHELL_ENV_GENERATORS; functions
+(this can be compared to the original execution environment,
+which is available as <literal>env['ENV']</literal>, to detect any changes).
</para>
+
+ <para>Example:</para>
+ <example_commands>
+def custom_shell_env(env, target, source, shell_env):
+ """customize shell_env if desired"""
+ if str(target[0]) == 'special_target':
+ shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source)
+ return shell_env
+
+env["SHELL_ENV_GENERATORS"] = [custom_shell_env]
+ </example_commands>
+
+ <para><emphasis>Available since 4.4</emphasis></para>
</summary>
</cvar>
diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py
index 29ed4a7..88bb36f 100644
--- a/SCons/ActionTests.py
+++ b/SCons/ActionTests.py
@@ -71,16 +71,16 @@ with open(sys.argv[1], 'w') as f:
if 'ACTPY_PIPE' in os.environ:
if 'PIPE_STDOUT_FILE' in os.environ:
- with open(os.environ['PIPE_STDOUT_FILE'], 'r') as f:
- stdout_msg = f.read()
+ with open(os.environ['PIPE_STDOUT_FILE'], 'r') as f:
+ stdout_msg = f.read()
else:
- stdout_msg = "act.py: stdout: executed act.py %s\\n" % ' '.join(sys.argv[1:])
- sys.stdout.write( stdout_msg )
+ stdout_msg = "act.py: stdout: executed act.py %s\\n" % ' '.join(sys.argv[1:])
+ sys.stdout.write(stdout_msg)
if 'PIPE_STDERR_FILE' in os.environ:
- with open(os.environ['PIPE_STDERR_FILE'], 'r') as f:
- stderr_msg = f.read()
+ with open(os.environ['PIPE_STDERR_FILE'], 'r') as f:
+ stderr_msg = f.read()
else:
- stderr_msg = "act.py: stderr: executed act.py %s\\n" % ' '.join(sys.argv[1:])
+ stderr_msg = "act.py: stderr: executed act.py %s\\n" % ' '.join(sys.argv[1:])
sys.stderr.write(stderr_msg)
sys.exit(0)
""")
@@ -113,8 +113,9 @@ class CmdStringHolder:
return self.literal
def escape(self, escape_func):
- """Escape the string with the supplied function. The
- function is expected to take an arbitrary string, then
+ """Escape the string with the supplied function.
+
+ The function is expected to take an arbitrary string, then
return it with all special characters escaped and ready
for passing to the command interpreter.
@@ -141,15 +142,15 @@ class Environment:
self.d[k] = v
# Just use the underlying scons_subst*() utility methods.
- def subst(self, strSubst, raw=0, target=[], source=[], conv=None):
+ def subst(self, strSubst, raw=0, target=[], source=[], conv=None, overrides=False):
return SCons.Subst.scons_subst(strSubst, self, raw,
- target, source, self.d, conv=conv)
+ target, source, self.d, conv=conv, overrides=overrides)
subst_target_source = subst
- def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None):
+ def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None, overrides=False):
return SCons.Subst.scons_subst_list(strSubst, self, raw,
- target, source, self.d, conv=conv)
+ target, source, self.d, conv=conv, overrides=overrides)
def __getitem__(self, item):
return self.d[item]
@@ -251,8 +252,8 @@ 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.
- """
+ """Test that Action returns the expected type and positional args work."""
+
act = SCons.Action.Action(cmd, **kw)
pos_callback(act)
assert act.varlist == (), act.varlist
@@ -293,7 +294,7 @@ def test_positional_args(pos_callback, cmd, **kw):
test_varlist(pos_callback, none, cmd, None, **kw)
- """Test handling of bad cmdstrfunc arguments """
+ # Test handling of bad cmdstrfunc arguments
try:
a = SCons.Action.Action(cmd, [], **kw)
except SCons.Errors.UserError as e:
@@ -310,8 +311,7 @@ class ActionTestCase(unittest.TestCase):
"""Test the Action() factory function"""
def test_FunctionAction(self):
- """Test the Action() factory's creation of FunctionAction objects
- """
+ """Test the Action() factory's creation of FunctionAction objects."""
def foo():
pass
@@ -325,8 +325,7 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(func_action, [foo])
def test_CommandAction(self):
- """Test the Action() factory's creation of CommandAction objects
- """
+ """Test the Action() factory's creation of CommandAction objects."""
def cmd_action(a):
assert isinstance(a, SCons.Action.CommandAction), a
@@ -343,8 +342,8 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(line_action, [["explicit", "command", "line"]])
def test_ListAction(self):
- """Test the Action() factory's creation of ListAction objects
- """
+ """Test the Action() factory's creation of ListAction objects."""
+
a1 = SCons.Action.Action(["x", "y", "z", ["a", "b", "c"]])
assert isinstance(a1, SCons.Action.ListAction), a1
assert a1.varlist == (), a1.varlist
@@ -401,8 +400,7 @@ class ActionTestCase(unittest.TestCase):
assert a5.list[1].strfunction == foo, a5.list[1].strfunction
def test_CommandGeneratorAction(self):
- """Test the Action() factory's creation of CommandGeneratorAction objects
- """
+ """Test the Action factory's creation of CommandGeneratorAction objects."""
def foo(): pass
@@ -413,8 +411,7 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(gen_action, foo, generator=1)
def test_LazyCmdGeneratorAction(self):
- """Test the Action() factory's creation of lazy CommandGeneratorAction objects
- """
+ """Test the Action factory's creation of lazy CommandGeneratorAction objects."""
def lazy_action(a):
assert isinstance(a, SCons.Action.LazyAction), a
@@ -425,8 +422,8 @@ class ActionTestCase(unittest.TestCase):
test_positional_args(lazy_action, "${FOO}")
def test_no_action(self):
- """Test when the Action() factory can't create an action object
- """
+ """Test when the Action() factory can't create an action object."""
+
try:
a5 = SCons.Action.Action(1)
except TypeError:
@@ -435,8 +432,8 @@ class ActionTestCase(unittest.TestCase):
assert 0, "Should have thrown a TypeError creating Action from an int."
def test_reentrance(self):
- """Test the Action() factory when the action is already an Action object
- """
+ """Test the Action factory when the action is already an Action object."""
+
a1 = SCons.Action.Action("foo")
a2 = SCons.Action.Action(a1)
assert a2 is a1, a2
@@ -445,8 +442,7 @@ class ActionTestCase(unittest.TestCase):
class _ActionActionTestCase(unittest.TestCase):
def test__init__(self):
- """Test creation of _ActionAction objects
- """
+ """Test creation of _ActionAction objects."""
def func1():
pass
@@ -519,8 +515,7 @@ class _ActionActionTestCase(unittest.TestCase):
assert a.varlist is t, a.varlist
def test_dup_keywords(self):
- """Test handling of both cmdstr and strfunction arguments
- """
+ """Test handling of both cmdstr and strfunction arguments."""
def func():
pass
@@ -535,8 +530,8 @@ class _ActionActionTestCase(unittest.TestCase):
raise Exception("did not catch expected UserError")
def test___cmp__(self):
- """Test Action comparison
- """
+ """Test Action comparison."""
+
a1 = SCons.Action.Action("x")
a2 = SCons.Action.Action("x")
assert a1 == a2
@@ -545,8 +540,8 @@ class _ActionActionTestCase(unittest.TestCase):
assert a2 != a3
def test_print_cmd_lines(self):
- """Test the print_cmd_lines() method
- """
+ """Test the print_cmd_lines() method."""
+
save_stdout = sys.stdout
try:
@@ -565,8 +560,8 @@ class _ActionActionTestCase(unittest.TestCase):
sys.stdout = save_stdout
def test___call__(self):
- """Test calling an Action
- """
+ """Test calling an Action."""
+
save_stdout = sys.stdout
save_print_actions = SCons.Action.print_actions
@@ -753,8 +748,8 @@ class _ActionActionTestCase(unittest.TestCase):
SCons.Action.execute_actions = save_execute_actions
def test_presub_lines(self):
- """Test the presub_lines() method
- """
+ """Test the presub_lines() method."""
+
env = Environment()
a = SCons.Action.Action("x")
s = a.presub_lines(env)
@@ -875,8 +870,8 @@ class _ActionActionTestCase(unittest.TestCase):
class CommandActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a command Action
- """
+ """Test creation of a command Action."""
+
a = SCons.Action.CommandAction(["xyzzy"])
assert a.cmd_list == ["xyzzy"], a.cmd_list
assert a.cmdstr is _null, a.cmdstr
@@ -886,8 +881,8 @@ class CommandActionTestCase(unittest.TestCase):
assert a.cmdstr == "cadabra", a.cmdstr
def test___str__(self):
- """Test fetching the pre-substitution string for command Actions
- """
+ """Test fetching the pre-substitution string for command Actions."""
+
env = Environment()
act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
s = str(act)
@@ -900,8 +895,7 @@ class CommandActionTestCase(unittest.TestCase):
assert s == "xyzzy $TARGET $SOURCE $TARGETS $SOURCES", s
def test_genstring(self):
- """Test the genstring() method for command Actions
- """
+ """Test the genstring() method for command Actions."""
env = Environment()
t1 = DummyNode('t1')
@@ -938,8 +932,7 @@ class CommandActionTestCase(unittest.TestCase):
assert s == expect, s
def test_strfunction(self):
- """Test fetching the string representation of command Actions
- """
+ """Test fetching the string representation of command Actions."""
env = Environment()
t1 = DummyNode('t1')
@@ -1090,9 +1083,8 @@ class CommandActionTestCase(unittest.TestCase):
assert s == "foo bar", s
def test_execute(self):
- """Test execution of command Actions
+ """Test execution of command Actions."""
- """
try:
env = self.env
except AttributeError:
@@ -1247,8 +1239,7 @@ class CommandActionTestCase(unittest.TestCase):
assert r == 0, r
def test_set_handler(self):
- """Test setting the command handler...
- """
+ """Test setting the command handler..."""
class Test:
def __init__(self):
@@ -1299,8 +1290,7 @@ class CommandActionTestCase(unittest.TestCase):
assert t.executed == ['**xyzzy**'], t.executed
def test_get_contents(self):
- """Test fetching the contents of a command Action
- """
+ """Test fetching the contents of a command Action."""
def CmdGen(target, source, env, for_signature):
assert for_signature
@@ -1373,6 +1363,7 @@ class CommandActionTestCase(unittest.TestCase):
def test_get_implicit_deps(self):
"""Test getting the implicit dependencies of a command Action."""
+
class SpecialEnvironment(Environment):
def WhereIs(self, prog):
return prog
@@ -1413,11 +1404,11 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
def factory(self, act, **kw):
"""Pass any keywords as a dict"""
+
return SCons.Action.CommandGeneratorAction(act, kw)
def test___init__(self):
- """Test creation of a command generator Action
- """
+ """Test creation of a command generator Action."""
def f(target, source, env):
pass
@@ -1426,8 +1417,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert a.generator == f
def test___str__(self):
- """Test the pre-substitution strings for command generator Actions
- """
+ """Test the pre-substitution strings for command generator Actions."""
def f(target, source, env, for_signature, self=self):
# See if "env" is really a construction environment (or
@@ -1444,8 +1434,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert s == 'FOO', s
def test_genstring(self):
- """Test the command generator Action genstring() method
- """
+ """Test the command generator Action genstring() method."""
def f(target, source, env, for_signature, self=self):
dummy = env['dummy']
@@ -1459,8 +1448,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s
def test_execute(self):
- """Test executing a command generator Action
- """
+ """Test executing a command generator Action."""
def f(target, source, env, for_signature, self=self):
dummy = env['dummy']
@@ -1519,8 +1507,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert self.rfile_called
def test_get_contents(self):
- """Test fetching the contents of a command generator Action
- """
+ """Test fetching the contents of a command generator Action."""
def f(target, source, env, for_signature):
foo = env['foo']
@@ -1540,8 +1527,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert c == b"guux FFF BBB test", c
def test_get_contents_of_function_action(self):
- """Test contents of a CommandGeneratorAction-generated FunctionAction
- """
+ """Test contents of a CommandGeneratorAction-generated FunctionAction."""
def LocalFunc():
pass
@@ -1554,6 +1540,8 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
+ (3, 12): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
meth_matches = [
@@ -1571,12 +1559,12 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
a = self.factory(f_global)
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr(
+ assert c == func_matches[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
func_matches[sys.version_info[:2]])
a = self.factory(f_local)
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr(
+ assert c == func_matches[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
func_matches[sys.version_info[:2]])
def f_global2(target, source, env, for_signature):
@@ -1599,8 +1587,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
class FunctionActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a function Action
- """
+ """Test creation of a function Action."""
def func1():
pass
@@ -1623,8 +1610,7 @@ class FunctionActionTestCase(unittest.TestCase):
assert a.strfunction == func3, a.strfunction
def test___str__(self):
- """Test the __str__() method for function Actions
- """
+ """Test the __str__() method for function Actions."""
def func1():
pass
@@ -1642,8 +1628,8 @@ class FunctionActionTestCase(unittest.TestCase):
assert s == "class1(target, source, env)", s
def test_execute(self):
- """Test executing a function Action
- """
+ """Test executing a function Action."""
+
self.inc = 0
def f(target, source, env):
@@ -1721,8 +1707,7 @@ class FunctionActionTestCase(unittest.TestCase):
assert self.string_it
def test_get_contents(self):
- """Test fetching the contents of a function Action
- """
+ """Test fetching the contents of a function Action."""
def LocalFunc():
pass
@@ -1734,6 +1719,8 @@ class FunctionActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
+ (3, 12): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
@@ -1744,6 +1731,8 @@ class FunctionActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'1, 1, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
+ (3, 12): bytearray(b'1, 1, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
def factory(act, **kw):
@@ -1751,20 +1740,23 @@ class FunctionActionTestCase(unittest.TestCase):
a = factory(GlobalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
a = factory(LocalFunc)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
matches_foo = func_matches[sys.version_info[:2]] + b'foo'
a = factory(GlobalFunc, varlist=['XYZ'])
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
# assert c in func_matches, repr(c)
c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
@@ -1775,8 +1767,9 @@ class FunctionActionTestCase(unittest.TestCase):
a = factory(GlobalFunc, varlist='XYZ')
c = a.get_contents(target=[], source=[], env=Environment())
# assert c in func_matches, repr(c)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
assert c in matches_foo, repr(c)
@@ -1796,12 +1789,12 @@ class FunctionActionTestCase(unittest.TestCase):
lc = LocalClass()
a = factory(lc.LocalMethod)
c = a.get_contents(target=[], source=[], env=Environment())
- assert c == meth_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- meth_matches[sys.version_info[:2]])
+ assert (
+ c == meth_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(meth_matches[sys.version_info[:2]])
def test_strfunction(self):
- """Test the FunctionAction.strfunction() method
- """
+ """Test the FunctionAction.strfunction() method."""
def func():
pass
@@ -1825,8 +1818,7 @@ class FunctionActionTestCase(unittest.TestCase):
class ListActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a list of subsidiary Actions
- """
+ """Test creation of a list of subsidiary Actions."""
def func():
pass
@@ -1838,8 +1830,7 @@ class ListActionTestCase(unittest.TestCase):
assert a.list[2].list[0].cmd_list == 'y'
def test___str__(self):
- """Test the __str__() method for a list of subsidiary Actions
- """
+ """Test the __str__() method for a list of subsidiary Actions."""
def f(target, source, env):
pass
@@ -1852,8 +1843,7 @@ class ListActionTestCase(unittest.TestCase):
assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s
def test_genstring(self):
- """Test the genstring() method for a list of subsidiary Actions
- """
+ """Test the genstring() method for a list of subsidiary Actions."""
def f(target, source, env):
pass
@@ -1867,8 +1857,7 @@ class ListActionTestCase(unittest.TestCase):
assert s == "f(target, source, env)\ngenerated foo.x bar.y\nXXX\nf(target, source, env)", s
def test_execute(self):
- """Test executing a list of subsidiary Actions
- """
+ """Test executing a list of subsidiary Actions."""
self.inc = 0
def f(target, source, env):
@@ -1904,8 +1893,8 @@ class ListActionTestCase(unittest.TestCase):
assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
def test_get_contents(self):
- """Test fetching the contents of a list of subsidiary Actions
- """
+ """Test fetching the contents of a list of subsidiary Actions."""
+
self.foo = 0
def gen(target, source, env, for_signature):
@@ -1923,8 +1912,8 @@ class ListActionTestCase(unittest.TestCase):
class LazyActionTestCase(unittest.TestCase):
def test___init__(self):
- """Test creation of a lazy-evaluation Action
- """
+ """Test creation of a lazy-evaluation Action."""
+
# Environment variable references should create a special type
# of LazyAction that lazily evaluates the variable for whether
# it's a string or something else before doing anything.
@@ -1937,8 +1926,7 @@ class LazyActionTestCase(unittest.TestCase):
assert a10.var == 'FOO', a10.var
def test_genstring(self):
- """Test the lazy-evaluation Action genstring() method
- """
+ """Test the lazy-evaluation Action genstring() method."""
def f(target, source, env):
pass
@@ -1952,8 +1940,7 @@ class LazyActionTestCase(unittest.TestCase):
assert s == 'xxx', s
def test_execute(self):
- """Test executing a lazy-evaluation Action
- """
+ """Test executing a lazy-evaluation Action."""
def f(target, source, env):
s = env['s']
@@ -1969,16 +1956,15 @@ class LazyActionTestCase(unittest.TestCase):
assert c == "act.py: 'lazy'\n", c
def test_get_contents(self):
- """Test fetching the contents of a lazy-evaluation Action
- """
+ """Test fetching the contents of a lazy-evaluation Action."""
+
a = SCons.Action.Action("${FOO}")
env = Environment(FOO=[["This", "is", "a", "test"]])
c = a.get_contents(target=[], source=[], env=env)
assert c == b"This is a test", c
def test_get_contents_of_function_action(self):
- """Test fetching the contents of a lazy-evaluation FunctionAction
- """
+ """Test fetching the contents of a lazy-evaluation FunctionAction."""
def LocalFunc():
pass
@@ -1990,6 +1976,8 @@ class LazyActionTestCase(unittest.TestCase):
(3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 9): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
(3, 10): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'),
+ (3, 11): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
+ (3, 12): bytearray(b'0, 0, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()'),
}
meth_matches = [
@@ -2005,21 +1993,24 @@ class LazyActionTestCase(unittest.TestCase):
env = Environment(FOO=factory(GlobalFunc))
c = a.get_contents(target=[], source=[], env=env)
# assert c in func_matches, "Got\n"+repr(c)+"\nExpected one of \n"+"\n".join([repr(f) for f in func_matches])
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
env = Environment(FOO=factory(LocalFunc))
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
# matches_foo = [x + b"foo" for x in func_matches]
matches_foo = func_matches[sys.version_info[:2]] + b'foo'
env = Environment(FOO=factory(GlobalFunc, varlist=['XYZ']))
c = a.get_contents(target=[], source=[], env=env)
- assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- func_matches[sys.version_info[:2]])
+ assert (
+ c == func_matches[sys.version_info[:2]]
+ ), f"Got\n{c!r}\nExpected one of \n" + repr(func_matches[sys.version_info[:2]])
env['XYZ'] = 'foo'
c = a.get_contents(target=[], source=[], env=env)
@@ -2029,6 +2020,7 @@ class LazyActionTestCase(unittest.TestCase):
class ActionCallerTestCase(unittest.TestCase):
def test___init__(self):
"""Test creation of an ActionCaller"""
+
ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO': 4, 'BAR': 5})
assert ac.parent == 1, ac.parent
assert ac.args == [2, 3], ac.args
@@ -2050,20 +2042,25 @@ class ActionCallerTestCase(unittest.TestCase):
(3, 8): b'd\x00S\x00',
(3, 9): b'd\x00S\x00',
(3, 10): b'd\x00S\x00',
-
+ (3, 11): b'\x97\x00d\x00S\x00',
+ (3, 12): b'\x97\x00d\x00S\x00',
}
af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
af = SCons.Action.ActionFactory(LocalFunc, strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
class LocalActFunc:
def __call__(self):
@@ -2072,14 +2069,18 @@ class ActionCallerTestCase(unittest.TestCase):
af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr(
- matches[sys.version_info[:2]])
+ assert c == matches[sys.version_info[:2]], (
+ f"Got\n{c!r}\nExpected one of \n"
+ + repr(matches[sys.version_info[:2]])
+ )
matches = [
b"<built-in function str>",
@@ -2089,13 +2090,12 @@ class ActionCallerTestCase(unittest.TestCase):
af = SCons.Action.ActionFactory(str, strfunc)
ac = SCons.Action.ActionCaller(af, [], {})
c = ac.get_contents([], [], Environment())
- assert c == "<built-in function str>" or \
- c == "<type 'str'>" or \
- c == "<class 'str'>", repr(c)
+ assert c in ("<built-in function str>", "<type 'str'>", "<class 'str'>"), repr(c)
# ^^ class str for python3
def test___call__(self):
"""Test calling an ActionCaller"""
+
actfunc_args = []
def actfunc(a1, a2, a3, args=actfunc_args):
@@ -2123,6 +2123,7 @@ class ActionCallerTestCase(unittest.TestCase):
def test_strfunction(self):
"""Test calling the ActionCaller strfunction() method"""
+
strfunc_args = []
def actfunc(a1, a2, a3, a4):
@@ -2159,6 +2160,7 @@ class ActionFactoryTestCase(unittest.TestCase):
def test___call__(self):
"""Test calling whatever's returned from an ActionFactory"""
+
actfunc_args = []
strfunc_args = []
@@ -2175,12 +2177,12 @@ class ActionFactoryTestCase(unittest.TestCase):
class ActionCompareTestCase(unittest.TestCase):
-
def test_1_solo_name(self):
"""Test Lazy Cmd Generator Action get_name alone.
Basically ensures we can locate the builder, comparing it to
itself along the way."""
+
bar = SCons.Builder.Builder(action={})
env = Environment(BUILDERS={'BAR': bar})
name = bar.get_name(env)
@@ -2191,12 +2193,12 @@ class ActionCompareTestCase(unittest.TestCase):
Ensure that we can compare builders (and thereby actions) to
each other safely."""
+
foo = SCons.Builder.Builder(action='$FOO', suffix='.foo')
bar = SCons.Builder.Builder(action={})
assert foo != bar
assert foo.action != bar.action
- env = Environment(BUILDERS={'FOO': foo,
- 'BAR': bar})
+ env = Environment(BUILDERS={'FOO': foo, 'BAR': bar})
name = foo.get_name(env)
assert name == 'FOO', name
name = bar.get_name(env)
@@ -2214,10 +2216,7 @@ class ActionCompareTestCase(unittest.TestCase):
bar = SCons.Builder.Builder(action={}, suffix={None: '.bar'})
bar.add_action('.cow', "$MOO")
dog = SCons.Builder.Builder(suffix='.bar')
-
- env = Environment(BUILDERS={'FOO': foo,
- 'BAR': bar,
- 'DOG': dog})
+ env = Environment(BUILDERS={'FOO': foo, 'BAR': bar, 'DOG': dog})
assert foo.get_name(env) == 'FOO', foo.get_name(env)
assert bar.get_name(env) == 'BAR', bar.get_name(env)
@@ -2236,7 +2235,6 @@ class TestClass:
class ObjectContentsTestCase(unittest.TestCase):
-
def test_function_contents(self):
"""Test that Action._function_contents works"""
@@ -2244,20 +2242,26 @@ class ObjectContentsTestCase(unittest.TestCase):
"""A test function"""
return a
- # Since the python bytecode has per version differences, we need different expected results per version
+ # Since the python bytecode has per version differences,
+ # we need different expected results per version
expected = {
(3, 5): (bytearray(b'3, 3, 0, 0,(),(),(|\x00\x00S),(),()')),
(3, 6): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
(3, 7): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
(3, 8): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
(3, 9): (bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')),
- (3, 10): (bytearray(b'3, 3, 0, 0,(N.),(),(|\x00S\x00),(),()'),
- bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()')) # 3.10.1, 3.10.2
+ (3, 10): (
+ bytearray(b'3, 3, 0, 0,(N.),(),(|\x00S\x00),(),()'),
+ bytearray(b'3, 3, 0, 0,(),(),(|\x00S\x00),(),()'),
+ ), # 3.10.1, 3.10.2
+ (3, 11): bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),
+ (3, 12): bytearray(b'3, 3, 0, 0,(),(),(\x97\x00|\x00S\x00),(),()'),
}
c = SCons.Action._function_contents(func1)
- assert c in expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr(
- expected[sys.version_info[:2]])
+ assert c in expected[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
+ expected[sys.version_info[:2]]
+ )
def test_object_contents(self):
"""Test that Action._object_contents works"""
@@ -2268,24 +2272,38 @@ class ObjectContentsTestCase(unittest.TestCase):
# c = SCons.Action._object_instance_content(o)
- # Since the python bytecode has per version differences, we need different expected results per version
+ # Since the python bytecode has per version differences,
+ # we need different expected results per version
expected = {
(3, 5): bytearray(
- b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01\x00|\x00\x00_\x00\x00d\x02\x00|\x00\x00_\x01\x00d\x00\x00S),(),(),2, 2, 0, 0,(),(),(d\x00\x00S),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 6): bytearray(
- b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 7): bytearray(
- b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 8): bytearray(
- b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 9): bytearray(
- b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
(3, 10): bytearray(
- b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"),
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(d\x01|\x00_\x00d\x02|\x00_\x01d\x00S\x00),(),(),2, 2, 0, 0,(),(),(d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
+ (3, 11): bytearray(
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00d\x00S\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
+ (3, 12): bytearray(
+ b"{TestClass:__main__}[[[(<class \'object\'>, ()), [(<class \'__main__.TestClass\'>, (<class \'object\'>,))]]]]{{1, 1, 0, 0,(a,b),(a,b),(\x97\x00d\x01|\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x02|\x00_\x01\x00\x00\x00\x00\x00\x00\x00\x00d\x00S\x00),(),(),2, 2, 0, 0,(),(),(\x97\x00d\x00S\x00),(),()}}{{{a=a,b=b}}}"
+ ),
}
- assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr(
- expected[sys.version_info[:2]])
+ assert c == expected[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
+ expected[sys.version_info[:2]]
+ )
def test_code_contents(self):
"""Test that Action._code_contents works"""
@@ -2295,25 +2313,47 @@ class ObjectContentsTestCase(unittest.TestCase):
# Since the python bytecode has per version differences, we need different expected results per version
expected = {
- (3, 5): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'),
- (3, 6): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 7): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 8): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 9): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
- (3, 10): bytearray(b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'),
+ (3, 5): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00\x00d\x00\x00\x83\x01\x00\x01d\x01\x00S)'
+ ),
+ (3, 6): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 7): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 8): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 9): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 10): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(e\x00d\x00\x83\x01\x01\x00d\x01S\x00)'
+ ),
+ (3, 11): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xa6\x01\x00\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x01S\x00)'
+ ),
+ (3, 12): bytearray(
+ b'0, 0, 0, 0,(Hello, World!),(print),(\x97\x00\x02\x00e\x00d\x00\xab\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00d\x01S\x00)'
+ ),
}
- assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr(expected[
- sys.version_info[:2]])
+ assert c == expected[sys.version_info[:2]], f"Got\n{c!r}\nExpected\n" + repr(
+ expected[sys.version_info[:2]]
+ )
def test_uncaught_exception_bubbles(self):
"""Test that _subproc bubbles uncaught exceptions"""
+
try:
- pobj = SCons.Action._subproc(Environment(),
- None,
- stdin='devnull',
- stderr='devnull',
- stdout=subprocess.PIPE)
+ pobj = SCons.Action._subproc(
+ Environment(),
+ None,
+ stdin='devnull',
+ stderr='devnull',
+ stdout=subprocess.PIPE,
+ )
pobj.wait()
except EnvironmentError:
pass
diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py
index 14e52ad..70c4f38 100644
--- a/SCons/CacheDir.py
+++ b/SCons/CacheDir.py
@@ -211,35 +211,42 @@ class CacheDir:
(self.requests, self.hits, self.misses, self.hit_ratio))
@classmethod
- def copy_from_cache(cls, env, src, dst):
+ def copy_from_cache(cls, env, src, dst) -> str:
+ """Copy a file from cache."""
if env.cache_timestamp_newer:
return env.fs.copy(src, dst)
else:
return env.fs.copy2(src, dst)
@classmethod
- def copy_to_cache(cls, env, src, dst):
+ def copy_to_cache(cls, env, src, dst) -> str:
+ """Copy a file to cache.
+
+ Just use the FS copy2 ("with metadata") method, except do an additional
+ check and if necessary a chmod to ensure the cachefile is writeable,
+ to forestall permission problems if the cache entry is later updated.
+ """
try:
result = env.fs.copy2(src, dst)
- fs = env.File(src).fs
- st = fs.stat(src)
- fs.chmod(dst, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
+ st = stat.S_IMODE(os.stat(result).st_mode)
+ if not st | stat.S_IWRITE:
+ os.chmod(dst, st | stat.S_IWRITE)
return result
except AttributeError as ex:
raise EnvironmentError from ex
@property
- def hit_ratio(self):
+ def hit_ratio(self) -> float:
return (100.0 * self.hits / self.requests if self.requests > 0 else 100)
@property
- def misses(self):
+ def misses(self) -> int:
return self.requests - self.hits
- def is_enabled(self):
+ def is_enabled(self) -> bool:
return cache_enabled and self.path is not None
- def is_readonly(self):
+ def is_readonly(self) -> bool:
return cache_readonly
def get_cachedir_csig(self, node):
@@ -247,18 +254,21 @@ class CacheDir:
if cachefile and os.path.exists(cachefile):
return SCons.Util.hash_file_signature(cachefile, SCons.Node.FS.File.hash_chunksize)
- def cachepath(self, node):
- """
+ def cachepath(self, node) -> tuple:
+ """Return where to cache a file.
+
+ Given a Node, obtain the configured cache directory and
+ the path to the cached file, which is generated from the
+ node's build signature. If caching is not enabled for the
+ None, return a tuple of None.
"""
if not self.is_enabled():
return None, None
sig = node.get_cachedir_bsig()
-
subdir = sig[:self.config['prefix_len']].upper()
-
- dir = os.path.join(self.path, subdir)
- return dir, os.path.join(dir, sig)
+ cachedir = os.path.join(self.path, subdir)
+ return cachedir, os.path.join(cachedir, sig)
def retrieve(self, node):
"""
diff --git a/SCons/Defaults.py b/SCons/Defaults.py
index 65bd75a..40c3e4a 100644
--- a/SCons/Defaults.py
+++ b/SCons/Defaults.py
@@ -41,10 +41,12 @@ import SCons.Action
import SCons.Builder
import SCons.CacheDir
import SCons.Environment
+import SCons.Errors
import SCons.PathList
import SCons.Scanner.Dir
import SCons.Subst
import SCons.Tool
+import SCons.Util
# A placeholder for a default Environment (for fetching source files
# from source code management systems and the like). This must be
@@ -79,7 +81,6 @@ def DefaultEnvironment(*args, **kw):
"""
global _default_env
if not _default_env:
- import SCons.Util
_default_env = SCons.Environment.Environment(*args, **kw)
_default_env.Decider('content')
global DefaultEnvironment
@@ -157,15 +158,19 @@ LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
ActionFactory = SCons.Action.ActionFactory
-def get_paths_str(dest):
- # If dest is a list, we need to manually call str() on each element
+def get_paths_str(dest) -> str:
+ """Generates a string from *dest* for use in a strfunction.
+
+ If *dest* is a list, manually converts each elem to a string.
+ """
+ def quote(arg):
+ return f'"{arg}"'
+
if SCons.Util.is_List(dest):
- elem_strs = []
- for element in dest:
- elem_strs.append('"' + str(element) + '"')
- return '[' + ', '.join(elem_strs) + ']'
+ elem_strs = [quote(d) for d in dest]
+ return f'[{", ".join(elem_strs)}]'
else:
- return '"' + str(dest) + '"'
+ return quote(dest)
permission_dic = {
@@ -187,8 +192,14 @@ permission_dic = {
}
-def chmod_func(dest, mode):
- import SCons.Util
+def chmod_func(dest, mode) -> None:
+ """Implementation of the Chmod action function.
+
+ *mode* can be either an integer (normally expressed in octal mode,
+ as in 0o755) or a string following the syntax of the POSIX chmod
+ command (for example "ugo+w"). The latter must be converted, since
+ the underlying Python only takes the numeric form.
+ """
from string import digits
SCons.Node.FS.invalidate_node_memos(dest)
if not SCons.Util.is_List(dest):
@@ -231,55 +242,85 @@ def chmod_func(dest, mode):
os.chmod(str(element), curr_perm & ~new_perm)
-def chmod_strfunc(dest, mode):
- import SCons.Util
+def chmod_strfunc(dest, mode) -> str:
+ """strfunction for the Chmod action function."""
if not SCons.Util.is_String(mode):
- return 'Chmod(%s, 0%o)' % (get_paths_str(dest), mode)
+ return f'Chmod({get_paths_str(dest)}, {mode:#o})'
else:
- return 'Chmod(%s, "%s")' % (get_paths_str(dest), str(mode))
+ return f'Chmod({get_paths_str(dest)}, "{mode}")'
+
Chmod = ActionFactory(chmod_func, chmod_strfunc)
-def copy_func(dest, src, symlinks=True):
- """
- If symlinks (is true), then a symbolic link will be
+
+def copy_func(dest, src, symlinks=True) -> int:
+ """Implementation of the Copy action function.
+
+ Copies *src* to *dest*. If *src* is a list, *dest* must be
+ a directory, or not exist (will be created).
+
+ Since Python :mod:`shutil` methods, which know nothing about
+ SCons Nodes, will be called to perform the actual copying,
+ args are converted to strings first.
+
+ If *symlinks* evaluates true, then a symbolic link will be
shallow copied and recreated as a symbolic link; otherwise, copying
a symbolic link will be equivalent to copying the symbolic link's
final target regardless of symbolic link depth.
"""
dest = str(dest)
- src = str(src)
+ src = [str(n) for n in src] if SCons.Util.is_List(src) else str(src)
SCons.Node.FS.invalidate_node_memos(dest)
- if SCons.Util.is_List(src) and os.path.isdir(dest):
+ if SCons.Util.is_List(src):
+ # this fails only if dest exists and is not a dir
+ try:
+ os.makedirs(dest, exist_ok=True)
+ except FileExistsError:
+ raise SCons.Errors.BuildError(
+ errstr=(
+ 'Error: Copy() called with a list of sources, '
+ 'which requires target to be a directory, '
+ f'but "{dest}" is not a directory.'
+ )
+ )
for file in src:
shutil.copy2(file, dest)
return 0
+
elif os.path.islink(src):
if symlinks:
- return os.symlink(os.readlink(src), dest)
- else:
- return copy_func(dest, os.path.realpath(src))
+ os.symlink(os.readlink(src), dest)
+ return 0
+
+ return copy_func(dest, os.path.realpath(src))
+
elif os.path.isfile(src):
shutil.copy2(src, dest)
return 0
+
else:
shutil.copytree(src, dest, symlinks)
- # copytree returns None in python2 and destination string in python3
- # A error is raised in both cases, so we can just return 0 for success
return 0
-Copy = ActionFactory(
- copy_func,
- lambda dest, src, symlinks=True: 'Copy("%s", "%s")' % (dest, src)
-)
+def copy_strfunc(dest, src, symlinks=True) -> str:
+ """strfunction for the Copy action function."""
+ return f'Copy({get_paths_str(dest)}, {get_paths_str(src)})'
+
+
+Copy = ActionFactory(copy_func, copy_strfunc)
-def delete_func(dest, must_exist=0):
+def delete_func(dest, must_exist=False) -> None:
+ """Implementation of the Delete action function.
+
+ Lets the Python :func:`os.unlink` raise an error if *dest* does not exist,
+ unless *must_exist* evaluates false (the default).
+ """
SCons.Node.FS.invalidate_node_memos(dest)
if not SCons.Util.is_List(dest):
dest = [dest]
@@ -296,14 +337,16 @@ def delete_func(dest, must_exist=0):
os.unlink(entry)
-def delete_strfunc(dest, must_exist=0):
- return 'Delete(%s)' % get_paths_str(dest)
+def delete_strfunc(dest, must_exist=False) -> str:
+ """strfunction for the Delete action function."""
+ return f'Delete({get_paths_str(dest)})'
Delete = ActionFactory(delete_func, delete_strfunc)
-def mkdir_func(dest):
+def mkdir_func(dest) -> None:
+ """Implementation of the Mkdir action function."""
SCons.Node.FS.invalidate_node_memos(dest)
if not SCons.Util.is_List(dest):
dest = [dest]
@@ -311,22 +354,23 @@ def mkdir_func(dest):
os.makedirs(str(entry), exist_ok=True)
-Mkdir = ActionFactory(mkdir_func,
- lambda _dir: 'Mkdir(%s)' % get_paths_str(_dir))
+Mkdir = ActionFactory(mkdir_func, lambda _dir: f'Mkdir({get_paths_str(_dir)})')
-def move_func(dest, src):
+def move_func(dest, src) -> None:
+ """Implementation of the Move action function."""
SCons.Node.FS.invalidate_node_memos(dest)
SCons.Node.FS.invalidate_node_memos(src)
shutil.move(src, dest)
-Move = ActionFactory(move_func,
- lambda dest, src: 'Move("%s", "%s")' % (dest, src),
- convert=str)
+Move = ActionFactory(
+ move_func, lambda dest, src: f'Move("{dest}", "{src}")', convert=str
+)
-def touch_func(dest):
+def touch_func(dest) -> None:
+ """Implementation of the Touch action function."""
SCons.Node.FS.invalidate_node_memos(dest)
if not SCons.Util.is_List(dest):
dest = [dest]
@@ -341,8 +385,7 @@ def touch_func(dest):
os.utime(file, (atime, mtime))
-Touch = ActionFactory(touch_func,
- lambda file: 'Touch(%s)' % get_paths_str(file))
+Touch = ActionFactory(touch_func, lambda file: f'Touch({get_paths_str(file)})')
# Internal utility functions
diff --git a/SCons/Environment.py b/SCons/Environment.py
index c38b51c..7212c89 100644
--- a/SCons/Environment.py
+++ b/SCons/Environment.py
@@ -481,7 +481,7 @@ class SubstitutionEnvironment:
def lvars(self):
return {}
- def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None):
+ def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None, overrides=False):
"""Recursively interpolates construction variables from the
Environment into the specified string, returning the expanded
result. Construction variables are specified by a $ prefix
@@ -496,7 +496,7 @@ class SubstitutionEnvironment:
lvars['__env__'] = self
if executor:
lvars.update(executor.get_lvars())
- return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
+ return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides)
def subst_kw(self, kw, raw=0, target=None, source=None):
nkw = {}
@@ -507,7 +507,7 @@ class SubstitutionEnvironment:
nkw[k] = v
return nkw
- def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None):
+ def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None, overrides=False):
"""Calls through to SCons.Subst.scons_subst_list(). See
the documentation for that function."""
gvars = self.gvars()
@@ -515,7 +515,7 @@ class SubstitutionEnvironment:
lvars['__env__'] = self
if executor:
lvars.update(executor.get_lvars())
- return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
+ return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv, overrides=overrides)
def subst_path(self, path, target=None, source=None):
"""Substitute a path list, turning EntryProxies into Nodes
@@ -802,7 +802,7 @@ class SubstitutionEnvironment:
'-openmp',
'-fmerge-all-constants',
'-fopenmp',
- ):
+ ) or arg.startswith('-fsanitize'):
mapping['CCFLAGS'].append(arg)
mapping['LINKFLAGS'].append(arg)
elif arg == '-mwindows':
@@ -1897,6 +1897,11 @@ class Base(SubstitutionEnvironment):
return self.fs.Dir(self.subst(tp)).srcnode().get_abspath()
def Tool(self, tool, toolpath=None, **kwargs) -> SCons.Tool.Tool:
+ """Find and run tool module *tool*.
+
+ .. versionchanged:: 4.2
+ returns the tool module rather than ``None``.
+ """
if is_String(tool):
tool = self.subst(tool)
if toolpath is None:
@@ -2335,7 +2340,10 @@ class Base(SubstitutionEnvironment):
return [self.subst(arg)]
def Value(self, value, built_value=None, name=None):
- """
+ """Return a Value (Python expression) node.
+
+ .. versionchanged:: 4.0
+ the *name* parameter was added.
"""
return SCons.Node.Python.ValueWithMemo(value, built_value, name)
@@ -2344,9 +2352,8 @@ class Base(SubstitutionEnvironment):
src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
self.fs.VariantDir(variant_dir, src_dir, duplicate)
- def FindSourceFiles(self, node='.'):
- """ returns a list of all source files.
- """
+ def FindSourceFiles(self, node='.') -> list:
+ """Return a list of all source files."""
node = self.arg2nodes(node, self.fs.Entry)[0]
sources = []
diff --git a/SCons/Environment.xml b/SCons/Environment.xml
index 3a6df97..2e06b1e 100644
--- a/SCons/Environment.xml
+++ b/SCons/Environment.xml
@@ -318,7 +318,7 @@ Added methods propagate through &f-env-Clone; calls.
</para>
<para>
-Examples:
+More examples:
</para>
<example_commands>
@@ -1630,46 +1630,50 @@ See the manpage section "Construction Environments" for more details.
<scons_function name="Execute">
<arguments>
-(action, [strfunction, varlist])
+(action, [actionargs ...])
</arguments>
<summary>
<para>
-Executes an Action object.
-The specified
+Executes an Action.
<parameter>action</parameter>
may be an Action object
-(see manpage section "Action Objects"
-for an explanation of behavior),
or it may be a command-line string,
list of commands,
or executable &Python; function,
-each of which will be converted
+each of which will first be converted
into an Action object
and then executed.
Any additional arguments to &f-Execute;
-(<parameter>strfunction</parameter>, <parameter>varlist</parameter>)
are passed on to the &f-link-Action; factory function
-which actually creates the Action object.
-The exit value of the command
-or return value of the &Python; function
-will be returned.
+which actually creates the Action object
+(see the manpage section <link linkend="action_objects">Action Objects</link>
+for a description). Example:
+</para>
+
+<example_commands>
+Execute(Copy('file.out', 'file.in'))
+</example_commands>
+
+<para>&f-Execute; performs its action immediately,
+as part of the SConscript-reading phase.
+There are no sources or targets declared in an
+&f-Execute; call, so any objects it manipulates
+will not be tracked as part of the &SCons; dependency graph.
+In the example above, neither
+<filename>file.out</filename> nor
+<filename>file.in</filename> will be tracked objects.
</para>
<para>
-Note that
+&f-Execute; returns the exit value of the command
+or return value of the &Python; function.
&scons;
-will print an error message if the executed
+prints an error message if the executed
<parameter>action</parameter>
-fails--that is,
-exits with or returns a non-zero value.
-&scons;
-will
+fails (exits with or returns a non-zero value),
+however it does
<emphasis>not</emphasis>,
-however,
-automatically terminate the build
-if the specified
-<parameter>action</parameter>
-fails.
+automatically terminate the build for such a failure.
If you want the build to stop in response to a failed
&f-Execute;
call,
@@ -1677,8 +1681,6 @@ you must explicitly check for a non-zero return value:
</para>
<example_commands>
-Execute(Copy('file.out', 'file.in'))
-
if Execute("mkdir sub/dir/ectory"):
# The mkdir failed, don't try to build.
Exit(1)
@@ -1899,48 +1901,81 @@ Nodes or strings representing path names.
<scons_function name="Glob">
<arguments>
-(pattern, [ondisk, source, strings, exclude])
+(pattern, [ondisk=True, source=False, strings=False, exclude=None])
</arguments>
<summary>
<para>
-Returns Nodes (or strings) that match the specified
-<parameter>pattern</parameter>,
-relative to the directory of the current
-&SConscript;
-file.
+Returns a possibly empty list of Nodes (or strings) that match
+pathname specification <parameter>pattern</parameter>.
+<parameter>pattern</parameter> can be absolute,
+top-relative,
+or (most commonly) relative to the directory of the current
+&SConscript; file.
+&f-Glob; matches both files stored on disk and Nodes
+which &SCons; already knows about, even if any corresponding
+file is not currently stored on disk.
The evironment method form (&f-env-Glob;)
performs string substition on
<parameter>pattern</parameter>
-and returns whatever matches
-the resulting expanded pattern.
+and returns whatever matches the resulting expanded pattern.
+The results are sorted, unlike for the similar &Python;
+<systemitem>glob.glob</systemitem> function,
+to ensure build order will be stable.
</para>
<para>
-The specified
<parameter>pattern</parameter>
-uses Unix shell style metacharacters for matching:
-</para>
-
-<example_commands>
- * matches everything
- ? matches any single character
- [seq] matches any character in seq
- [!seq] matches any char not in seq
-</example_commands>
-
-<para>
-If the first character of a filename is a dot,
-it must be matched explicitly.
-Character matches do
-<emphasis>not</emphasis>
-span directory separators.
+can contain POSIX-style shell metacharacters for matching:
+</para>
+
+<informaltable rowsep="1" colsep="1" frame="topbot">
+<tgroup cols="2">
+<thead>
+ <row>
+ <entry>Pattern</entry>
+ <entry>Meaning</entry>
+ </row>
+</thead>
+<tbody>
+ <row>
+ <entry><literal>*</literal></entry>
+ <entry>matches everything</entry>
+ </row>
+ <row>
+ <entry><literal>?</literal></entry>
+ <entry>matches any single character</entry>
+ </row>
+ <row>
+ <entry><literal>[seq]</literal></entry>
+ <entry>matches any character in <emphasis>seq</emphasis>
+ (can be a list or a range).</entry>
+ </row>
+ <row>
+ <entry><literal>[!seq]</literal></entry>
+ <entry>matches any character not in <emphasis>seq</emphasis></entry>
+ </row>
+</tbody>
+</tgroup>
+</informaltable>
+
+<para>
+For a literal match, wrap the metacharacter in brackets to
+escape the normal behavior.
+For example, <literal>'[?]'</literal> matches the character
+<literal>'?'</literal>.
+</para>
+
+<para>
+Filenames starting with a dot are specially handled -
+they can only be matched by patterns that start with a dot
+(or have a dot immediately following a pathname separator
+character, or slash), they are not not matched by the metacharacters.
+Metacharacter matches also do not span directory separators.
</para>
<para>
-The
&f-Glob;
-knows about
-repositories
+understands repositories
(see the
&f-link-Repository;
function)
@@ -1948,8 +1983,7 @@ and source directories
(see the
&f-link-VariantDir;
function)
-and
-returns a Node (or string, if so configured)
+and returns a Node (or string, if so configured) match
in the local (SConscript) directory
if a matching Node is found
anywhere in a corresponding
@@ -1957,65 +1991,60 @@ repository or source directory.
</para>
<para>
-The
+If the optional
<parameter>ondisk</parameter>
-argument may be set to a value which evaluates
-<constant>False</constant>
-to disable the search for matches on disk,
-thereby only returning matches among
-already-configured File or Dir Nodes.
-The default behavior is to
-return corresponding Nodes
-for any on-disk matches found.
+argument evaluates false,
+the search for matches on disk is disabled,
+and only matches from
+already-configured File or Dir Nodes are returned.
+The default is to return Nodes for
+matches on disk as well.
</para>
<para>
-The
+If the optional
<parameter>source</parameter>
-argument may be set to a value which evaluates
-<constant>True</constant>
-to specify that,
-when the local directory is a
-&f-VariantDir;,
-the returned Nodes should be from the
-corresponding source directory,
-not the local directory.
+argument evaluates true,
+and the local directory is a variant directory,
+then &f-Glob; returnes Nodes from
+the corresponding source directory,
+rather than the local directory.
+<!-- XXX what about generated files that don't exist in src but will be sources? -->
</para>
<para>
-The
+If the optional
<parameter>strings</parameter>
-argument may be set to a value which evaluates
-<constant>True</constant>
-to have the
+argument evaluates true,
&f-Glob;
-function return strings, not Nodes,
-that represent the matched files or directories.
+returns matches as strings, rather than Nodes.
The returned strings will be relative to
the local (SConscript) directory.
-(Note that This may make it easier to perform
+(Note that while this may make it easier to perform
arbitrary manipulation of file names,
-but if the returned strings are
+it loses the context &SCons; would have in the Node,
+so if the returned strings are
passed to a different
&SConscript;
file,
-any Node translation will be relative
-to the other
+any Node translation there will be relative
+to that
&SConscript;
directory,
-not the original
+not to the original
&SConscript;
directory.)
</para>
<para>
-The
+The optional
<parameter>exclude</parameter>
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.
+descibing files or directories
+to filter out of the match list.
+Elements matching a least one specified pattern will be excluded.
+These patterns use the same syntax as for
+<parameter>pattern</parameter>.
</para>
<para>
@@ -2025,9 +2054,10 @@ Examples:
<example_commands>
Program("foo", Glob("*.c"))
Zip("/tmp/everything", Glob(".??*") + Glob("*"))
-sources = Glob("*.cpp", exclude=["os_*_specific_*.cpp"]) + \
- Glob( "os_%s_specific_*.cpp" % currentOS)
+sources = Glob("*.cpp", exclude=["os_*_specific_*.cpp"]) \
+ + Glob("os_%s_specific_*.cpp" % currentOS)
</example_commands>
+
</summary>
</scons_function>
@@ -2064,24 +2094,26 @@ to call all builders.
</arguments>
<summary>
<para>
-The specified dependency file(s)
-will be ignored when deciding if
-the target file(s) need to be rebuilt.
-</para>
-
-<para>
-You can also use
-&f-Ignore;
-to remove a target from the default build.
-In order to do this you must specify the directory the target will
-be built in as the target, and the file you want to skip building
-as the dependency.
+Ignores <parameter>dependency</parameter>
+when deciding if
+<parameter>target</parameter> needs to be rebuilt.
+<parameter>target</parameter> and
+<parameter>dependency</parameter>
+can each be a single filename or Node
+or a list of filenames or Nodes.
</para>
<para>
-Note that this will only remove the dependencies listed from
-the files built by default. It will still be built if that
-dependency is needed by another object being built.
+&f-Ignore; can also be used to
+remove a target from the default build
+by specifying the directory the target will be built in as
+<parameter>target</parameter>
+and the file you want to skip selecting for building as
+<parameter>dependency</parameter>.
+Note that this only removes the target from
+the default target selection algorithm:
+if it is a dependency of another object being
+built &SCons; still builds it normally.
See the third and forth examples below.
</para>
@@ -2491,15 +2523,15 @@ This can be used to call a <filename>*-config</filename>
command typical of the POSIX programming environment
(for example,
<command>pkg-config</command>).
-Note that such a comamnd is executed using the
+Note that such a command is executed using the
SCons execution environment;
if the command needs additional information,
-that information needs to be explcitly provided.
+that information needs to be explicitly provided.
See &f-link-ParseConfig; for more details.
</para>
<para>
-Flag values are translated accordig to the prefix found,
+Flag values are translated according to the prefix found,
and added to the following construction variables:
</para>
@@ -2510,6 +2542,7 @@ and added to the following construction variables:
-frameworkdir= FRAMEWORKPATH
-fmerge-all-constants CCFLAGS, LINKFLAGS
-fopenmp CCFLAGS, LINKFLAGS
+-fsanitize CCFLAGS, LINKFLAGS
-include CCFLAGS
-imacros CCFLAGS
-isysroot CCFLAGS, LINKFLAGS
@@ -2859,7 +2892,7 @@ for a complete explanation of the arguments and behavior.
</scons_function>
<scons_function name="SConscriptChdir">
-<arguments>
+<arguments signature="global">
(value)
</arguments>
<summary>
@@ -2868,18 +2901,11 @@ By default,
&scons;
changes its working directory
to the directory in which each
-subsidiary SConscript file lives.
+subsidiary SConscript file lives
+while reading and processing that script.
This behavior may be disabled
-by specifying either:
-</para>
-
-<example_commands>
-SConscriptChdir(0)
-env.SConscriptChdir(0)
-</example_commands>
-
-<para>
-in which case
+by specifying an argument which
+evaluates false, in which case
&scons;
will stay in the top-level directory
while reading all SConscript files.
@@ -2897,10 +2923,9 @@ Example:
</para>
<example_commands>
-env = Environment()
-SConscriptChdir(0)
+SConscriptChdir(False)
SConscript('foo/SConscript') # will not chdir to foo
-env.SConscriptChdir(1)
+SConscriptChdir(True)
SConscript('bar/SConscript') # will chdir to bar
</example_commands>
</summary>
@@ -3286,6 +3311,12 @@ appended to the &cv-link-TOOLS;
</para>
<para>
+<emphasis>Changed in version 4.2:</emphasis>
+&f-env-Tool; now returns the tool object,
+previously it did not return (i.e. returned <constant>None</constant>).
+</para>
+
+<para>
Examples:
</para>
@@ -3322,12 +3353,6 @@ msvctool(env) # adds 'msvc' to the TOOLS variable
gltool = Tool('opengl', toolpath = ['tools'])
gltool(env) # adds 'opengl' to the TOOLS variable
</example_commands>
-
-<para>
-<emphasis>Changed in &SCons; 4.2: &f-env-Tool; now returns
-the tool object, previously it did not return
-(i.e. returned <constant>None</constant>).</emphasis>
-</para>
</summary>
</scons_function>
@@ -3348,10 +3373,6 @@ will be rebuilt.
files are up-to-date.)
When using timestamp source signatures, Value Nodes'
timestamps are equal to the system time when the Node is created.
-<parameter>name</parameter> can be provided as an alternative name
-for the resulting <literal>Value</literal> node; this is advised
-if the <parameter>value</parameter> parameter can't be converted to
-a string.
</para>
<para>
@@ -3371,6 +3392,18 @@ method that will return the built value of the Node.
</para>
<para>
+The optional <parameter>name</parameter> parameter can be provided as an
+alternative name for the resulting <literal>Value</literal> node;
+this is advised if the <parameter>value</parameter> parameter
+cannot be converted to a string.
+</para>
+
+<para>
+<emphasis>Changed in version 4.0:</emphasis>
+the <parameter>name</parameter> parameter was added.
+</para>
+
+<para>
Examples:
</para>
@@ -3390,8 +3423,8 @@ prefix = ARGUMENTS.get('prefix', '/usr/local')
# Attach a .Config() builder for the above function action
# to the construction environment.
-env['BUILDERS']['Config'] = Builder(action = create)
-env.Config(target = 'package-config', source = Value(prefix))
+env['BUILDERS']['Config'] = Builder(action=create)
+env.Config(target='package-config', source=Value(prefix))
def build_value(target, source, env):
# A function that "builds" a Python Value by updating
@@ -3404,8 +3437,8 @@ input = env.Value('after')
# Attach a .UpdateValue() builder for the above function
# action to the construction environment.
-env['BUILDERS']['UpdateValue'] = Builder(action = build_value)
-env.UpdateValue(target = Value(output), source = Value(input))
+env['BUILDERS']['UpdateValue'] = Builder(action=build_value)
+env.UpdateValue(target=Value(output), source=Value(input))
</example_commands>
</summary>
</scons_function>
@@ -3416,42 +3449,41 @@ env.UpdateValue(target = Value(output), source = Value(input))
</arguments>
<summary>
<para>
-Sets up an alternate build location.
-When building in the <parameter>variant_dir</parameter>,
-&SCons; backfills as needed with files from <parameter>src_dir</parameter>
-to create a complete build directory.
+Sets up a mapping to define a variant build directory in
+<parameter>variant_dir</parameter>.
+<parameter>src_dir</parameter> may not be underneath
+<parameter>variant_dir</parameter>.
+A &f-VariantDir; mapping is global, even if called using the
+&f-env-VariantDir; form.
&f-VariantDir;
can be called multiple times with the same
<parameter>src_dir</parameter>
-to set up multiple builds with different options
-(<emphasis>variants</emphasis>).
+to set up multiple variant builds with different options.
</para>
<para>
-The
-<parameter>variant</parameter>
-location must be in or underneath the project top directory,
-and <parameter>src_dir</parameter>
-may not be underneath
-<parameter>variant_dir</parameter>.
+Note if <parameter>variant_dir</parameter>
+is not under the project top directory,
+target selection rules will not pick targets in the
+variant directory unless they are explicitly specified.
</para>
<para>
+When files in <parameter>variant_dir</parameter> are referenced,
+&SCons; backfills as needed with files from <parameter>src_dir</parameter>
+to create a complete build directory.
By default, &SCons;
-physically duplicates the source files and SConscript files
-as needed into the variant tree.
-Thus, a build performed in the variant tree is guaranteed to be identical
-to a build performed in the source tree even if
+physically duplicates the source files, SConscript files,
+and directory structure as needed into the variant directory.
+Thus, a build performed in the variant directory is guaranteed to be identical
+to a build performed in the source directory even if
intermediate source files are generated during the build,
or if preprocessors or other scanners search for included files
-relative to the source file,
+using paths relative to the source file,
or if individual compilers or other invoked tools are hard-coded
to put derived files in the same directory as source files.
Only the files &SCons; calculates are needed for the build are
duplicated into <parameter>variant_dir</parameter>.
-</para>
-
-<para>
If possible on the platform,
the duplication is performed by linking rather than copying.
This behavior is affected by the
@@ -3470,44 +3502,46 @@ to invoke Builders using the path names of source files in
<parameter>src_dir</parameter>
and the path names of derived files within
<parameter>variant_dir</parameter>.
-This is more efficient than
-<literal>duplicate=True</literal>,
+This is more efficient than duplicating,
and is safe for most builds;
-revert to <constant>True</constant>
+revert to <literal>duplicate=True</literal>
if it causes problems.
</para>
<para>
&f-VariantDir;
-works most naturally with used with a subsidiary SConscript file.
-The subsidiary SConscript file is called as if it
-were in
+works most naturally when used with a subsidiary SConscript file.
+The subsidiary SConscript file must be called as if it were in
<parameter>variant_dir</parameter>,
regardless of the value of
<parameter>duplicate</parameter>.
-This is how you tell
-&scons;
-which variant of a source tree to build:
+When calling an SConscript file, you can use the
+<parameter>exports</parameter> keyword argument
+to pass parameters (individually or as an appropriately set up environment)
+so the SConscript can pick up the right settings for that variant build.
+The SConscript must &f-link-Import; these to use them. Example:
</para>
<example_commands>
+env1 = Environment(...settings for variant1...)
+env2 = Environment(...settings for variant2...)
+
# run src/SConscript in two variant directories
VariantDir('build/variant1', 'src')
-SConscript('build/variant1/SConscript')
+SConscript('build/variant1/SConscript', exports={"env": env1})
VariantDir('build/variant2', 'src')
-SConscript('build/variant2/SConscript')
+SConscript('build/variant2/SConscript', exports={"env": env2})
</example_commands>
<para>
See also the
-&f-link-SConscript;
-function, described above,
+&f-link-SConscript; function
for another way to specify a variant directory
in conjunction with calling a subsidiary SConscript file.
</para>
<para>
-Examples:
+More examples:
</para>
<example_commands>
diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py
index 8bf327a..a021794 100644
--- a/SCons/EnvironmentTests.py
+++ b/SCons/EnvironmentTests.py
@@ -820,6 +820,8 @@ sys.exit(0)
"--param l1-cache-size=32 --param l2-cache-size=6144 "
"+DD64 "
"-DFOO -DBAR=value -D BAZ "
+ "-fsanitize=memory "
+ "-fsanitize-address-use-after-return "
)
d = env.ParseFlags(s)
@@ -836,7 +838,9 @@ sys.exit(0)
('-imacros', env.fs.File('/usr/include/foo4')),
('-include', env.fs.File('/usr/include/foo5')),
('--param', 'l1-cache-size=32'), ('--param', 'l2-cache-size=6144'),
- '+DD64'], repr(d['CCFLAGS'])
+ '+DD64',
+ '-fsanitize=memory',
+ '-fsanitize-address-use-after-return'], repr(d['CCFLAGS'])
assert d['CXXFLAGS'] == ['-std=c++0x'], repr(d['CXXFLAGS'])
assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
@@ -856,7 +860,9 @@ sys.exit(0)
'-mno-cygwin', '-mwindows',
('-arch', 'i386'),
('-isysroot', '/tmp'),
- '+DD64'], repr(d['LINKFLAGS'])
+ '+DD64',
+ '-fsanitize=memory',
+ '-fsanitize-address-use-after-return'], repr(d['LINKFLAGS'])
assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH']
diff --git a/SCons/Errors.py b/SCons/Errors.py
index 04cea38..d37cb6d 100644
--- a/SCons/Errors.py
+++ b/SCons/Errors.py
@@ -24,60 +24,49 @@
"""SCons exception classes.
Used to handle internal and user errors in SCons.
-
"""
import shutil
import SCons.Util
+# Note that not all Errors are defined here, some are at the point of use
+
class BuildError(Exception):
"""SCons Errors that can occur while building.
- Attributes:
- Information about the cause of the build error :
-
- errstr: a description of the error message
-
- status: the return code of the action that caused the build error.
- Must be set to a non-zero value even if the build error is not due
- to an action returning a non-zero returned code.
-
- exitstatus: SCons exit status due to this build error.
- Must be nonzero unless due to an explicit Exit()
- call. Not always the same as status, since
- actions return a status code that should be
- respected, but SCons typically exits with 2
- irrespective of the return value of the failed
- action.
-
- filename: The name of the file or directory that caused the
- build error. Set to None if no files are associated with
- this error. This might be different from the target
- being built. For example, failure to create the
- directory in which the target file will appear. It
- can be None if the error is not due to a particular
- filename.
+ A :class:`BuildError` exception contains information both
+ about the erorr itself, and what caused the error.
- exc_info: Info about exception that caused the build
- error. Set to (None, None, None) if this build
- error is not due to an exception.
-
- Information about the what caused the build error :
-
- node: the error occurred while building this target node(s)
-
- executor: the executor that caused the build to fail (might
- be None if the build failures is not due to the
- executor failing)
-
- action: the action that caused the build to fail (might be
- None if the build failures is not due to the an
- action failure)
-
- command: the command line for the action that caused the
- build to fail (might be None if the build failures
- is not due to the an action failure)
+ Attributes:
+ node: (*cause*) the error occurred while building this target node(s)
+ errstr: (*info*) a description of the error message
+ status: (*info*) the return code of the action that caused the build error.
+ Must be set to a non-zero value even if the build error is not due
+ to an action returning a non-zero returned code.
+ exitstatus: (*info*) SCons exit status due to this build error.
+ Must be nonzero unless due to an explicit :meth:`Exit` call.
+ Not always the same as ``status``, since actions return a status
+ code that should be respected, but SCons typically exits with 2
+ irrespective of the return value of the failed action.
+ filename: (*info*) The name of the file or directory that caused the
+ build error. Set to ``None`` if no files are associated with
+ this error. This might be different from the target
+ being built. For example, failure to create the
+ directory in which the target file will appear. It
+ can be ``None`` if the error is not due to a particular
+ filename.
+ executor: (*cause*) the executor that caused the build to fail (might
+ be ``None`` if the build failures is not due to the executor failing)
+ action: (*cause*) the action that caused the build to fail (might be
+ ``None`` if the build failures is not due to the an
+ action failure)
+ command: (*cause*) the command line for the action that caused the
+ build to fail (might be ``None`` if the build failures
+ is not due to the an action failure)
+ exc_info: (*info*) Info about exception that caused the build
+ error. Set to ``(None, None, None)`` if this build
+ error is not due to an exception.
"""
diff --git a/SCons/Executor.py b/SCons/Executor.py
index 492ebe3..274af6a 100644
--- a/SCons/Executor.py
+++ b/SCons/Executor.py
@@ -306,30 +306,30 @@ class Executor(object, metaclass=NoSlotsPyPy):
over and over), so removing the duplicates once up front should
save the Taskmaster a lot of work.
"""
- result = SCons.Util.UniqueList([])
+ result = []
for target in self.get_all_targets():
result.extend(target.children())
- return result
+ return SCons.Util.uniquer_hashables(result)
def get_all_prerequisites(self):
"""Returns all unique (order-only) prerequisites for all batches
of this Executor.
"""
- result = SCons.Util.UniqueList([])
+ result = []
for target in self.get_all_targets():
if target.prerequisites is not None:
result.extend(target.prerequisites)
- return result
+ return SCons.Util.uniquer_hashables(result)
def get_action_side_effects(self):
"""Returns all side effects for all batches of this
Executor used by the underlying Action.
"""
- result = SCons.Util.UniqueList([])
+ result = []
for target in self.get_action_targets():
result.extend(target.side_effects)
- return result
+ return SCons.Util.uniquer_hashables(result)
@SCons.Memoize.CountMethodCall
def get_build_env(self):
diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py
index b4de337..67e1ff6 100644
--- a/SCons/Node/FS.py
+++ b/SCons/Node/FS.py
@@ -378,20 +378,33 @@ else:
return x.upper()
-
class DiskChecker:
- def __init__(self, type, do, ignore):
- self.type = type
- self.do = do
- self.ignore = ignore
- self.func = do
+ """
+ Implement disk check variation.
+
+ This Class will hold functions to determine what this particular disk
+ checking implementation should do when enabled or disabled.
+ """
+ def __init__(self, disk_check_type, do_check_function, ignore_check_function):
+ self.disk_check_type = disk_check_type
+ self.do_check_function = do_check_function
+ self.ignore_check_function = ignore_check_function
+ self.func = do_check_function
+
def __call__(self, *args, **kw):
return self.func(*args, **kw)
- def set(self, list):
- if self.type in list:
- self.func = self.do
+
+ def enable(self, disk_check_type_list):
+ """
+ If the current object's disk_check_type matches any in the list passed
+ :param disk_check_type_list: List of disk checks to enable
+ :return:
+ """
+ if self.disk_check_type in disk_check_type_list:
+ self.func = self.do_check_function
else:
- self.func = self.ignore
+ self.func = self.ignore_check_function
+
def do_diskcheck_match(node, predicate, errorfmt):
result = predicate()
@@ -409,24 +422,25 @@ def do_diskcheck_match(node, predicate, errorfmt):
if result:
raise TypeError(errorfmt % node.get_abspath())
+
def ignore_diskcheck_match(node, predicate, errorfmt):
pass
-
diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match)
diskcheckers = [
diskcheck_match,
]
-def set_diskcheck(list):
+
+def set_diskcheck(enabled_checkers):
for dc in diskcheckers:
- dc.set(list)
+ dc.enable(enabled_checkers)
-def diskcheck_types():
- return [dc.type for dc in diskcheckers]
+def diskcheck_types():
+ return [dc.disk_check_type for dc in diskcheckers]
class EntryProxy(SCons.Util.Proxy):
@@ -1239,7 +1253,7 @@ class FS(LocalFS):
else:
return "<no cwd>"
- def chdir(self, dir, change_os_dir=0):
+ def chdir(self, dir, change_os_dir=False):
"""Change the current working directory for lookups.
If change_os_dir is true, we will also change the "real" cwd
to match.
@@ -2157,49 +2171,52 @@ 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, exclude=None):
- """
- Returns a list of Nodes (or strings) matching a specified
- pathname pattern.
+ def glob(self, pathname, ondisk=True, source=False, strings=False, exclude=None) -> list:
+ """Returns a list of Nodes (or strings) matching a pathname pattern.
- Pathname patterns follow UNIX shell semantics: * matches
- any-length strings of any characters, ? matches any character,
- and [] can enclose lists or ranges of characters. Matches do
- not span directory separators.
+ Pathname patterns follow POSIX shell syntax::
- The matches take into account Repositories, returning local
- Nodes if a corresponding entry exists in a Repository (either
- an in-memory Node or something on disk).
+ * matches everything
+ ? matches any single character
+ [seq] matches any character in seq (ranges allowed)
+ [!seq] matches any char not in seq
- By defafult, the glob() function matches entries that exist
- on-disk, in addition to in-memory Nodes. Setting the "ondisk"
- argument to False (or some other non-true value) causes the glob()
- function to only match in-memory Nodes. The default behavior is
- to return both the on-disk and in-memory Nodes.
+ The wildcard characters can be escaped by enclosing in brackets.
+ A leading dot is not matched by a wildcard, and needs to be
+ explicitly included in the pattern to be matched. Matches also
+ do not span directory separators.
- The "source" argument, when true, specifies that corresponding
- source Nodes must be returned if you're globbing in a build
- directory (initialized with VariantDir()). The default behavior
- is to return Nodes local to the VariantDir().
+ The matches take into account Repositories, returning a local
+ Node if a corresponding entry exists in a Repository (either
+ an in-memory Node or something on disk).
- The "strings" argument, when true, returns the matches as strings,
- not Nodes. The strings are path names relative to this directory.
+ The underlying algorithm is adapted from a rather old version
+ of :func:`glob.glob` function in the Python standard library
+ (heavily modified), and uses :func:`fnmatch.fnmatch` under the covers.
- 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.
+ This is the internal implementation of the external Glob API.
+
+ Args:
+ pattern: pathname pattern to match.
+ ondisk: if false, restricts matches to in-memory Nodes.
+ By defafult, matches entries that exist on-disk in addition
+ to in-memory Nodes.
+ source: if true, corresponding source Nodes are returned if
+ globbing in a variant directory. The default behavior
+ is to return Nodes local to the variant directory.
+ strings: if true, returns the matches as strings instead of
+ Nodes. The strings are path names relative to this directory.
+ exclude: if not ``None``, must be a pattern or a list of patterns
+ following the same POSIX shell semantics. Elements matching at
+ least one pattern from *exclude* 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:
result = self._glob1(basename, ondisk, source, strings)
else:
if has_glob_magic(dirname):
- list = self.glob(dirname, ondisk, source, False, exclude)
+ list = self.glob(dirname, ondisk, source, strings=False, exclude=exclude)
else:
list = [self.Dir(dirname, create=True)]
result = []
@@ -2226,7 +2243,8 @@ class Dir(Base):
corresponding entries and returns a Node (or string) relative
to the current directory if an entry is found anywhere.
- TODO: handle pattern with no wildcard
+ TODO: handle pattern with no wildcard. Python's glob.glob uses
+ a separate _glob0 function to do this.
"""
search_dir_list = self.get_all_rdirs()
for srcdir in self.srcdir_list():
@@ -2399,7 +2417,7 @@ class RootDir(Dir):
return
Base.must_be_same(self, klass)
- def _lookup_abs(self, p, klass, create=1):
+ def _lookup_abs(self, p, klass, create=True):
"""
Fast (?) lookup of a *normalized* absolute path.
@@ -2424,7 +2442,7 @@ class RootDir(Dir):
raise SCons.Errors.UserError(msg)
# There is no Node for this path name, and we're allowed
# to create it.
- dir_name, file_name = p.rsplit('/',1)
+ dir_name, file_name = p.rsplit('/', 1)
dir_node = self._lookup_abs(dir_name, Dir)
result = klass(file_name, dir_node, self.fs)
diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py
index a2400f2..f3aface 100644
--- a/SCons/Node/FSTests.py
+++ b/SCons/Node/FSTests.py
@@ -1822,7 +1822,7 @@ class FSTestCase(_tempdirTestCase):
test.write(['subdir', 'build'], "subdir/build\n")
subdir = fs.Dir('subdir')
- fs.chdir(subdir, change_os_dir=1)
+ fs.chdir(subdir, change_os_dir=True)
self.fs._lookup('#build/file', subdir, SCons.Node.FS.File)
def test_above_root(self):
diff --git a/SCons/Node/Python.py b/SCons/Node/Python.py
index 80d2762..57416ef 100644
--- a/SCons/Node/Python.py
+++ b/SCons/Node/Python.py
@@ -75,8 +75,13 @@ class ValueBuildInfo(SCons.Node.BuildInfoBase):
class Value(SCons.Node.Node):
- """A class for Python variables, typically passed on the command line
- or generated by a script, but not from a file or some other source.
+ """A Node class for values represented by Python expressions.
+
+ Values are typically passed on the command line or generated
+ by a script, but not from a file or some other source.
+
+ .. versionchanged:: 4.0
+ the *name* parameter was added.
"""
NodeInfo = ValueNodeInfo
@@ -165,8 +170,10 @@ class Value(SCons.Node.Node):
def ValueWithMemo(value, built_value=None, name=None):
- """
- Memoized Value() node factory.
+ """Memoized :class:`Value` node factory.
+
+ .. versionchanged:: 4.0
+ the *name* parameter was added.
"""
global _memo_lookup_map
diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py
index ec742a6..bb09868 100644
--- a/SCons/Node/__init__.py
+++ b/SCons/Node/__init__.py
@@ -685,8 +685,8 @@ class Node(object, metaclass=NoSlotsPyPy):
"""Try to retrieve the node's content from a cache
This method is called from multiple threads in a parallel build,
- so only do thread safe stuff here. Do thread unsafe stuff in
- built().
+ so only do thread safe stuff here. Do thread unsafe stuff
+ in :meth:`built`.
Returns true if the node was successfully retrieved.
"""
@@ -743,12 +743,12 @@ class Node(object, metaclass=NoSlotsPyPy):
"""Actually build the node.
This is called by the Taskmaster after it's decided that the
- Node is out-of-date and must be rebuilt, and after the prepare()
- method has gotten everything, uh, prepared.
+ Node is out-of-date and must be rebuilt, and after the
+ :meth:`prepare` method has gotten everything, uh, prepared.
This method is called from multiple threads in a parallel build,
so only do thread safe stuff here. Do thread unsafe stuff
- in built().
+ in :meth:`built`.
"""
try:
diff --git a/SCons/Platform/PlatformTests.py b/SCons/Platform/PlatformTests.py
index bdb8996..ee0ab75 100644
--- a/SCons/Platform/PlatformTests.py
+++ b/SCons/Platform/PlatformTests.py
@@ -159,10 +159,6 @@ class PlatformTestCase(unittest.TestCase):
assert p.synonyms != '', 'SCons.Platform.win32.get_architecture() not setting synonyms'
-
-
-
-
class TempFileMungeTestCase(unittest.TestCase):
def test_MAXLINELENGTH(self):
""" Test different values for MAXLINELENGTH with the same
diff --git a/SCons/Platform/__init__.py b/SCons/Platform/__init__.py
index 93c5cd3..3fa5a75 100644
--- a/SCons/Platform/__init__.py
+++ b/SCons/Platform/__init__.py
@@ -83,7 +83,7 @@ def platform_default():
return sys.platform
-def platform_module(name = platform_default()):
+def platform_module(name=platform_default()):
"""Return the imported module for the platform.
This looks for a module name that matches the specified argument.
@@ -91,27 +91,41 @@ def platform_module(name = platform_default()):
our execution environment.
"""
full_name = 'SCons.Platform.' + name
- if full_name not in sys.modules:
- if os.name == 'java':
- eval(full_name)
- else:
+ try:
+ return sys.modules[full_name]
+ except KeyError:
+ try:
+ # the specific platform module is a relative import
+ mod = importlib.import_module("." + name, __name__)
+ except ModuleNotFoundError:
try:
- # the specific platform module is a relative import
- mod = importlib.import_module("." + name, __name__)
- except ImportError:
- try:
- import zipimport
- importer = zipimport.zipimporter( sys.modules['SCons.Platform'].__path__[0] )
+ # This support was added to enable running inside
+ # a py2exe bundle a long time ago - unclear if it's
+ # still needed. It is *not* intended to load individual
+ # platform modules stored in a zipfile.
+ import zipimport
+
+ platform = sys.modules['SCons.Platform'].__path__[0]
+ importer = zipimport.zipimporter(platform)
+ if not hasattr(importer, 'find_spec'):
+ # zipimport only added find_spec, exec_module in 3.10,
+ # unlike importlib, where they've been around since 3.4.
+ # If we don't have 'em, use the old way.
mod = importer.load_module(full_name)
- except ImportError:
- raise SCons.Errors.UserError("No platform named '%s'" % name)
- setattr(SCons.Platform, name, mod)
- return sys.modules[full_name]
+ else:
+ spec = importer.find_spec(full_name)
+ mod = importlib.util.module_from_spec(spec)
+ importer.exec_module(mod)
+ sys.modules[full_name] = mod
+ except zipimport.ZipImportError:
+ raise SCons.Errors.UserError("No platform named '%s'" % name)
+
+ setattr(SCons.Platform, name, mod)
+ return mod
def DefaultToolList(platform, env):
- """Select a default tool list for the specified platform.
- """
+ """Select a default tool list for the specified platform."""
return SCons.Tool.tool_list(platform, env)
@@ -328,8 +342,8 @@ class TempFileMunge:
def Platform(name = platform_default()):
- """Select a canned Platform specification.
- """
+ """Select a canned Platform specification."""
+
module = platform_module(name)
spec = PlatformSpec(name, module.generate)
return spec
diff --git a/SCons/Platform/darwin.py b/SCons/Platform/darwin.py
index f997a7d..dcaf5c8 100644
--- a/SCons/Platform/darwin.py
+++ b/SCons/Platform/darwin.py
@@ -31,6 +31,7 @@ selection method.
from . import posix
import os
+
def generate(env):
posix.generate(env)
env['SHLIBSUFFIX'] = '.dylib'
@@ -40,7 +41,7 @@ def generate(env):
# env['ENV']['PATH'] = '/opt/local/bin:/opt/local/sbin:' + env['ENV']['PATH'] + ':/sw/bin'
# Store extra system paths in env['ENV']['PATHOSX']
-
+
filelist = ['/etc/paths',]
# make sure this works on Macs with Tiger or earlier
try:
diff --git a/SCons/SConf.py b/SCons/SConf.py
index 0fd813b..633c411 100644
--- a/SCons/SConf.py
+++ b/SCons/SConf.py
@@ -43,7 +43,7 @@ import traceback
import SCons.Action
import SCons.Builder
import SCons.Errors
-import SCons.Job
+import SCons.Taskmaster.Job
import SCons.Node.FS
import SCons.Taskmaster
import SCons.Util
@@ -536,7 +536,7 @@ class SConfBase:
# the engine assumes the current path is the SConstruct directory ...
old_fs_dir = SConfFS.getcwd()
old_os_dir = os.getcwd()
- SConfFS.chdir(SConfFS.Top, change_os_dir=1)
+ SConfFS.chdir(SConfFS.Top, change_os_dir=True)
# Because we take responsibility here for writing out our
# own .sconsign info (see SConfBuildTask.execute(), above),
@@ -572,7 +572,7 @@ class SConfBase:
SConfFS.set_max_drift(0)
tm = SCons.Taskmaster.Taskmaster(nodes, SConfBuildTask)
# we don't want to build tests in parallel
- jobs = SCons.Job.Jobs(1, tm )
+ jobs = SCons.Taskmaster.Job.Jobs(1, tm)
jobs.run()
for n in nodes:
state = n.get_state()
@@ -583,7 +583,7 @@ class SConfBase:
finally:
SConfFS.set_max_drift(save_max_drift)
os.chdir(old_os_dir)
- SConfFS.chdir(old_fs_dir, change_os_dir=0)
+ SConfFS.chdir(old_fs_dir, change_os_dir=False)
if self.logstream is not None:
# restore stdout / stderr
sys.stdout = oldStdout
@@ -793,7 +793,7 @@ class SConfBase:
tb = traceback.extract_stack()[-3-self.depth]
old_fs_dir = SConfFS.getcwd()
- SConfFS.chdir(SConfFS.Top, change_os_dir=0)
+ SConfFS.chdir(SConfFS.Top, change_os_dir=False)
self.logstream.write('file %s,line %d:\n\tConfigure(confdir = %s)\n' %
(tb[0], tb[1], str(self.confdir)) )
SConfFS.chdir(old_fs_dir)
@@ -1088,7 +1088,7 @@ def CheckCXXHeader(context, header, include_quotes = '""'):
def CheckLib(context, library = None, symbol = "main",
- header = None, language = None, autoadd = 1):
+ header = None, language = None, autoadd=True, append=True,) -> bool:
"""
A test for a library. See also CheckLibWithHeader.
Note that library may also be None to test whether the given symbol
@@ -1103,15 +1103,16 @@ def CheckLib(context, library = None, symbol = "main",
# ToDo: accept path for the library
res = SCons.Conftest.CheckLib(context, library, symbol, header = header,
- language = language, autoadd = autoadd)
- context.did_show_result = 1
+ language = language, autoadd = autoadd,
+ append=append)
+ context.did_show_result = True
return not res
# XXX
# Bram: Can only include one header and can't use #ifdef HAVE_HEADER_H.
def CheckLibWithHeader(context, libs, header, language,
- call = None, autoadd = 1):
+ call = None, autoadd=True, append=True) -> bool:
# ToDo: accept path for library. Support system header files.
"""
Another (more sophisticated) test for a library.
@@ -1120,8 +1121,7 @@ def CheckLibWithHeader(context, libs, header, language,
As in CheckLib, we support library=None, to test if the call compiles
without extra link flags.
"""
- prog_prefix, dummy = \
- createIncludesFromHeaders(header, 0)
+ prog_prefix, dummy = createIncludesFromHeaders(header, 0)
if not libs:
libs = [None]
@@ -1129,7 +1129,7 @@ def CheckLibWithHeader(context, libs, header, language,
libs = [libs]
res = SCons.Conftest.CheckLib(context, libs, None, prog_prefix,
- call = call, language = language, autoadd = autoadd)
+ call = call, language = language, autoadd=autoadd, append=append)
context.did_show_result = 1
return not res
diff --git a/SCons/SConfTests.py b/SCons/SConfTests.py
index 477226e..033a4ff 100644
--- a/SCons/SConfTests.py
+++ b/SCons/SConfTests.py
@@ -574,14 +574,27 @@ int main(void) {
env = sconf.env.Clone()
try:
- r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=1 )
+ r = sconf.CheckLibWithHeader(
+ existing_lib, "math.h", "C", autoadd=True, append=True
+ )
assert r, "did not find math.h with %s" % existing_lib
expect = libs(env) + [existing_lib]
got = libs(sconf.env)
assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
sconf.env = env.Clone()
- r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
+ r = sconf.CheckLibWithHeader(
+ existing_lib, "math.h", "C", autoadd=True, append=False
+ )
+ assert r, "did not find math.h with %s" % existing_lib
+ expect = [existing_lib] + libs(env)
+ got = libs(sconf.env)
+ assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
+
+ sconf.env = env.Clone()
+ r = sconf.CheckLibWithHeader(
+ existing_lib, "math.h", "C", autoadd=False
+ )
assert r, "did not find math.h with %s" % existing_lib
expect = libs(env)
got = libs(sconf.env)
diff --git a/SCons/Scanner/Java.py b/SCons/Scanner/Java.py
index ab1f4e6..e6c2db9 100644
--- a/SCons/Scanner/Java.py
+++ b/SCons/Scanner/Java.py
@@ -21,56 +21,65 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-import os
+import os
import SCons.Node
import SCons.Node.FS
import SCons.Scanner
-import SCons.Util
+from SCons.Util import flatten, is_String
-def _subst_libs(env, libs):
- """
- Substitute environment variables and split into list.
+def _subst_paths(env, paths) -> list:
+ """Return a list of substituted path elements.
+
+ If *paths* is a string, it is split on the search-path separator.
+ Otherwise, substitution is done on string-valued list elements but
+ they are not split.
+
+ Note helps support behavior like pulling in the external ``CLASSPATH``
+ and setting it directly into ``JAVACLASSPATH``, however splitting on
+ ``os.pathsep`` makes the interpretation system-specific (this is
+ warned about in the manpage entry for ``JAVACLASSPATH``).
"""
- 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 lib in libs:
- _libs += _subst_libs(env, lib)
- libs = _libs
+ if is_String(paths):
+ paths = env.subst(paths)
+ if SCons.Util.is_String(paths):
+ paths = paths.split(os.pathsep)
else:
- # libs is an object (Node, for example)
- libs = [libs]
- return libs
+ # TODO: may want to revisit splitting list-element strings if requested
+ paths = flatten(paths)
+ paths = [env.subst(path) if is_String(path) else path for path in paths]
+ return paths
-def _collect_classes(list, dirname, files):
+def _collect_classes(classlist, dirname, files):
for fname in files:
- if os.path.splitext(fname)[1] == ".class":
- list.append(os.path.join(str(dirname), fname))
+ if fname.endswith(".class"):
+ classlist.append(os.path.join(str(dirname), fname))
-def scan(node, env, libpath=()):
- """Scan for files on the JAVACLASSPATH.
+def scan(node, env, libpath=()) -> list:
+ """Scan for files both on JAVACLASSPATH and JAVAPROCESSORPATH.
- The classpath can contain:
+ JAVACLASSPATH/JAVAPROCESSORPATH path can contain:
- Explicit paths to JAR/Zip files
- Wildcards (*)
- Directories which contain classes in an unnamed package
- Parent directories of the root package for classes in a named package
- Class path entries that are neither directories nor archives (.zip or JAR files) nor the asterisk (*) wildcard character are ignored.
- """
- classpath = env.get('JAVACLASSPATH', [])
- classpath = _subst_libs(env, classpath)
+ Class path entries that are neither directories nor archives (.zip
+ or JAR files) nor the asterisk (*) wildcard character are ignored.
+ """
+ classpath = []
+ for var in ['JAVACLASSPATH', 'JAVAPROCESSORPATH']:
+ classpath += _subst_paths(env, env.get(var, []))
result = []
for path in classpath:
- if SCons.Util.is_String(path) and "*" in path:
+ if is_String(path) and "*" in path:
+ # This matches more than the Java docs describe: a '*' only
+ # matches jar files. The filter later should trim this down.
+ # TODO: should we filter here? use .endswith('*') rather than "in"?
libs = env.Glob(path)
else:
libs = [path]
@@ -89,8 +98,11 @@ def scan(node, env, libpath=()):
def JavaScanner():
- return SCons.Scanner.Base(scan, 'JavaScanner',
- skeys=['.java'])
+ """Scanner for .java files.
+
+ .. versionadded:: 4.4
+ """
+ return SCons.Scanner.Base(scan, 'JavaScanner', skeys=['.java'])
# Local Variables:
# tab-width:4
diff --git a/SCons/Scanner/JavaTests.py b/SCons/Scanner/JavaTests.py
index 9fb39ce..faa0c49 100644
--- a/SCons/Scanner/JavaTests.py
+++ b/SCons/Scanner/JavaTests.py
@@ -33,18 +33,25 @@ import SCons.Warnings
test = TestCmd.TestCmd(workdir = '')
-test.subdir('com')
files = [
'bootclasspath.jar',
'classpath.jar',
'Test.class',
- 'com/Test.class'
]
for fname in files:
test.write(fname, "\n")
+test.subdir('com')
+test.subdir('java space')
+subfiles = [
+ 'com/Test.class',
+ 'java space/Test.class'
+]
+
+for fname in subfiles:
+ test.write(fname.split('/'), "\n")
class DummyEnvironment(collections.UserDict):
def __init__(self,**kw):
@@ -52,7 +59,7 @@ class DummyEnvironment(collections.UserDict):
self.data.update(kw)
self.fs = SCons.Node.FS.FS(test.workpath(''))
self['ENV'] = {}
-
+
def Dictionary(self, *args):
return self.data
@@ -80,7 +87,7 @@ class DummyEnvironment(collections.UserDict):
def File(self, filename):
return self.fs.File(filename)
-
+
def Glob(self, path):
return self.fs.Glob(path)
@@ -111,8 +118,7 @@ def deps_match(self, deps, headers):
class JavaScannerEmptyClasspath(unittest.TestCase):
def runTest(self):
path = []
- env = DummyEnvironment(JAVASUFFIXES=['.java'],
- JAVACLASSPATH=path)
+ env = DummyEnvironment(JAVASUFFIXES=['.java'], JAVACLASSPATH=path)
s = SCons.Scanner.Java.JavaScanner()
deps = s(DummyNode('dummy'), env)
expected = []
@@ -145,10 +151,97 @@ class JavaScannerDirClasspath(unittest.TestCase):
JAVACLASSPATH=[test.workpath()])
s = SCons.Scanner.Java.JavaScanner()
deps = s(DummyNode('dummy'), env)
- expected = ['Test.class', 'com/Test.class']
+ expected = ['Test.class', 'com/Test.class', 'java space/Test.class']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerNamedDirClasspath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(
+ JAVASUFFIXES=['.java'],
+ JAVACLASSPATH=[test.workpath('com'), test.workpath('java space')],
+ )
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['com/Test.class', 'java space/Test.class']
deps_match(self, deps, expected)
+class JavaScannerSearchPathClasspath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(
+ JAVASUFFIXES=['.java'],
+ JAVACLASSPATH=os.pathsep.join([test.workpath('com'), test.workpath('java space')]),
+ )
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['com/Test.class', 'java space/Test.class']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerEmptyProcessorpath(unittest.TestCase):
+ def runTest(self):
+ path = []
+ env = DummyEnvironment(JAVASUFFIXES=['.java'], JAVAPROCESSORPATH=path)
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = []
+ deps_match(self, deps, expected)
+
+
+class JavaScannerProcessorpath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVAPROCESSORPATH=[test.workpath('classpath.jar')])
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['classpath.jar']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerWildcardProcessorpath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVAPROCESSORPATH=[test.workpath('*')])
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['bootclasspath.jar', 'classpath.jar', 'Test.class']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerDirProcessorpath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(JAVASUFFIXES=['.java'],
+ JAVAPROCESSORPATH=[test.workpath()])
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['Test.class', 'com/Test.class', 'java space/Test.class']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerNamedDirProcessorpath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(
+ JAVASUFFIXES=['.java'],
+ JAVAPROCESSORPATH=[test.workpath('com'), test.workpath('java space')],
+ )
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['com/Test.class', 'java space/Test.class']
+ deps_match(self, deps, expected)
+
+
+class JavaScannerSearchPathProcessorpath(unittest.TestCase):
+ def runTest(self):
+ env = DummyEnvironment(
+ JAVASUFFIXES=['.java'],
+ JAVAPROCESSORPATH=os.pathsep.join([test.workpath('com'), test.workpath('java space')]),
+ )
+ s = SCons.Scanner.Java.JavaScanner()
+ deps = s(DummyNode('dummy'), env)
+ expected = ['com/Test.class', 'java space/Test.class']
+ deps_match(self, deps, expected)
+
if __name__ == "__main__":
unittest.main()
diff --git a/SCons/Scanner/ScannerTests.py b/SCons/Scanner/ScannerTests.py
index 68332a0..b9cb209 100644
--- a/SCons/Scanner/ScannerTests.py
+++ b/SCons/Scanner/ScannerTests.py
@@ -621,7 +621,9 @@ def suite():
ClassicCPPTestCase,
]
for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ names = loader.getTestCaseNames(tclass)
suite.addTests(list(map(tclass, names)))
return suite
diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py
index ab2dc0e..b902842 100644
--- a/SCons/Script/Main.py
+++ b/SCons/Script/Main.py
@@ -52,7 +52,7 @@ import SCons.Debug
import SCons.Defaults
import SCons.Environment
import SCons.Errors
-import SCons.Job
+import SCons.Taskmaster.Job
import SCons.Node
import SCons.Node.FS
import SCons.Platform
@@ -492,6 +492,26 @@ def GetOption(name):
def SetOption(name, value):
return OptionsParser.values.set_option(name, value)
+
+def ValidateOptions(throw_exception=False) -> None:
+ """Validate options passed to SCons on the command line.
+
+ If you call this after you set all your command line options with AddOption(),
+ it will verify that all command line options are valid.
+ So if you added an option --xyz and you call SCons with --xyy you can cause
+ SCons to issue an error message and exit by calling this function.
+
+ :param bool throw_exception: (Optional) Should this function raise an error if there's an invalid option on the command line, or issue a message and exit with error status.
+
+ :raises SConsBadOptionError: If throw_exception is True and there are invalid options on command line.
+
+ .. versionadded:: 4.4.1
+ """
+
+ OptionsParser.raise_exception_on_error = throw_exception
+ OptionsParser.preserve_unknown_options = False
+ OptionsParser.parse_args(OptionsParser.largs, OptionsParser.values)
+
def PrintHelp(file=None):
OptionsParser.print_help(file=file)
@@ -1114,7 +1134,7 @@ def _main(parser):
SCons.Node.FS.set_duplicate(options.duplicate)
fs.set_max_drift(options.max_drift)
- SCons.Job.explicit_stack_size = options.stack_size
+ SCons.Taskmaster.Job.explicit_stack_size = options.stack_size
# Hash format and chunksize are set late to support SetOption being called
# in a SConscript or SConstruct file.
@@ -1271,23 +1291,12 @@ def _build_targets(fs, options, targets, target_top):
"""Leave the order of dependencies alone."""
return dependencies
- def tmtrace_cleanup(tfile):
- tfile.close()
-
- if options.taskmastertrace_file == '-':
- tmtrace = sys.stdout
- elif options.taskmastertrace_file:
- tmtrace = open(options.taskmastertrace_file, 'w')
- atexit.register(tmtrace_cleanup, tmtrace)
- else:
- tmtrace = None
- taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace)
+ taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, options.taskmastertrace_file)
# Let the BuildTask objects get at the options to respond to the
# various print_* settings, tree_printer list, etc.
BuildTask.options = options
-
is_pypy = platform.python_implementation() == 'PyPy'
# As of 3.7, python removed support for threadless platforms.
# See https://www.python.org/dev/peps/pep-0011/
@@ -1301,7 +1310,7 @@ def _build_targets(fs, options, targets, target_top):
# to check if python configured with threads.
global num_jobs
num_jobs = options.num_jobs
- jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(num_jobs, taskmaster)
if num_jobs > 1:
msg = None
if jobs.num_jobs == 1 or not python_has_threads:
@@ -1364,6 +1373,7 @@ def _exec_main(parser, values):
else:
_main(parser)
+
def main():
global OptionsParser
global exit_status
diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml
index f4f4705..fbb90fd 100644
--- a/SCons/Script/Main.xml
+++ b/SCons/Script/Main.xml
@@ -316,18 +316,22 @@ atexit.register(print_build_failures)
</arguments>
<summary>
<para>
-This function provides a way to query the value of
-options which can be set via the command line or using the
-&f-link-SetOption; function.
+Query the value of settable options which may have been set
+on the command line, or by using the &f-link-SetOption; function.
+The value of the option is returned in a type matching how the
+option was declared - see the documentation for the
+corresponding command line option for information about each specific
+option.
</para>
+
<para>
<parameter>name</parameter> can be an entry from the following table,
which shows the corresponding command line arguments
that could affect the value.
<parameter>name</parameter> can be also be the destination
variable name from a project-specific option added using the
-&f-link-AddOption; function, as long as the addition
-happens prior to the &f-GetOption; call in the SConscript files.
+&f-link-AddOption; function, as long as that addition has been
+processed prior to the &f-GetOption; call in the &SConscript; files.
</para>
<informaltable rowsep="1" colsep="1" frame="topbot">
@@ -552,12 +556,6 @@ happens prior to the &f-GetOption; call in the SConscript files.
</tbody>
</tgroup>
</informaltable>
-
-<para>
-See the documentation for the
-corresponding command line option for information about each specific
-option.
-</para>
</summary>
</scons_function>
@@ -740,6 +738,7 @@ Multiple targets can be passed in to a single call to
</para>
</summary>
</scons_function>
+
<scons_function name="SetOption">
<arguments>
(name, value)
@@ -943,6 +942,68 @@ Example:
SetOption('max_drift', 0)
</example_commands>
</summary>
+
+
</scons_function>
+ <scons_function name="ValidateOptions">
+ <arguments signature="global">
+ ([throw_exception=False])
+ </arguments>
+
+ <summary>
+ <para>
+ Check that all the options specified on the command line are either defined by SCons itself
+ or defined by calls to &f-link-AddOption;.
+ </para>
+ <para>
+ This function should only be called after the last &f-link-AddOption; call in your &SConscript;
+ logic.
+ </para>
+ <para>
+ Be aware that some tools call &f-link-AddOption;, if you are getting error messages for arguments
+ that they add, you will need to ensure that you load those tools before you call &f-ValidateOptions;.
+ </para>
+ <para>
+ If there are any command line options not defined, calling this function will cause SCons to issue an
+ error message and then exit with an error exit
+ status.</para>
+ <para>If the optional <parameter>throw_exception</parameter> is <literal>True</literal>, &f-ValidateOptions; will raise a
+ <exceptionname>SConsBadOptionError</exceptionname>
+ exception. This would allow the calling
+ &SConscript; logic can catch that exception and handle invalid options itself.
+ </para>
+
+ <para>
+ Example:
+ </para>
+
+ <example_commands>
+try:
+ ValidateOptions(throw_exception=True)
+except SConsBadOptionError as e:
+ print("Parser is SConsOptionParser:%s" % (isinstance(e.parser, SConsOptionParser)))
+ print("Message is :%s" % e.opt_str)
+ Exit(3)
+ </example_commands>
+
+ <para>
+ This function is useful to force SCons to fail fast before you execute any expensive logic later in your
+ build logic.
+ For example if you specify build options via any flags, a simple typo could yield the incorrect build
+ option throughout your entire build.
+ </para>
+ <example_commands>
+scons --compilers=mingw (the correct flag is --compiler)
+ </example_commands>
+ <para>
+ Could cause SCons to run configure steps with the incorrect compiler. Costing developer time trying to
+ track down why the configure logic failed with a compiler which should work.
+ </para>
+
+
+
+ </summary>
+ </scons_function>
+
</sconsdoc>
diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py
index e2631fb..8391d62 100644
--- a/SCons/Script/SConsOptions.py
+++ b/SCons/Script/SConsOptions.py
@@ -40,7 +40,7 @@ SUPPRESS_HELP = optparse.SUPPRESS_HELP
diskcheck_all = SCons.Node.FS.diskcheck_types()
-experimental_features = {'warp_speed', 'transporter', 'ninja'}
+experimental_features = {'warp_speed', 'transporter', 'ninja', 'tm_v2'}
def diskcheck_convert(value):
@@ -54,7 +54,10 @@ def diskcheck_convert(value):
if v == 'all':
result = diskcheck_all
elif v == 'none':
- result = []
+ # Don't use an empty list here as that fails the normal check
+ # to see if an optparse parser of if parser.argname:
+ # Changed to ['none'] as diskcheck expects a list value
+ result = ['none']
elif v in diskcheck_all:
result.append(v)
else:
@@ -65,7 +68,7 @@ def diskcheck_convert(value):
class SConsValues(optparse.Values):
"""
Holder class for uniform access to SCons options, regardless
- of whether or not they can be set on the command line or in the
+ of whether they can be set on the command line or in the
SConscript files (using the SetOption() function).
A SCons option value can originate three different ways:
@@ -147,11 +150,12 @@ class SConsValues(optparse.Values):
'warn',
# TODO: Remove these once we update the AddOption() API to allow setting
- # added flag as setable.
- # Requested setable flag in : https://github.com/SCons/scons/issues/3983
+ # added flag as settable.
+ # Requested settable flag in : https://github.com/SCons/scons/issues/3983
# From experimental ninja
'disable_execute_ninja',
- 'disable_ninja'
+ 'disable_ninja',
+ 'skip_ninja_regen'
]
def set_option(self, name, value):
@@ -288,14 +292,36 @@ class SConsOptionGroup(optparse.OptionGroup):
return result
+class SConsBadOptionError(optparse.BadOptionError):
+ """Exception used to indicate that invalid command line options were specified
+
+ :ivar str opt_str: The offending option specified on command line which is not recognized
+ :ivar OptionParser parser: The active argument parser
+
+ """
+
+ def __init__(self, opt_str, parser=None):
+ self.opt_str = opt_str
+ self.parser = parser
+
+ def __str__(self):
+ return _("no such option: %s") % self.opt_str
+
+
class SConsOptionParser(optparse.OptionParser):
preserve_unknown_options = False
+ raise_exception_on_error = False
def error(self, msg):
- # overridden OptionValueError exception handler
- self.print_usage(sys.stderr)
- sys.stderr.write("SCons Error: %s\n" % msg)
- sys.exit(2)
+ """
+ overridden OptionValueError exception handler
+ """
+ if self.raise_exception_on_error:
+ raise SConsBadOptionError(msg, self)
+ else:
+ self.print_usage(sys.stderr)
+ sys.stderr.write("SCons Error: %s\n" % msg)
+ sys.exit(2)
def _process_long_opt(self, rargs, values):
""" SCons-specific processing of long options.
diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py
index 6188151..b72f30e 100644
--- a/SCons/Script/SConscript.py
+++ b/SCons/Script/SConscript.py
@@ -57,7 +57,7 @@ GlobalDict = None
global_exports = {}
# chdir flag
-sconscript_chdir = 1
+sconscript_chdir: bool = True
def get_calling_namespaces():
"""Return the locals and globals for the function that called
@@ -205,7 +205,7 @@ def _SConscript(fs, *files, **kw):
# Change directory to the top of the source
# tree to make sure the os's cwd and the cwd of
# fs match so we can open the SConscript.
- fs.chdir(top, change_os_dir=1)
+ fs.chdir(top, change_os_dir=True)
if f.rexists():
actual = f.rfile()
_file_ = open(actual.get_abspath(), "rb")
@@ -254,7 +254,7 @@ def _SConscript(fs, *files, **kw):
# fs.chdir(), because we still need to
# interpret the stuff within the SConscript file
# relative to where we are logically.
- fs.chdir(ldir, change_os_dir=0)
+ fs.chdir(ldir, change_os_dir=False)
os.chdir(actual.dir.get_abspath())
# Append the SConscript directory to the beginning
@@ -292,7 +292,7 @@ def _SConscript(fs, *files, **kw):
if old_file is not None:
call_stack[-1].globals.update({__file__:old_file})
-
+
else:
handle_missing_SConscript(f, kw.get('must_exist', None))
@@ -306,7 +306,7 @@ def _SConscript(fs, *files, **kw):
# There was no local directory, so chdir to the
# Repository directory. Like above, we do this
# directly.
- fs.chdir(frame.prev_dir, change_os_dir=0)
+ fs.chdir(frame.prev_dir, change_os_dir=False)
rdir = frame.prev_dir.rdir()
rdir._create() # Make sure there's a directory there.
try:
@@ -385,12 +385,8 @@ class SConsEnvironment(SCons.Environment.Base):
#
# Private methods of an SConsEnvironment.
#
- def _exceeds_version(self, major, minor, v_major, v_minor):
- """Return 1 if 'major' and 'minor' are greater than the version
- in 'v_major' and 'v_minor', and 0 otherwise."""
- return (major > v_major or (major == v_major and minor > v_minor))
-
- def _get_major_minor_revision(self, version_string):
+ @staticmethod
+ def _get_major_minor_revision(version_string):
"""Split a version string into major, minor and (optionally)
revision parts.
@@ -488,14 +484,15 @@ class SConsEnvironment(SCons.Environment.Base):
def Default(self, *targets):
SCons.Script._Set_Default_Targets(self, targets)
- def EnsureSConsVersion(self, major, minor, revision=0):
+ @staticmethod
+ def EnsureSConsVersion(major, minor, revision=0):
"""Exit abnormally if the SCons version is not late enough."""
# 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
- scons_ver = self._get_major_minor_revision(SCons.__version__)
+ scons_ver = SConsEnvironment._get_major_minor_revision(SCons.__version__)
if scons_ver < (major, minor, revision):
if revision:
scons_ver_string = '%d.%d.%d' % (major, minor, revision)
@@ -505,14 +502,16 @@ class SConsEnvironment(SCons.Environment.Base):
(scons_ver_string, SCons.__version__))
sys.exit(2)
- def EnsurePythonVersion(self, major, minor):
+ @staticmethod
+ def EnsurePythonVersion(major, minor):
"""Exit abnormally if the Python version is not late enough."""
if sys.version_info < (major, minor):
v = sys.version.split()[0]
print("Python %d.%d or greater required, but you have Python %s" %(major,minor,v))
sys.exit(2)
- def Exit(self, value=0):
+ @staticmethod
+ def Exit(value=0):
sys.exit(value)
def Export(self, *vars, **kw):
@@ -520,7 +519,8 @@ class SConsEnvironment(SCons.Environment.Base):
global_exports.update(compute_exports(self.Split(var)))
global_exports.update(kw)
- def GetLaunchDir(self):
+ @staticmethod
+ def GetLaunchDir():
global launch_dir
return launch_dir
@@ -528,6 +528,7 @@ class SConsEnvironment(SCons.Environment.Base):
name = self.subst(name)
return SCons.Script.Main.GetOption(name)
+
def Help(self, text, append=False):
text = self.subst(text, raw=1)
SCons.Script.HelpFunction(text, append=append)
@@ -596,7 +597,8 @@ class SConsEnvironment(SCons.Environment.Base):
subst_kw['exports'] = exports
return _SConscript(self.fs, *files, **subst_kw)
- def SConscriptChdir(self, flag):
+ @staticmethod
+ def SConscriptChdir(flag: bool) -> None:
global sconscript_chdir
sconscript_chdir = flag
diff --git a/SCons/Script/SConscript.xml b/SCons/Script/SConscript.xml
index 3c5b907..7a4bc29 100644
--- a/SCons/Script/SConscript.xml
+++ b/SCons/Script/SConscript.xml
@@ -84,7 +84,7 @@ env.Default(hello)
</scons_function>
<scons_function name="EnsurePythonVersion">
-<arguments>
+<arguments signature="global">
(major, minor)
</arguments>
<summary>
@@ -107,7 +107,7 @@ EnsurePythonVersion(2,2)
</scons_function>
<scons_function name="EnsureSConsVersion">
-<arguments>
+<arguments signature="global">
(major, minor, [revision])
</arguments>
<summary>
@@ -137,7 +137,7 @@ EnsureSConsVersion(0,96,90)
</scons_function>
<scons_function name="Exit">
-<arguments>
+<arguments signature="global">
([value])
</arguments>
<summary>
@@ -215,7 +215,7 @@ See the description below.
</scons_function>
<scons_function name="GetLaunchDir">
-<arguments>
+<arguments signature="global">
()
</arguments>
<summary>
@@ -363,42 +363,38 @@ Return('val1 val2')
<!-- (scripts, [exports, variant_dir, src_dir, duplicate, must_exist]) -->
</arguments>
<arguments>
-(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist])
-<!-- (dirs=subdirs, [name=script, exports, variant_dir, src_dir, duplicate, must_exist]) -->
+(dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist])
+<!-- (dirs=subdirs, [name=scriptname, exports, variant_dir, src_dir, duplicate, must_exist]) -->
</arguments>
<summary>
<para>
-Execute one or more subsidiary SConscript (configuration) files.
+Executes one or more subsidiary SConscript (configuration) files.
There are two ways to call the
&f-SConscript; function.
</para>
<para>
-The first calling style
-is to explicitly specify one or more
-<varname>scripts</varname>
-as the first argument.
+The first calling style is to supply
+one or more SConscript file names
+as the first (positional) argument.
A single script may be specified as a string;
-multiple scripts must be specified as a list
+multiple scripts must be specified as a list of strings
(either explicitly or as created by
a function like
&f-link-Split;).
Examples:
</para>
<example_commands>
-SConscript('SConscript') # run SConscript in the current directory
+SConscript('SConscript') # run SConscript in the current directory
SConscript('src/SConscript') # run SConscript in the src directory
SConscript(['src/SConscript', 'doc/SConscript'])
config = SConscript('MyConfig.py')
</example_commands>
<para>
-The second way to call
-&f-SConscript;
-is to specify a list of (sub)directory names
-as a
-<varname>dirs</varname>=<replaceable>subdirs</replaceable>
-keyword argument.
+The other calling style is to omit the positional argument naming
+scripts and instead specify a list of directory names using the
+<varname>dirs</varname> keyword argument.
In this case,
&scons;
will
@@ -408,14 +404,14 @@ in each of the specified directories.
You may specify a name other than
&SConscript;
by supplying an optional
-<varname>name</varname>=<replaceable>script</replaceable>
+<varname>name</varname>=<replaceable>scriptname</replaceable>
keyword argument.
The first three examples below have the same effect
as the first three examples above:
</para>
<example_commands>
-SConscript(dirs='.') # run SConscript in the current directory
-SConscript(dirs='src') # run SConscript in the src directory
+SConscript(dirs='.') # run SConscript in the current directory
+SConscript(dirs='src') # run SConscript in the src directory
SConscript(dirs=['src', 'doc'])
SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
</example_commands>
@@ -423,8 +419,12 @@ SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
<para>
The optional
<varname>exports</varname>
-argument provides a string or list of strings representing
+keyword argument provides a string or list of strings representing
variable names, or a dictionary of named values, to export.
+For the first calling style only, a second positional argument
+will be interpreted as <varname>exports</varname>; the
+second calling style must use the keyword argument form
+for <varname>exports</varname>.
These variables are locally exported only to the called
SConscript file(s)
and do not affect the global pool of variables managed by the
@@ -448,38 +448,24 @@ SConscript(dirs=['one', 'two', 'three'], exports='shared_info')
If the optional
<varname>variant_dir</varname>
argument is present, it causes an effect equivalent to the
-&f-link-VariantDir; function.
+&f-link-VariantDir; function,
+but in effect only within the scope of the &f-SConscript; call.
The <varname>variant_dir</varname>
-argument is interpreted relative to the directory of the calling
-SConscript file.
-The optional
-<varname>duplicate</varname> argument is
-interpreted as for &f-link-VariantDir;.
-If <varname>variant_dir</varname>
-is omitted, the <varname>duplicate</varname> argument is ignored.
-See the description of
-&f-link-VariantDir;
-below for additional details and restrictions.
-</para>
-
-<para>
-If
-<varname>variant_dir</varname>
-is present,
-the source directory is the directory in which the
-SConscript
-file resides and the
-SConscript
+argument is interpreted relative to the directory of the
+<emphasis>calling</emphasis> SConscript file.
+The source directory is the directory in which the
+<emphasis>called</emphasis> SConscript
+file resides and the SConscript
file is evaluated as if it were in the
<varname>variant_dir</varname>
-directory:
+directory. Thus:
</para>
<example_commands>
SConscript('src/SConscript', variant_dir='build')
</example_commands>
<para>
-is equivalent to
+is equivalent to:
</para>
<example_commands>
@@ -488,9 +474,8 @@ SConscript('build/SConscript')
</example_commands>
<para>
-This later paradigm is often used when the sources are
-in the same directory as the
-&SConstruct;:
+If the sources are in the same directory as the
+&SConstruct;,
</para>
<example_commands>
@@ -498,7 +483,7 @@ SConscript('SConscript', variant_dir='build')
</example_commands>
<para>
-is equivalent to
+is equivalent to:
</para>
<example_commands>
@@ -507,6 +492,17 @@ SConscript('build/SConscript')
</example_commands>
<para>
+The optional
+<varname>duplicate</varname> argument is
+interpreted as for &f-link-VariantDir;.
+If the <varname>variant_dir</varname> argument
+is omitted, the <varname>duplicate</varname> argument is ignored.
+See the description of
+&f-link-VariantDir;
+for additional details and restrictions.
+</para>
+
+<para>
<!--
If
<varname>variant_dir</varname>
@@ -589,11 +585,11 @@ SConscript('src/SConscript', variant_dir='build/ppc', duplicate=0)
<para>
&f-SConscript; returns the values of any variables
-named by the executed SConscript(s) in arguments
-to the &f-link-Return; function (see above for details).
+named by the executed SConscript file(s) in arguments
+to the &f-link-Return; function.
If a single &f-SConscript; call causes multiple scripts to
be executed, the return value is a tuple containing
-the returns of all of the scripts. If an executed
+the returns of each of the scripts. If an executed
script does not explicitly call &Return;, it returns
<constant>None</constant>.
</para>
diff --git a/SCons/Script/__init__.py b/SCons/Script/__init__.py
index 40be95e..6cfea1b 100644
--- a/SCons/Script/__init__.py
+++ b/SCons/Script/__init__.py
@@ -107,6 +107,7 @@ AddOption = Main.AddOption
PrintHelp = Main.PrintHelp
GetOption = Main.GetOption
SetOption = Main.SetOption
+ValidateOptions = Main.ValidateOptions
Progress = Main.Progress
GetBuildFailures = Main.GetBuildFailures
@@ -125,8 +126,7 @@ GetBuildFailures = Main.GetBuildFailures
#profiling = Main.profiling
#repositories = Main.repositories
-from . import SConscript
-_SConscript = SConscript
+from . import SConscript as _SConscript
call_stack = _SConscript.call_stack
@@ -287,21 +287,25 @@ def Variables(files=None, args=ARGUMENTS):
return SCons.Variables.Variables(files, args)
-# The list of global functions to add to the SConscript name space
-# that end up calling corresponding methods or Builders in the
+# Adding global functions to the SConscript name space.
+#
+# Static functions that do not trigger initialization of
+# DefaultEnvironment() and don't use its state.
+EnsureSConsVersion = _SConscript.SConsEnvironment.EnsureSConsVersion
+EnsurePythonVersion = _SConscript.SConsEnvironment.EnsurePythonVersion
+Exit = _SConscript.SConsEnvironment.Exit
+GetLaunchDir = _SConscript.SConsEnvironment.GetLaunchDir
+SConscriptChdir = _SConscript.SConsEnvironment.SConscriptChdir
+
+# Functions that end up calling methods or Builders in the
# DefaultEnvironment().
GlobalDefaultEnvironmentFunctions = [
# Methods from the SConsEnvironment class, above.
'Default',
- 'EnsurePythonVersion',
- 'EnsureSConsVersion',
- 'Exit',
'Export',
- 'GetLaunchDir',
'Help',
'Import',
#'SConscript', is handled separately, below.
- 'SConscriptChdir',
# Methods from the Environment.Base class.
'AddPostAction',
@@ -375,6 +379,8 @@ GlobalDefaultBuilders = [
'Package',
]
+# DefaultEnvironmentCall() initializes DefaultEnvironment() if it is not
+# created yet.
for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
exec ("%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name)))
del name
diff --git a/SCons/Subst.py b/SCons/Subst.py
index 7535772..645639b 100644
--- a/SCons/Subst.py
+++ b/SCons/Subst.py
@@ -804,7 +804,8 @@ _separate_args = re.compile(r'(%s|\s+|[^\s$]+|\$)' % _dollar_exps_str)
# space characters in the string result from the scons_subst() function.
_space_sep = re.compile(r'[\t ]+(?![^{]*})')
-def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
+
+def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None, overrides=False):
"""Expand a string or list containing construction variable
substitutions.
@@ -834,6 +835,10 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
lvars = lvars.copy()
lvars.update(d)
+ # Allow last ditch chance to override lvars
+ if overrides:
+ lvars.update(overrides)
+
# We're (most likely) going to eval() things. If Python doesn't
# find a __builtins__ value in the global dictionary used for eval(),
# it copies the current global values for you. Avoid this by
@@ -882,7 +887,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={
return result
-def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
+def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None,overrides=False):
"""Substitute construction variables in a string (or list or other
object) and separate the arguments into a command list.
@@ -908,6 +913,10 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gv
lvars = lvars.copy()
lvars.update(d)
+ # Allow caller to specify last ditch override of lvars
+ if overrides:
+ lvars.update(overrides)
+
# We're (most likely) going to eval() things. If Python doesn't
# find a __builtins__ value in the global dictionary used for eval(),
# it copies the current global values for you. Avoid this by
diff --git a/SCons/SubstTests.py b/SCons/SubstTests.py
index 1a203a0..16bd3c7 100644
--- a/SCons/SubstTests.py
+++ b/SCons/SubstTests.py
@@ -713,6 +713,14 @@ class CLVar_TestCase(unittest.TestCase):
assert cmd_list[0][3] == "call", cmd_list[0][3]
assert cmd_list[0][4] == "test", cmd_list[0][4]
+
+ def test_subst_overriding_lvars_overrides(self):
+ """Test that optional passed arg overrides overrides gvars, and existing lvars."""
+ env=DummyEnv({'XXX' : 'xxx'})
+ result = scons_subst('$XXX', env, gvars=env.Dictionary(), overrides={'XXX': 'yyz'})
+ assert result == 'yyz', result
+
+
class scons_subst_list_TestCase(SubstTestCase):
basic_cases = [
@@ -1102,6 +1110,13 @@ class scons_subst_list_TestCase(SubstTestCase):
result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
assert result == [['yyy']], result
+ def test_subst_list_overriding_lvars_overrides(self):
+ """Test that optional passed arg overrides overrides gvars, and existing lvars."""
+ env = DummyEnv({'XXX':'xxx'})
+ result = scons_subst_list('$XXX', env, gvars=env.Dictionary(), overrides={'XXX': 'yyy'})
+ assert result == [['yyy']], result
+
+
class scons_subst_once_TestCase(unittest.TestCase):
loc = {
diff --git a/SCons/Job.py b/SCons/Taskmaster/Job.py
index b398790..a63b529 100644
--- a/SCons/Job.py
+++ b/SCons/Taskmaster/Job.py
@@ -29,12 +29,18 @@ stop, and wait on jobs.
import SCons.compat
+import logging
import os
import signal
+import sys
+import threading
+
+from enum import Enum
import SCons.Errors
import SCons.Warnings
+
# The default stack size (in kilobytes) of the threads used to execute
# jobs in parallel.
#
@@ -48,7 +54,6 @@ default_stack_size = 256
interrupt_msg = 'Build interrupted.'
-
class InterruptState:
def __init__(self):
self.interrupted = False
@@ -79,6 +84,11 @@ class Jobs:
care should check the value of 'num_jobs' after initialization.
"""
+ # Importing GetOption here instead of at top of file to avoid
+ # circular imports
+ # pylint: disable=import-outside-toplevel
+ from SCons.Script import GetOption
+
self.job = None
if num > 1:
stack_size = explicit_stack_size
@@ -86,7 +96,12 @@ class Jobs:
stack_size = default_stack_size
try:
- self.job = Parallel(taskmaster, num, stack_size)
+ experimental_option = GetOption('experimental')
+ if 'tm_v2' in experimental_option:
+ self.job = NewParallel(taskmaster, num, stack_size)
+ else:
+ self.job = LegacyParallel(taskmaster, num, stack_size)
+
self.num_jobs = num
except NameError:
pass
@@ -142,7 +157,7 @@ class Jobs:
else:
os._exit(2) # pylint: disable=protected-access
- self.old_sigint = signal.signal(signal.SIGINT, handler)
+ self.old_sigint = signal.signal(signal.SIGINT, handler)
self.old_sigterm = signal.signal(signal.SIGTERM, handler)
try:
self.old_sighup = signal.signal(signal.SIGHUP, handler)
@@ -167,6 +182,7 @@ class Jobs:
except AttributeError:
pass
+
class Serial:
"""This class is used to execute tasks in series, and is more efficient
than Parallel, but is only appropriate for non-parallel builds. Only
@@ -208,7 +224,7 @@ class Serial:
try:
raise SCons.Errors.BuildError(
task.targets[0], errstr=interrupt_msg)
- except:
+ except Exception:
task.exception_set()
else:
task.exception_set()
@@ -260,7 +276,7 @@ else:
raise SCons.Errors.BuildError(
task.targets[0], errstr=interrupt_msg)
task.execute()
- except:
+ except Exception:
task.exception_set()
ok = False
else:
@@ -281,7 +297,7 @@ else:
self.resultsQueue = queue.Queue(0)
try:
- prev_size = threading.stack_size(stack_size*1024)
+ prev_size = threading.stack_size(stack_size * 1024)
except AttributeError as e:
# Only print a warning if the stack size has been
# explicitly set.
@@ -342,7 +358,7 @@ else:
worker.join(1.0)
self.workers = []
- class Parallel:
+ class LegacyParallel:
"""This class is used to execute tasks in parallel, and is somewhat
less efficient than Serial, but is appropriate for parallel builds.
@@ -389,7 +405,7 @@ else:
try:
# prepare task for execution
task.prepare()
- except:
+ except Exception:
task.exception_set()
task.failed()
task.postprocess()
@@ -402,7 +418,8 @@ else:
task.executed()
task.postprocess()
- if not task and not jobs: break
+ if not task and not jobs:
+ break
# Let any/all completed tasks finish up before we go
# back and put the next batch of tasks on the queue.
@@ -417,7 +434,7 @@ else:
try:
raise SCons.Errors.BuildError(
task.targets[0], errstr=interrupt_msg)
- except:
+ except Exception:
task.exception_set()
# Let the failed() callback function arrange
@@ -432,6 +449,296 @@ else:
self.tp.cleanup()
self.taskmaster.cleanup()
+ # An experimental new parallel scheduler that uses a leaders/followers pattern.
+ class NewParallel:
+
+ class State(Enum):
+ READY = 0
+ SEARCHING = 1
+ STALLED = 2
+ COMPLETED = 3
+
+ class Worker(threading.Thread):
+ def __init__(self, owner):
+ super().__init__()
+ self.daemon = True
+ self.owner = owner
+ self.start()
+
+ def run(self):
+ self.owner._work()
+
+ def __init__(self, taskmaster, num, stack_size):
+ self.taskmaster = taskmaster
+ self.num_workers = num
+ self.stack_size = stack_size
+ self.interrupted = InterruptState()
+ self.workers = []
+
+ # The `tm_lock` is what ensures that we only have one
+ # thread interacting with the taskmaster at a time. It
+ # also protects access to our state that gets updated
+ # concurrently. The `can_search_cv` is associated with
+ # this mutex.
+ self.tm_lock = threading.Lock()
+
+ # Guarded under `tm_lock`.
+ self.jobs = 0
+ self.state = NewParallel.State.READY
+
+ # The `can_search_cv` is used to manage a leader /
+ # follower pattern for access to the taskmaster, and to
+ # awaken from stalls.
+ self.can_search_cv = threading.Condition(self.tm_lock)
+
+ # The queue of tasks that have completed execution. The
+ # next thread to obtain `tm_lock`` will retire them.
+ self.results_queue_lock = threading.Lock()
+ self.results_queue = []
+
+ if self.taskmaster.trace:
+ self.trace = self._setup_logging()
+ else:
+ self.trace = False
+
+ def _setup_logging(self):
+ jl = logging.getLogger("Job")
+ jl.setLevel(level=logging.DEBUG)
+ jl.addHandler(self.taskmaster.trace.log_handler)
+ return jl
+
+ def trace_message(self, message):
+ # This grabs the name of the function which calls trace_message()
+ method_name = sys._getframe(1).f_code.co_name + "():"
+ thread_id=threading.get_ident()
+ self.trace.debug('%s.%s [Thread:%s] %s' % (type(self).__name__, method_name, thread_id, message))
+ # print('%-15s %s' % (method_name, message))
+
+ def start(self):
+ self._start_workers()
+ for worker in self.workers:
+ worker.join()
+ self.workers = []
+ self.taskmaster.cleanup()
+
+ def _start_workers(self):
+ prev_size = self._adjust_stack_size()
+ for _ in range(self.num_workers):
+ self.workers.append(NewParallel.Worker(self))
+ self._restore_stack_size(prev_size)
+
+ def _adjust_stack_size(self):
+ try:
+ prev_size = threading.stack_size(self.stack_size * 1024)
+ return prev_size
+ except AttributeError as e:
+ # Only print a warning if the stack size has been
+ # explicitly set.
+ if explicit_stack_size is not None:
+ msg = "Setting stack size is unsupported by this version of Python:\n " + \
+ e.args[0]
+ SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
+ except ValueError as e:
+ msg = "Setting stack size failed:\n " + str(e)
+ SCons.Warnings.warn(SCons.Warnings.StackSizeWarning, msg)
+
+ return None
+
+ def _restore_stack_size(self, prev_size):
+ if prev_size is not None:
+ threading.stack_size(prev_size)
+
+ def _work(self):
+
+ task = None
+
+ while True:
+
+ # Obtain `tm_lock`, granting exclusive access to the taskmaster.
+ with self.can_search_cv:
+
+ if self.trace:
+ self.trace_message("Gained exclusive access")
+
+ # Capture whether we got here with `task` set,
+ # then drop our reference to the task as we are no
+ # longer interested in the actual object.
+ completed_task = (task is not None)
+ task = None
+
+ # We will only have `completed_task` set here if
+ # we have looped back after executing a task. If
+ # we have completed a task and find that we are
+ # stalled, we should speculatively indicate that
+ # we are no longer stalled by transitioning to the
+ # 'ready' state which will bypass the condition
+ # wait so that we immediately process the results
+ # queue and hopefully light up new
+ # work. Otherwise, stay stalled, and we will wait
+ # in the condvar. Some other thread will come back
+ # here with a completed task.
+ if self.state == NewParallel.State.STALLED and completed_task:
+ if self.trace:
+ self.trace_message("Detected stall with completed task, bypassing wait")
+ self.state = NewParallel.State.READY
+
+ # Wait until we are neither searching nor stalled.
+ while self.state == NewParallel.State.SEARCHING or self.state == NewParallel.State.STALLED:
+ if self.trace:
+ self.trace_message("Search already in progress, waiting")
+ self.can_search_cv.wait()
+
+ # If someone set the completed flag, bail.
+ if self.state == NewParallel.State.COMPLETED:
+ if self.trace:
+ self.trace_message("Completion detected, breaking from main loop")
+ break
+
+ # Set the searching flag to indicate that a thread
+ # is currently in the critical section for
+ # taskmaster work.
+ #
+ if self.trace:
+ self.trace_message("Starting search")
+ self.state = NewParallel.State.SEARCHING
+
+ # Bulk acquire the tasks in the results queue
+ # under the result queue lock, then process them
+ # all outside that lock. We need to process the
+ # tasks in the results queue before looking for
+ # new work because we might be unable to find new
+ # work if we don't.
+ results_queue = []
+ with self.results_queue_lock:
+ results_queue, self.results_queue = self.results_queue, results_queue
+
+ if self.trace:
+ self.trace_message("Found {len(results_queue)} completed tasks to process")
+ for (rtask, rresult) in results_queue:
+ if rresult:
+ rtask.executed()
+ else:
+ if self.interrupted():
+ try:
+ raise SCons.Errors.BuildError(
+ rtask.targets[0], errstr=interrupt_msg)
+ except Exception:
+ rtask.exception_set()
+
+ # Let the failed() callback function arrange
+ # for the build to stop if that's appropriate.
+ rtask.failed()
+
+ rtask.postprocess()
+ self.jobs -= 1
+
+ # We are done with any task objects that were in
+ # the results queue.
+ results_queue.clear()
+
+ # Now, turn the crank on the taskmaster until we
+ # either run out of tasks, or find a task that
+ # needs execution. If we run out of tasks, go idle
+ # until results arrive if jobs are pending, or
+ # mark the walk as complete if not.
+ while self.state == NewParallel.State.SEARCHING:
+ if self.trace:
+ self.trace_message("Searching for new tasks")
+ task = self.taskmaster.next_task()
+
+ if task:
+ # We found a task. Walk it through the
+ # task lifecycle. If it does not need
+ # execution, just complete the task and
+ # look for the next one. Otherwise,
+ # indicate that we are no longer searching
+ # so we can drop out of this loop, execute
+ # the task outside the lock, and allow
+ # another thread in to search.
+ try:
+ task.prepare()
+ except Exception:
+ task.exception_set()
+ task.failed()
+ task.postprocess()
+ else:
+ if not task.needs_execute():
+ if self.trace:
+ self.trace_message("Found internal task")
+ task.executed()
+ task.postprocess()
+ else:
+ self.jobs += 1
+ if self.trace:
+ self.trace_message("Found task requiring execution")
+ self.state = NewParallel.State.READY
+ self.can_search_cv.notify()
+
+ else:
+ # We failed to find a task, so this thread
+ # cannot continue turning the taskmaster
+ # crank. We must exit the loop.
+ if self.jobs:
+ # No task was found, but there are
+ # outstanding jobs executing that
+ # might unblock new tasks when they
+ # complete. Transition to the stalled
+ # state. We do not need a notify,
+ # because we know there are threads
+ # outstanding that will re-enter the
+ # loop.
+ #
+ if self.trace:
+ self.trace_message("Found no task requiring execution, but have jobs: marking stalled")
+ self.state = NewParallel.State.STALLED
+ else:
+ # We didn't find a task and there are
+ # no jobs outstanding, so there is
+ # nothing that will ever return
+ # results which might unblock new
+ # tasks. We can conclude that the walk
+ # is complete. Update our state to
+ # note completion and awaken anyone
+ # sleeping on the condvar.
+ #
+ if self.trace:
+ self.trace_message("Found no task requiring execution, and have no jobs: marking complete")
+ self.state = NewParallel.State.COMPLETED
+ self.can_search_cv.notify_all()
+
+ # We no longer hold `tm_lock` here. If we have a task,
+ # we can now execute it. If there are threads waiting
+ # to search, one of them can now begin turning the
+ # taskmaster crank in NewParallel.
+ if task:
+ if self.trace:
+ self.trace_message("Executing task")
+ ok = True
+ try:
+ if self.interrupted():
+ raise SCons.Errors.BuildError(
+ task.targets[0], errstr=interrupt_msg)
+ task.execute()
+ except Exception:
+ ok = False
+ task.exception_set()
+
+ # Grab the results queue lock and enqueue the
+ # executed task and state. The next thread into
+ # the searching loop will complete the
+ # postprocessing work under the taskmaster lock.
+ #
+ if self.trace:
+ self.trace_message("Enqueueing executed task results")
+ with self.results_queue_lock:
+ self.results_queue.append((task, ok))
+
+ # Tricky state "fallthrough" here. We are going back
+ # to the top of the loop, which behaves differently
+ # depending on whether `task` is set. Do not perturb
+ # the value of the `task` variable if you add new code
+ # after this comment.
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/SCons/JobTests.py b/SCons/Taskmaster/JobTests.py
index 54d7fa4..9e7b080 100644
--- a/SCons/JobTests.py
+++ b/SCons/Taskmaster/JobTests.py
@@ -24,13 +24,10 @@
import unittest
import random
import math
-import sys
-import time
import os
-import TestUnit
-
-import SCons.Job
+import SCons.Taskmaster.Job
+from SCons.Script.Main import OptionsParser
def get_cpu_nums():
@@ -245,10 +242,25 @@ class Taskmaster:
def cleanup(self):
pass
+
SaveThreadPool = None
ThreadPoolCallList = []
-class ParallelTestCase(unittest.TestCase):
+
+class JobTestCase(unittest.TestCase):
+ """
+ Setup common items needed for many Job test cases
+ """
+ def setUp(self) -> None:
+ """
+ Simulating real options parser experimental value.
+ Since we're in a unit test we're actually using FakeOptionParser()
+ Which has no values and no defaults.
+ """
+ OptionsParser.values.experimental = []
+
+
+class ParallelTestCase(JobTestCase):
def runTest(self):
"""test parallel jobs"""
@@ -258,7 +270,7 @@ class ParallelTestCase(unittest.TestCase):
raise NoThreadsException()
taskmaster = Taskmaster(num_tasks, self, RandomTask)
- jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(num_jobs, taskmaster)
jobs.run()
self.assertTrue(not taskmaster.tasks_were_serial(),
@@ -284,7 +296,7 @@ class ParallelTestCase(unittest.TestCase):
time.sleep(0.01)
global SaveThreadPool
- SaveThreadPool = SCons.Job.ThreadPool
+ SaveThreadPool = SCons.Taskmaster.Job.ThreadPool
class WaitThreadPool(SaveThreadPool):
def put(self, task):
@@ -296,11 +308,11 @@ class ParallelTestCase(unittest.TestCase):
ThreadPoolCallList.append('get(%s)' % result[0].i)
return result
- SCons.Job.ThreadPool = WaitThreadPool
+ SCons.Taskmaster.Job.ThreadPool = WaitThreadPool
try:
taskmaster = Taskmaster(3, self, SleepTask)
- jobs = SCons.Job.Jobs(2, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(2, taskmaster)
jobs.run()
# The key here is that we get(1) and get(2) from the
@@ -314,14 +326,14 @@ class ParallelTestCase(unittest.TestCase):
assert ThreadPoolCallList in expect, ThreadPoolCallList
finally:
- SCons.Job.ThreadPool = SaveThreadPool
+ SCons.Taskmaster.Job.ThreadPool = SaveThreadPool
class SerialTestCase(unittest.TestCase):
def runTest(self):
"""test a serial job"""
taskmaster = Taskmaster(num_tasks, self, RandomTask)
- jobs = SCons.Job.Jobs(1, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(1, taskmaster)
jobs.run()
self.assertTrue(taskmaster.tasks_were_serial(),
@@ -335,16 +347,18 @@ class SerialTestCase(unittest.TestCase):
self.assertFalse(taskmaster.num_failed,
"some task(s) failed to execute")
-class NoParallelTestCase(unittest.TestCase):
+
+class NoParallelTestCase(JobTestCase):
+
def runTest(self):
"""test handling lack of parallel support"""
def NoParallel(tm, num, stack_size):
raise NameError
- save_Parallel = SCons.Job.Parallel
- SCons.Job.Parallel = NoParallel
+ save_Parallel = SCons.Taskmaster.Job.LegacyParallel
+ SCons.Taskmaster.Job.LegacyParallel = NoParallel
try:
taskmaster = Taskmaster(num_tasks, self, RandomTask)
- jobs = SCons.Job.Jobs(2, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(2, taskmaster)
self.assertTrue(jobs.num_jobs == 1,
"unexpected number of jobs %d" % jobs.num_jobs)
jobs.run()
@@ -359,7 +373,7 @@ class NoParallelTestCase(unittest.TestCase):
self.assertFalse(taskmaster.num_failed,
"some task(s) failed to execute")
finally:
- SCons.Job.Parallel = save_Parallel
+ SCons.Taskmaster.Job.LegacyParallel = save_Parallel
class SerialExceptionTestCase(unittest.TestCase):
@@ -367,7 +381,7 @@ class SerialExceptionTestCase(unittest.TestCase):
"""test a serial job with tasks that raise exceptions"""
taskmaster = Taskmaster(num_tasks, self, ExceptionTask)
- jobs = SCons.Job.Jobs(1, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(1, taskmaster)
jobs.run()
self.assertFalse(taskmaster.num_executed,
@@ -379,12 +393,14 @@ class SerialExceptionTestCase(unittest.TestCase):
self.assertTrue(taskmaster.num_postprocessed == 1,
"exactly one task should have been postprocessed")
-class ParallelExceptionTestCase(unittest.TestCase):
+
+class ParallelExceptionTestCase(JobTestCase):
+
def runTest(self):
"""test parallel jobs with tasks that raise exceptions"""
taskmaster = Taskmaster(num_tasks, self, ExceptionTask)
- jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(num_jobs, taskmaster)
jobs.run()
self.assertFalse(taskmaster.num_executed,
@@ -448,7 +464,8 @@ class badpreparenode (badnode):
def prepare(self):
raise Exception('badpreparenode exception')
-class _SConsTaskTest(unittest.TestCase):
+
+class _SConsTaskTest(JobTestCase):
def _test_seq(self, num_jobs):
for node_seq in [
@@ -476,7 +493,7 @@ class _SConsTaskTest(unittest.TestCase):
taskmaster = SCons.Taskmaster.Taskmaster(testnodes,
tasker=SCons.Taskmaster.AlwaysTask)
- jobs = SCons.Job.Jobs(num_jobs, taskmaster)
+ jobs = SCons.Taskmaster.Job.Jobs(num_jobs, taskmaster)
# Exceptions thrown by tasks are not actually propagated to
# this level, but are instead stored in the Taskmaster.
@@ -542,31 +559,19 @@ class ParallelTaskTest(_SConsTaskTest):
"""test parallel jobs with actual Taskmaster and Task"""
self._test_seq(num_jobs)
+ # Now run test with NewParallel() instead of LegacyParallel
+ OptionsParser.values.experimental=['tm_v2']
+ self._test_seq(num_jobs)
-#---------------------------------------------------------------------
-def suite():
- suite = unittest.TestSuite()
- suite.addTest(ParallelTestCase())
- suite.addTest(SerialTestCase())
- suite.addTest(NoParallelTestCase())
- suite.addTest(SerialExceptionTestCase())
- suite.addTest(ParallelExceptionTestCase())
- suite.addTest(SerialTaskTest())
- suite.addTest(ParallelTaskTest())
- return suite
+
+#---------------------------------------------------------------------
if __name__ == "__main__":
- runner = TestUnit.cli.get_runner()
- result = runner().run(suite())
- if (len(result.failures) == 0
- and len(result.errors) == 1
- and isinstance(result.errors[0][0], SerialTestCase)
- and isinstance(result.errors[0][1][0], NoThreadsException)):
- sys.exit(2)
- elif not result.wasSuccessful():
- sys.exit(1)
+ unittest.main()
+
+
# Local Variables:
# tab-width:4
diff --git a/SCons/TaskmasterTests.py b/SCons/Taskmaster/TaskmasterTests.py
index f20fd71..95150fd 100644
--- a/SCons/TaskmasterTests.py
+++ b/SCons/Taskmaster/TaskmasterTests.py
@@ -26,10 +26,10 @@ import SCons.compat
import sys
import unittest
-
import SCons.Taskmaster
import SCons.Errors
+import TestCommon
built_text = None
cache_text = []
@@ -37,8 +37,9 @@ visited_nodes = []
executed = None
scan_called = 0
+
class Node:
- def __init__(self, name, kids = [], scans = []):
+ def __init__(self, name, kids=[], scans=[]):
self.name = name
self.kids = kids
self.scans = scans
@@ -47,9 +48,11 @@ class Node:
self.scanner = None
self.targets = [self]
self.prerequisites = None
+
class Builder:
def targets(self, node):
return node.targets
+
self.builder = Builder()
self.bsig = None
self.csig = None
@@ -83,7 +86,7 @@ class Node:
def prepare(self):
self.prepared = 1
- self.get_binfo()
+ self.get_binfo()
def build(self):
global built_text
@@ -119,7 +122,7 @@ class Node:
self.binfo = binfo
return binfo
-
+
def clear(self):
# The del_binfo() call here isn't necessary for normal execution,
# but is for interactive mode, where we might rebuild the same
@@ -130,14 +133,14 @@ class Node:
global built_text
if not self.cached:
built_text = built_text + " really"
-
+
# Clear the implicit dependency caches of any Nodes
# waiting for this Node to be built.
for parent in self.waiting_parents:
parent.implicit = None
self.clear()
-
+
def release_target_info(self):
pass
@@ -199,7 +202,7 @@ class Node:
def is_up_to_date(self):
return self._current_val
-
+
def depends_on(self, nodes):
for node in nodes:
if node in self.kids:
@@ -218,26 +221,40 @@ class Node:
class Executor:
def prepare(self):
pass
+
def get_action_targets(self):
return self.targets
+
def get_all_targets(self):
return self.targets
+
def get_all_children(self):
result = []
for node in self.targets:
result.extend(node.children())
return result
+
def get_all_prerequisites(self):
return []
+
def get_action_side_effects(self):
return []
+
self.executor = Executor()
self.executor.targets = self.targets
return self.executor
+ def get_internal_path(self):
+ """
+ Should only be used (currently) by TaskmasterTestCase.test_cached_execute_target_unlink_fails
+ """
+ return str(self)
+
+
class OtherError(Exception):
pass
+
class MyException(Exception):
pass
@@ -306,7 +323,7 @@ class TaskmasterTestCase(unittest.TestCase):
n2._current_val = 1
n3.set_state(SCons.Node.no_state)
n3._current_val = 1
- tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask)
+ tm = SCons.Taskmaster.Taskmaster(targets=[n3], tasker=MyTask)
t = tm.next_task()
t.prepare()
@@ -331,7 +348,6 @@ class TaskmasterTestCase(unittest.TestCase):
assert tm.next_task() is None
-
n1 = Node("n1")
n2 = Node("n2")
n3 = Node("n3", [n1, n2])
@@ -366,7 +382,6 @@ class TaskmasterTestCase(unittest.TestCase):
assert tm.next_task() is None
-
n4 = Node("n4")
n4.set_state(SCons.Node.executed)
tm = SCons.Taskmaster.Taskmaster([n4])
@@ -374,14 +389,13 @@ class TaskmasterTestCase(unittest.TestCase):
n1 = Node("n1")
n2 = Node("n2", [n1])
- tm = SCons.Taskmaster.Taskmaster([n2,n2])
+ tm = SCons.Taskmaster.Taskmaster([n2, n2])
t = tm.next_task()
t.executed()
t.postprocess()
t = tm.next_task()
assert tm.next_task() is None
-
n1 = Node("n1")
n2 = Node("n2")
n3 = Node("n3", [n1], [n2])
@@ -440,11 +454,11 @@ class TaskmasterTestCase(unittest.TestCase):
n1 = Node("n1")
n2 = Node("n2")
n3 = Node("n3")
- n4 = Node("n4", [n1,n2,n3])
+ n4 = Node("n4", [n1, n2, n3])
n5 = Node("n5", [n4])
n3.side_effect = 1
n1.side_effects = n2.side_effects = n3.side_effects = [n4]
- tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5])
+ tm = SCons.Taskmaster.Taskmaster([n1, n2, n3, n4, n5])
t = tm.next_task()
assert t.get_target() == n1
assert n4.state == SCons.Node.executing, n4.state
@@ -471,10 +485,12 @@ class TaskmasterTestCase(unittest.TestCase):
n1 = Node("n1")
n2 = Node("n2")
n3 = Node("n3")
- n4 = Node("n4", [n1,n2,n3])
+ n4 = Node("n4", [n1, n2, n3])
+
def reverse(dependencies):
dependencies.reverse()
return dependencies
+
tm = SCons.Taskmaster.Taskmaster([n4], order=reverse)
t = tm.next_task()
assert t.get_target() == n3, t.get_target()
@@ -534,11 +550,11 @@ class TaskmasterTestCase(unittest.TestCase):
s = n2.get_state()
assert s == SCons.Node.executed, s
-
def test_make_ready_out_of_date(self):
"""Test the Task.make_ready() method's list of out-of-date Nodes
"""
ood = []
+
def TaskGen(tm, targets, top, node, ood=ood):
class MyTask(SCons.Taskmaster.AlwaysTask):
def make_ready(self):
@@ -558,8 +574,8 @@ class TaskmasterTestCase(unittest.TestCase):
a5 = Node("a5")
a5._current_val = 1
a5.always_build = 1
- tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5],
- tasker = TaskGen)
+ tm = SCons.Taskmaster.Taskmaster(targets=[n1, c2, n3, c4, a5],
+ tasker=TaskGen)
del ood[:]
t = tm.next_task()
@@ -584,12 +600,13 @@ class TaskmasterTestCase(unittest.TestCase):
def test_make_ready_exception(self):
"""Test handling exceptions from Task.make_ready()
"""
+
class MyTask(SCons.Taskmaster.AlwaysTask):
def make_ready(self):
raise MyException("from make_ready()")
n1 = Node("n1")
- tm = SCons.Taskmaster.Taskmaster(targets = [n1], tasker = MyTask)
+ tm = SCons.Taskmaster.Taskmaster(targets=[n1], tasker=MyTask)
t = tm.next_task()
exc_type, exc_value, exc_tb = t.exception
assert exc_type == MyException, repr(exc_type)
@@ -601,6 +618,7 @@ class TaskmasterTestCase(unittest.TestCase):
We should be getting:
TypeError: Can't instantiate abstract class MyTask with abstract methods needs_execute
"""
+
class MyTask(SCons.Taskmaster.Task):
pass
@@ -611,6 +629,7 @@ class TaskmasterTestCase(unittest.TestCase):
def test_make_ready_all(self):
"""Test the make_ready_all() method"""
+
class MyTask(SCons.Taskmaster.AlwaysTask):
make_ready = SCons.Taskmaster.Task.make_ready_all
@@ -621,7 +640,7 @@ class TaskmasterTestCase(unittest.TestCase):
c4 = Node("c4")
c4._current_val = 1
- tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4])
+ tm = SCons.Taskmaster.Taskmaster(targets=[n1, c2, n3, c4])
t = tm.next_task()
target = t.get_target()
@@ -647,8 +666,8 @@ class TaskmasterTestCase(unittest.TestCase):
n3 = Node("n3")
c4 = Node("c4")
- tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4],
- tasker = MyTask)
+ tm = SCons.Taskmaster.Taskmaster(targets=[n1, c2, n3, c4],
+ tasker=MyTask)
t = tm.next_task()
target = t.get_target()
@@ -669,13 +688,14 @@ class TaskmasterTestCase(unittest.TestCase):
t = tm.next_task()
assert t is None
-
def test_children_errors(self):
"""Test errors when fetching the children of a node.
"""
+
class StopNode(Node):
def children(self):
raise SCons.Errors.StopError("stop!")
+
class ExitNode(Node):
def children(self):
sys.exit(77)
@@ -879,8 +899,8 @@ class TaskmasterTestCase(unittest.TestCase):
n9 = Node("n9")
n10 = Node("n10")
- n6.side_effects = [ n8 ]
- n7.side_effects = [ n9, n10 ]
+ n6.side_effects = [n8]
+ n7.side_effects = [n9, n10]
tm = SCons.Taskmaster.Taskmaster([n6, n7])
t = tm.next_task()
@@ -897,15 +917,19 @@ class TaskmasterTestCase(unittest.TestCase):
class ExceptionExecutor:
def prepare(self):
raise Exception("Executor.prepare() exception")
+
def get_all_targets(self):
return self.nodes
+
def get_all_children(self):
result = []
for node in self.nodes:
result.extend(node.children())
return result
+
def get_all_prerequisites(self):
return []
+
def get_action_side_effects(self):
return []
@@ -935,6 +959,7 @@ class TaskmasterTestCase(unittest.TestCase):
def raise_UserError():
raise SCons.Errors.UserError
+
n2 = Node("n2")
n2.build = raise_UserError
tm = SCons.Taskmaster.Taskmaster([n2])
@@ -948,6 +973,7 @@ class TaskmasterTestCase(unittest.TestCase):
def raise_BuildError():
raise SCons.Errors.BuildError
+
n3 = Node("n3")
n3.build = raise_BuildError
tm = SCons.Taskmaster.Taskmaster([n3])
@@ -964,6 +990,7 @@ class TaskmasterTestCase(unittest.TestCase):
# args set to the exception value, instance, and traceback.
def raise_OtherError():
raise OtherError
+
n4 = Node("n4")
n4.build = raise_OtherError
tm = SCons.Taskmaster.Taskmaster([n4])
@@ -1050,7 +1077,7 @@ class TaskmasterTestCase(unittest.TestCase):
n1 = Node("n1")
# Mark the node as being cached
- n1.cached = 1
+ n1.cached = True
tm = SCons.Taskmaster.Taskmaster([n1])
t = tm.next_task()
t.prepare()
@@ -1060,13 +1087,62 @@ class TaskmasterTestCase(unittest.TestCase):
has_binfo = hasattr(n1, 'binfo')
assert has_binfo, has_binfo
+ def test_cached_execute_target_unlink_fails(self):
+ """Test executing a task with cached targets where unlinking one of the targets fail
+ """
+ global cache_text
+ import SCons.Warnings
+
+ cache_text = []
+ n1 = Node("n1")
+ n2 = Node("not-cached")
+
+ class DummyFS:
+ def unlink(self, _):
+ raise IOError
+
+ n1.fs = DummyFS()
+
+ # Mark the node as being cached
+ n1.cached = True
+ # Add n2 as a target for n1
+ n1.targets.append(n2)
+ # Explicitly mark n2 as not cached
+ n2.cached = False
+
+ # Save SCons.Warnings.warn so we can mock it and catch it being called for unlink failures
+ _save_warn = SCons.Warnings.warn
+ issued_warnings = []
+
+ def fake_warnings_warn(clz, message):
+ nonlocal issued_warnings
+ issued_warnings.append((clz, message))
+ SCons.Warnings.warn = fake_warnings_warn
+
+ tm = SCons.Taskmaster.Taskmaster([n1, n2])
+ t = tm.next_task()
+ t.prepare()
+ t.execute()
+
+ # Restore saved warn
+ SCons.Warnings.warn = _save_warn
+
+ self.assertTrue(len(issued_warnings) == 1,
+ msg='More than expected warnings (1) were issued %d' % len(issued_warnings))
+ self.assertEqual(issued_warnings[0][0], SCons.Warnings.CacheCleanupErrorWarning,
+ msg='Incorrect warning class')
+ self.assertEqual(issued_warnings[0][1],
+ 'Failed copying all target files from cache, Error while attempting to remove file n1 retrieved from cache: ')
+ self.assertEqual(cache_text, ["n1 retrieved"], msg=cache_text)
+
+
def test_exception(self):
"""Test generic Taskmaster exception handling
"""
n1 = Node("n1")
tm = SCons.Taskmaster.Taskmaster([n1])
- t = tm.next_task()
+ t = tm.next_task()
t.exception_set((1, 2))
exc_type, exc_value = t.exception
@@ -1076,25 +1152,24 @@ class TaskmasterTestCase(unittest.TestCase):
t.exception_set(3)
assert t.exception == 3
- try: 1//0
+ try:
+ 1 // 0
except:
# Moved from below
t.exception_set(None)
- #pass
-
-# import pdb; pdb.set_trace()
+ # pass
# Having this here works for python 2.x,
# but it is a tuple (None, None, None) when called outside
# an except statement
# t.exception_set(None)
-
+
exc_type, exc_value, exc_tb = t.exception
- assert exc_type is ZeroDivisionError, "Expecting ZeroDevisionError got:%s"%exc_type
+ assert exc_type is ZeroDivisionError, "Expecting ZeroDevisionError got:%s" % exc_type
exception_values = [
"integer division or modulo",
"integer division or modulo by zero",
- "integer division by zero", # PyPy2
+ "integer division by zero", # PyPy2
]
assert str(exc_value) in exception_values, exc_value
@@ -1108,7 +1183,7 @@ class TaskmasterTestCase(unittest.TestCase):
except:
exc_type, exc_value = sys.exc_info()[:2]
assert exc_type == Exception1, exc_type
- assert str(exc_value) == '', "Expecting empty string got:%s (type %s)"%(exc_value,type(exc_value))
+ assert str(exc_value) == '', "Expecting empty string got:%s (type %s)" % (exc_value, type(exc_value))
else:
assert 0, "did not catch expected exception"
@@ -1129,7 +1204,7 @@ class TaskmasterTestCase(unittest.TestCase):
pass
try:
- 1//0
+ 1 // 0
except:
tb = sys.exc_info()[2]
t.exception_set((Exception3, "arg", tb))
@@ -1241,10 +1316,12 @@ Task.postprocess(): node <executing 0 'n3'>
Taskmaster: Looking for a node to evaluate
Taskmaster: No candidate anymore.
-
"""
- assert value == expect, value
+ if value != expect:
+ TestCommon.TestCommon.detailed_diff(value, expect)
+
+ assert value == expect, "Expected taskmaster trace contents didn't match. See above"
if __name__ == "__main__":
diff --git a/SCons/Taskmaster.py b/SCons/Taskmaster/__init__.py
index d571795..7ab864e 100644
--- a/SCons/Taskmaster.py
+++ b/SCons/Taskmaster/__init__.py
@@ -46,14 +46,16 @@ Task
The Taskmaster instantiates a Task object for each (set of)
target(s) that it decides need to be evaluated and/or built.
"""
-
+import io
import sys
from abc import ABC, abstractmethod
from itertools import chain
+import logging
import SCons.Errors
import SCons.Node
import SCons.Warnings
+from SCons.Util import DispatchingFormatter
StateString = SCons.Node.StateString
NODE_NO_STATE = SCons.Node.no_state
@@ -62,7 +64,6 @@ NODE_EXECUTING = SCons.Node.executing
NODE_UP_TO_DATE = SCons.Node.up_to_date
NODE_EXECUTED = SCons.Node.executed
NODE_FAILED = SCons.Node.failed
-
print_prepare = False # set by option --debug=prepare
# A subsystem for recording stats about how different Nodes are handled by
@@ -71,6 +72,7 @@ print_prepare = False # set by option --debug=prepare
CollectStats = None
+
class Stats:
"""
A simple class for holding statistics about the disposition of a
@@ -92,6 +94,7 @@ class Stats:
self.side_effects = 0
self.build = 0
+
StatsNodes = []
fmt = "%(considered)3d "\
@@ -102,6 +105,7 @@ fmt = "%(considered)3d "\
"%(side_effects)3d " \
"%(build)3d "
+
def dump_stats():
for n in sorted(StatsNodes, key=lambda a: str(a)):
print((fmt % n.attributes.stats.__dict__) + str(n))
@@ -125,6 +129,9 @@ class Task(ABC):
these methods explicitly to update state, etc., rather than
roll their own interaction with Taskmaster from scratch.
"""
+
+ LOGGER = None
+
def __init__(self, tm, targets, top, node):
self.tm = tm
self.targets = targets
@@ -132,9 +139,10 @@ class Task(ABC):
self.node = node
self.exc_clear()
- def trace_message(self, method, node, description='node'):
- fmt = '%-20s %s %s\n'
- return fmt % (method + ':', description, self.tm.trace_node(node))
+ def trace_message(self, node, description='node'):
+ # This grabs the name of the function which calls trace_message()
+ method_name=sys._getframe(1).f_code.co_name+"():"
+ Task.LOGGER.debug('%-15s %s %s' % (method_name, description, self.tm.tm_trace_node(node)))
def display(self, message):
"""
@@ -159,7 +167,8 @@ class Task(ABC):
"""
global print_prepare
T = self.tm.trace
- if T: T.write(self.trace_message('Task.prepare()', self.node))
+ if T:
+ self.trace_message(self.node)
# Now that it's the appropriate time, give the TaskMaster a
# chance to raise any exceptions it encountered while preparing
@@ -213,7 +222,8 @@ class Task(ABC):
prepare(), executed() or failed().
"""
T = self.tm.trace
- if T: T.write(self.trace_message('Task.execute()', self.node))
+ if T:
+ self.trace_message(self.node)
try:
cached_targets = []
@@ -230,8 +240,9 @@ class Task(ABC):
for t in cached_targets:
try:
t.fs.unlink(t.get_internal_path())
- except (IOError, OSError):
- pass
+ except (IOError, OSError) as e:
+ SCons.Warnings.warn(SCons.Warnings.CacheCleanupErrorWarning,
+ "Failed copying all target files from cache, Error while attempting to remove file %s retrieved from cache: %s" % (t.get_internal_path(), e))
self.targets[0].build()
else:
for t in cached_targets:
@@ -256,8 +267,8 @@ class Task(ABC):
the Node's callback methods.
"""
T = self.tm.trace
- if T: T.write(self.trace_message('Task.executed_without_callbacks()',
- self.node))
+ if T:
+ self.trace_message(self.node)
for t in self.targets:
if t.get_state() == NODE_EXECUTING:
@@ -280,8 +291,8 @@ class Task(ABC):
"""
global print_prepare
T = self.tm.trace
- if T: T.write(self.trace_message('Task.executed_with_callbacks()',
- self.node))
+ if T:
+ self.trace_message(self.node)
for t in self.targets:
if t.get_state() == NODE_EXECUTING:
@@ -322,7 +333,8 @@ class Task(ABC):
nodes when using Configure().
"""
T = self.tm.trace
- if T: T.write(self.trace_message('Task.failed_stop()', self.node))
+ if T:
+ self.trace_message(self.node)
# Invoke will_not_build() to clean-up the pending children
# list.
@@ -349,7 +361,8 @@ class Task(ABC):
nodes when using Configure().
"""
T = self.tm.trace
- if T: T.write(self.trace_message('Task.failed_continue()', self.node))
+ if T:
+ self.trace_message(self.node)
self.tm.will_not_build(self.targets, lambda n: n.set_state(NODE_FAILED))
@@ -361,7 +374,8 @@ class Task(ABC):
visited--the canonical example being the "scons -c" option.
"""
T = self.tm.trace
- if T: T.write(self.trace_message('Task.make_ready_all()', self.node))
+ if T:
+ self.trace_message(self.node)
self.out_of_date = self.targets[:]
for t in self.targets:
@@ -379,8 +393,9 @@ class Task(ABC):
"""
global print_prepare
T = self.tm.trace
- if T: T.write(self.trace_message('Task.make_ready_current()',
- self.node))
+ if T:
+ T.log_handler.stream.write('\n') # Prefix message with new line. This is a hack
+ self.trace_message(self.node)
self.out_of_date = []
needs_executing = False
@@ -427,7 +442,8 @@ class Task(ABC):
that can be put back on the candidates list.
"""
T = self.tm.trace
- if T: T.write(self.trace_message('Task.postprocess()', self.node))
+ if T:
+ self.trace_message(self.node)
# We may have built multiple targets, some of which may have
# common parents waiting for this build. Count up how many
@@ -444,9 +460,8 @@ class Task(ABC):
# A node can only be in the pending_children set if it has
# some waiting_parents.
if t.waiting_parents:
- if T: T.write(self.trace_message('Task.postprocess()',
- t,
- 'removing'))
+ if T:
+ self.trace_message(t, 'removing')
pending_children.discard(t)
for p in t.waiting_parents:
parents[p] = parents.get(p, 0) + 1
@@ -473,9 +488,8 @@ class Task(ABC):
for p, subtract in parents.items():
p.ref_count = p.ref_count - subtract
- if T: T.write(self.trace_message('Task.postprocess()',
- p,
- 'adjusted parent ref count'))
+ if T:
+ self.trace_message(p, 'adjusted parent ref count')
if p.ref_count == 0:
self.tm.candidates.append(p)
@@ -604,9 +618,63 @@ class Taskmaster:
order = lambda l: l
self.order = order
self.message = None
- self.trace = trace
self.next_candidate = self.find_next_candidate
self.pending_children = set()
+ self.trace = False
+ self.configure_trace(trace)
+
+ def configure_trace(self, trace=None):
+ """
+ This handles the command line option --taskmastertrace=
+ It can be:
+ - : output to stdout
+ <filename> : output to a file
+ False/None : Do not trace
+ """
+ if not trace:
+ self.trace = False
+ return
+
+ # TODO: May want to switch format to something like this.
+ # log_format = (
+ # '%(relativeCreated)05dms'
+ # ':%(relfilename)s'
+ # ':%(funcName)s'
+ # '#%(lineno)s'
+ # ': %(message)s'
+ # )
+ tm_formatter = logging.Formatter('Taskmaster: %(message)s')
+ if isinstance(trace, io.StringIO):
+ log_handler = logging.StreamHandler(trace)
+ elif trace == '-':
+ log_handler = logging.StreamHandler(sys.stdout)
+ elif trace:
+ log_handler = logging.FileHandler(filename=trace)
+
+ logger = logging.getLogger('Taskmaster')
+ logger.setLevel(level=logging.DEBUG)
+ logger.addHandler(log_handler)
+ self.trace = logger
+
+ logger.log_handler = log_handler
+
+ # Now setup Task's logger.
+ tl = logging.getLogger("Task")
+ tl.setLevel(level=logging.DEBUG)
+ tl.addHandler(log_handler)
+ task_formatter = logging.Formatter('%(name)s.%(message)s')
+ Task.LOGGER = tl
+
+ self.trace.log_handler = log_handler
+
+ log_handler.setFormatter(DispatchingFormatter(
+ formatters={
+ 'Taskmaster': tm_formatter,
+ 'Task': task_formatter,
+ 'Job': task_formatter,
+ },
+ default_formatter=logging.Formatter('%(message)s')
+ ))
def find_next_candidate(self):
"""
@@ -735,14 +803,10 @@ class Taskmaster:
for p in n.waiting_parents:
assert p.ref_count > 0, (str(n), str(p), p.ref_count)
-
- def trace_message(self, message):
- return 'Taskmaster: %s\n' % message
-
- def trace_node(self, node):
- return '<%-10s %-3s %s>' % (StateString[node.get_state()],
+ def tm_trace_node(self, node):
+ return('<%-10s %-3s %s>' % (StateString[node.get_state()],
node.ref_count,
- repr(str(node)))
+ repr(str(node))))
def _find_next_ready_node(self):
"""
@@ -769,12 +833,15 @@ class Taskmaster:
self.ready_exc = None
T = self.trace
- if T: T.write('\n' + self.trace_message('Looking for a node to evaluate'))
+ if T:
+ T.log_handler.stream.write('\n') # Prefix message with new line. This is a hack
+ self.trace.debug('Looking for a node to evaluate')
while True:
node = self.next_candidate()
if node is None:
- if T: T.write(self.trace_message('No candidate anymore.') + '\n')
+ if T:
+ self.trace.debug('No candidate anymore.')
return None
node = node.disambiguate()
@@ -797,7 +864,8 @@ class Taskmaster:
else:
S = None
- if T: T.write(self.trace_message(' Considering node %s and its children:' % self.trace_node(node)))
+ if T:
+ self.trace.debug(' Considering node %s and its children:' % self.tm_trace_node(node))
if state == NODE_NO_STATE:
# Mark this node as being on the execution stack:
@@ -805,7 +873,8 @@ class Taskmaster:
elif state > NODE_PENDING:
# Skip this node if it has already been evaluated:
if S: S.already_handled = S.already_handled + 1
- if T: T.write(self.trace_message(' already handled (executed)'))
+ if T:
+ self.trace.debug(' already handled (executed)')
continue
executor = node.get_executor()
@@ -816,7 +885,8 @@ class Taskmaster:
exc_value = sys.exc_info()[1]
e = SCons.Errors.ExplicitExit(node, exc_value.code)
self.ready_exc = (SCons.Errors.ExplicitExit, e)
- if T: T.write(self.trace_message(' SystemExit'))
+ if T:
+ self.trace.debug(' SystemExit')
return node
except Exception as e:
# We had a problem just trying to figure out the
@@ -825,7 +895,8 @@ class Taskmaster:
# raise the exception when the Task is "executed."
self.ready_exc = sys.exc_info()
if S: S.problem = S.problem + 1
- if T: T.write(self.trace_message(' exception %s while scanning children.\n' % e))
+ if T:
+ self.trace.debug(' exception %s while scanning children.' % e)
return node
children_not_visited = []
@@ -836,7 +907,8 @@ class Taskmaster:
for child in chain(executor.get_all_prerequisites(), children):
childstate = child.get_state()
- if T: T.write(self.trace_message(' ' + self.trace_node(child)))
+ if T:
+ self.trace.debug(' ' + self.tm_trace_node(child))
if childstate == NODE_NO_STATE:
children_not_visited.append(child)
@@ -857,8 +929,8 @@ class Taskmaster:
self.candidates.extend(self.order(children_not_visited))
# if T and children_not_visited:
- # T.write(self.trace_message(' adding to candidates: %s' % map(str, children_not_visited)))
- # T.write(self.trace_message(' candidates now: %s\n' % map(str, self.candidates)))
+ # self.trace.debug(' adding to candidates: %s' % map(str, children_not_visited))
+ # self.trace.debug(' candidates now: %s\n' % map(str, self.candidates))
# Skip this node if any of its children have failed.
#
@@ -883,7 +955,8 @@ class Taskmaster:
n.set_state(NODE_FAILED)
if S: S.child_failed = S.child_failed + 1
- if T: T.write(self.trace_message('****** %s\n' % self.trace_node(node)))
+ if T:
+ self.trace.debug('****** %s' % self.tm_trace_node(node))
continue
if children_not_ready:
@@ -897,13 +970,14 @@ class Taskmaster:
# count so we can be put back on the list for
# re-evaluation when they've all finished.
node.ref_count = node.ref_count + child.add_to_waiting_parents(node)
- if T: T.write(self.trace_message(' adjusted ref count: %s, child %s' %
- (self.trace_node(node), repr(str(child)))))
+ if T:
+ self.trace.debug(' adjusted ref count: %s, child %s' %
+ (self.tm_trace_node(node), repr(str(child))))
if T:
for pc in children_pending:
- T.write(self.trace_message(' adding %s to the pending children set\n' %
- self.trace_node(pc)))
+ self.trace.debug(' adding %s to the pending children set' %
+ self.tm_trace_node(pc))
self.pending_children = self.pending_children | children_pending
continue
@@ -923,8 +997,8 @@ class Taskmaster:
# The default when we've gotten through all of the checks above:
# this node is ready to be built.
if S: S.build = S.build + 1
- if T: T.write(self.trace_message('Evaluating %s\n' %
- self.trace_node(node)))
+ if T:
+ self.trace.debug('Evaluating %s' % self.tm_trace_node(node))
# For debugging only:
#
@@ -989,8 +1063,8 @@ class Taskmaster:
if T:
for n in nodes:
- T.write(self.trace_message(' removing node %s from the pending children set\n' %
- self.trace_node(n)))
+ self.trace.debug(' removing node %s from the pending children set\n' %
+ self.tm_trace_node(n))
try:
while len(to_visit):
node = to_visit.pop()
@@ -1006,8 +1080,9 @@ class Taskmaster:
for p in parents:
p.ref_count = p.ref_count - 1
- if T: T.write(self.trace_message(' removing parent %s from the pending children set\n' %
- self.trace_node(p)))
+ if T:
+ self.trace.debug(' removing parent %s from the pending children set\n' %
+ self.tm_trace_node(p))
except KeyError:
# The container to_visit has been emptied.
pass
diff --git a/SCons/Tool/FortranCommon.py b/SCons/Tool/FortranCommon.py
index a73de5d..aff0f92 100644
--- a/SCons/Tool/FortranCommon.py
+++ b/SCons/Tool/FortranCommon.py
@@ -20,42 +20,49 @@
# 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.
-"""SCons.Tool.FortranCommon
-Stuff for processing Fortran, common to all fortran dialects.
-
-"""
+"""Routines for setting up Fortran, common to all dialects."""
import re
import os.path
+from typing import Tuple
-import SCons.Action
import SCons.Scanner.Fortran
import SCons.Tool
import SCons.Util
+from SCons.Action import Action
+
+def isfortran(env, source) -> bool:
+ """Returns True if source has any fortran files in it.
-def isfortran(env, source):
- """Return 1 if any of code in source has fortran files in it, 0
- otherwise."""
+ Only checks based on filename suffixes, does not examine code.
+ """
try:
fsuffixes = env['FORTRANSUFFIXES']
except KeyError:
# If no FORTRANSUFFIXES, no fortran tool, so there is no need to look
# for fortran sources.
- return 0
+ return False
if not source:
# Source might be None for unusual cases like SConf.
- return 0
+ return False
for s in source:
if s.sources:
ext = os.path.splitext(str(s.sources[0]))[1]
if ext in fsuffixes:
- return 1
- return 0
+ return True
+ return False
+
+
+def _fortranEmitter(target, source, env) -> Tuple:
+ """Common code for Fortran emitter.
+
+ Called by both the static and shared object emitters,
+ mainly to account for generated module files.
+ """
-def _fortranEmitter(target, source, env):
node = source[0].rfile()
if not node.exists() and not node.is_derived():
print("Could not locate " + str(node.name))
@@ -75,19 +82,30 @@ def _fortranEmitter(target, source, env):
target.append(env.fs.File(m, moddir))
return (target, source)
-def FortranEmitter(target, source, env):
+
+def FortranEmitter(target, source, env) -> Tuple:
import SCons.Defaults
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.StaticObjectEmitter(target, source, env)
-def ShFortranEmitter(target, source, env):
+
+def ShFortranEmitter(target, source, env) -> Tuple:
import SCons.Defaults
target, source = _fortranEmitter(target, source, env)
return SCons.Defaults.SharedObjectEmitter(target, source, env)
-def ComputeFortranSuffixes(suffixes, ppsuffixes):
- """suffixes are fortran source files, and ppsuffixes the ones to be
- pre-processed. Both should be sequences, not strings."""
+
+def ComputeFortranSuffixes(suffixes, ppsuffixes) -> None:
+ """Update the suffix lists to reflect the platform requirements.
+
+ If upper-cased suffixes can be distinguished from lower, those are
+ added to *ppsuffixes*. If not, they are added to *suffixes*.
+
+ Args:
+ suffixes (list): indicate regular Fortran source files
+ ppsuffixes (list): indicate Fortran source files that should be
+ be run through the pre-processor
+ """
assert len(suffixes) > 0
s = suffixes[0]
sup = s.upper()
@@ -97,29 +115,34 @@ def ComputeFortranSuffixes(suffixes, ppsuffixes):
else:
suffixes.extend(upper_suffixes)
-def CreateDialectActions(dialect):
+def CreateDialectActions(dialect) -> Tuple[Action, Action, Action, Action]:
"""Create dialect specific actions."""
- CompAction = SCons.Action.Action('$%sCOM ' % dialect, '$%sCOMSTR' % dialect)
- CompPPAction = SCons.Action.Action('$%sPPCOM ' % dialect, '$%sPPCOMSTR' % dialect)
- ShCompAction = SCons.Action.Action('$SH%sCOM ' % dialect, '$SH%sCOMSTR' % dialect)
- ShCompPPAction = SCons.Action.Action('$SH%sPPCOM ' % dialect, '$SH%sPPCOMSTR' % dialect)
-
+ CompAction = Action(f'${dialect}COM ', cmdstr=f'${dialect}COMSTR')
+ CompPPAction = Action(f'${dialect}PPCOM ', cmdstr=f'${dialect}PPCOMSTR')
+ ShCompAction = Action(f'$SH{dialect}COM ', cmdstr=f'$SH{dialect}COMSTR')
+ ShCompPPAction = Action(f'$SH{dialect}PPCOM ', cmdstr=f'$SH{dialect}PPCOMSTR')
return CompAction, CompPPAction, ShCompAction, ShCompPPAction
-def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0):
- """Add dialect specific construction variables."""
- ComputeFortranSuffixes(suffixes, ppsuffixes)
- fscan = SCons.Scanner.Fortran.FortranScan("%sPATH" % dialect)
+def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_mods=False) -> None:
+ """Add dialect specific construction variables.
+
+ Args:
+ dialect (str): dialect name
+ suffixes (list): suffixes associated with this dialect
+ ppsuffixes (list): suffixes using cpp associated with this dialect
+ support_mods (bool): whether this dialect supports modules
+ """
+ ComputeFortranSuffixes(suffixes, ppsuffixes)
+ fscan = SCons.Scanner.Fortran.FortranScan(f"{dialect}PATH")
for suffix in suffixes + ppsuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, fscan)
- env.AppendUnique(FORTRANSUFFIXES = suffixes + ppsuffixes)
+ env.AppendUnique(FORTRANSUFFIXES=suffixes + ppsuffixes)
compaction, compppaction, shcompaction, shcompppaction = \
CreateDialectActions(dialect)
-
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in suffixes:
@@ -134,64 +157,60 @@ def DialectAddToEnv(env, dialect, suffixes, ppsuffixes, support_module = 0):
static_obj.add_emitter(suffix, FortranEmitter)
shared_obj.add_emitter(suffix, ShFortranEmitter)
- if '%sFLAGS' % dialect not in env:
- env['%sFLAGS' % dialect] = SCons.Util.CLVar('')
-
- if 'SH%sFLAGS' % dialect not in env:
- env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
+ if f'{dialect}FLAGS' not in env:
+ env[f'{dialect}FLAGS'] = SCons.Util.CLVar('')
+ if f'SH{dialect}FLAGS' not in env:
+ env[f'SH{dialect}FLAGS'] = SCons.Util.CLVar(f'${dialect}FLAGS')
# If a tool does not define fortran prefix/suffix for include path, use C ones
- if 'INC%sPREFIX' % dialect not in env:
- env['INC%sPREFIX' % dialect] = '$INCPREFIX'
-
- if 'INC%sSUFFIX' % dialect not in env:
- env['INC%sSUFFIX' % dialect] = '$INCSUFFIX'
-
- env['_%sINCFLAGS' % dialect] = '${_concat(INC%sPREFIX, %sPATH, INC%sSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}' % (dialect, dialect, dialect)
-
- if support_module == 1:
- env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
- env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
- env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
- env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $_FORTRANMODFLAG $SOURCES' % (dialect, dialect, dialect)
+ if f'INC{dialect}PREFIX' not in env:
+ env[f'INC{dialect}PREFIX'] = '$INCPREFIX'
+ if f'INC{dialect}SUFFIX' not in env:
+ env[f'INC{dialect}SUFFIX'] = '$INCSUFFIX'
+
+ env[f'_{dialect}INCFLAGS'] = f'${{_concat(INC{dialect}PREFIX, {dialect}PATH, INC{dialect}SUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}}'
+
+ if support_mods:
+ env[f'{dialect}COM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
+ env[f'{dialect}PPCOM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
+ env[f'SH{dialect}COM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
+ env[f'SH{dialect}PPCOM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $_FORTRANMODFLAG $SOURCES'
else:
- env['%sCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
- env['%sPPCOM' % dialect] = '$%s -o $TARGET -c $%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
- env['SH%sCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
- env['SH%sPPCOM' % dialect] = '$SH%s -o $TARGET -c $SH%sFLAGS $CPPFLAGS $_CPPDEFFLAGS $_%sINCFLAGS $SOURCES' % (dialect, dialect, dialect)
+ env[f'{dialect}COM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $_{dialect}INCFLAGS $SOURCES'
+ env[f'{dialect}PPCOM'] = f'${dialect} -o $TARGET -c $FORTRANCOMMONFLAGS ${dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $SOURCES'
+ env[f'SH{dialect}COM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $_{dialect}INCFLAGS $SOURCES'
+ env[f'SH{dialect}PPCOM'] = f'$SH{dialect} -o $TARGET -c $FORTRANCOMMONFLAGS $SH{dialect}FLAGS $CPPFLAGS $_CPPDEFFLAGS $_{dialect}INCFLAGS $SOURCES'
-def add_fortran_to_env(env):
- """Add Builders and construction variables for Fortran to an Environment."""
+
+def add_fortran_to_env(env) -> None:
+ """Add Builders and construction variables for Fortran/generic."""
try:
FortranSuffixes = env['FORTRANFILESUFFIXES']
except KeyError:
FortranSuffixes = ['.f', '.for', '.ftn']
- #print("Adding %s to fortran suffixes" % FortranSuffixes)
try:
FortranPPSuffixes = env['FORTRANPPFILESUFFIXES']
except KeyError:
FortranPPSuffixes = ['.fpp', '.FPP']
- DialectAddToEnv(env, "FORTRAN", FortranSuffixes,
- FortranPPSuffixes, support_module = 1)
+ DialectAddToEnv(env, "FORTRAN", FortranSuffixes, FortranPPSuffixes, support_mods=True)
+ # Module support
env['FORTRANMODPREFIX'] = '' # like $LIBPREFIX
env['FORTRANMODSUFFIX'] = '.mod' # like $LIBSUFFIX
-
env['FORTRANMODDIR'] = '' # where the compiler should place .mod files
env['FORTRANMODDIRPREFIX'] = '' # some prefix to $FORTRANMODDIR - similar to $INCPREFIX
env['FORTRANMODDIRSUFFIX'] = '' # some suffix to $FORTRANMODDIR - similar to $INCSUFFIX
env['_FORTRANMODFLAG'] = '$( ${_concat(FORTRANMODDIRPREFIX, FORTRANMODDIR, FORTRANMODDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
-def add_f77_to_env(env):
- """Add Builders and construction variables for f77 to an Environment."""
+def add_f77_to_env(env) -> None:
+ """Add Builders and construction variables for f77 dialect."""
try:
F77Suffixes = env['F77FILESUFFIXES']
except KeyError:
F77Suffixes = ['.f77']
- #print("Adding %s to f77 suffixes" % F77Suffixes)
try:
F77PPSuffixes = env['F77PPFILESUFFIXES']
except KeyError:
@@ -199,56 +218,50 @@ def add_f77_to_env(env):
DialectAddToEnv(env, "F77", F77Suffixes, F77PPSuffixes)
-def add_f90_to_env(env):
- """Add Builders and construction variables for f90 to an Environment."""
+def add_f90_to_env(env) -> None:
+ """Add Builders and construction variables for f90 dialect."""
try:
F90Suffixes = env['F90FILESUFFIXES']
except KeyError:
F90Suffixes = ['.f90']
- #print("Adding %s to f90 suffixes" % F90Suffixes)
try:
F90PPSuffixes = env['F90PPFILESUFFIXES']
except KeyError:
F90PPSuffixes = []
- DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes,
- support_module = 1)
+ DialectAddToEnv(env, "F90", F90Suffixes, F90PPSuffixes, support_mods=True)
-def add_f95_to_env(env):
- """Add Builders and construction variables for f95 to an Environment."""
+def add_f95_to_env(env) -> None:
+ """Add Builders and construction variables for f95 dialect."""
try:
F95Suffixes = env['F95FILESUFFIXES']
except KeyError:
F95Suffixes = ['.f95']
- #print("Adding %s to f95 suffixes" % F95Suffixes)
try:
F95PPSuffixes = env['F95PPFILESUFFIXES']
except KeyError:
F95PPSuffixes = []
- DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes,
- support_module = 1)
+ DialectAddToEnv(env, "F95", F95Suffixes, F95PPSuffixes, support_mods=True)
-def add_f03_to_env(env):
- """Add Builders and construction variables for f03 to an Environment."""
+def add_f03_to_env(env) -> None:
+ """Add Builders and construction variables for f03 dialect."""
try:
F03Suffixes = env['F03FILESUFFIXES']
except KeyError:
F03Suffixes = ['.f03']
- #print("Adding %s to f95 suffixes" % F95Suffixes)
try:
F03PPSuffixes = env['F03PPFILESUFFIXES']
except KeyError:
F03PPSuffixes = []
- DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes,
- support_module = 1)
+ DialectAddToEnv(env, "F03", F03Suffixes, F03PPSuffixes, support_mods=True)
-def add_f08_to_env(env):
- """Add Builders and construction variables for f08 to an Environment."""
+def add_f08_to_env(env) -> None:
+ """Add Builders and construction variables for f08 dialect."""
try:
F08Suffixes = env['F08FILESUFFIXES']
except KeyError:
@@ -259,12 +272,10 @@ def add_f08_to_env(env):
except KeyError:
F08PPSuffixes = []
- DialectAddToEnv(env, "F08", F08Suffixes, F08PPSuffixes,
- support_module = 1)
+ DialectAddToEnv(env, "F08", F08Suffixes, F08PPSuffixes, support_mods=True)
-def add_all_to_env(env):
- """Add builders and construction variables for all supported fortran
- dialects."""
+def add_all_to_env(env) -> None:
+ """Add builders and construction variables for all supported dialects."""
add_fortran_to_env(env)
add_f77_to_env(env)
add_f90_to_env(env)
diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py
new file mode 100644
index 0000000..015cf72
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Config.py
@@ -0,0 +1,331 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Constants and initialized data structures for Microsoft Visual C/C++.
+"""
+
+from collections import (
+ namedtuple,
+)
+
+from . import Util
+
+from .Exceptions import (
+ MSVCInternalError,
+)
+
+from . import Dispatcher
+Dispatcher.register_modulename(__name__)
+
+
+UNDEFINED = object()
+
+BOOLEAN_SYMBOLS = {}
+BOOLEAN_EXTERNAL = {}
+
+for bool_val, symbol_list, symbol_case_list in [
+ (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')),
+ (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')),
+]:
+ BOOLEAN_SYMBOLS[bool_val] = list(symbol_list)
+ for symbol in symbol_case_list:
+ BOOLEAN_SYMBOLS[bool_val].extend([symbol, symbol.lower(), symbol.upper()])
+
+ for symbol in BOOLEAN_SYMBOLS[bool_val]:
+ BOOLEAN_EXTERNAL[symbol] = bool_val
+
+MSVC_PLATFORM_DEFINITION = namedtuple('MSVCPlatform', [
+ 'vc_platform',
+ 'is_uwp',
+])
+
+MSVC_PLATFORM_DEFINITION_LIST = []
+
+MSVC_PLATFORM_INTERNAL = {}
+MSVC_PLATFORM_EXTERNAL = {}
+
+for vc_platform, is_uwp in [
+ ('Desktop', False),
+ ('UWP', True),
+]:
+
+ vc_platform_def = MSVC_PLATFORM_DEFINITION(
+ vc_platform = vc_platform,
+ is_uwp = is_uwp,
+ )
+
+ MSVC_PLATFORM_DEFINITION_LIST.append(vc_platform_def)
+
+ MSVC_PLATFORM_INTERNAL[vc_platform] = vc_platform_def
+
+ for symbol in [vc_platform, vc_platform.lower(), vc_platform.upper()]:
+ MSVC_PLATFORM_EXTERNAL[symbol] = vc_platform_def
+
+MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [
+ 'vc_runtime',
+ 'vc_runtime_numeric',
+ 'vc_runtime_alias_list',
+ 'vc_runtime_vsdef_list',
+])
+
+MSVC_RUNTIME_DEFINITION_LIST = []
+
+MSVC_RUNTIME_INTERNAL = {}
+MSVC_RUNTIME_EXTERNAL = {}
+
+for vc_runtime, vc_runtime_numeric, vc_runtime_alias_list in [
+ ('140', 140, ['ucrt']),
+ ('120', 120, ['msvcr120']),
+ ('110', 110, ['msvcr110']),
+ ('100', 100, ['msvcr100']),
+ ('90', 90, ['msvcr90']),
+ ('80', 80, ['msvcr80']),
+ ('71', 71, ['msvcr71']),
+ ('70', 70, ['msvcr70']),
+ ('60', 60, ['msvcrt']),
+]:
+ vc_runtime_def = MSVC_RUNTIME_DEFINITION(
+ vc_runtime = vc_runtime,
+ vc_runtime_numeric = vc_runtime_numeric,
+ vc_runtime_alias_list = vc_runtime_alias_list,
+ vc_runtime_vsdef_list = [],
+ )
+
+ MSVC_RUNTIME_DEFINITION_LIST.append(vc_runtime_def)
+
+ MSVC_RUNTIME_INTERNAL[vc_runtime] = vc_runtime_def
+ MSVC_RUNTIME_EXTERNAL[vc_runtime] = vc_runtime_def
+
+ for vc_runtime_alias in vc_runtime_alias_list:
+ MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def
+
+MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [
+ 'vc_buildtools',
+ 'vc_buildtools_numeric',
+ 'vc_version',
+ 'vc_version_numeric',
+ 'cl_version',
+ 'cl_version_numeric',
+ 'vc_runtime_def',
+ 'vc_istoolset',
+])
+
+MSVC_BUILDTOOLS_DEFINITION_LIST = []
+
+MSVC_BUILDTOOLS_INTERNAL = {}
+MSVC_BUILDTOOLS_EXTERNAL = {}
+
+VC_VERSION_MAP = {}
+
+for vc_buildtools, vc_version, cl_version, vc_runtime, vc_istoolset in [
+ ('v143', '14.3', '19.3', '140', True),
+ ('v142', '14.2', '19.2', '140', True),
+ ('v141', '14.1', '19.1', '140', True),
+ ('v140', '14.0', '19.0', '140', True),
+ ('v120', '12.0', '18.0', '120', False),
+ ('v110', '11.0', '17.0', '110', False),
+ ('v100', '10.0', '16.0', '100', False),
+ ('v90', '9.0', '15.0', '90', False),
+ ('v80', '8.0', '14.0', '80', False),
+ ('v71', '7.1', '13.1', '71', False),
+ ('v70', '7.0', '13.0', '70', False),
+ ('v60', '6.0', '12.0', '60', False),
+]:
+
+ vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime]
+
+ vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION(
+ vc_buildtools = vc_buildtools,
+ vc_buildtools_numeric = int(vc_buildtools[1:]),
+ vc_version = vc_version,
+ vc_version_numeric = float(vc_version),
+ cl_version = cl_version,
+ cl_version_numeric = float(cl_version),
+ vc_runtime_def = vc_runtime_def,
+ vc_istoolset = vc_istoolset,
+ )
+
+ MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def)
+
+ MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def
+ MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def
+ MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def
+
+ VC_VERSION_MAP[vc_version] = vc_buildtools_def
+
+MSVS_VERSION_INTERNAL = {}
+MSVS_VERSION_EXTERNAL = {}
+
+MSVC_VERSION_INTERNAL = {}
+MSVC_VERSION_EXTERNAL = {}
+MSVC_VERSION_SUFFIX = {}
+
+MSVS_VERSION_MAJOR_MAP = {}
+
+CL_VERSION_MAP = {}
+
+MSVC_SDK_VERSIONS = set()
+
+VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [
+ 'vs_product',
+ 'vs_product_alias_list',
+ 'vs_version',
+ 'vs_version_major',
+ 'vs_envvar',
+ 'vs_express',
+ 'vs_lookup',
+ 'vc_sdk_versions',
+ 'vc_ucrt_versions',
+ 'vc_uwp',
+ 'vc_buildtools_def',
+ 'vc_buildtools_all',
+])
+
+VISUALSTUDIO_DEFINITION_LIST = []
+
+VS_PRODUCT_ALIAS = {
+ '1998': ['6']
+}
+
+# vs_envvar: VisualStudioVersion defined in environment for MSVS 2012 and later
+# MSVS 2010 and earlier cl_version -> vs_def is a 1:1 mapping
+# SDK attached to product or buildtools?
+for vs_product, vs_version, vs_envvar, vs_express, vs_lookup, vc_sdk, vc_ucrt, vc_uwp, vc_buildtools_all in [
+ ('2022', '17.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v143', 'v142', 'v141', 'v140']),
+ ('2019', '16.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v142', 'v141', 'v140']),
+ ('2017', '15.0', True, True, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v141', 'v140']),
+ ('2015', '14.0', True, True, 'registry', ['10.0', '8.1'], ['10'], 'store', ['v140']),
+ ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']),
+ ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']),
+ ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']),
+ ('2008', '9.0', False, True, 'registry', None, None, None, ['v90']),
+ ('2005', '8.0', False, True, 'registry', None, None, None, ['v80']),
+ ('2003', '7.1', False, False, 'registry', None, None, None, ['v71']),
+ ('2002', '7.0', False, False, 'registry', None, None, None, ['v70']),
+ ('1998', '6.0', False, False, 'registry', None, None, None, ['v60']),
+]:
+
+ vs_version_major = vs_version.split('.')[0]
+
+ vc_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools_all[0]]
+
+ vs_def = VISUALSTUDIO_DEFINITION(
+ vs_product = vs_product,
+ vs_product_alias_list = [],
+ vs_version = vs_version,
+ vs_version_major = vs_version_major,
+ vs_envvar = vs_envvar,
+ vs_express = vs_express,
+ vs_lookup = vs_lookup,
+ vc_sdk_versions = vc_sdk,
+ vc_ucrt_versions = vc_ucrt,
+ vc_uwp = vc_uwp,
+ vc_buildtools_def = vc_buildtools_def,
+ vc_buildtools_all = vc_buildtools_all,
+ )
+
+ VISUALSTUDIO_DEFINITION_LIST.append(vs_def)
+
+ vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def)
+
+ vc_version = vc_buildtools_def.vc_version
+
+ MSVS_VERSION_INTERNAL[vs_product] = vs_def
+ MSVS_VERSION_EXTERNAL[vs_product] = vs_def
+ MSVS_VERSION_EXTERNAL[vs_version] = vs_def
+
+ MSVC_VERSION_INTERNAL[vc_version] = vs_def
+ MSVC_VERSION_EXTERNAL[vs_product] = vs_def
+ MSVC_VERSION_EXTERNAL[vc_version] = vs_def
+ MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def
+
+ if vs_product in VS_PRODUCT_ALIAS:
+ for vs_product_alias in VS_PRODUCT_ALIAS[vs_product]:
+ vs_def.vs_product_alias_list.append(vs_product_alias)
+ MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def
+ MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def
+
+ MSVC_VERSION_SUFFIX[vc_version] = vs_def
+ if vs_express:
+ MSVC_VERSION_SUFFIX[vc_version + 'Exp'] = vs_def
+
+ MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def
+
+ CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def
+
+ if vc_sdk:
+ MSVC_SDK_VERSIONS.update(vc_sdk)
+
+# EXPERIMENTAL: msvc version/toolset search lists
+#
+# VS2017 example:
+#
+# defaults['14.1'] = ['14.1', '14.1Exp']
+# defaults['14.1Exp'] = ['14.1Exp']
+#
+# search['14.1'] = ['14.3', '14.2', '14.1', '14.1Exp']
+# search['14.1Exp'] = ['14.1Exp']
+
+MSVC_VERSION_TOOLSET_DEFAULTS_MAP = {}
+MSVC_VERSION_TOOLSET_SEARCH_MAP = {}
+
+# Pass 1: Build defaults lists and setup express versions
+for vs_def in VISUALSTUDIO_DEFINITION_LIST:
+ if not vs_def.vc_buildtools_def.vc_istoolset:
+ continue
+ version_key = vs_def.vc_buildtools_def.vc_version
+ MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key] = [version_key]
+ MSVC_VERSION_TOOLSET_SEARCH_MAP[version_key] = []
+ if vs_def.vs_express:
+ express_key = version_key + 'Exp'
+ MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key].append(express_key)
+ MSVC_VERSION_TOOLSET_DEFAULTS_MAP[express_key] = [express_key]
+ MSVC_VERSION_TOOLSET_SEARCH_MAP[express_key] = [express_key]
+
+# Pass 2: Extend search lists (decreasing version order)
+for vs_def in VISUALSTUDIO_DEFINITION_LIST:
+ if not vs_def.vc_buildtools_def.vc_istoolset:
+ continue
+ version_key = vs_def.vc_buildtools_def.vc_version
+ for vc_buildtools in vs_def.vc_buildtools_all:
+ toolset_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools]
+ toolset_vs_def = MSVC_VERSION_INTERNAL[toolset_buildtools_def.vc_version]
+ buildtools_key = toolset_buildtools_def.vc_version
+ MSVC_VERSION_TOOLSET_SEARCH_MAP[buildtools_key].extend(MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key])
+
+# convert string version set to string version list ranked in descending order
+MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)]
+
+
+def verify():
+ from .. import vc
+ for msvc_version in vc._VCVER:
+ if msvc_version not in MSVC_VERSION_SUFFIX:
+ err_msg = 'msvc_version {} not in MSVC_VERSION_SUFFIX'.format(repr(msvc_version))
+ raise MSVCInternalError(err_msg)
+ vc_version = Util.get_msvc_version_prefix(msvc_version)
+ if vc_version not in MSVC_VERSION_INTERNAL:
+ err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version))
+ raise MSVCInternalError(err_msg)
+
diff --git a/SCons/Tool/MSCommon/MSVC/ConfigTests.py b/SCons/Tool/MSCommon/MSVC/ConfigTests.py
new file mode 100644
index 0000000..89db6cd
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/ConfigTests.py
@@ -0,0 +1,88 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test constants and initialized data structures for Microsoft Visual C/C++.
+"""
+
+import unittest
+
+from SCons.Tool.MSCommon import vc
+from SCons.Tool.MSCommon.MSVC import Config
+from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError
+
+
+class Patch:
+ class vc:
+ class _VCVER:
+ _VCVER = vc._VCVER
+
+ @classmethod
+ def enable_copy(cls):
+ hook = list(cls._VCVER)
+ vc._VCVER = hook
+ return hook
+
+ @classmethod
+ def restore(cls):
+ vc._VCVER = cls._VCVER
+
+ class Config:
+ class MSVC_VERSION_INTERNAL:
+ MSVC_VERSION_INTERNAL = Config.MSVC_VERSION_INTERNAL
+
+ @classmethod
+ def enable_copy(cls):
+ hook = dict(cls.MSVC_VERSION_INTERNAL)
+ Config.MSVC_VERSION_INTERNAL = hook
+ return hook
+
+ @classmethod
+ def restore(cls):
+ Config.MSVC_VERSION_INTERNAL = cls.MSVC_VERSION_INTERNAL
+
+
+class ConfigTests(unittest.TestCase):
+
+ def test_vcver(self):
+ # all vc._VCVER in Config.MSVC_VERSION_SUFFIX
+ _VCVER = Patch.vc._VCVER.enable_copy()
+ _VCVER.append('99.9')
+ with self.assertRaises(MSVCInternalError):
+ Config.verify()
+ Patch.vc._VCVER.restore()
+
+ def test_msvc_version_internal(self):
+ # all vc._VCVER numstr in Config.MSVC_VERSION_INTERNAL
+ MSVC_VERSION_INTERNAL = Patch.Config.MSVC_VERSION_INTERNAL.enable_copy()
+ del MSVC_VERSION_INTERNAL['14.3']
+ with self.assertRaises(MSVCInternalError):
+ Config.verify()
+ Patch.Config.MSVC_VERSION_INTERNAL.restore()
+
+ def test_verify(self):
+ Config.verify()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
new file mode 100644
index 0000000..42b5287
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py
@@ -0,0 +1,84 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Internal method dispatcher for Microsoft Visual C/C++.
+
+MSVC modules can register their module (register_modulename) and individual
+classes (register_class) with the method dispatcher during initialization. MSVC
+modules tend to be registered immediately after the Dispatcher import near the
+top of the file. Methods in the MSVC modules can be invoked indirectly without
+having to hard-code the method calls effectively decoupling the upstream module
+with the downstream modules:
+
+The reset method dispatches calls to all registered objects with a reset method
+and/or a _reset method. The reset methods are used to restore data structures
+to their initial state for testing purposes. Typically, this involves clearing
+cached values.
+
+The verify method dispatches calls to all registered objects with a verify
+method and/or a _verify method. The verify methods are used to check that
+initialized data structures distributed across multiple modules are internally
+consistent. An exception is raised when a verification constraint violation
+is detected. Typically, this verifies that initialized dictionaries support
+all of the requisite keys as new versions are added.
+"""
+
+import sys
+
+from ..common import (
+ debug,
+)
+
+_refs = []
+
+
+def register_modulename(modname):
+ module = sys.modules[modname]
+ _refs.append(module)
+
+
+def register_class(ref):
+ _refs.append(ref)
+
+
+def reset():
+ debug('')
+ for ref in _refs:
+ for method in ['reset', '_reset']:
+ if not hasattr(ref, method) or not callable(getattr(ref, method, None)):
+ continue
+ debug('call %s.%s()', ref.__name__, method)
+ func = getattr(ref, method)
+ func()
+
+
+def verify():
+ debug('')
+ for ref in _refs:
+ for method in ['verify', '_verify']:
+ if not hasattr(ref, method) or not callable(getattr(ref, method, None)):
+ continue
+ debug('call %s.%s()', ref.__name__, method)
+ func = getattr(ref, method)
+ func()
diff --git a/SCons/Tool/MSCommon/MSVC/DispatcherTests.py b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py
new file mode 100644
index 0000000..d6af8d4
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py
@@ -0,0 +1,119 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test internal method dispatcher for Microsoft Visual C/C++.
+"""
+
+import unittest
+
+from SCons.Tool.MSCommon import MSVC
+
+MSVC.Dispatcher.register_modulename(__name__)
+
+
+class Data:
+ reset_count = 0
+ verify_count = 0
+
+
+# current module - not callable
+_reset = None
+reset = None
+_verify = None
+verify = None
+
+
+class StaticMethods:
+ @staticmethod
+ def _reset():
+ Data.reset_count += 1
+
+ @staticmethod
+ def reset():
+ Data.reset_count += 1
+
+ @staticmethod
+ def _verify():
+ Data.verify_count += 1
+
+ @staticmethod
+ def verify():
+ Data.verify_count += 1
+
+
+class ClassMethods:
+ @classmethod
+ def _reset(cls):
+ Data.reset_count += 1
+
+ @classmethod
+ def reset(cls):
+ Data.reset_count += 1
+
+ @classmethod
+ def _verify(cls):
+ Data.verify_count += 1
+
+ @classmethod
+ def verify(cls):
+ Data.verify_count += 1
+
+
+class NotCallable:
+ _reset = None
+ reset = None
+
+ _verify = None
+ _verify = None
+
+
+MSVC.Dispatcher.register_class(StaticMethods)
+MSVC.Dispatcher.register_class(ClassMethods)
+MSVC.Dispatcher.register_class(NotCallable)
+
+
+class DispatcherTests(unittest.TestCase):
+ def test_dispatcher_reset(self):
+ MSVC.Dispatcher.reset()
+ self.assertTrue(Data.reset_count == 4, "MSVC.Dispatcher.reset() count failed")
+ Data.reset_count = 0
+
+ def test_dispatcher_verify(self):
+ MSVC.Dispatcher.verify()
+ self.assertTrue(Data.verify_count == 4, "MSVC.Dispatcher.verify() count failed")
+ Data.verify_count = 0
+
+ def test_msvc_reset(self):
+ MSVC._reset()
+ self.assertTrue(Data.reset_count == 4, "MSVC._reset() count failed")
+ Data.reset_count = 0
+
+ def test_msvc_verify(self):
+ MSVC._verify()
+ self.assertTrue(Data.verify_count == 4, "MSVC._verify() count failed")
+ Data.verify_count = 0
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py
new file mode 100644
index 0000000..7b24a2b
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py
@@ -0,0 +1,56 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Exceptions for Microsoft Visual C/C++.
+"""
+
+# reminder: add exceptions to MSCommon if necessary
+
+class VisualCException(Exception):
+ pass
+
+class MSVCInternalError(VisualCException):
+ pass
+
+class MSVCUserError(VisualCException):
+ pass
+
+class MSVCScriptExecutionError(VisualCException):
+ pass
+
+class MSVCVersionNotFound(MSVCUserError):
+ pass
+
+class MSVCSDKVersionNotFound(MSVCUserError):
+ pass
+
+class MSVCToolsetVersionNotFound(MSVCUserError):
+ pass
+
+class MSVCSpectreLibsNotFound(MSVCUserError):
+ pass
+
+class MSVCArgumentError(MSVCUserError):
+ pass
+
diff --git a/SCons/Tool/MSCommon/MSVC/Policy.py b/SCons/Tool/MSCommon/MSVC/Policy.py
new file mode 100644
index 0000000..fe8da31
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Policy.py
@@ -0,0 +1,301 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Microsoft Visual C/C++ policy handlers.
+
+Notes:
+ * The default msvc not found policy is that a warning is issued. This can be
+ changed globally via the function set_msvc_notfound_policy and/or through
+ the environment via the MSVC_NOTFOUND_POLICY construction variable.
+ * The default msvc script error policy is to suppress all msvc batch file
+ error messages. This can be changed globally via the function
+ set_msvc_scripterror_policy and/or through the environment via the
+ MSVC_SCRIPTERROR_POLICY construction variable.
+"""
+
+from collections import (
+ namedtuple,
+)
+
+import SCons.Warnings
+
+from ..common import (
+ debug,
+)
+
+from .Exceptions import (
+ MSVCArgumentError,
+ MSVCVersionNotFound,
+ MSVCScriptExecutionError,
+)
+
+from .Warnings import (
+ MSVCScriptExecutionWarning,
+)
+
+from . import Dispatcher
+Dispatcher.register_modulename(__name__)
+
+
+# MSVC_NOTFOUND_POLICY definition:
+# error: raise exception
+# warning: issue warning and continue
+# ignore: continue
+
+MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [
+ 'value',
+ 'symbol',
+])
+
+MSVC_NOTFOUND_DEFINITION_LIST = []
+
+MSVC_NOTFOUND_POLICY_INTERNAL = {}
+MSVC_NOTFOUND_POLICY_EXTERNAL = {}
+
+for policy_value, policy_symbol_list in [
+ (True, ['Error', 'Exception']),
+ (False, ['Warning', 'Warn']),
+ (None, ['Ignore', 'Suppress']),
+]:
+
+ policy_symbol = policy_symbol_list[0].lower()
+ policy_def = MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol)
+
+ MSVC_NOTFOUND_DEFINITION_LIST.append(policy_def)
+
+ MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def
+
+ for policy_symbol in policy_symbol_list:
+ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def
+ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def
+ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def
+
+# default definition
+_MSVC_NOTFOUND_POLICY_DEF = MSVC_NOTFOUND_POLICY_INTERNAL['warning']
+
+
+# MSVC_SCRIPTERROR_POLICY definition:
+# error: raise exception
+# warning: issue warning and continue
+# ignore: continue
+
+MSVC_SCRIPTERROR_POLICY_DEFINITION = namedtuple('MSVCBatchErrorPolicyDefinition', [
+ 'value',
+ 'symbol',
+])
+
+MSVC_SCRIPTERROR_DEFINITION_LIST = []
+
+MSVC_SCRIPTERROR_POLICY_INTERNAL = {}
+MSVC_SCRIPTERROR_POLICY_EXTERNAL = {}
+
+for policy_value, policy_symbol_list in [
+ (True, ['Error', 'Exception']),
+ (False, ['Warning', 'Warn']),
+ (None, ['Ignore', 'Suppress']),
+]:
+
+ policy_symbol = policy_symbol_list[0].lower()
+ policy_def = MSVC_SCRIPTERROR_POLICY_DEFINITION(policy_value, policy_symbol)
+
+ MSVC_SCRIPTERROR_DEFINITION_LIST.append(policy_def)
+
+ MSVC_SCRIPTERROR_POLICY_INTERNAL[policy_symbol] = policy_def
+
+ for policy_symbol in policy_symbol_list:
+ MSVC_SCRIPTERROR_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def
+ MSVC_SCRIPTERROR_POLICY_EXTERNAL[policy_symbol] = policy_def
+ MSVC_SCRIPTERROR_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def
+
+# default definition
+_MSVC_SCRIPTERROR_POLICY_DEF = MSVC_SCRIPTERROR_POLICY_INTERNAL['ignore']
+
+
+def _msvc_notfound_policy_lookup(symbol):
+
+ try:
+ notfound_policy_def = MSVC_NOTFOUND_POLICY_EXTERNAL[symbol]
+ except KeyError:
+ err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \
+ " Valid values are: {}".format(
+ repr(symbol),
+ ', '.join([repr(s) for s in MSVC_NOTFOUND_POLICY_EXTERNAL.keys()])
+ )
+ raise MSVCArgumentError(err_msg)
+
+ return notfound_policy_def
+
+def msvc_set_notfound_policy(MSVC_NOTFOUND_POLICY=None):
+ """ Set the default policy when MSVC is not found.
+
+ Args:
+ MSVC_NOTFOUND_POLICY:
+ string representing the policy behavior
+ when MSVC is not found or None
+
+ Returns:
+ The previous policy is returned when the MSVC_NOTFOUND_POLICY argument
+ is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY
+ argument is None.
+
+ """
+ global _MSVC_NOTFOUND_POLICY_DEF
+
+ prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol
+
+ policy = MSVC_NOTFOUND_POLICY
+ if policy is not None:
+ _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy)
+
+ debug(
+ 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
+ repr(prev_policy), repr(policy),
+ repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
+ )
+
+ return prev_policy
+
+def msvc_get_notfound_policy():
+ """Return the active policy when MSVC is not found."""
+ debug(
+ 'policy.symbol=%s, policy.value=%s',
+ repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value)
+ )
+ return _MSVC_NOTFOUND_POLICY_DEF.symbol
+
+def msvc_notfound_handler(env, msg):
+
+ if env and 'MSVC_NOTFOUND_POLICY' in env:
+ # environment setting
+ notfound_policy_src = 'environment'
+ policy = env['MSVC_NOTFOUND_POLICY']
+ if policy is not None:
+ # user policy request
+ notfound_policy_def = _msvc_notfound_policy_lookup(policy)
+ else:
+ # active global setting
+ notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF
+ else:
+ # active global setting
+ notfound_policy_src = 'default'
+ policy = None
+ notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF
+
+ debug(
+ 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
+ notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value)
+ )
+
+ if notfound_policy_def.value is None:
+ # ignore
+ pass
+ elif notfound_policy_def.value:
+ raise MSVCVersionNotFound(msg)
+ else:
+ SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
+
+
+def _msvc_scripterror_policy_lookup(symbol):
+
+ try:
+ scripterror_policy_def = MSVC_SCRIPTERROR_POLICY_EXTERNAL[symbol]
+ except KeyError:
+ err_msg = "Value specified for MSVC_SCRIPTERROR_POLICY is not supported: {}.\n" \
+ " Valid values are: {}".format(
+ repr(symbol),
+ ', '.join([repr(s) for s in MSVC_SCRIPTERROR_POLICY_EXTERNAL.keys()])
+ )
+ raise MSVCArgumentError(err_msg)
+
+ return scripterror_policy_def
+
+def msvc_set_scripterror_policy(MSVC_SCRIPTERROR_POLICY=None):
+ """ Set the default policy when msvc batch file execution errors are detected.
+
+ Args:
+ MSVC_SCRIPTERROR_POLICY:
+ string representing the policy behavior
+ when msvc batch file execution errors are detected or None
+
+ Returns:
+ The previous policy is returned when the MSVC_SCRIPTERROR_POLICY argument
+ is not None. The active policy is returned when the MSVC_SCRIPTERROR_POLICY
+ argument is None.
+
+ """
+ global _MSVC_SCRIPTERROR_POLICY_DEF
+
+ prev_policy = _MSVC_SCRIPTERROR_POLICY_DEF.symbol
+
+ policy = MSVC_SCRIPTERROR_POLICY
+ if policy is not None:
+ _MSVC_SCRIPTERROR_POLICY_DEF = _msvc_scripterror_policy_lookup(policy)
+
+ debug(
+ 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
+ repr(prev_policy), repr(policy),
+ repr(_MSVC_SCRIPTERROR_POLICY_DEF.symbol), repr(_MSVC_SCRIPTERROR_POLICY_DEF.value)
+ )
+
+ return prev_policy
+
+def msvc_get_scripterror_policy():
+ """Return the active policy when msvc batch file execution errors are detected."""
+ debug(
+ 'policy.symbol=%s, policy.value=%s',
+ repr(_MSVC_SCRIPTERROR_POLICY_DEF.symbol), repr(_MSVC_SCRIPTERROR_POLICY_DEF.value)
+ )
+ return _MSVC_SCRIPTERROR_POLICY_DEF.symbol
+
+def msvc_scripterror_handler(env, msg):
+
+ if env and 'MSVC_SCRIPTERROR_POLICY' in env:
+ # environment setting
+ scripterror_policy_src = 'environment'
+ policy = env['MSVC_SCRIPTERROR_POLICY']
+ if policy is not None:
+ # user policy request
+ scripterror_policy_def = _msvc_scripterror_policy_lookup(policy)
+ else:
+ # active global setting
+ scripterror_policy_def = _MSVC_SCRIPTERROR_POLICY_DEF
+ else:
+ # active global setting
+ scripterror_policy_src = 'default'
+ policy = None
+ scripterror_policy_def = _MSVC_SCRIPTERROR_POLICY_DEF
+
+ debug(
+ 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s',
+ scripterror_policy_src, repr(policy), repr(scripterror_policy_def.symbol), repr(scripterror_policy_def.value)
+ )
+
+ if scripterror_policy_def.value is None:
+ # ignore
+ pass
+ elif scripterror_policy_def.value:
+ raise MSVCScriptExecutionError(msg)
+ else:
+ SCons.Warnings.warn(MSVCScriptExecutionWarning, msg)
+
diff --git a/SCons/Tool/MSCommon/MSVC/PolicyTests.py b/SCons/Tool/MSCommon/MSVC/PolicyTests.py
new file mode 100644
index 0000000..013fd47
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/PolicyTests.py
@@ -0,0 +1,169 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test Microsoft Visual C/C++ policy handlers.
+"""
+
+import unittest
+
+import SCons.Warnings
+
+from SCons.Tool.MSCommon.MSVC import Policy
+
+from SCons.Tool.MSCommon.MSVC.Exceptions import (
+ MSVCArgumentError,
+ MSVCVersionNotFound,
+ MSVCScriptExecutionError,
+)
+
+from SCons.Tool.MSCommon.MSVC.Warnings import (
+ MSVCScriptExecutionWarning,
+)
+
+class PolicyTests(unittest.TestCase):
+
+ def setUp(self):
+ self.warnstack = []
+
+ def push_warning_as_exception(self, warning_class):
+ SCons.Warnings.enableWarningClass(warning_class)
+ prev_state = SCons.Warnings.warningAsException()
+ self.warnstack.append((warning_class, prev_state))
+
+ def pop_warning_as_exception(self):
+ warning_class, prev_state = self.warnstack.pop()
+ SCons.Warnings.warningAsException(prev_state)
+ SCons.Warnings.suppressWarningClass(warning_class)
+
+ # msvc_set_notfound_policy, msvc_get_notfound_policy, and MSVC_NOTFOUND_POLICY
+
+ def test_notfound_func_valid_symbols(self):
+ def_policy = Policy.msvc_get_notfound_policy()
+ last_policy = def_policy
+ for notfound_def in Policy.MSVC_NOTFOUND_DEFINITION_LIST:
+ for symbol in [notfound_def.symbol, notfound_def.symbol.lower(), notfound_def.symbol.upper()]:
+ prev_policy = Policy.msvc_set_notfound_policy(symbol)
+ self.assertTrue(prev_policy == last_policy, "notfound policy: {} != {}".format(
+ repr(prev_policy), repr(last_policy)
+ ))
+ cur_set_policy = Policy.msvc_set_notfound_policy()
+ cur_get_policy = Policy.msvc_get_notfound_policy()
+ self.assertTrue(cur_set_policy == cur_get_policy, "notfound policy: {} != {}".format(
+ repr(cur_set_policy), repr(cur_get_policy)
+ ))
+ last_policy = cur_get_policy
+ Policy.msvc_set_notfound_policy(def_policy)
+
+ def test_notfound_func_invalid_symbol(self):
+ with self.assertRaises(MSVCArgumentError):
+ Policy.msvc_set_notfound_policy('Undefined')
+
+ def test_notfound_handler_invalid_symbol(self):
+ with self.assertRaises(MSVCArgumentError):
+ Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': 'Undefined'}, '')
+
+ def test_notfound_handler_ignore(self):
+ def_policy = Policy.msvc_set_notfound_policy('Ignore')
+ Policy.msvc_notfound_handler(None, '')
+ Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': None}, '')
+ Policy.msvc_set_notfound_policy(def_policy)
+
+ def test_notfound_handler_warning(self):
+ # treat warning as exception for testing
+ self.push_warning_as_exception(SCons.Warnings.VisualCMissingWarning)
+ def_policy = Policy.msvc_set_notfound_policy('Warning')
+ with self.assertRaises(SCons.Warnings.VisualCMissingWarning):
+ Policy.msvc_notfound_handler(None, '')
+ Policy.msvc_set_notfound_policy('Ignore')
+ with self.assertRaises(SCons.Warnings.VisualCMissingWarning):
+ Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': 'Warning'}, '')
+ Policy.msvc_set_notfound_policy(def_policy)
+ self.pop_warning_as_exception()
+
+ def test_notfound_handler_error(self):
+ def_policy = Policy.msvc_set_notfound_policy('Error')
+ with self.assertRaises(MSVCVersionNotFound):
+ Policy.msvc_notfound_handler(None, '')
+ Policy.msvc_set_notfound_policy('Ignore')
+ with self.assertRaises(MSVCVersionNotFound):
+ Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': 'Error'}, '')
+ Policy.msvc_set_notfound_policy(def_policy)
+
+ # msvc_set_scripterror_policy, msvc_get_scripterror_policy, and MSVC_SCRIPTERROR_POLICY
+
+ def test_scripterror_func_valid_symbols(self):
+ def_policy = Policy.msvc_get_scripterror_policy()
+ last_policy = def_policy
+ for scripterror_def in Policy.MSVC_SCRIPTERROR_DEFINITION_LIST:
+ for symbol in [scripterror_def.symbol, scripterror_def.symbol.lower(), scripterror_def.symbol.upper()]:
+ prev_policy = Policy.msvc_set_scripterror_policy(symbol)
+ self.assertTrue(prev_policy == last_policy, "scripterror policy: {} != {}".format(
+ repr(prev_policy), repr(last_policy)
+ ))
+ cur_set_policy = Policy.msvc_set_scripterror_policy()
+ cur_get_policy = Policy.msvc_get_scripterror_policy()
+ self.assertTrue(cur_set_policy == cur_get_policy, "scripterror policy: {} != {}".format(
+ repr(cur_set_policy), repr(cur_get_policy)
+ ))
+ last_policy = cur_get_policy
+ Policy.msvc_set_scripterror_policy(def_policy)
+
+ def test_scripterror_func_invalid_symbol(self):
+ with self.assertRaises(MSVCArgumentError):
+ Policy.msvc_set_scripterror_policy('Undefined')
+
+ def test_scripterror_handler_invalid_symbol(self):
+ with self.assertRaises(MSVCArgumentError):
+ Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': 'Undefined'}, '')
+
+ def test_scripterror_handler_ignore(self):
+ def_policy = Policy.msvc_set_scripterror_policy('Ignore')
+ Policy.msvc_scripterror_handler(None, '')
+ Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': None}, '')
+ Policy.msvc_set_scripterror_policy(def_policy)
+
+ def test_scripterror_handler_warning(self):
+ # treat warning as exception for testing
+ self.push_warning_as_exception(MSVCScriptExecutionWarning)
+ def_policy = Policy.msvc_set_scripterror_policy('Warning')
+ with self.assertRaises(MSVCScriptExecutionWarning):
+ Policy.msvc_scripterror_handler(None, '')
+ Policy.msvc_set_scripterror_policy('Ignore')
+ with self.assertRaises(MSVCScriptExecutionWarning):
+ Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': 'Warning'}, '')
+ Policy.msvc_set_scripterror_policy(def_policy)
+ self.pop_warning_as_exception()
+
+ def test_scripterror_handler_error(self):
+ def_policy = Policy.msvc_set_scripterror_policy('Error')
+ with self.assertRaises(MSVCScriptExecutionError):
+ Policy.msvc_scripterror_handler(None, '')
+ Policy.msvc_set_scripterror_policy('Ignore')
+ with self.assertRaises(MSVCScriptExecutionError):
+ Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': 'Error'}, '')
+ Policy.msvc_set_scripterror_policy(def_policy)
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py
new file mode 100644
index 0000000..9519e15
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Registry.py
@@ -0,0 +1,118 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Windows registry functions for Microsoft Visual C/C++.
+"""
+
+import os
+
+from SCons.Util import (
+ HKEY_LOCAL_MACHINE,
+ HKEY_CURRENT_USER,
+ RegGetValue,
+)
+
+from .. common import (
+ debug,
+)
+
+from . import Util
+
+from . import Dispatcher
+Dispatcher.register_modulename(__name__)
+
+
+# A null-terminated string that contains unexpanded references to environment variables.
+REG_EXPAND_SZ = 2
+
+def read_value(hkey, subkey_valname, expand=True):
+ try:
+ rval_t = RegGetValue(hkey, subkey_valname)
+ except OSError:
+ debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname))
+ return None
+ rval, regtype = rval_t
+ if regtype == REG_EXPAND_SZ and expand:
+ rval = os.path.expandvars(rval)
+ debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval))
+ return rval
+
+def registry_query_path(key, val, suffix, expand=True):
+ extval = val + '\\' + suffix if suffix else val
+ qpath = read_value(key, extval, expand=expand)
+ if qpath and os.path.exists(qpath):
+ qpath = Util.process_path(qpath)
+ else:
+ qpath = None
+ return (qpath, key, val, extval)
+
+REG_SOFTWARE_MICROSOFT = [
+ (HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'),
+ (HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries
+ (HKEY_LOCAL_MACHINE, r'Software\Microsoft'),
+ (HKEY_CURRENT_USER, r'Software\Microsoft'),
+]
+
+def microsoft_query_paths(suffix, usrval=None, expand=True):
+ paths = []
+ records = []
+ for key, val in REG_SOFTWARE_MICROSOFT:
+ extval = val + '\\' + suffix if suffix else val
+ qpath = read_value(key, extval, expand=expand)
+ if qpath and os.path.exists(qpath):
+ qpath = Util.process_path(qpath)
+ if qpath not in paths:
+ paths.append(qpath)
+ records.append((qpath, key, val, extval, usrval))
+ return records
+
+def microsoft_query_keys(suffix, usrval=None, expand=True):
+ records = []
+ for key, val in REG_SOFTWARE_MICROSOFT:
+ extval = val + '\\' + suffix if suffix else val
+ rval = read_value(key, extval, expand=expand)
+ if rval:
+ records.append((rval, key, val, extval, usrval))
+ return records
+
+def microsoft_sdks(version):
+ return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder'])
+
+def sdk_query_paths(version):
+ q = microsoft_sdks(version)
+ return microsoft_query_paths(q)
+
+def windows_kits(version):
+ return r'Windows Kits\Installed Roots\KitsRoot' + version
+
+def windows_kit_query_paths(version):
+ q = windows_kits(version)
+ return microsoft_query_paths(q)
+
+def vstudio_sxs_vc7(version):
+ return '\\'.join([r'VisualStudio\SxS\VC7', version])
+
+def devdiv_vs_servicing_component(version, component):
+ return '\\'.join([r'DevDiv\VS\Servicing', version, component, 'Install'])
+
diff --git a/SCons/Tool/MSCommon/MSVC/RegistryTests.py b/SCons/Tool/MSCommon/MSVC/RegistryTests.py
new file mode 100644
index 0000000..aff3b3f
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/RegistryTests.py
@@ -0,0 +1,83 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test windows registry functions for Microsoft Visual C/C++.
+"""
+
+import unittest
+import sys
+
+from SCons.Tool.MSCommon.MSVC import Config
+from SCons.Tool.MSCommon.MSVC import Registry
+
+@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
+class RegistryTests(unittest.TestCase):
+
+ _sdk_versions = None
+
+ @classmethod
+ def setUpClass(cls):
+ cls._sdk_versions = []
+ sdk_seen = set()
+ for vs_def in Config.VISUALSTUDIO_DEFINITION_LIST:
+ if not vs_def.vc_sdk_versions:
+ continue
+ for sdk_version in vs_def.vc_sdk_versions:
+ if sdk_version in sdk_seen:
+ continue
+ sdk_seen.add(sdk_version)
+ cls._sdk_versions.append(sdk_version)
+
+ def setUp(self):
+ self.sdk_versions = self.__class__._sdk_versions
+
+ def test_sdk_query_paths(self):
+ for sdk_version in self.sdk_versions:
+ _ = Registry.sdk_query_paths(sdk_version)
+
+ def test_vstudio_sxs_vc7(self):
+ suffix = Registry.vstudio_sxs_vc7('14.0')
+ _ = Registry.microsoft_query_paths(suffix)
+
+ def test_microsoft_query_keys(self):
+ val = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
+ for suffix in ['Temp', 'Tmp']:
+ _ = Registry.registry_query_path(Registry.HKEY_LOCAL_MACHINE, val, suffix, expand=True)
+ _ = Registry.registry_query_path(Registry.HKEY_LOCAL_MACHINE, val, suffix, expand=False)
+
+ def test_registry_query_path(self):
+ # need a better test for when VS2015 is no longer installed
+ for component_reg in ('enterprise', 'professional', 'community'):
+ suffix = Registry.devdiv_vs_servicing_component('14.0', component_reg)
+ rval = Registry.microsoft_query_keys(suffix, component_reg)
+ if not rval:
+ continue
+
+ def test_windows_kit_query_paths(self):
+ for sdk_version in self.sdk_versions:
+ _ = Registry.windows_kit_query_paths(sdk_version)
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
new file mode 100644
index 0000000..57dbf9d
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py
@@ -0,0 +1,1031 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Batch file argument functions for Microsoft Visual C/C++.
+"""
+
+import os
+import re
+import enum
+
+from collections import (
+ namedtuple,
+)
+
+from ..common import (
+ CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS,
+ debug,
+)
+
+from . import Util
+from . import Config
+from . import Registry
+from . import WinSDK
+
+from .Exceptions import (
+ MSVCInternalError,
+ MSVCSDKVersionNotFound,
+ MSVCToolsetVersionNotFound,
+ MSVCSpectreLibsNotFound,
+ MSVCArgumentError,
+)
+
+from . import Dispatcher
+Dispatcher.register_modulename(__name__)
+
+
+# Script argument: boolean True
+_ARGUMENT_BOOLEAN_TRUE_LEGACY = (True, '1') # MSVC_UWP_APP
+_ARGUMENT_BOOLEAN_TRUE = (True,)
+
+# TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
+re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
+re_sdk_version_81 = re.compile(r'^8[.]1$')
+
+re_sdk_dispatch_map = {
+ '10.0': re_sdk_version_100,
+ '8.1': re_sdk_version_81,
+}
+
+def _verify_re_sdk_dispatch_map():
+ debug('')
+ for sdk_version in Config.MSVC_SDK_VERSIONS:
+ if sdk_version in re_sdk_dispatch_map:
+ continue
+ err_msg = 'sdk version {} not in re_sdk_dispatch_map'.format(sdk_version)
+ raise MSVCInternalError(err_msg)
+ return None
+
+# SxS version bugfix
+_msvc_sxs_bugfix_map = {}
+_msvc_sxs_bugfix_folder = {}
+_msvc_sxs_bugfix_version = {}
+
+for msvc_version, sxs_version, sxs_bugfix in [
+ # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
+ # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
+ # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
+ ('14.2', '14.28.16.8', '14.28')
+]:
+ _msvc_sxs_bugfix_map.setdefault(msvc_version, []).append((sxs_version, sxs_bugfix))
+ _msvc_sxs_bugfix_folder[(msvc_version, sxs_bugfix)] = sxs_version
+ _msvc_sxs_bugfix_version[(msvc_version, sxs_version)] = sxs_bugfix
+
+# MSVC_SCRIPT_ARGS
+re_vcvars_uwp = re.compile(r'(?:(?<!\S)|^)(?P<uwp>(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
+re_vcvars_sdk = re.compile(r'(?:(?<!\S)|^)(?P<sdk>(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
+re_vcvars_toolset = re.compile(r'(?:(?<!\S)|^)(?P<toolset_arg>(?:[-]{1,2}|[/])vcvars_ver[=](?P<toolset>\S*))(?:(?!\S)|$)', re.IGNORECASE)
+re_vcvars_spectre = re.compile(r'(?:(?<!\S)|^)(?P<spectre_arg>(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P<spectre>\S*))(?:(?!\S)|$)',re.IGNORECASE)
+
+# Force default sdk argument
+_MSVC_FORCE_DEFAULT_SDK = False
+
+# Force default toolset argument
+_MSVC_FORCE_DEFAULT_TOOLSET = False
+
+# Force default arguments
+_MSVC_FORCE_DEFAULT_ARGUMENTS = False
+
+def _msvc_force_default_sdk(force=True):
+ global _MSVC_FORCE_DEFAULT_SDK
+ _MSVC_FORCE_DEFAULT_SDK = force
+ debug('_MSVC_FORCE_DEFAULT_SDK=%s', repr(force))
+
+def _msvc_force_default_toolset(force=True):
+ global _MSVC_FORCE_DEFAULT_TOOLSET
+ _MSVC_FORCE_DEFAULT_TOOLSET = force
+ debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force))
+
+def msvc_force_default_arguments(force=None):
+ global _MSVC_FORCE_DEFAULT_ARGUMENTS
+ prev_policy = _MSVC_FORCE_DEFAULT_ARGUMENTS
+ if force is not None:
+ _MSVC_FORCE_DEFAULT_ARGUMENTS = force
+ _msvc_force_default_sdk(force)
+ _msvc_force_default_toolset(force)
+ return prev_policy
+
+if CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS:
+ msvc_force_default_arguments(force=True)
+
+# UWP SDK 8.1 and SDK 10:
+#
+# https://stackoverflow.com/questions/46659238/build-windows-app-compatible-for-8-1-and-10
+# VS2019 - UWP (Except for Win10Mobile)
+# VS2017 - UWP
+# VS2015 - UWP, Win8.1 StoreApp, WP8/8.1 StoreApp
+# VS2013 - Win8/8.1 StoreApp, WP8/8.1 StoreApp
+
+# SPECTRE LIBS (msvc documentation):
+# "There are no versions of Spectre-mitigated libraries for Universal Windows (UWP) apps or
+# components. App-local deployment of such libraries isn't possible."
+
+# MSVC batch file arguments:
+#
+# VS2022: UWP, SDK, TOOLSET, SPECTRE
+# VS2019: UWP, SDK, TOOLSET, SPECTRE
+# VS2017: UWP, SDK, TOOLSET, SPECTRE
+# VS2015: UWP, SDK
+#
+# MSVC_SCRIPT_ARGS: VS2015+
+#
+# MSVC_UWP_APP: VS2015+
+# MSVC_SDK_VERSION: VS2015+
+# MSVC_TOOLSET_VERSION: VS2017+
+# MSVC_SPECTRE_LIBS: VS2017+
+
+@enum.unique
+class SortOrder(enum.IntEnum):
+ UWP = 1 # MSVC_UWP_APP
+ SDK = 2 # MSVC_SDK_VERSION
+ TOOLSET = 3 # MSVC_TOOLSET_VERSION
+ SPECTRE = 4 # MSVC_SPECTRE_LIBS
+ USER = 5 # MSVC_SCRIPT_ARGS
+
+VS2019 = Config.MSVS_VERSION_INTERNAL['2019']
+VS2017 = Config.MSVS_VERSION_INTERNAL['2017']
+VS2015 = Config.MSVS_VERSION_INTERNAL['2015']
+
+MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
+ 'version', # full version (e.g., '14.1Exp', '14.32.31326')
+ 'vs_def',
+])
+
+def _msvc_version(version):
+
+ verstr = Util.get_msvc_version_prefix(version)
+ vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
+
+ version_args = MSVC_VERSION_ARGS_DEFINITION(
+ version = version,
+ vs_def = vs_def,
+ )
+
+ return version_args
+
+def _toolset_version(version):
+
+ verstr = Util.get_msvc_version_prefix(version)
+ vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
+
+ version_args = MSVC_VERSION_ARGS_DEFINITION(
+ version = version,
+ vs_def = vs_def,
+ )
+
+ return version_args
+
+def _msvc_script_argument_uwp(env, msvc, arglist):
+
+ uwp_app = env['MSVC_UWP_APP']
+ debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
+
+ if not uwp_app:
+ return None
+
+ if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY:
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ # VS2017+ rewrites uwp => store for 14.0 toolset
+ uwp_arg = msvc.vs_def.vc_uwp
+
+ # store/uwp may not be fully installed
+ argpair = (SortOrder.UWP, uwp_arg)
+ arglist.append(argpair)
+
+ return uwp_arg
+
+def _user_script_argument_uwp(env, uwp, user_argstr):
+
+ matches = [m for m in re_vcvars_uwp.finditer(user_argstr)]
+ if not matches:
+ return False
+
+ if len(matches) > 1:
+ debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not uwp:
+ return True
+
+ env_argstr = env.get('MSVC_UWP_APP','')
+ debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def _msvc_script_argument_sdk_constraints(msvc, sdk_version):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc_version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ for msvc_sdk_version in msvc.vs_def.vc_sdk_versions:
+ re_sdk_version = re_sdk_dispatch_map[msvc_sdk_version]
+ if re_sdk_version.match(sdk_version):
+ debug('valid: sdk_version=%s', repr(sdk_version))
+ return None
+
+ debug('invalid: method exit: sdk_version=%s', repr(sdk_version))
+ err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version))
+ return err_msg
+
+def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def):
+
+ if sdk_version == '8.1' and platform_def.is_uwp:
+
+ vs_def = toolset.vs_def if toolset else msvc.vs_def
+
+ if vs_def.vc_buildtools_def.vc_version_numeric > VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015',
+ repr(vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ if toolset and toolset.vs_def != msvc.vs_def:
+ err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset version {} > {} VS2015".format(
+ repr(sdk_version), repr(platform_def.vc_platform),
+ repr(toolset.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ else:
+ err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format(
+ repr(sdk_version), repr(platform_def.vc_platform),
+ repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ return None
+
+def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist):
+
+ sdk_version = env['MSVC_SDK_VERSION']
+ debug(
+ 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s',
+ repr(msvc.version), repr(sdk_version), repr(platform_def.vc_platform)
+ )
+
+ if not sdk_version:
+ return None
+
+ err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
+
+ if sdk_version not in sdk_list:
+ err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format(
+ repr(sdk_version), repr(platform_def.vc_platform)
+ )
+ raise MSVCSDKVersionNotFound(err_msg)
+
+ err_msg = _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ argpair = (SortOrder.SDK, sdk_version)
+ arglist.append(argpair)
+
+ return sdk_version
+
+def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk=False):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ return None
+
+ sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
+ if not len(sdk_list):
+ return None
+
+ sdk_default = sdk_list[0]
+
+ debug(
+ 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s',
+ repr(msvc.version), repr(sdk_default), repr(platform_def.vc_platform)
+ )
+
+ if force_sdk:
+ argpair = (SortOrder.SDK, sdk_default)
+ arglist.append(argpair)
+
+ return sdk_default
+
+def _user_script_argument_sdk(env, sdk_version, user_argstr):
+
+ matches = [m for m in re_vcvars_sdk.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not sdk_version:
+ user_sdk = matches[0].group('sdk')
+ return user_sdk
+
+ env_argstr = env.get('MSVC_SDK_VERSION','')
+ debug('multiple sdk version declarations: MSVC_SDK_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+_toolset_have140_cache = None
+
+def _msvc_have140_toolset():
+ global _toolset_have140_cache
+
+ if _toolset_have140_cache is None:
+ suffix = Registry.vstudio_sxs_vc7('14.0')
+ vcinstalldirs = [record[0] for record in Registry.microsoft_query_paths(suffix)]
+ debug('vc140 toolset: paths=%s', repr(vcinstalldirs))
+ _toolset_have140_cache = True if vcinstalldirs else False
+
+ return _toolset_have140_cache
+
+def _reset_have140_cache():
+ global _toolset_have140_cache
+ debug('reset: cache')
+ _toolset_have140_cache = None
+
+def _msvc_read_toolset_file(msvc, filename):
+ toolset_version = None
+ try:
+ with open(filename) as f:
+ toolset_version = f.readlines()[0].strip()
+ debug(
+ 'msvc_version=%s, filename=%s, toolset_version=%s',
+ repr(msvc.version), repr(filename), repr(toolset_version)
+ )
+ except OSError:
+ debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ except IndexError:
+ debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
+ return toolset_version
+
+def _msvc_sxs_toolset_folder(msvc, sxs_folder):
+
+ if Util.is_toolset_sxs(sxs_folder):
+ return sxs_folder, sxs_folder
+
+ key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder)
+ if key in _msvc_sxs_bugfix_folder:
+ sxs_version = _msvc_sxs_bugfix_folder[key]
+ return sxs_folder, sxs_version
+
+ debug('sxs folder: ignore version=%s', repr(sxs_folder))
+ return None, None
+
+def _msvc_read_toolset_folders(msvc, vc_dir):
+
+ toolsets_sxs = {}
+ toolsets_full = []
+
+ build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+ if os.path.exists(build_dir):
+ for sxs_folder, sxs_path in Util.listdir_dirs(build_dir):
+ sxs_folder, sxs_version = _msvc_sxs_toolset_folder(msvc, sxs_folder)
+ if not sxs_version:
+ continue
+ filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_folder)
+ filepath = os.path.join(sxs_path, filename)
+ debug('sxs toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_version = _msvc_read_toolset_file(msvc, filepath)
+ if not toolset_version:
+ continue
+ toolsets_sxs[sxs_version] = toolset_version
+ debug(
+ 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(sxs_version), repr(toolset_version)
+ )
+
+ toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
+ if os.path.exists(toolset_dir):
+ for toolset_version, toolset_path in Util.listdir_dirs(toolset_dir):
+ binpath = os.path.join(toolset_path, "bin")
+ debug('toolset: check binpath=%s', repr(binpath))
+ if os.path.exists(binpath):
+ toolsets_full.append(toolset_version)
+ debug(
+ 'toolset: msvc_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(toolset_version)
+ )
+
+ vcvars140 = os.path.join(vc_dir, "..", "Common7", "Tools", "vsdevcmd", "ext", "vcvars", "vcvars140.bat")
+ if os.path.exists(vcvars140) and _msvc_have140_toolset():
+ toolset_version = '14.0'
+ toolsets_full.append(toolset_version)
+ debug(
+ 'toolset: msvc_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(toolset_version)
+ )
+
+ toolsets_full.sort(reverse=True)
+
+ # SxS bugfix fixup (if necessary)
+ if msvc.version in _msvc_sxs_bugfix_map:
+ for sxs_version, sxs_bugfix in _msvc_sxs_bugfix_map[msvc.version]:
+ if sxs_version in toolsets_sxs:
+ # have SxS version (folder/file mapping exists)
+ continue
+ for toolset_version in toolsets_full:
+ if not toolset_version.startswith(sxs_bugfix):
+ continue
+ debug(
+ 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
+ repr(msvc.version), repr(sxs_version), repr(toolset_version)
+ )
+ # SxS compatible bugfix version (equivalent to toolset search)
+ toolsets_sxs[sxs_version] = toolset_version
+ break
+
+ debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
+
+ return toolsets_sxs, toolsets_full
+
+def _msvc_read_toolset_default(msvc, vc_dir):
+
+ build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
+
+ # VS2019+
+ filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools)
+ filepath = os.path.join(build_dir, filename)
+
+ debug('default toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_buildtools = _msvc_read_toolset_file(msvc, filepath)
+ if toolset_buildtools:
+ return toolset_buildtools
+
+ # VS2017+
+ filename = "Microsoft.VCToolsVersion.default.txt"
+ filepath = os.path.join(build_dir, filename)
+
+ debug('default toolset: check file=%s', repr(filepath))
+ if os.path.exists(filepath):
+ toolset_default = _msvc_read_toolset_file(msvc, filepath)
+ if toolset_default:
+ return toolset_default
+
+ return None
+
+_toolset_version_cache = {}
+_toolset_default_cache = {}
+
+def _reset_toolset_cache():
+ global _toolset_version_cache
+ global _toolset_default_cache
+ debug('reset: toolset cache')
+ _toolset_version_cache = {}
+ _toolset_default_cache = {}
+
+def _msvc_version_toolsets(msvc, vc_dir):
+
+ if msvc.version in _toolset_version_cache:
+ toolsets_sxs, toolsets_full = _toolset_version_cache[msvc.version]
+ else:
+ toolsets_sxs, toolsets_full = _msvc_read_toolset_folders(msvc, vc_dir)
+ _toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full
+
+ return toolsets_sxs, toolsets_full
+
+def _msvc_default_toolset(msvc, vc_dir):
+
+ if msvc.version in _toolset_default_cache:
+ toolset_default = _toolset_default_cache[msvc.version]
+ else:
+ toolset_default = _msvc_read_toolset_default(msvc, vc_dir)
+ _toolset_default_cache[msvc.version] = toolset_default
+
+ return toolset_default
+
+def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
+
+ toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
+
+ if toolset_version in toolsets_full:
+ # full toolset version provided
+ toolset_vcvars = toolset_version
+ return toolset_vcvars
+
+ if Util.is_toolset_sxs(toolset_version):
+ # SxS version provided
+ sxs_version = toolsets_sxs.get(toolset_version, None)
+ if sxs_version and sxs_version in toolsets_full:
+ # SxS full toolset version
+ toolset_vcvars = sxs_version
+ return toolset_vcvars
+ return None
+
+ for toolset_full in toolsets_full:
+ if toolset_full.startswith(toolset_version):
+ toolset_vcvars = toolset_full
+ return toolset_vcvars
+
+ return None
+
+def _msvc_script_argument_toolset_constraints(msvc, toolset_version):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2017',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
+ repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ toolset_verstr = Util.get_msvc_version_prefix(toolset_version)
+
+ if not toolset_verstr:
+ debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
+ err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
+ repr(toolset_version)
+ )
+ return err_msg
+
+ toolset_vernum = float(toolset_verstr)
+
+ if toolset_vernum < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: %s < %s VS2015',
+ repr(toolset_vernum), repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format(
+ repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: toolset %s > %s msvc',
+ repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format(
+ repr(toolset_version), repr(toolset_verstr), repr(msvc.version)
+ )
+ return err_msg
+
+ if toolset_vernum == VS2015.vc_buildtools_def.vc_version_numeric:
+ # tooset = 14.0
+ if Util.is_toolset_full(toolset_version):
+ if not Util.is_toolset_140(toolset_version):
+ debug(
+ 'invalid: toolset version 14.0 constraint: %s != 14.0',
+ repr(toolset_version)
+ )
+ err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format(
+ repr(toolset_version), repr(toolset_version)
+ )
+ return err_msg
+ return None
+
+ if Util.is_toolset_full(toolset_version):
+ debug('valid: toolset full: toolset_version=%s', repr(toolset_version))
+ return None
+
+ if Util.is_toolset_sxs(toolset_version):
+ debug('valid: toolset sxs: toolset_version=%s', repr(toolset_version))
+ return None
+
+ debug('invalid: method exit: toolset_version=%s', repr(toolset_version))
+ err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version))
+ return err_msg
+
+def _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir):
+
+ err_msg = _msvc_script_argument_toolset_constraints(msvc, toolset_version)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'):
+ new_toolset_version = '14.0'
+ debug(
+ 'rewrite toolset_version=%s => toolset_version=%s',
+ repr(toolset_version), repr(new_toolset_version)
+ )
+ toolset_version = new_toolset_version
+
+ toolset_vcvars = _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version)
+ debug(
+ 'toolset: toolset_version=%s, toolset_vcvars=%s',
+ repr(toolset_version), repr(toolset_vcvars)
+ )
+
+ if not toolset_vcvars:
+ err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
+ repr(toolset_version), repr(msvc.version)
+ )
+ raise MSVCToolsetVersionNotFound(err_msg)
+
+ return toolset_vcvars
+
+def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist):
+
+ toolset_version = env['MSVC_TOOLSET_VERSION']
+ debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version))
+
+ if not toolset_version:
+ return None
+
+ toolset_vcvars = _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir)
+
+ # toolset may not be installed for host/target
+ argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars))
+ arglist.append(argpair)
+
+ return toolset_vcvars
+
+def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset=False):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ return None
+
+ toolset_default = _msvc_default_toolset(msvc, vc_dir)
+ if not toolset_default:
+ return None
+
+ debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default))
+
+ if force_toolset:
+ argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default))
+ arglist.append(argpair)
+
+ return toolset_default
+
+def _user_script_argument_toolset(env, toolset_version, user_argstr):
+
+ matches = [m for m in re_vcvars_toolset.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple toolset version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not toolset_version:
+ user_toolset = matches[0].group('toolset')
+ return user_toolset
+
+ env_argstr = env.get('MSVC_TOOLSET_VERSION','')
+ debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def):
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2017',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
+ repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+ if toolset:
+ if toolset.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: toolset version constraint: %s < %s VS2017',
+ repr(toolset.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2017.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format(
+ repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.vc_version)
+ )
+ return err_msg
+
+
+ if platform_def.is_uwp:
+ debug(
+ 'invalid: spectre_libs=%s and platform_type=%s',
+ repr(spectre_libs), repr(platform_def.vc_platform)
+ )
+ err_msg = "MSVC_SPECTRE_LIBS ({}) are not supported for platform type ({})".format(
+ repr(spectre_libs), repr(platform_def.vc_platform)
+ )
+ return err_msg
+
+ return None
+
+def _msvc_toolset_version_spectre_path(vc_dir, toolset_version):
+ spectre_dir = os.path.join(vc_dir, "Tools", "MSVC", toolset_version, "lib", "spectre")
+ return spectre_dir
+
+def _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist):
+
+ spectre_libs = env['MSVC_SPECTRE_LIBS']
+ debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
+
+ if not spectre_libs:
+ return None
+
+ if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE:
+ return None
+
+ err_msg = _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def)
+ if err_msg:
+ raise MSVCArgumentError(err_msg)
+
+ if toolset:
+ spectre_dir = _msvc_toolset_version_spectre_path(vc_dir, toolset.version)
+ if not os.path.exists(spectre_dir):
+ debug(
+ 'spectre libs: msvc_version=%s, toolset_version=%s, spectre_dir=%s',
+ repr(msvc.version), repr(toolset.version), repr(spectre_dir)
+ )
+ err_msg = "Spectre libraries not found for MSVC_VERSION {} toolset version {}".format(
+ repr(msvc.version), repr(toolset.version)
+ )
+ raise MSVCSpectreLibsNotFound(err_msg)
+
+ spectre_arg = 'spectre'
+
+ # spectre libs may not be installed for host/target
+ argpair = (SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg))
+ arglist.append(argpair)
+
+ return spectre_arg
+
+def _user_script_argument_spectre(env, spectre, user_argstr):
+
+ matches = [m for m in re_vcvars_spectre.finditer(user_argstr)]
+ if not matches:
+ return None
+
+ if len(matches) > 1:
+ debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
+ err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr))
+ raise MSVCArgumentError(err_msg)
+
+ if not spectre:
+ return None
+
+ env_argstr = env.get('MSVC_SPECTRE_LIBS','')
+ debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
+
+ err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format(
+ repr(env_argstr), repr(user_argstr)
+ )
+
+ raise MSVCArgumentError(err_msg)
+
+def _msvc_script_argument_user(env, msvc, arglist):
+
+ # subst None -> empty string
+ script_args = env.subst('$MSVC_SCRIPT_ARGS')
+ debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args))
+
+ if not script_args:
+ return None
+
+ if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
+ debug(
+ 'invalid: msvc version constraint: %s < %s VS2015',
+ repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
+ repr(VS2015.vc_buildtools_def.vc_version_numeric)
+ )
+ err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
+ repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
+ )
+ raise MSVCArgumentError(err_msg)
+
+ # user arguments are not validated
+ argpair = (SortOrder.USER, script_args)
+ arglist.append(argpair)
+
+ return script_args
+
+def _msvc_process_construction_variables(env):
+
+ for cache_variable in [
+ _MSVC_FORCE_DEFAULT_TOOLSET,
+ _MSVC_FORCE_DEFAULT_SDK,
+ ]:
+ if cache_variable:
+ return True
+
+ for env_variable in [
+ 'MSVC_UWP_APP',
+ 'MSVC_TOOLSET_VERSION',
+ 'MSVC_SDK_VERSION',
+ 'MSVC_SPECTRE_LIBS',
+ ]:
+ if env.get(env_variable, None) is not None:
+ return True
+
+ return False
+
+def msvc_script_arguments(env, version, vc_dir, arg):
+
+ arguments = [arg] if arg else []
+
+ arglist = []
+ arglist_reverse = False
+
+ msvc = _msvc_version(version)
+
+ if 'MSVC_SCRIPT_ARGS' in env:
+ user_argstr = _msvc_script_argument_user(env, msvc, arglist)
+ else:
+ user_argstr = None
+
+ if _msvc_process_construction_variables(env):
+
+ # MSVC_UWP_APP
+
+ if 'MSVC_UWP_APP' in env:
+ uwp = _msvc_script_argument_uwp(env, msvc, arglist)
+ else:
+ uwp = None
+
+ if user_argstr:
+ user_uwp = _user_script_argument_uwp(env, uwp, user_argstr)
+ else:
+ user_uwp = None
+
+ is_uwp = True if uwp else False
+ platform_def = WinSDK.get_msvc_platform(is_uwp)
+
+ # MSVC_TOOLSET_VERSION
+
+ if 'MSVC_TOOLSET_VERSION' in env:
+ toolset_version = _msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
+ else:
+ toolset_version = None
+
+ if user_argstr:
+ user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr)
+ else:
+ user_toolset = None
+
+ if not toolset_version and not user_toolset:
+ default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist, _MSVC_FORCE_DEFAULT_TOOLSET)
+ if _MSVC_FORCE_DEFAULT_TOOLSET:
+ toolset_version = default_toolset
+ else:
+ default_toolset = None
+
+ if user_toolset:
+ toolset = None
+ elif toolset_version:
+ toolset = _toolset_version(toolset_version)
+ elif default_toolset:
+ toolset = _toolset_version(default_toolset)
+ else:
+ toolset = None
+
+ # MSVC_SDK_VERSION
+
+ if 'MSVC_SDK_VERSION' in env:
+ sdk_version = _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist)
+ else:
+ sdk_version = None
+
+ if user_argstr:
+ user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr)
+ else:
+ user_sdk = None
+
+ if _MSVC_FORCE_DEFAULT_SDK:
+ if not sdk_version and not user_sdk:
+ sdk_version = _msvc_script_default_sdk(env, msvc, platform_def, arglist, _MSVC_FORCE_DEFAULT_SDK)
+
+ # MSVC_SPECTRE_LIBS
+
+ if 'MSVC_SPECTRE_LIBS' in env:
+ spectre = _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist)
+ else:
+ spectre = None
+
+ if user_argstr:
+ _user_script_argument_spectre(env, spectre, user_argstr)
+
+ if msvc.vs_def.vc_buildtools_def.vc_version == '14.0':
+ if user_uwp and sdk_version and len(arglist) == 2:
+ # VS2015 toolset argument order issue: SDK store => store SDK
+ arglist_reverse = True
+
+ if len(arglist) > 1:
+ arglist.sort()
+ if arglist_reverse:
+ arglist.reverse()
+
+ arguments.extend([argpair[-1] for argpair in arglist])
+ argstr = ' '.join(arguments).strip()
+
+ debug('arguments: %s', repr(argstr))
+ return argstr
+
+def _msvc_toolset_internal(msvc_version, toolset_version, vc_dir):
+
+ msvc = _msvc_version(msvc_version)
+
+ toolset_vcvars = _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir)
+
+ return toolset_vcvars
+
+def _msvc_toolset_versions_internal(msvc_version, vc_dir, full=True, sxs=False):
+
+ msvc = _msvc_version(msvc_version)
+
+ if len(msvc.vs_def.vc_buildtools_all) <= 1:
+ return None
+
+ toolset_versions = []
+
+ toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
+
+ if sxs:
+ sxs_versions = list(toolsets_sxs.keys())
+ sxs_versions.sort(reverse=True)
+ toolset_versions.extend(sxs_versions)
+
+ if full:
+ toolset_versions.extend(toolsets_full)
+
+ return toolset_versions
+
+def _msvc_toolset_versions_spectre_internal(msvc_version, vc_dir):
+
+ msvc = _msvc_version(msvc_version)
+
+ if len(msvc.vs_def.vc_buildtools_all) <= 1:
+ return None
+
+ _, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
+
+ spectre_toolset_versions = [
+ toolset_version
+ for toolset_version in toolsets_full
+ if os.path.exists(_msvc_toolset_version_spectre_path(vc_dir, toolset_version))
+ ]
+
+ return spectre_toolset_versions
+
+def reset():
+ debug('')
+ _reset_have140_cache()
+ _reset_toolset_cache()
+
+def verify():
+ debug('')
+ _verify_re_sdk_dispatch_map()
+
diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py
new file mode 100644
index 0000000..4413256
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py
@@ -0,0 +1,591 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test batch file argument functions for Microsoft Visual C/C++.
+"""
+
+import unittest
+
+import SCons.Environment
+
+from SCons.Tool.MSCommon import vc
+from SCons.Tool.MSCommon import vcTests
+
+from SCons.Tool.MSCommon.MSVC import Config
+from SCons.Tool.MSCommon.MSVC import Util
+from SCons.Tool.MSCommon.MSVC import WinSDK
+from SCons.Tool.MSCommon.MSVC import ScriptArguments
+
+from SCons.Tool.MSCommon.MSVC.Exceptions import (
+ MSVCInternalError,
+ MSVCArgumentError,
+ MSVCToolsetVersionNotFound,
+ MSVCSDKVersionNotFound,
+ MSVCSpectreLibsNotFound,
+)
+
+def Environment(**kwargs):
+ tools_key = 'tools'
+ if tools_key not in kwargs:
+ tools = []
+ else:
+ tools = kwargs[tools_key]
+ del kwargs[tools_key]
+ return SCons.Environment.Base(tools=tools, **kwargs)
+
+def _sdk_versions_comps_dict_seen(installed_version_pairs):
+
+ sdk_versions_comps_dict = {}
+ sdk_versions_seen = set()
+
+ _sdk_version_list_seen = {}
+ for version_def, _ in installed_version_pairs:
+ sdk_versions_comps_dict[version_def.msvc_version] = {}
+ for msvc_uwp_app in (True, False):
+ sdk_version_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app)
+ key = tuple(sdk_version_list)
+ if key in _sdk_version_list_seen:
+ sdk_comps_list = _sdk_version_list_seen[key]
+ else:
+ sdk_versions_seen.update(sdk_version_list)
+ sdk_comps_list = [Util.msvc_sdk_version_components(sdk_version) for sdk_version in sdk_version_list]
+ _sdk_version_list_seen[key] = sdk_comps_list
+ sdk_versions_comps_dict[version_def.msvc_version][msvc_uwp_app] = sdk_comps_list
+
+ return sdk_versions_comps_dict, sdk_versions_seen
+
+def _sdk_versions_notfound(installed_version_pairs, sdk_versions_comps_dict, sdk_versions_seen):
+
+ sdk_versions_notfound_dict = {}
+ sdk_notfound_seen = {}
+
+ def _make_notfound_version(sdk_seen, sdk_def):
+ if len(sdk_def.sdk_comps) == 4:
+ nloop = 0
+ while nloop < 10:
+ ival = int(sdk_def.sdk_comps[-2])
+ if ival == 0:
+ ival = 1000000
+ ival -= 1
+ version = '{}.{}.{:05d}.{}'.format(
+ sdk_def.sdk_comps[0], sdk_def.sdk_comps[1], ival, sdk_def.sdk_comps[-1]
+ )
+ if version not in sdk_seen:
+ return version
+ nloop += 1
+ return None
+
+ for version_def, _ in installed_version_pairs:
+ sdk_versions_notfound_dict[version_def.msvc_version] = {}
+ for msvc_uwp_app in (True, False):
+ sdk_notfound_list = []
+ sdk_versions_notfound_dict[version_def.msvc_version][msvc_uwp_app] = sdk_notfound_list
+ sdk_comps_list = sdk_versions_comps_dict[version_def.msvc_version][msvc_uwp_app]
+ for sdk_def in sdk_comps_list:
+ if sdk_def.sdk_version in sdk_notfound_seen:
+ sdk_notfound_version = sdk_notfound_seen[sdk_def.sdk_version]
+ else:
+ sdk_notfound_version = _make_notfound_version(sdk_versions_seen, sdk_def)
+ sdk_notfound_seen[sdk_def.sdk_version] = sdk_notfound_version
+ if not sdk_notfound_version:
+ continue
+ sdk_notfound_list.append(sdk_notfound_version)
+
+ return sdk_versions_notfound_dict
+
+class Data:
+
+ # all versions
+ ALL_VERSIONS_PAIRS = []
+
+ # installed versions
+ INSTALLED_VERSIONS_PAIRS = []
+
+ # VS2015 installed
+ HAVE140_TOOLSET = ScriptArguments._msvc_have140_toolset()
+
+ for vcver in Config.MSVC_VERSION_SUFFIX.keys():
+ version_def = Util.msvc_version_components(vcver)
+ vc_dir = vc.find_vc_pdir(None, vcver)
+ t = (version_def, vc_dir)
+ ALL_VERSIONS_PAIRS.append(t)
+ if vc_dir:
+ INSTALLED_VERSIONS_PAIRS.append(t)
+
+ HAVE_MSVC = True if len(INSTALLED_VERSIONS_PAIRS) else False
+
+ SPECTRE_TOOLSET_VERSIONS = {
+ version_def.msvc_version: vc.msvc_toolset_versions_spectre(version_def.msvc_version)
+ for version_def, _ in INSTALLED_VERSIONS_PAIRS
+ }
+
+ SDK_VERSIONS_COMPS_DICT, SDK_VERSIONS_SEEN = _sdk_versions_comps_dict_seen(INSTALLED_VERSIONS_PAIRS)
+
+ SDK_VERSIONS_NOTFOUND_DICT = _sdk_versions_notfound(
+ INSTALLED_VERSIONS_PAIRS, SDK_VERSIONS_COMPS_DICT, SDK_VERSIONS_SEEN
+ )
+
+ @classmethod
+ def msvc_sdk_version_list_components(cls, msvc_version, msvc_uwp_app=False):
+ comps_dict = cls.SDK_VERSIONS_COMPS_DICT.get(msvc_version, {})
+ comps_list = comps_dict.get(msvc_uwp_app, [])
+ return comps_list
+
+ @classmethod
+ def msvc_sdk_version(cls, msvc_version, msvc_uwp_app=False):
+ comps_dict = cls.SDK_VERSIONS_COMPS_DICT.get(msvc_version, {})
+ comps_list = comps_dict.get(msvc_uwp_app, [])
+ if not comps_list:
+ sdk_version = '10.0.20348.0'
+ else:
+ sdk_version = comps_list[0].sdk_version
+ return sdk_version
+
+ @classmethod
+ def msvc_sdk_notfound_version(cls, msvc_version, msvc_uwp_app=False):
+ notfound_dict = cls.SDK_VERSIONS_NOTFOUND_DICT.get(msvc_version, {})
+ notfound_list = notfound_dict.get(msvc_uwp_app, [])
+ if not notfound_list:
+ notfound_version = '10.0.00000.1'
+ else:
+ notfound_version = notfound_list[0]
+ return notfound_version
+
+ @classmethod
+ def msvc_toolset_notfound_dict(cls):
+ return vcTests.Data.msvc_toolset_notfound_dict()
+
+ @classmethod
+ def msvc_toolset_notfound_version(cls, msvc_version):
+ d = cls.msvc_toolset_notfound_dict()
+ notfound_versions = d.get(msvc_version,[])
+ if not notfound_versions:
+ notfound_version = msvc_version + '0.00001'
+ else:
+ notfound_version = notfound_versions[0]
+ return notfound_version
+
+class Patch:
+
+ class Config:
+
+ class MSVC_SDK_VERSIONS:
+
+ MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS
+
+ @classmethod
+ def enable_copy(cls):
+ hook = set(cls.MSVC_SDK_VERSIONS)
+ Config.MSVC_SDK_VERSIONS = hook
+ return hook
+
+ @classmethod
+ def restore(cls):
+ Config.MSVC_SDK_VERSIONS = cls.MSVC_SDK_VERSIONS
+
+class ScriptArgumentsTests(unittest.TestCase):
+
+ def test_verify(self):
+ MSVC_SDK_VERSIONS = Patch.Config.MSVC_SDK_VERSIONS.enable_copy()
+ MSVC_SDK_VERSIONS.add('99.0')
+ with self.assertRaises(MSVCInternalError):
+ ScriptArguments.verify()
+ Patch.Config.MSVC_SDK_VERSIONS.restore()
+
+ def test_msvc_script_arguments_defaults(self):
+ func = ScriptArguments.msvc_script_arguments
+ env = Environment()
+ # disable forcing sdk and toolset versions as arguments
+ force = ScriptArguments.msvc_force_default_arguments(force=False)
+ for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS:
+ for arg in ('', 'arch'):
+ scriptargs = func(env, version_def.msvc_version, vc_dir, arg)
+ self.assertTrue(scriptargs == arg, "{}({},{}) != {} [force=False]".format(
+ func.__name__, repr(version_def.msvc_version), repr(arg), repr(scriptargs)
+ ))
+ # enable forcing sdk and toolset versions as arguments
+ ScriptArguments.msvc_force_default_arguments(force=True)
+ for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS:
+ for arg in ('', 'arch'):
+ scriptargs = func(env, version_def.msvc_version, vc_dir, arg)
+ if version_def.msvc_vernum >= 14.0:
+ if arg and scriptargs.startswith(arg):
+ testargs = scriptargs[len(arg):].lstrip()
+ else:
+ testargs = scriptargs
+ self.assertTrue(testargs, "{}({},{}) is empty [force=True]".format(
+ func.__name__, repr(version_def.msvc_version), repr(arg)
+ ))
+ else:
+ self.assertTrue(scriptargs == arg, "{}({},{}) != {} [force=True]".format(
+ func.__name__, repr(version_def.msvc_version), repr(arg), repr(scriptargs)
+ ))
+ # restore forcing sdk and toolset versions as arguments
+ ScriptArguments.msvc_force_default_arguments(force=force)
+
+ def test_msvc_toolset_versions_internal(self):
+ func = ScriptArguments._msvc_toolset_versions_internal
+ for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS:
+ for full in (True, False):
+ for sxs in (True, False):
+ toolset_versions = func(version_def.msvc_version, vc_dir, full=full, sxs=sxs)
+ if version_def.msvc_vernum < 14.1:
+ self.assertTrue(toolset_versions is None, "{}({},{},full={},sxs={}) is not None ({})".format(
+ func.__name__, repr(version_def.msvc_version), repr(vc_dir), repr(full), repr(sxs),
+ repr(toolset_versions)
+ ))
+ elif full:
+ self.assertTrue(len(toolset_versions), "{}({},{},full={},sxs={}) is empty".format(
+ func.__name__, repr(version_def.msvc_version), repr(vc_dir), repr(full), repr(sxs)
+ ))
+ elif sxs:
+ # sxs list can be empty
+ pass
+ else:
+ self.assertFalse(len(toolset_versions), "{}({},{},full={},sxs={}) is not empty".format(
+ func.__name__, repr(version_def.msvc_version), repr(vc_dir), repr(full), repr(sxs)
+ ))
+
+ def test_msvc_toolset_internal(self):
+ if not Data.HAVE_MSVC:
+ return
+ func = ScriptArguments._msvc_toolset_internal
+ for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS:
+ toolset_versions = ScriptArguments._msvc_toolset_versions_internal(version_def.msvc_version, vc_dir, full=True, sxs=True)
+ if not toolset_versions:
+ continue
+ for toolset_version in toolset_versions:
+ _ = func(version_def.msvc_version, toolset_version, vc_dir)
+
+ def run_msvc_script_args_none(self):
+ func = ScriptArguments.msvc_script_arguments
+ for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS:
+ for kwargs in [
+ {'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_SCRIPT_ARGS': None, 'MSVC_UWP_APP': None},
+ {'MSVC_SCRIPT_ARGS': None, 'MSVC_TOOLSET_VERSION': None},
+ {'MSVC_SCRIPT_ARGS': None, 'MSVC_SDK_VERSION': None},
+ {'MSVC_SCRIPT_ARGS': None, 'MSVC_SPECTRE_LIBS': None},
+ ]:
+ env = Environment(**kwargs)
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ def run_msvc_script_args(self):
+ func = ScriptArguments.msvc_script_arguments
+ for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS:
+ if version_def.msvc_vernum >= 14.1:
+ # VS2017 and later
+
+ toolset_versions = [
+ Util.msvc_extended_version_components(toolset_version)
+ for toolset_version in
+ ScriptArguments._msvc_toolset_versions_internal(
+ version_def.msvc_version, vc_dir, full=True, sxs=False
+ )
+ ]
+
+ toolset_def = toolset_versions[0] if toolset_versions else Util.msvc_extended_version_components(version_def.msvc_verstr)
+
+ earlier_toolset_versions = [toolset_def for toolset_def in toolset_versions if toolset_def.msvc_vernum != version_def.msvc_vernum]
+ earlier_toolset_def = earlier_toolset_versions[0] if earlier_toolset_versions else None
+
+ # should not raise exception (argument not validated)
+ env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol')
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ for kwargs in [
+ {'MSVC_UWP_APP': False, 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_UWP_APP': '0', 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_UWP_APP': False, 'MSVC_SCRIPT_ARGS': 'store'},
+ {'MSVC_UWP_APP': '0', 'MSVC_SCRIPT_ARGS': 'store'},
+ {'MSVC_SPECTRE_LIBS': False, 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre'},
+ {'MSVC_SPECTRE_LIBS': 'True', 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre'}, # not boolean ignored
+ ]:
+ env = Environment(**kwargs)
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ for msvc_uwp_app in (True, False):
+
+ sdk_list = Data.msvc_sdk_version_list_components(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app)
+ for sdk_def in sdk_list:
+
+ if sdk_def.sdk_verstr == '8.1' and msvc_uwp_app:
+
+ more_tests = []
+
+ if earlier_toolset_def:
+ # SDK 8.1 and UWP: toolset must be 14.0
+ expect = True if earlier_toolset_def.msvc_vernum > 14.0 else False
+ more_tests.append(
+ (expect, {
+ 'MSVC_SDK_VERSION': sdk_def.sdk_version,
+ 'MSVC_UWP_APP': msvc_uwp_app,
+ 'MSVC_TOOLSET_VERSION': earlier_toolset_def.msvc_toolset_version
+ })
+ )
+
+ expect = True if version_def.msvc_vernum > 14.0 else False
+
+ for exc, kwargs in [
+ # script args not validated
+ (False, {
+ 'MSVC_SCRIPT_ARGS': sdk_def.sdk_version,
+ 'MSVC_UWP_APP': msvc_uwp_app
+ }),
+ # SDK 8.1 and UWP: msvc_version > 14.0
+ (True, {
+ 'MSVC_SDK_VERSION': sdk_def.sdk_version,
+ 'MSVC_UWP_APP': msvc_uwp_app
+ }),
+ # SDK 8.1 and UWP: toolset must be 14.0
+ (expect, {
+ 'MSVC_SDK_VERSION': sdk_def.sdk_version,
+ 'MSVC_UWP_APP': msvc_uwp_app,
+ 'MSVC_TOOLSET_VERSION': version_def.msvc_verstr
+ }),
+ ] + more_tests:
+ env = Environment(**kwargs)
+ if exc:
+ with self.assertRaises(MSVCArgumentError):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+ else:
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ else:
+
+ for kwargs in [
+ {'MSVC_SCRIPT_ARGS': sdk_def.sdk_version, 'MSVC_UWP_APP': msvc_uwp_app},
+ {'MSVC_SDK_VERSION': sdk_def.sdk_version, 'MSVC_UWP_APP': msvc_uwp_app},
+ ]:
+ env = Environment(**kwargs)
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ for kwargs in [
+ {'MSVC_SCRIPT_ARGS': '-vcvars_ver={}'.format(version_def.msvc_verstr)},
+ {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr},
+ ]:
+ env = Environment(**kwargs)
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ msvc_toolset_notfound_version = Data.msvc_toolset_notfound_version(version_def.msvc_version)
+
+ for kwargs in [
+ {'MSVC_TOOLSET_VERSION': msvc_toolset_notfound_version},
+ {'MSVC_TOOLSET_VERSION': "{}.{}.00.0".format(
+ toolset_def.msvc_toolset_comps[0], toolset_def.msvc_toolset_comps[1]
+ )},
+ ]:
+ env = Environment(**kwargs)
+ with self.assertRaises(MSVCToolsetVersionNotFound):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ msvc_sdk_notfound_version = Data.msvc_sdk_notfound_version(version_def.msvc_version)
+
+ for kwargs in [
+ {'MSVC_SDK_VERSION': msvc_sdk_notfound_version},
+ ]:
+ env = Environment(**kwargs)
+ with self.assertRaises(MSVCSDKVersionNotFound):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ have_spectre = toolset_def.msvc_toolset_version in Data.SPECTRE_TOOLSET_VERSIONS.get(version_def.msvc_version,[])
+ env = Environment(MSVC_SPECTRE_LIBS=True, MSVC_TOOLSET_VERSION=toolset_def.msvc_toolset_version)
+ if not have_spectre:
+ with self.assertRaises(MSVCSpectreLibsNotFound):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+ else:
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ msvc_sdk_version = Data.msvc_sdk_version(version_def.msvc_version)
+
+ more_tests = []
+
+ if Data.HAVE140_TOOLSET:
+
+ more_tests.append(
+ # toolset != 14.0
+ ({'MSVC_TOOLSET_VERSION': '14.00.00001',
+ },
+ (MSVCArgumentError, ),
+ ),
+ )
+
+ for kwargs, exc_t in [
+ # multiple definitions
+ ({'MSVC_UWP_APP': True,
+ 'MSVC_SCRIPT_ARGS': 'uwp'
+ }, (MSVCArgumentError, ),
+ ),
+ # multiple definitions (args)
+ ({'MSVC_UWP_APP': True,
+ 'MSVC_SCRIPT_ARGS': 'uwp undefined store'
+ }, (MSVCArgumentError, ),
+ ),
+ # multiple definitions
+ ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr,
+ 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(version_def.msvc_verstr)
+ },
+ (MSVCArgumentError, ),
+ ),
+ # multiple definitions (args)
+ ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr,
+ 'MSVC_SCRIPT_ARGS': "-vcvars_ver={0} undefined -vcvars_ver={0}".format(version_def.msvc_verstr)
+ },
+ (MSVCArgumentError, ),
+ ),
+ # multiple definitions
+ ({'MSVC_SDK_VERSION': msvc_sdk_version,
+ 'MSVC_SCRIPT_ARGS': msvc_sdk_version
+ },
+ (MSVCArgumentError, ),
+ ),
+ # multiple definitions (args)
+ ({'MSVC_SDK_VERSION': msvc_sdk_version,
+ 'MSVC_SCRIPT_ARGS': '{0} undefined {0}'.format(msvc_sdk_version)
+ },
+ (MSVCArgumentError, ),
+ ),
+ # multiple definitions
+ ({'MSVC_SPECTRE_LIBS': True,
+ 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre'
+ },
+ (MSVCArgumentError, MSVCSpectreLibsNotFound),
+ ),
+ # multiple definitions (args)
+ ({'MSVC_SPECTRE_LIBS': True,
+ 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre undefined -vcvars_spectre_libs=spectre'
+ },
+ (MSVCArgumentError, MSVCSpectreLibsNotFound),
+ ),
+ # toolset < 14.0
+ ({'MSVC_TOOLSET_VERSION': '12.0',
+ },
+ (MSVCArgumentError, ),
+ ),
+ # toolset > msvc_version
+ ({'MSVC_TOOLSET_VERSION': '{}.{}'.format(version_def.msvc_major, version_def.msvc_minor+1),
+ },
+ (MSVCArgumentError, ),
+ ),
+ # version not supported
+ ({'MSVC_TOOLSET_VERSION': "{}".format(version_def.msvc_major),
+ },
+ (MSVCArgumentError, ),
+ ),
+ # version not supported
+ ({'MSVC_TOOLSET_VERSION': "{}.{}.00000.0".format(
+ toolset_def.msvc_toolset_comps[0], toolset_def.msvc_toolset_comps[1]
+ )},
+ (MSVCArgumentError, ),
+ ),
+ # version not supported
+ ({'MSVC_SDK_VERSION': '9.1',
+ },
+ (MSVCArgumentError, ),
+ ),
+ # spectre not available for UWP
+ ({'MSVC_SPECTRE_LIBS': True,
+ 'MSVC_UWP_APP': True,
+ },
+ (MSVCArgumentError, MSVCSpectreLibsNotFound),
+ ),
+ # spectre not available in VS2015
+ ({'MSVC_SPECTRE_LIBS': True,
+ 'MSVC_TOOLSET_VERSION': '14.00.00000',
+ },
+ (MSVCArgumentError, MSVCSpectreLibsNotFound, MSVCToolsetVersionNotFound),
+ ),
+ ] + more_tests:
+ env = Environment(**kwargs)
+ with self.assertRaises(exc_t):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ elif version_def.msvc_verstr == '14.0':
+ # VS2015: MSVC_SDK_VERSION and MSVC_UWP_APP
+
+ env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol')
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ for msvc_uwp_app in (True, False):
+
+ sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app)
+ for sdk_version in sdk_list:
+
+ for kwargs in [
+ {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app},
+ {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app},
+ ]:
+ env = Environment(**kwargs)
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ for kwargs in [
+ {'MSVC_SPECTRE_LIBS': True, 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, 'MSVC_SCRIPT_ARGS': None},
+ ]:
+ env = Environment(**kwargs)
+ with self.assertRaises(MSVCArgumentError):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ else:
+ # VS2013 and earlier: no arguments
+
+ env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol')
+ with self.assertRaises(MSVCArgumentError):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ for kwargs in [
+ {'MSVC_UWP_APP': True, 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_UWP_APP': '1', 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_SPECTRE_LIBS': True, 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, 'MSVC_SCRIPT_ARGS': None},
+ {'MSVC_SDK_VERSION': '10.0.00000.0', 'MSVC_SCRIPT_ARGS': None},
+ ]:
+ env = Environment(**kwargs)
+ with self.assertRaises(MSVCArgumentError):
+ _ = func(env, version_def.msvc_version, vc_dir, '')
+
+ def test_msvc_script_args_none(self):
+ force = ScriptArguments.msvc_force_default_arguments(force=False)
+ self.run_msvc_script_args_none()
+ if Data.HAVE_MSVC:
+ ScriptArguments.msvc_force_default_arguments(force=True)
+ self.run_msvc_script_args_none()
+ ScriptArguments.msvc_force_default_arguments(force=force)
+
+ def test_msvc_script_args(self):
+ force = ScriptArguments.msvc_force_default_arguments(force=False)
+ self.run_msvc_script_args()
+ ScriptArguments.msvc_force_default_arguments(force=True)
+ self.run_msvc_script_args()
+ ScriptArguments.msvc_force_default_arguments(force=force)
+
+ def test_reset(self):
+ ScriptArguments.reset()
+ self.assertTrue(ScriptArguments._toolset_have140_cache is None, "ScriptArguments._toolset_have140_cache was not reset")
+ self.assertFalse(ScriptArguments._toolset_version_cache, "ScriptArguments._toolset_version_cache was not reset")
+ self.assertFalse(ScriptArguments._toolset_default_cache, "ScriptArguments._toolset_default_cache was not reset")
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
new file mode 100644
index 0000000..e1c05bc
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py
@@ -0,0 +1,233 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Determine if and/or when an error/warning should be issued when there
+are no versions of msvc installed. If there is at least one version of
+msvc installed, these routines do (almost) nothing.
+
+Notes:
+ * When msvc is the default compiler because there are no compilers
+ installed, a build may fail due to the cl.exe command not being
+ recognized. Currently, there is no easy way to detect during
+ msvc initialization if the default environment will be used later
+ to build a program and/or library. There is no error/warning
+ as there are legitimate SCons uses that do not require a c compiler.
+ * An error is indicated by returning a non-empty tool list from the
+ function register_iserror.
+"""
+
+import re
+
+from .. common import (
+ debug,
+)
+
+from . import Dispatcher
+Dispatcher.register_modulename(__name__)
+
+
+class _Data:
+
+ separator = r';'
+
+ need_init = True
+
+ @classmethod
+ def reset(cls):
+ debug('msvc default:init')
+ cls.n_setup = 0 # number of calls to msvc_setup_env_once
+ cls.default_ismsvc = False # is msvc the default compiler
+ cls.default_tools_re_list = [] # list of default tools regular expressions
+ cls.msvc_tools_init = set() # tools registered via msvc_exists
+ cls.msvc_tools = None # tools registered via msvc_setup_env_once
+ cls.msvc_installed = False # is msvc installed (vcs_installed > 0)
+ cls.msvc_nodefault = False # is there a default version of msvc
+ cls.need_init = True # reset initialization indicator
+
+def _initialize(env, msvc_exists_func):
+ if _Data.need_init:
+ _Data.reset()
+ _Data.need_init = False
+ _Data.msvc_installed = msvc_exists_func(env)
+ debug('msvc default:msvc_installed=%s', _Data.msvc_installed)
+
+def register_tool(env, tool, msvc_exists_func):
+ if _Data.need_init:
+ _initialize(env, msvc_exists_func)
+ if _Data.msvc_installed:
+ return None
+ if not tool:
+ return None
+ if _Data.n_setup == 0:
+ if tool not in _Data.msvc_tools_init:
+ _Data.msvc_tools_init.add(tool)
+ debug('msvc default:tool=%s, msvc_tools_init=%s', tool, _Data.msvc_tools_init)
+ return None
+ if tool not in _Data.msvc_tools:
+ _Data.msvc_tools.add(tool)
+ debug('msvc default:tool=%s, msvc_tools=%s', tool, _Data.msvc_tools)
+
+def register_setup(env, msvc_exists_func):
+ if _Data.need_init:
+ _initialize(env, msvc_exists_func)
+ _Data.n_setup += 1
+ if not _Data.msvc_installed:
+ _Data.msvc_tools = set(_Data.msvc_tools_init)
+ if _Data.n_setup == 1:
+ tool_list = env.get('TOOLS', None)
+ if tool_list and tool_list[0] == 'default':
+ if len(tool_list) > 1 and tool_list[1] in _Data.msvc_tools:
+ # msvc tools are the default compiler
+ _Data.default_ismsvc = True
+ _Data.msvc_nodefault = False
+ debug(
+ 'msvc default:n_setup=%d, msvc_installed=%s, default_ismsvc=%s',
+ _Data.n_setup, _Data.msvc_installed, _Data.default_ismsvc
+ )
+
+def set_nodefault():
+ # default msvc version, msvc not installed
+ _Data.msvc_nodefault = True
+ debug('msvc default:msvc_nodefault=%s', _Data.msvc_nodefault)
+
+def register_iserror(env, tool, msvc_exists_func):
+
+ register_tool(env, tool, msvc_exists_func)
+
+ if _Data.msvc_installed:
+ # msvc installed
+ return None
+
+ if not _Data.msvc_nodefault:
+ # msvc version specified
+ return None
+
+ tool_list = env.get('TOOLS', None)
+ if not tool_list:
+ # tool list is empty
+ return None
+
+ debug(
+ 'msvc default:n_setup=%s, default_ismsvc=%s, msvc_tools=%s, tool_list=%s',
+ _Data.n_setup, _Data.default_ismsvc, _Data.msvc_tools, tool_list
+ )
+
+ if not _Data.default_ismsvc:
+
+ # Summary:
+ # * msvc is not installed
+ # * msvc version not specified (default)
+ # * msvc is not the default compiler
+
+ # construct tools set
+ tools_set = set(tool_list)
+
+ else:
+
+ if _Data.n_setup == 1:
+ # first setup and msvc is default compiler:
+ # build default tools regex for current tool state
+ tools = _Data.separator.join(tool_list)
+ tools_nchar = len(tools)
+ debug('msvc default:add regex:nchar=%d, tools=%s', tools_nchar, tools)
+ re_default_tools = re.compile(re.escape(tools))
+ _Data.default_tools_re_list.insert(0, (tools_nchar, re_default_tools))
+ # early exit: no error for default environment when msvc is not installed
+ return None
+
+ # Summary:
+ # * msvc is not installed
+ # * msvc version not specified (default)
+ # * environment tools list is not empty
+ # * default tools regex list constructed
+ # * msvc tools set constructed
+ #
+ # Algorithm using tools string and sets:
+ # * convert environment tools list to a string
+ # * iteratively remove default tools sequences via regex
+ # substition list built from longest sequence (first)
+ # to shortest sequence (last)
+ # * build environment tools set with remaining tools
+ # * compute intersection of environment tools and msvc tools sets
+ # * if the intersection is:
+ # empty - no error: default tools and/or no additional msvc tools
+ # not empty - error: user specified one or more msvc tool(s)
+ #
+ # This will not produce an error or warning when there are no
+ # msvc installed instances nor any other recognized compilers
+ # and the default environment is needed for a build. The msvc
+ # compiler is forcibly added to the environment tools list when
+ # there are no compilers installed on win32. In this case, cl.exe
+ # will not be found on the path resulting in a failed build.
+
+ # construct tools string
+ tools = _Data.separator.join(tool_list)
+ tools_nchar = len(tools)
+
+ debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
+
+ # iteratively remove default tool sequences (longest to shortest)
+ re_nchar_min, re_tools_min = _Data.default_tools_re_list[-1]
+ if tools_nchar >= re_nchar_min and re_tools_min.search(tools):
+ # minimum characters satisfied and minimum pattern exists
+ for re_nchar, re_default_tool in _Data.default_tools_re_list:
+ if tools_nchar < re_nchar:
+ # not enough characters for pattern
+ continue
+ tools = re_default_tool.sub('', tools).strip(_Data.separator)
+ tools_nchar = len(tools)
+ debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools)
+ if tools_nchar < re_nchar_min or not re_tools_min.search(tools):
+ # less than minimum characters or minimum pattern does not exist
+ break
+
+ # construct non-default list(s) tools set
+ tools_set = {msvc_tool for msvc_tool in tools.split(_Data.separator) if msvc_tool}
+
+ debug('msvc default:tools=%s', tools_set)
+ if not tools_set:
+ return None
+
+ # compute intersection of remaining tools set and msvc tools set
+ tools_found = _Data.msvc_tools.intersection(tools_set)
+ debug('msvc default:tools_exist=%s', tools_found)
+ if not tools_found:
+ return None
+
+ # construct in same order as tools list
+ tools_found_list = []
+ seen_tool = set()
+ for tool in tool_list:
+ if tool not in seen_tool:
+ seen_tool.add(tool)
+ if tool in tools_found:
+ tools_found_list.append(tool)
+
+ # return tool list in order presented
+ return tools_found_list
+
+def reset():
+ debug('')
+ _Data.reset()
+
diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py
new file mode 100644
index 0000000..4b487da
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Util.py
@@ -0,0 +1,366 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Helper functions for Microsoft Visual C/C++.
+"""
+
+import os
+import re
+
+from collections import (
+ namedtuple,
+)
+
+from . import Config
+
+# path utilities
+
+def listdir_dirs(p):
+ """
+ Return a list of tuples for each subdirectory of the given directory path.
+ Each tuple is comprised of the subdirectory name and the qualified subdirectory path.
+
+ Args:
+ p: str
+ directory path
+
+ Returns:
+ list[tuple[str,str]]: a list of tuples
+
+ """
+ dirs = []
+ if p and os.path.exists(p) and os.path.isdir(p):
+ for dir_name in os.listdir(p):
+ dir_path = os.path.join(p, dir_name)
+ if os.path.isdir(dir_path):
+ dirs.append((dir_name, dir_path))
+ return dirs
+
+def process_path(p):
+ """
+ Normalize a system path
+
+ Args:
+ p: str
+ system path
+
+ Returns:
+ str: normalized system path
+
+ """
+ if p:
+ p = os.path.normpath(p)
+ p = os.path.realpath(p)
+ p = os.path.normcase(p)
+ return p
+
+# msvc version and msvc toolset version regexes
+
+re_version_prefix = re.compile('^(?P<version>[0-9]+(?:[.][0-9]+)*)(?![.]).*$')
+
+re_msvc_version_prefix = re.compile(r'^(?P<version>[1-9][0-9]?[.][0-9]).*$')
+
+re_msvc_version = re.compile(r'^(?P<msvc_version>[1-9][0-9]?[.][0-9])(?P<suffix>[A-Z]+)*$', re.IGNORECASE)
+
+re_extended_version = re.compile(r'''^
+ (?P<version>(?:
+ ([1-9][0-9]?[.][0-9]{1,2})| # XX.Y - XX.YY
+ ([1-9][0-9][.][0-9]{2}[.][0-9]{1,5})| # XX.YY.Z - XX.YY.ZZZZZ
+ ([1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}) # XX.YY.AA.B - XX.YY.AA.BB
+ ))
+ (?P<suffix>[A-Z]+)*
+$''', re.IGNORECASE | re.VERBOSE)
+
+re_toolset_full = re.compile(r'''^(?:
+ (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY
+ (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ
+)$''', re.VERBOSE)
+
+re_toolset_140 = re.compile(r'''^(?:
+ (?:14[.]0{1,2})| # 14.0 - 14.00
+ (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000
+)$''', re.VERBOSE)
+
+re_toolset_sxs = re.compile(
+ r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$' # MM.mm.VV.vv format
+)
+
+# msvc sdk version regexes
+
+re_msvc_sdk_version = re.compile(r'''^
+ (?P<version>(?:
+ ([1-9][0-9]?[.][0-9])| # XX.Y
+ ([1-9][0-9][.][0-9]{1}[.][0-9]{5}[.][0-9]{1,2}) # XX.Y.ZZZZZ.A - XX.Y.ZZZZZ.AA
+ ))
+$''', re.IGNORECASE | re.VERBOSE)
+
+# version prefix utilities
+
+def get_version_prefix(version):
+ """
+ Get the version number prefix from a string.
+
+ Args:
+ version: str
+ version specification
+
+ Returns:
+ str: the version number prefix
+
+ """
+ rval = ''
+ if version:
+ m = re_version_prefix.match(version)
+ if m:
+ rval = m.group('version')
+ return rval
+
+def get_msvc_version_prefix(version):
+ """
+ Get the msvc version number prefix from a string.
+
+ Args:
+ version: str
+ version specification
+
+ Returns:
+ str: the msvc version number prefix
+
+ """
+ rval = ''
+ if version:
+ m = re_msvc_version_prefix.match(version)
+ if m:
+ rval = m.group('version')
+ return rval
+
+# toolset version query utilities
+
+def is_toolset_full(toolset_version):
+ rval = False
+ if toolset_version:
+ if re_toolset_full.match(toolset_version):
+ rval = True
+ return rval
+
+def is_toolset_140(toolset_version):
+ rval = False
+ if toolset_version:
+ if re_toolset_140.match(toolset_version):
+ rval = True
+ return rval
+
+def is_toolset_sxs(toolset_version):
+ rval = False
+ if toolset_version:
+ if re_toolset_sxs.match(toolset_version):
+ rval = True
+ return rval
+
+# msvc version and msvc toolset version decomposition utilties
+
+_MSVC_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCVersionComponentsDefinition', [
+ 'msvc_version', # msvc version (e.g., '14.1Exp')
+ 'msvc_verstr', # msvc version numeric string (e.g., '14.1')
+ 'msvc_suffix', # msvc version component type (e.g., 'Exp')
+ 'msvc_vernum', # msvc version floating point number (e.g, 14.1)
+ 'msvc_major', # msvc major version integer number (e.g., 14)
+ 'msvc_minor', # msvc minor version integer number (e.g., 1)
+ 'msvc_comps', # msvc version components tuple (e.g., ('14', '1'))
+])
+
+def msvc_version_components(vcver):
+ """
+ Decompose an msvc version into components.
+
+ Tuple fields:
+ msvc_version: msvc version (e.g., '14.1Exp')
+ msvc_verstr: msvc version numeric string (e.g., '14.1')
+ msvc_suffix: msvc version component type (e.g., 'Exp')
+ msvc_vernum: msvc version floating point number (e.g., 14.1)
+ msvc_major: msvc major version integer number (e.g., 14)
+ msvc_minor: msvc minor version integer number (e.g., 1)
+ msvc_comps: msvc version components tuple (e.g., ('14', '1'))
+
+ Args:
+ vcver: str
+ msvc version specification
+
+ Returns:
+ None or MSVCVersionComponents namedtuple:
+ """
+
+ if not vcver:
+ return None
+
+ m = re_msvc_version.match(vcver)
+ if not m:
+ return None
+
+ vs_def = Config.MSVC_VERSION_SUFFIX.get(vcver)
+ if not vs_def:
+ return None
+
+ msvc_version = vcver
+ msvc_verstr = m.group('msvc_version')
+ msvc_suffix = m.group('suffix') if m.group('suffix') else ''
+ msvc_vernum = float(msvc_verstr)
+
+ msvc_comps = tuple(msvc_verstr.split('.'))
+ msvc_major, msvc_minor = [int(x) for x in msvc_comps]
+
+ msvc_version_components_def = _MSVC_VERSION_COMPONENTS_DEFINITION(
+ msvc_version = msvc_version,
+ msvc_verstr = msvc_verstr,
+ msvc_suffix = msvc_suffix,
+ msvc_vernum = msvc_vernum,
+ msvc_major = msvc_major,
+ msvc_minor = msvc_minor,
+ msvc_comps = msvc_comps,
+ )
+
+ return msvc_version_components_def
+
+_MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCExtendedVersionComponentsDefinition', [
+ 'msvc_version', # msvc version (e.g., '14.1Exp')
+ 'msvc_verstr', # msvc version numeric string (e.g., '14.1')
+ 'msvc_suffix', # msvc version component type (e.g., 'Exp')
+ 'msvc_vernum', # msvc version floating point number (e.g, 14.1)
+ 'msvc_major', # msvc major version integer number (e.g., 14)
+ 'msvc_minor', # msvc minor version integer number (e.g., 1)
+ 'msvc_comps', # msvc version components tuple (e.g., ('14', '1'))
+ 'msvc_toolset_version', # msvc toolset version
+ 'msvc_toolset_comps', # msvc toolset version components
+ 'version', # msvc version or msvc toolset version
+])
+
+def msvc_extended_version_components(version):
+ """
+ Decompose an msvc version or msvc toolset version into components.
+
+ Args:
+ version: str
+ version specification
+
+ Returns:
+ None or MSVCExtendedVersionComponents namedtuple:
+ """
+
+ if not version:
+ return None
+
+ m = re_extended_version.match(version)
+ if not m:
+ return None
+
+ msvc_toolset_version = m.group('version')
+ msvc_toolset_comps = tuple(msvc_toolset_version.split('.'))
+
+ msvc_verstr = get_msvc_version_prefix(msvc_toolset_version)
+ if not msvc_verstr:
+ return None
+
+ msvc_suffix = m.group('suffix') if m.group('suffix') else ''
+ msvc_version = msvc_verstr + msvc_suffix
+
+ vs_def = Config.MSVC_VERSION_SUFFIX.get(msvc_version)
+ if not vs_def:
+ return None
+
+ msvc_vernum = float(msvc_verstr)
+
+ msvc_comps = tuple(msvc_verstr.split('.'))
+ msvc_major, msvc_minor = [int(x) for x in msvc_comps]
+
+ msvc_extended_version_components_def = _MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION(
+ msvc_version = msvc_version,
+ msvc_verstr = msvc_verstr,
+ msvc_suffix = msvc_suffix,
+ msvc_vernum = msvc_vernum,
+ msvc_major = msvc_major,
+ msvc_minor = msvc_minor,
+ msvc_comps = msvc_comps,
+ msvc_toolset_version = msvc_toolset_version,
+ msvc_toolset_comps = msvc_toolset_comps,
+ version = version,
+ )
+
+ return msvc_extended_version_components_def
+
+# msvc sdk version decomposition utilties
+
+_MSVC_SDK_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCSDKVersionComponentsDefinition', [
+ 'sdk_version', # sdk version (e.g., '10.0.20348.0')
+ 'sdk_verstr', # sdk version numeric string (e.g., '10.0')
+ 'sdk_vernum', # sdk version floating point number (e.g, 10.0)
+ 'sdk_major', # sdk major version integer number (e.g., 10)
+ 'sdk_minor', # sdk minor version integer number (e.g., 0)
+ 'sdk_comps', # sdk version components tuple (e.g., ('10', '0', '20348', '0'))
+])
+
+def msvc_sdk_version_components(version):
+ """
+ Decompose an msvc sdk version into components.
+
+ Tuple fields:
+ sdk_version: sdk version (e.g., '10.0.20348.0')
+ sdk_verstr: sdk version numeric string (e.g., '10.0')
+ sdk_vernum: sdk version floating point number (e.g., 10.0)
+ sdk_major: sdk major version integer number (e.g., 10)
+ sdk_minor: sdk minor version integer number (e.g., 0)
+ sdk_comps: sdk version components tuple (e.g., ('10', '0', '20348', '0'))
+
+ Args:
+ version: str
+ sdk version specification
+
+ Returns:
+ None or MSVCSDKVersionComponents namedtuple:
+ """
+
+ if not version:
+ return None
+
+ m = re_msvc_sdk_version.match(version)
+ if not m:
+ return None
+
+ sdk_version = version
+ sdk_comps = tuple(sdk_version.split('.'))
+ sdk_verstr = '.'.join(sdk_comps[:2])
+ sdk_vernum = float(sdk_verstr)
+
+ sdk_major, sdk_minor = [int(x) for x in sdk_comps[:2]]
+
+ msvc_sdk_version_components_def = _MSVC_SDK_VERSION_COMPONENTS_DEFINITION(
+ sdk_version = sdk_version,
+ sdk_verstr = sdk_verstr,
+ sdk_vernum = sdk_vernum,
+ sdk_major = sdk_major,
+ sdk_minor = sdk_minor,
+ sdk_comps = sdk_comps,
+ )
+
+ return msvc_sdk_version_components_def
+
diff --git a/SCons/Tool/MSCommon/MSVC/UtilTests.py b/SCons/Tool/MSCommon/MSVC/UtilTests.py
new file mode 100644
index 0000000..5e14d50
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/UtilTests.py
@@ -0,0 +1,209 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test helper functions for Microsoft Visual C/C++.
+"""
+
+import unittest
+import os
+import re
+
+from SCons.Tool.MSCommon.MSVC import Config
+from SCons.Tool.MSCommon.MSVC import Util
+from SCons.Tool.MSCommon.MSVC import WinSDK
+
+class Data:
+
+ UTIL_PARENT_DIR = os.path.join(os.path.dirname(Util.__file__), os.pardir)
+
+class UtilTests(unittest.TestCase):
+
+ def test_listdir_dirs(self):
+ func = Util.listdir_dirs
+ for dirname, expect in [
+ (None, False), ('', False), ('doesnotexist.xyz.abc', False),
+ (Data.UTIL_PARENT_DIR, True),
+ ]:
+ dirs = func(dirname)
+ self.assertTrue((len(dirs) > 0) == expect, "{}({}): {}".format(
+ func.__name__, repr(dirname), 'list is empty' if expect else 'list is not empty'
+ ))
+
+ def test_process_path(self):
+ func = Util.process_path
+ for p, expect in [
+ (None, True), ('', True),
+ ('doesnotexist.xyz.abc', False), (Data.UTIL_PARENT_DIR, False),
+ ]:
+ rval = func(p)
+ self.assertTrue((p == rval) == expect, "{}({}): {}".format(
+ func.__name__, repr(p), repr(rval)
+ ))
+
+ def test_get_version_prefix(self):
+ func = Util.get_version_prefix
+ for version, expect in [
+ (None, ''), ('', ''),
+ ('.', ''), ('0..0', ''), ('.0', ''), ('0.', ''), ('0.0.', ''),
+ ('0', '0'), ('0Abc', '0'), ('0 0', '0'), ('0,0', '0'),
+ ('0.0', '0.0'), ('0.0.0', '0.0.0'), ('0.0.0.0', '0.0.0.0'), ('0.0.0.0.0', '0.0.0.0.0'),
+ ('00.00.00000', '00.00.00000'), ('00.00.00.0', '00.00.00.0'), ('00.00.00.00', '00.00.00.00'), ('00.0.00000.0', '00.0.00000.0'),
+ ('0.0A', '0.0'), ('0.0.0B', '0.0.0'), ('0.0.0.0 C', '0.0.0.0'), ('0.0.0.0.0 D', '0.0.0.0.0'),
+ ]:
+ prefix = func(version)
+ self.assertTrue(prefix == expect, "{}({}): {} != {}".format(
+ func.__name__, repr(version), repr(prefix), repr(expect)
+ ))
+
+ def test_get_msvc_version_prefix(self):
+ func = Util.get_msvc_version_prefix
+ for version, expect in [
+ (None, ''), ('', ''),
+ ('.', ''), ('0..0', ''), ('.0', ''), ('0.', ''), ('0.0.', ''),
+ ('0', ''), ('0Abc', ''), ('0 0', ''), ('0,0', ''),
+ ('0.0', ''), ('0.0.0', ''), ('0.0.0.0', ''), ('0.0.0.0.0', ''),
+ ('1.0A', '1.0'), ('1.0.0B', '1.0'), ('1.0.0.0 C', '1.0'), ('1.0.0.0.0 D', '1.0'),
+ ('1.00A', '1.0'), ('1.00.0B', '1.0'), ('1.00.0.0 C', '1.0'), ('1.00.0.0.0 D', '1.0'),
+ ]:
+ prefix = func(version)
+ self.assertTrue(prefix == expect, "{}({}): {} != {}".format(
+ func.__name__, repr(version), repr(prefix), repr(expect)
+ ))
+
+ def test_is_toolset_full(self):
+ func = Util.is_toolset_full
+ for toolset, expect in [
+ (None, False), ('', False),
+ ('14.1.', False), ('14.10.', False), ('14.10.00000.', False), ('14.10.000000', False), ('14.1Exp', False),
+ ('14.1', True), ('14.10', True), ('14.10.0', True), ('14.10.00', True), ('14.10.000', True), ('14.10.0000', True), ('14.10.0000', True),
+ ]:
+ rval = func(toolset)
+ self.assertTrue(rval == expect, "{}({}) != {}".format(
+ func.__name__, repr(toolset), repr(rval)
+ ))
+
+ def test_is_toolset_140(self):
+ func = Util.is_toolset_140
+ for toolset, expect in [
+ (None, False), ('', False),
+ ('14.0.', False), ('14.00.', False), ('14.00.00000.', False), ('14.00.000000', False), ('14.0Exp', False),
+ ('14.0', True), ('14.00', True), ('14.00.0', True), ('14.00.00', True), ('14.00.000', True), ('14.00.0000', True), ('14.00.0000', True),
+ ]:
+ rval = func(toolset)
+ self.assertTrue(rval == expect, "{}({}) != {}".format(
+ func.__name__, repr(toolset), repr(rval)
+ ))
+
+ def test_is_toolset_sxs(self):
+ func = Util.is_toolset_sxs
+ for toolset, expect in [
+ (None, False), ('', False),
+ ('14.2.', False), ('14.29.', False), ('14.29.1.', False), ('14.29.16.', False), ('14.29.16.1.', False),
+ ('14.29.16.1', True), ('14.29.16.10', True),
+ ]:
+ rval = func(toolset)
+ self.assertTrue(rval == expect, "{}({}) != {}".format(
+ func.__name__, repr(toolset), repr(rval)
+ ))
+
+ def test_msvc_version_components(self):
+ func = Util.msvc_version_components
+ for vcver, expect in [
+ (None, False), ('', False), ('ABC', False), ('14', False), ('14.1.', False), ('14.16', False),
+ ('14.1', True), ('14.1Exp', True),
+ ('14.1Bug', False),
+ ]:
+ comps_def = func(vcver)
+ msg = 'msvc version components definition is None' if expect else 'msvc version components definition is not None'
+ self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format(
+ func.__name__, repr(vcver), repr(msg)
+ ))
+ for vcver in Config.MSVC_VERSION_SUFFIX.keys():
+ comps_def = func(vcver)
+ self.assertNotEqual(comps_def, None, "{}({}) is None".format(
+ func.__name__, repr(vcver)
+ ))
+
+ def test_msvc_extended_version_components(self):
+ func = Util.msvc_extended_version_components
+ # normal code paths
+ for vcver, expect in [
+ (None, False), ('', False), ('ABC', False), ('14', False), ('14.1.', False),
+ ('14.1', True), ('14.16', True),
+ ('14.1Exp', True), ('14.16Exp', True),
+ ('14.16.2', True), ('14.16.27', True), ('14.16.270', True),
+ ('14.16.2702', True), ('14.16.2702', True), ('14.16.27023', True),
+ ('14.16.270239', False),
+ ('14.16.2Exp', True), ('14.16.27Exp', True), ('14.16.270Exp', True),
+ ('14.16.2702Exp', True), ('14.16.2702Exp', True), ('14.16.27023Exp', True),
+ ('14.16.270239Exp', False),
+ ('14.28.16.9', True), ('14.28.16.10', True),
+ ('14.28.16.9Exp', False), ('14.28.16.10Exp', False),
+ ]:
+ comps_def = func(vcver)
+ msg = 'msvc extended version components definition is None' if expect else 'msvc extended version components definition is not None'
+ self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format(
+ func.__name__, repr(vcver), repr(msg)
+ ))
+ for vcver in Config.MSVC_VERSION_SUFFIX.keys():
+ comps_def = func(vcver)
+ self.assertNotEqual(comps_def, None, "{}({}) is None".format(
+ func.__name__, repr(vcver)
+ ))
+ # force 'just in case' guard code path
+ save_re = Util.re_extended_version
+ Util.re_extended_version = re.compile(r'^(?P<version>[0-9]+)$')
+ for vcver, expect in [
+ ('14', False),
+ ]:
+ comps_def = func(vcver)
+ msg = 'msvc extended version components definition is None' if expect else 'msvc extended version components definition is not None'
+ self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format(
+ func.__name__, repr(vcver), repr(msg)
+ ))
+ Util.re_extended_version = save_re
+
+ def test_msvc_sdk_version_components(self):
+ func = Util.msvc_sdk_version_components
+ for vcver, expect in [
+ (None, False), ('', False), ('ABC', False), ('14', False), ('14.1.', False), ('14.16', False),
+ ('8.1', True), ('10.0', True), ('10.0.20348.0', True),
+ ]:
+ comps_def = func(vcver)
+ msg = 'msvc sdk version components definition is None' if expect else 'msvc sdk version components definition is not None'
+ self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format(
+ func.__name__, repr(vcver), repr(msg)
+ ))
+ for vcver in Config.MSVC_VERSION_SUFFIX.keys():
+ comps_def = func(vcver)
+ sdk_list = WinSDK.get_msvc_sdk_version_list(vcver, msvc_uwp_app=False)
+ for sdk_version in sdk_list:
+ comps_def = func(sdk_version)
+ self.assertNotEqual(comps_def, None, "{}({}) is None".format(
+ func.__name__, repr(vcver)
+ ))
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/SCons/Tool/MSCommon/MSVC/Warnings.py b/SCons/Tool/MSCommon/MSVC/Warnings.py
new file mode 100644
index 0000000..cab5145
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/Warnings.py
@@ -0,0 +1,35 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Warnings for Microsoft Visual C/C++.
+"""
+
+import SCons.Warnings
+
+class VisualCWarning(SCons.Warnings.WarningOnByDefault):
+ pass
+
+class MSVCScriptExecutionWarning(VisualCWarning):
+ pass
+
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py
new file mode 100644
index 0000000..6d18d07
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py
@@ -0,0 +1,264 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Windows SDK functions for Microsoft Visual C/C++.
+"""
+
+import os
+
+from ..common import (
+ debug,
+)
+
+from . import Util
+from . import Config
+from . import Registry
+
+from .Exceptions import (
+ MSVCInternalError,
+)
+
+from . import Dispatcher
+Dispatcher.register_modulename(__name__)
+
+
+_DESKTOP = Config.MSVC_PLATFORM_INTERNAL['Desktop']
+_UWP = Config.MSVC_PLATFORM_INTERNAL['UWP']
+
+def _new_sdk_map():
+ sdk_map = {
+ _DESKTOP.vc_platform: [],
+ _UWP.vc_platform: [],
+ }
+ return sdk_map
+
+def _sdk_10_layout(version):
+
+ folder_prefix = version + '.'
+
+ sdk_map = _new_sdk_map()
+
+ sdk_roots = Registry.sdk_query_paths(version)
+
+ sdk_version_platform_seen = set()
+ sdk_roots_seen = set()
+
+ for sdk_t in sdk_roots:
+
+ sdk_root = sdk_t[0]
+ if sdk_root in sdk_roots_seen:
+ continue
+ sdk_roots_seen.add(sdk_root)
+
+ if not os.path.exists(sdk_root):
+ continue
+
+ sdk_include_path = os.path.join(sdk_root, 'include')
+ if not os.path.exists(sdk_include_path):
+ continue
+
+ for version_nbr, version_nbr_path in Util.listdir_dirs(sdk_include_path):
+
+ if not version_nbr.startswith(folder_prefix):
+ continue
+
+ sdk_inc_path = Util.process_path(os.path.join(version_nbr_path, 'um'))
+ if not os.path.exists(sdk_inc_path):
+ continue
+
+ for vc_platform, sdk_inc_file in [
+ (_DESKTOP.vc_platform, 'winsdkver.h'),
+ (_UWP.vc_platform, 'windows.h'),
+ ]:
+
+ if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
+ continue
+
+ key = (version_nbr, vc_platform)
+ if key in sdk_version_platform_seen:
+ continue
+ sdk_version_platform_seen.add(key)
+
+ sdk_map[vc_platform].append(version_nbr)
+
+ for key, val in sdk_map.items():
+ val.sort(reverse=True)
+
+ return sdk_map
+
+def _sdk_81_layout(version):
+
+ version_nbr = version
+
+ sdk_map = _new_sdk_map()
+
+ sdk_roots = Registry.sdk_query_paths(version)
+
+ sdk_version_platform_seen = set()
+ sdk_roots_seen = set()
+
+ for sdk_t in sdk_roots:
+
+ sdk_root = sdk_t[0]
+ if sdk_root in sdk_roots_seen:
+ continue
+ sdk_roots_seen.add(sdk_root)
+
+ # msvc does not check for existence of root or other files
+
+ sdk_inc_path = Util.process_path(os.path.join(sdk_root, r'include\um'))
+ if not os.path.exists(sdk_inc_path):
+ continue
+
+ for vc_platform, sdk_inc_file in [
+ (_DESKTOP.vc_platform, 'winsdkver.h'),
+ (_UWP.vc_platform, 'windows.h'),
+ ]:
+
+ if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)):
+ continue
+
+ key = (version_nbr, vc_platform)
+ if key in sdk_version_platform_seen:
+ continue
+ sdk_version_platform_seen.add(key)
+
+ sdk_map[vc_platform].append(version_nbr)
+
+ for key, val in sdk_map.items():
+ val.sort(reverse=True)
+
+ return sdk_map
+
+_sdk_map_cache = {}
+_sdk_cache = {}
+
+def _reset_sdk_cache():
+ global _sdk_map_cache
+ global _sdk_cache
+ debug('')
+ _sdk_map_cache = {}
+ _sdk_cache = {}
+
+def _sdk_10(key, reg_version):
+ if key in _sdk_map_cache:
+ sdk_map = _sdk_map_cache[key]
+ else:
+ sdk_map = _sdk_10_layout(reg_version)
+ _sdk_map_cache[key] = sdk_map
+ return sdk_map
+
+def _sdk_81(key, reg_version):
+ if key in _sdk_map_cache:
+ sdk_map = _sdk_map_cache[key]
+ else:
+ sdk_map = _sdk_81_layout(reg_version)
+ _sdk_map_cache[key] = sdk_map
+ return sdk_map
+
+def _combine_sdk_map_list(sdk_map_list):
+ combined_sdk_map = _new_sdk_map()
+ for sdk_map in sdk_map_list:
+ for key, val in sdk_map.items():
+ combined_sdk_map[key].extend(val)
+ return combined_sdk_map
+
+_sdk_dispatch_map = {
+ '10.0': (_sdk_10, '10.0'),
+ '8.1': (_sdk_81, '8.1'),
+}
+
+def _verify_sdk_dispatch_map():
+ debug('')
+ for sdk_version in Config.MSVC_SDK_VERSIONS:
+ if sdk_version in _sdk_dispatch_map:
+ continue
+ err_msg = 'sdk version {} not in sdk_dispatch_map'.format(sdk_version)
+ raise MSVCInternalError(err_msg)
+ return None
+
+def _version_list_sdk_map(version_list):
+ sdk_map_list = []
+ for version in version_list:
+ func, reg_version = _sdk_dispatch_map[version]
+ sdk_map = func(version, reg_version)
+ sdk_map_list.append(sdk_map)
+
+ combined_sdk_map = _combine_sdk_map_list(sdk_map_list)
+ return combined_sdk_map
+
+def _sdk_map(version_list):
+ key = tuple(version_list)
+ if key in _sdk_cache:
+ sdk_map = _sdk_cache[key]
+ else:
+ version_numlist = [float(v) for v in version_list]
+ version_numlist.sort(reverse=True)
+ key = tuple([str(v) for v in version_numlist])
+ sdk_map = _version_list_sdk_map(key)
+ _sdk_cache[key] = sdk_map
+ return sdk_map
+
+def get_msvc_platform(is_uwp=False):
+ platform_def = _UWP if is_uwp else _DESKTOP
+ return platform_def
+
+def get_sdk_version_list(vs_def, platform_def):
+ version_list = vs_def.vc_sdk_versions if vs_def.vc_sdk_versions is not None else []
+ sdk_map = _sdk_map(version_list)
+ sdk_list = sdk_map.get(platform_def.vc_platform, [])
+ return sdk_list
+
+def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False):
+ debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app))
+
+ sdk_versions = []
+
+ verstr = Util.get_msvc_version_prefix(msvc_version)
+ if not verstr:
+ debug('msvc_version is not defined')
+ return sdk_versions
+
+ vs_def = Config.MSVC_VERSION_EXTERNAL.get(verstr, None)
+ if not vs_def:
+ debug('vs_def is not defined')
+ return sdk_versions
+
+ is_uwp = True if msvc_uwp_app in Config.BOOLEAN_SYMBOLS[True] else False
+ platform_def = get_msvc_platform(is_uwp)
+ sdk_list = get_sdk_version_list(vs_def, platform_def)
+
+ sdk_versions.extend(sdk_list)
+ debug('sdk_versions=%s', repr(sdk_versions))
+
+ return sdk_versions
+
+def reset():
+ debug('')
+ _reset_sdk_cache()
+
+def verify():
+ debug('')
+ _verify_sdk_dispatch_map()
+
diff --git a/SCons/Tool/MSCommon/MSVC/WinSDKTests.py b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py
new file mode 100644
index 0000000..2a40e9a
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py
@@ -0,0 +1,132 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test Windows SDK functions for Microsoft Visual C/C++.
+"""
+
+import unittest
+
+from SCons.Tool.MSCommon.MSVC import Config
+from SCons.Tool.MSCommon.MSVC import WinSDK
+from SCons.Tool.MSCommon.MSVC import Registry
+from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError
+
+class Patch:
+
+ class Config:
+
+ class MSVC_SDK_VERSIONS:
+
+ MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS
+
+ @classmethod
+ def enable_copy(cls):
+ hook = set(cls.MSVC_SDK_VERSIONS)
+ Config.MSVC_SDK_VERSIONS = hook
+ return hook
+
+ @classmethod
+ def restore(cls):
+ Config.MSVC_SDK_VERSIONS = cls.MSVC_SDK_VERSIONS
+
+ class Registry:
+
+ class sdk_query_paths:
+
+ sdk_query_paths = Registry.sdk_query_paths
+
+ @classmethod
+ def sdk_query_paths_duplicate(cls, version):
+ sdk_roots = cls.sdk_query_paths(version)
+ sdk_roots = sdk_roots + sdk_roots if sdk_roots else sdk_roots
+ return sdk_roots
+
+ @classmethod
+ def enable_duplicate(cls):
+ hook = cls.sdk_query_paths_duplicate
+ Registry.sdk_query_paths = hook
+ return hook
+
+ @classmethod
+ def restore(cls):
+ Registry.sdk_query_paths = cls.sdk_query_paths
+
+class WinSDKTests(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ Patch.Registry.sdk_query_paths.enable_duplicate()
+
+ @classmethod
+ def tearDownClass(cls):
+ Patch.Registry.sdk_query_paths.restore()
+
+ def test_verify(self):
+ MSVC_SDK_VERSIONS = Patch.Config.MSVC_SDK_VERSIONS.enable_copy()
+ MSVC_SDK_VERSIONS.add('99.0')
+ with self.assertRaises(MSVCInternalError):
+ WinSDK.verify()
+ Patch.Config.MSVC_SDK_VERSIONS.restore()
+
+ def _run_reset(self):
+ WinSDK.reset()
+ self.assertFalse(WinSDK._sdk_map_cache, "WinSDK._sdk_map_cache was not reset")
+ self.assertFalse(WinSDK._sdk_cache, "WinSDK._sdk_cache was not reset")
+
+ def _run_get_msvc_sdk_version_list(self):
+ for vcver in Config.MSVC_VERSION_SUFFIX.keys():
+ for msvc_uwp_app in (True, False):
+ _ = WinSDK.get_msvc_sdk_version_list(vcver, msvc_uwp_app=msvc_uwp_app)
+
+ def _run_version_list_sdk_map(self):
+ for vcver in Config.MSVC_VERSION_SUFFIX.keys():
+ vs_def = Config.MSVC_VERSION_SUFFIX.get(vcver)
+ if not vs_def.vc_sdk_versions:
+ continue
+ _ = WinSDK._version_list_sdk_map(vs_def.vc_sdk_versions)
+
+ def test_version_list_sdk_map(self):
+ self._run_version_list_sdk_map()
+ self._run_version_list_sdk_map()
+ self.assertTrue(WinSDK._sdk_map_cache, "WinSDK._sdk_map_cache is empty")
+
+ def test_get_msvc_sdk_version_list(self):
+ self._run_get_msvc_sdk_version_list()
+ self._run_get_msvc_sdk_version_list()
+ self.assertTrue(WinSDK._sdk_cache, "WinSDK._sdk_cache is empty")
+
+ def test_get_msvc_sdk_version_list_empty(self):
+ func = WinSDK.get_msvc_sdk_version_list
+ for vcver in [None, '', '99', '99.9']:
+ sdk_versions = func(vcver)
+ self.assertFalse(sdk_versions, "{}: sdk versions list was not empty for msvc version {}".format(
+ func.__name__, repr(vcver)
+ ))
+
+ def test_reset(self):
+ self._run_reset()
+
+if __name__ == "__main__":
+ unittest.main()
+
diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py
new file mode 100644
index 0000000..849c82d
--- /dev/null
+++ b/SCons/Tool/MSCommon/MSVC/__init__.py
@@ -0,0 +1,55 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Functions for Microsoft Visual C/C++.
+
+The _reset method is used to restore MSVC module data structures to their
+initial state for testing purposes.
+
+The _verify method is used as a sanity check that MSVC module data structures
+are internally consistent.
+
+Currently:
+* _reset is invoked from reset_installed_vcs in the vc module.
+* _verify is invoked from the last line in the vc module.
+"""
+
+from . import Exceptions # noqa: F401
+
+from . import Config # noqa: F401
+from . import Util # noqa: F401
+from . import Registry # noqa: F401
+from . import SetupEnvDefault # noqa: F401
+from . import Policy # noqa: F401
+from . import WinSDK # noqa: F401
+from . import ScriptArguments # noqa: F401
+
+from . import Dispatcher as _Dispatcher
+
+def _reset():
+ _Dispatcher.reset()
+
+def _verify():
+ _Dispatcher.verify()
+
diff --git a/SCons/Tool/MSCommon/README b/SCons/Tool/MSCommon/README
deleted file mode 100644
index 2268651..0000000
--- a/SCons/Tool/MSCommon/README
+++ /dev/null
@@ -1,107 +0,0 @@
-This is the flow of the compiler detection logic:
-
-External to MSCommon:
-
- The Tool init modules, in their exists() routines, call -> msvc_exists(env)
-
-At the moment, those modules are:
- SCons/Tool/midl.py
- SCons/Tool/mslib.py
- SCons/Tool/mslink.py
- SCons/Tool/msvc.py
- SCons/Tool/msvs.py
-
-env may contain a version request in MSVC_VERSION, but this is not used
-in the detection that follows from msvc_exists(), only in the later
-batch that starts with a call to msvc_setup_env().
-
-Internal to MSCommon/vc.py:
-
-+ MSCommon/vc.py:msvc_exists:
-| vcs = cached_get_installed_vcs(env)
-| returns True if vcs > 0
-|
-+-> MSCommon/vc.py:cached_get_installed_vcs:
- | checks global if we've run previously, if so return it
- | populate the global from -> get_installed_vcs(env)
- |
- +-> MSCommon/vc.py:get_installed_vcs:
- | loop through "known" versions of msvc, granularity is maj.min
- | check for product dir -> find_vc_pdir(env, ver)
- |
- +-> MSCommon/vc.py:find_vc_pdir:
- | From the msvc-version to pdir mapping dict, get reg key base and value
- | If value is none -> find_vc_pdir_vswhere(ver, env)
- |
- +-> MSCommon/vc.py:find_vc_pdir_vswhere:
- | From the vc-version to VS-version mapping table get string
- | Figure out where vswhere is -> msvc_find_vswhere()
- | Use subprocess to call vswhere, return first line of match
- /
- | else get product directory from registry (<= 14.0)
- /
- | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver)
- |
- +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir:
- | Figure out host/target pair
- | if version > 14.0 get specific version by looking in
- | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt
- | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe
- | if 14.0 or less, "do older stuff"
-
-All of this just got us a yes-no answer on whether /some/ msvc version
-exists, but does populate __INSTALLED_VCS_RUN with all of the top-level
-versions as noted for get_installed_vcs
-
-Externally:
-
- Once a module's exists() has been called (or, in the case of
- clang/clangxx, after the compiler has been detected by other means -
- those still expect the rest of the msvc chain but not cl.exe)
- the module's generate() function calls -> msvc_setup_env_once(env)
-
-Internally:
-
-+ MSCommon/vc.py:msvc_setup_env_once:
-| checks for environment flag MSVC_SETUP_RUN
-| if not, -> msvc_setup_env(env) and set flag
-|
-+-+ MSCommon/vc.py:msvc_setup_env:
- | set ver from -> get_default_version(env)
- |
- +-+ MSCommon/vc.py:get_default_version:
- | if no version specified in env.MSVC_VERSION:
- | return first entry from -> cached_get_installed_vcs(env)
- | else return requested version
- /
- | get script from MSVC_USE_SCRIPT if set to a filename
- | -> script_env(script)
- |
- +-+ MSCommon/vc.py:script_env:
- | return (possibly cached) script variables matching script arg
- /
- | else -> msvc_find_valid_batch_script(env, version)
- |
- +-+ MSCommon/vc.py:msvc_find_valid_batch_script:
- | Build a list of plausible target values, and loop through
- | look for host + target -> find_batch_file(env, ver, host, target)
- |
- +-+ MSCommon/vc.py:find_batch_file:
- | call -> find_vc_pdir (see above)
- | use the return to construct a version-biased batfile path, check
- /
- | if not found, try sdk scripts (unknown if this is still useful)
-
-
-Problems:
-- For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS
-- For vswhere-ready versions, detection does not proceed beyond the
- product level ("2019") into individual "features" (individual msvc)
-- As documented for MSVC_VERSION, compilers can only be requested if versions
- are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023
-- Information found in the first pass (msvs_exists) isn't really
- available anywhere except the cached version list, since we just
- return true/false.
-- Since msvc_exists chain of calls does not look at version, we
- can proceed to compiler setup if *any* msvc was found, even if the
- one requested wasn't found.
diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst
new file mode 100644
index 0000000..36e58aa
--- /dev/null
+++ b/SCons/Tool/MSCommon/README.rst
@@ -0,0 +1,501 @@
+.. sectnum::
+
+README - SCons.Tool.MSCommon
+############################
+
+.. contents:: **Table of Contents**
+ :depth: 2
+ :local:
+
+
+Design Notes
+============
+
+* Public, user-callable functions and exception types are available via
+ the ``SCons.Tool.MSCommon`` namespace.
+
+* Some existing code has been moved from ``MSCommon/vc.py`` to the appropriate
+ ``MSCommon/MSVC/<modulename>``.
+
+* No functions from the MSVC module or its child modules are intended to be invoked directly.
+ All functions of interest are made available via the ``SCons.Tool.MSCommon`` namespace.
+ It is anticipated that more code may be moved in the future as new features are added.
+ By exposing the public API through ``SCons.Tool.MSCommon`` there should not be a problem
+ with code movement.
+
+* Additional helper functions primarily used for the test suite were added to
+ ``MSCommon/vc.py`` and are available via the ``SCons.Tool.MSCommon`` namespace.
+
+
+Known Issues
+============
+
+The following issues are known to exist:
+
+* Using ``MSVC_USE_SCRIPT`` and ``MSVC_USE_SCRIPT_ARGS`` to call older Microsoft SDK
+ ``SetEnv.cmd`` batch files may result in build failures. Some of these batch files
+ require delayed expansion to be enabled which is not usually the Windows default.
+ One solution would be to launch the MSVC batch file command in a new command interpreter
+ instance with delayed expansion enabled via command-line options.
+
+* The code to suppress the "No versions of the MSVC compiler were found" warning for
+ the default environment was moved from ``MSCommon/vc.py`` to ``MSCommon/MSVC/SetupEnvDefault.py``.
+ There very few, if any, existing unit tests. Now that the code is isolated in its own
+ module with a limited API, unit tests may be easier to implement.
+
+
+Experimental Features
+=====================
+
+msvc_query_version_toolset(version=None, prefer_newest=True)
+------------------------------------------------------------
+
+The experimental function ``msvc_query_version_toolset`` was added to ``MSCommon/vc.py``
+and is available via the ``SCons.Tool.MSCommon`` namespace. This function takes a version
+specification or a toolset version specification and a product preference as arguments and
+returns the msvc version and the msvc toolset version for the corresponding version specification.
+
+This is a proxy for using the toolset version for selection until that functionality can be added.
+
+Example usage:
+::
+ for version in [
+ '14.3',
+ '14.2',
+ '14.1',
+ '14.0',
+ '14.32',
+ '14.31',
+ '14.29',
+ '14.16',
+ '14.00',
+ '14.28.29333', # only 14.2
+ '14.20.29333', # fictitious for testing
+ ]:
+
+ for prefer_newest in (True, False):
+ try:
+ msvc_version, msvc_toolset_version = msvc_query_version_toolset(version, prefer_newest=prefer_newest)
+ failed = False
+ except MSVCToolsetVersionNotFound:
+ failed = True
+ if failed:
+ msg = 'FAILED'
+ newline = '\n'
+ else:
+ env = Environment(MSVC_VERSION=msvc_version, MSVC_TOOLSET_VERSION=msvc_toolset_version)
+ msg = 'passed'
+ newline = ''
+ print('{}Query: {} version={}, prefer_newest={}'.format(newline, msg, version, prefer_newest))
+
+Example output fragment
+::
+ Build: _build003 {'MSVC_VERSION': '14.3', 'MSVC_TOOLSET_VERSION': '14.29.30133'}
+ Where: C:\Software\MSVS-2022-143-Com\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64\cl.exe
+ Where: C:\Software\MSVS-2022-143-Com\Common7\Tools\guidgen.exe
+ Query: passed version=14.2, prefer_newest=True
+
+ Build: _build004 {'MSVC_VERSION': '14.2', 'MSVC_TOOLSET_VERSION': '14.29.30133'}
+ Where: C:\Software\MSVS-2019-142-Com\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64\cl.exe
+ Where: C:\Software\MSVS-2019-142-Com\Common7\Tools\guidgen.exe
+ Query: passed version=14.2, prefer_newest=False
+
+
+Undocumented Features
+=====================
+
+set SCONS_CACHE_MSVC_FORCE_DEFAULTS=1
+-------------------------------------
+
+The Windows system environment variable ``SCONS_CACHE_MSVC_FORCE_DEFAULTS`` was added. This variable is only
+evaluated when the msvc cache is enabled and accepts the values ``1``, ``true``, and ``True``.
+
+When enabled, the default msvc toolset version and the default sdk version, if not otherwise specified, are
+added to the batch file argument list. This is intended to make the cache more resilient to Visual Studio
+updates that may change the default toolset version and/or the default SDK version.
+
+Example usage:
+::
+
+ @echo Enabling scons cache ...
+ @set "SCONS_CACHE_MSVC_CONFIG=mycachefile.json"
+ @set "SCONS_CACHE_MSVC_FORCE_DEFAULTS=True"
+
+
+End-User Diagnostic Tools
+=========================
+
+Due to the proliferation of user-defined msvc batch file arguments, the likelihood of end-user build
+failures has increased.
+
+Some of the options that may be employed in diagnosing end-user msvc build failures are listed below.
+
+msvc_set_scripterror_policy('Warning') and MSVC_SCRIPTERROR_POLICY='Warning'
+----------------------------------------------------------------------------
+
+Enabling warnings to be produced for detected msvc batch file errors may provide additional context
+for build failures. Refer to the documentation for details.
+
+Change the default policy:
+::
+ from SCons.Tool.MSCommon import msvc_set_scripterror_policy
+
+ msvc_set_scripterror_policy('Warning')
+
+Specify the policy per-environment:
+::
+
+ env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True, MSVC_SCRIPTERROR_POLICY='Warning')
+
+
+set SCONS_MSCOMMON_DEBUG=mydebugfile.txt
+----------------------------------------
+
+The traditional method of diagnosing end-user issues is to enable the internal msvc debug logging.
+
+
+set SCONS_CACHE_MSVC_CONFIG=mycachefile.json
+--------------------------------------------
+
+On occasion, enabling the cache file can prove to be a useful diagnostic tool. If nothing else,
+issues with the msvc environment may be readily apparent.
+
+
+vswhere.exe
+-----------
+
+On occasion, the raw vswhere output may prove useful especially if there are suspected issues with
+detection of installed msvc instances.
+
+Windows command-line sample invocations:
+::
+ @rem 64-Bit Windows
+ "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -all -sort -prerelease -products * -legacy -format json >MYVSWHEREOUTPUT.json
+
+ @rem 32-Bit Windows:
+ "%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe" -all -sort -prerelease -products * -legacy -format json >MYVSWHEREOUTPUT.json
+
+
+Visual Studio Implementation Notes
+==================================
+
+Batch File Arguments
+--------------------
+
+Supported MSVC batch file arguments by product:
+
+======= === === ======= =======
+Product UWP SDK Toolset Spectre
+======= === === ======= =======
+VS2022 X X X X
+------- --- --- ------- -------
+VS2019 X X X X
+------- --- --- ------- -------
+VS2017 X X X X
+------- --- --- ------- -------
+VS2015 X X
+======= === === ======= =======
+
+Supported MSVC batch file arguments in SCons:
+
+======== ====================================== ===================================================
+Argument Construction Variable Script Argument Equivalent
+======== ====================================== ===================================================
+UWP ``MSVC_UWP_APP=True`` ``MSVC_SCRIPT_ARGS='store'``
+-------- -------------------------------------- ---------------------------------------------------
+SDK ``MSVC_SDK_VERSION='10.0.20348.0'`` ``MSVC_SCRIPT_ARGS='10.0.20348.0'``
+-------- -------------------------------------- ---------------------------------------------------
+Toolset ``MSVC_TOOLSET_VERSION='14.31.31103'`` ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'``
+-------- -------------------------------------- ---------------------------------------------------
+Spectre ``MSVC_SPECTRE_LIBS=True`` ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'``
+======== ====================================== ===================================================
+
+**MSVC_SCRIPT_ARGS contents are not validated. Utilizing script arguments that have construction
+variable equivalents is discouraged and may lead to difficult to diagnose build errors.**
+
+Additional constraints:
+
+* ``MSVC_SDK_VERSION='8.1'`` and ``MSVC_UWP_APP=True`` is supported only for the v140
+ build tools (i.e., ``MSVC_VERSION='14.0'`` or ``MSVC_TOOLSET_VERSION='14.0'``).
+
+* ``MSVC_SPECTRE_LIBS=True`` and ``MSVC_UWP_APP=True`` is not supported (i.e., there
+ are no spectre mitigations libraries for UWP builds).
+
+Default Toolset Version
+-----------------------
+
+Side-by-side toolset versions were introduced in Visual Studio 2017.
+The examples shown below are for Visual Studio 2022.
+
+The msvc default toolset version is dependent on the installation options
+selected. This means that the default toolset version may be different for
+each machine given the same Visual Studio product.
+
+The msvc default toolset is not necessarily the latest toolset installed.
+This has implications when a toolset version is specified using only one minor
+digit (e.g., ``MSVC_TOOLSET_VERSION='14.3'`` or ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.3'``).
+
+Explicitly defining ``MSVC_TOOLSET_VERSION=None`` will return the same toolset
+that the msvc batch files would return. When using ``MSVC_SCRIPT_ARGS``, the
+toolset specification should be omitted entirely.
+
+Local installation and summary test results:
+::
+ VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt
+ 14.31.31103
+
+ VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt
+ 14.32.31326
+
+Toolset version summary:
+::
+ 14.31.31103 Environment()
+ 14.31.31103 Environment(MSVC_TOOLSET_VERSION=None)
+
+ 14.32.31326* Environment(MSVC_TOOLSET_VERSION='14.3')
+ 14.32.31326* Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.3'])
+
+ 14.31.31103 Environment(MSVC_TOOLSET_VERSION='14.31')
+ 14.31.31103 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.31'])
+
+ 14.32.31326 Environment(MSVC_TOOLSET_VERSION='14.32')
+ 14.32.31326 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.32'])
+
+VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment:
+::
+ @echo -vcvars_ver=version : Version of VC++ Toolset to select
+ @echo ** [Default] : If -vcvars_ver=version is NOT specified, the toolset specified by
+ @echo [VSInstallDir]\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt will be used.
+ @echo ** 14.0 : VS 2015 (v140) VC++ Toolset (installation of the v140 toolset is a prerequisite)
+ @echo ** 14.xx : VS 2017 or VS 2019 VC++ Toolset, if that version is installed on the system under
+ @echo [VSInstallDir]\VC\MSVC\Tools\[version]. Where '14.xx' specifies a partial
+ @echo [version]. The latest [version] directory that matches the specified value will
+ @echo be used.
+ @echo ** 14.xx.yyyyy : VS 2017 or VS 2019 VC++ Toolset, if that version is installed on the system under
+ @echo [VSInstallDir]\VC\MSVC\Tools\[version]. Where '14.xx.yyyyy' specifies an
+ @echo exact [version] directory to be used.
+ @echo ** 14.xx.VV.vv : VS 2019 C++ side-by-side toolset package identity alias, if the SxS toolset has been installed on the system.
+ @echo Where '14.xx.VV.vv' corresponds to a SxS toolset
+ @echo VV = VS Update Major Version (e.g. "16" for VS 2019 v16.9)
+ @echo vv = VS Update Minor version (e.g. "9" for VS 2019 v16.9)
+ @echo Please see [VSInstallDir]\VC\Auxiliary\Build\[version]\Microsoft.VCToolsVersion.[version].txt for mapping of
+ @echo SxS toolset to [VSInstallDir]\VC\MSVC\Tools\ directory.
+
+VS2022 batch file fragment to determine the default toolset version:
+::
+ @REM Add MSVC
+ set "__VCVARS_DEFAULT_CONFIG_FILE=%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
+
+ @REM We will "fallback" to Microsoft.VCToolsVersion.default.txt (latest) if Microsoft.VCToolsVersion.v143.default.txt does not exist.
+ if EXIST "%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt" (
+ if "%VSCMD_DEBUG%" GEQ "2" @echo [DEBUG:ext\%~nx0] Microsoft.VCToolsVersion.v143.default.txt was found.
+ set "__VCVARS_DEFAULT_CONFIG_FILE=%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt"
+
+ ) else (
+ if "%VSCMD_DEBUG%" GEQ "1" @echo [DEBUG:ext\%~nx0] Microsoft.VCToolsVersion.v143.default.txt was not found. Defaulting to 'Microsoft.VCToolsVersion.default.txt'.
+ )
+
+Empirical evidence suggests that the default toolset version is different from the latest
+toolset version when the toolset version immediately preceding the latest version is
+installed. For example, the ``14.31`` toolset version is installed when the ``14.32``
+toolset version is the latest.
+
+
+Visual Studio Version Notes
+============================
+
+SDK Versions
+------------
+
+==== ============
+SDK Format
+==== ============
+10.0 10.0.XXXXX.Y
+---- ------------
+8.1 8.1
+==== ============
+
+BuildTools Versions
+-------------------
+
+========== ===== ===== ========
+BuildTools VCVER CLVER MSVCRT
+========== ===== ===== ========
+v143 14.3 19.3 140/ucrt
+---------- ----- ----- --------
+v142 14.2 19.2 140/ucrt
+---------- ----- ----- --------
+v141 14.1 19.1 140/ucrt
+---------- ----- ----- --------
+v140 14.0 19.0 140/ucrt
+---------- ----- ----- --------
+v120 12.0 18.0 120
+---------- ----- ----- --------
+v110 11.0 17.0 110
+---------- ----- ----- --------
+v100 10.0 16.0 100
+---------- ----- ----- --------
+v90 9.0 15.0 90
+---------- ----- ----- --------
+v80 8.0 14.0 80
+---------- ----- ----- --------
+v71 7.1 13.1 71
+---------- ----- ----- --------
+v70 7.0 13.0 70
+---------- ----- ----- --------
+v60 6.0 12.0 60
+========== ===== ===== ========
+
+Product Versions
+----------------
+
+======== ===== ========= ============
+Product VSVER SDK BuildTools
+======== ===== ========= ============
+2022 17.0 10.0, 8.1 v143 .. v140
+-------- ----- --------- ------------
+2019 16.0 10.0, 8.1 v142 .. v140
+-------- ----- --------- ------------
+2017 15.0 10.0, 8.1 v141 .. v140
+-------- ----- --------- ------------
+2015 14.0 10.0, 8.1 v140
+-------- ----- --------- ------------
+2013 12.0 v120
+-------- ----- --------- ------------
+2012 11.0 v110
+-------- ----- --------- ------------
+2010 10.0 v100
+-------- ----- --------- ------------
+2008 9.0 v90
+-------- ----- --------- ------------
+2005 8.0 v80
+-------- ----- --------- ------------
+2003.NET 7.1 v71
+-------- ----- --------- ------------
+2002.NET 7.0 v70
+-------- ----- --------- ------------
+6.0 6.0 v60
+======== ===== ========= ============
+
+
+SCons Implementation Notes
+==========================
+
+Compiler Detection Logic
+------------------------
+
+**WARNING: the compiler detection logic documentation below is likely out-of-date.**
+
+In the future, the compiler detection logic documentation will be updated and integrated
+into the current document format as appropriate.
+
+::
+
+ This is the flow of the compiler detection logic:
+
+ External to MSCommon:
+
+ The Tool init modules, in their exists() routines, call -> msvc_exists(env)
+
+ At the moment, those modules are:
+ SCons/Tool/midl.py
+ SCons/Tool/mslib.py
+ SCons/Tool/mslink.py
+ SCons/Tool/msvc.py
+ SCons/Tool/msvs.py
+
+ env may contain a version request in MSVC_VERSION, but this is not used
+ in the detection that follows from msvc_exists(), only in the later
+ batch that starts with a call to msvc_setup_env().
+
+ Internal to MSCommon/vc.py:
+
+ + MSCommon/vc.py:msvc_exists:
+ | vcs = cached_get_installed_vcs(env)
+ | returns True if vcs > 0
+ |
+ +-> MSCommon/vc.py:cached_get_installed_vcs:
+ | checks global if we've run previously, if so return it
+ | populate the global from -> get_installed_vcs(env)
+ |
+ +-> MSCommon/vc.py:get_installed_vcs:
+ | loop through "known" versions of msvc, granularity is maj.min
+ | check for product dir -> find_vc_pdir(env, ver)
+ |
+ +-> MSCommon/vc.py:find_vc_pdir:
+ | From the msvc-version to pdir mapping dict, get reg key base and value
+ | If value is none -> find_vc_pdir_vswhere(ver, env)
+ |
+ +-> MSCommon/vc.py:find_vc_pdir_vswhere:
+ | From the vc-version to VS-version mapping table get string
+ | Figure out where vswhere is -> msvc_find_vswhere()
+ | Use subprocess to call vswhere, return first line of match
+ /
+ | else get product directory from registry (<= 14.0)
+ /
+ | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver)
+ |
+ +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir:
+ | Figure out host/target pair
+ | if version > 14.0 get specific version by looking in
+ | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt
+ | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe
+ | if 14.0 or less, "do older stuff"
+
+ All of this just got us a yes-no answer on whether /some/ msvc version
+ exists, but does populate __INSTALLED_VCS_RUN with all of the top-level
+ versions as noted for get_installed_vcs
+
+ Externally:
+
+ Once a module's exists() has been called (or, in the case of
+ clang/clangxx, after the compiler has been detected by other means -
+ those still expect the rest of the msvc chain but not cl.exe)
+ the module's generate() function calls -> msvc_setup_env_once(env)
+
+ Internally:
+
+ + MSCommon/vc.py:msvc_setup_env_once:
+ | checks for environment flag MSVC_SETUP_RUN
+ | if not, -> msvc_setup_env(env) and set flag
+ |
+ +-+ MSCommon/vc.py:msvc_setup_env:
+ | set ver from -> get_default_version(env)
+ |
+ +-+ MSCommon/vc.py:get_default_version:
+ | if no version specified in env.MSVC_VERSION:
+ | return first entry from -> cached_get_installed_vcs(env)
+ | else return requested version
+ /
+ | get script from MSVC_USE_SCRIPT if set to a filename
+ | -> script_env(script)
+ |
+ +-+ MSCommon/vc.py:script_env:
+ | return (possibly cached) script variables matching script arg
+ /
+ | else -> msvc_find_valid_batch_script(env, version)
+ |
+ +-+ MSCommon/vc.py:msvc_find_valid_batch_script:
+ | Build a list of plausible target values, and loop through
+ | look for host + target -> find_batch_file(env, ver, host, target)
+ |
+ +-+ MSCommon/vc.py:find_batch_file:
+ | call -> find_vc_pdir (see above)
+ | use the return to construct a version-biased batfile path, check
+ /
+ | if not found, try sdk scripts (unknown if this is still useful)
+
+
+ Problems:
+ - For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS
+ - For vswhere-ready versions, detection does not proceed beyond the
+ product level ("2019") into individual "features" (individual msvc)
+ - As documented for MSVC_VERSION, compilers can only be requested if versions
+ are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023
+ - Information found in the first pass (msvs_exists) isn't really
+ available anywhere except the cached version list, since we just
+ return true/false.
+ - Since msvc_exists chain of calls does not look at version, we
+ can proceed to compiler setup if *any* msvc was found, even if the
+ one requested wasn't found.
+
diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py
index 50eed73..c3078ac 100644
--- a/SCons/Tool/MSCommon/__init__.py
+++ b/SCons/Tool/MSCommon/__init__.py
@@ -28,19 +28,26 @@ Common functions for Microsoft Visual Studio and Visual C/C++.
import SCons.Errors
import SCons.Platform.win32
-import SCons.Util
+import SCons.Util # noqa: F401
-from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env
+from SCons.Tool.MSCommon.sdk import ( # noqa: F401
+ mssdk_exists,
+ mssdk_setup_env,
+)
-from SCons.Tool.MSCommon.vc import (
+from SCons.Tool.MSCommon.vc import ( # noqa: F401
msvc_exists,
- msvc_setup_env,
+ msvc_setup_env_tool,
msvc_setup_env_once,
msvc_version_to_maj_min,
msvc_find_vswhere,
+ msvc_sdk_versions,
+ msvc_toolset_versions,
+ msvc_toolset_versions_spectre,
+ msvc_query_version_toolset,
)
-from SCons.Tool.MSCommon.vs import (
+from SCons.Tool.MSCommon.vs import ( # noqa: F401
get_default_version,
get_vs_by_version,
merge_default_version,
@@ -48,6 +55,38 @@ from SCons.Tool.MSCommon.vs import (
query_versions,
)
+from .MSVC.Policy import ( # noqa: F401
+ msvc_set_notfound_policy,
+ msvc_get_notfound_policy,
+ msvc_set_scripterror_policy,
+ msvc_get_scripterror_policy,
+)
+
+from .MSVC.Exceptions import ( # noqa: F401
+ VisualCException,
+ MSVCInternalError,
+ MSVCUserError,
+ MSVCScriptExecutionError,
+ MSVCVersionNotFound,
+ MSVCSDKVersionNotFound,
+ MSVCToolsetVersionNotFound,
+ MSVCSpectreLibsNotFound,
+ MSVCArgumentError,
+)
+
+from .vc import ( # noqa: F401
+ MSVCUnsupportedHostArch,
+ MSVCUnsupportedTargetArch,
+ MSVCScriptNotFound,
+ MSVCUseSettingsError,
+)
+
+from .MSVC.Util import ( # noqa: F401
+ msvc_version_components,
+ msvc_extended_version_components,
+ msvc_sdk_version_components,
+)
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py
index f542e02..ad4c827 100644
--- a/SCons/Tool/MSCommon/common.py
+++ b/SCons/Tool/MSCommon/common.py
@@ -35,12 +35,17 @@ from contextlib import suppress
from pathlib import Path
import SCons.Util
+import SCons.Warnings
+
+class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault):
+ pass
# SCONS_MSCOMMON_DEBUG is internal-use so undocumented:
# set to '-' to print to console, else set to filename to log to
LOGFILE = os.environ.get('SCONS_MSCOMMON_DEBUG')
if LOGFILE:
import logging
+
modulelist = (
# root module and parent/root module
'MSCommon', 'Tool',
@@ -49,6 +54,7 @@ if LOGFILE:
# scons modules
'SCons', 'test', 'scons'
)
+
def get_relative_filename(filename, module_list):
if not filename:
return filename
@@ -59,6 +65,7 @@ if LOGFILE:
except ValueError:
pass
return filename
+
class _Debug_Filter(logging.Filter):
# custom filter for module relative filename
def filter(self, record):
@@ -66,6 +73,7 @@ if LOGFILE:
relfilename = relfilename.replace('\\', '/')
record.relfilename = relfilename
return True
+
# Log format looks like:
# 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [file]
# debug: 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [stdout]
@@ -81,11 +89,11 @@ if LOGFILE:
log_handler = logging.StreamHandler(sys.stdout)
else:
log_handler = logging.FileHandler(filename=LOGFILE)
- logging.basicConfig(
- format=log_format,
- handlers=[log_handler],
- level=logging.DEBUG)
+ log_formatter = logging.Formatter(log_format)
+ log_handler.setFormatter(log_formatter)
logger = logging.getLogger(name=__name__)
+ logger.setLevel(level=logging.DEBUG)
+ logger.addHandler(log_handler)
logger.addFilter(_Debug_Filter())
debug = logger.debug
else:
@@ -98,6 +106,11 @@ CONFIG_CACHE = os.environ.get('SCONS_CACHE_MSVC_CONFIG')
if CONFIG_CACHE in ('1', 'true', 'True'):
CONFIG_CACHE = os.path.join(os.path.expanduser('~'), 'scons_msvc_cache.json')
+# SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal-use so undocumented.
+CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS = False
+if CONFIG_CACHE:
+ if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in ('1', 'true', 'True'):
+ CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS = True
def read_script_env_cache():
""" fetch cached msvc env vars if requested, else return empty dict """
@@ -110,7 +123,15 @@ def read_script_env_cache():
# json to the cache dictionary. Reconstruct the cache key
# tuple from the key list written to json.
envcache_list = json.load(f)
- envcache = {tuple(d['key']): d['data'] for d in envcache_list}
+ if isinstance(envcache_list, list):
+ envcache = {tuple(d['key']): d['data'] for d in envcache_list}
+ else:
+ # don't fail if incompatible format, just proceed without it
+ warn_msg = "Incompatible format for msvc cache file {}: file may be overwritten.".format(
+ repr(CONFIG_CACHE)
+ )
+ SCons.Warnings.warn(MSVCCacheInvalidWarning, warn_msg)
+ debug(warn_msg)
except FileNotFoundError:
# don't fail if no cache file, just proceed without it
pass
@@ -214,7 +235,7 @@ def normalize_env(env, keys, force=False):
# should include it, but keep this here to be safe (needed for reg.exe)
sys32_dir = os.path.join(
os.environ.get("SystemRoot", os.environ.get("windir", r"C:\Windows")), "System32"
-)
+ )
if sys32_dir not in normenv["PATH"]:
normenv["PATH"] = normenv["PATH"] + os.pathsep + sys32_dir
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index a3fd183..7871940 100644
--- a/SCons/Tool/MSCommon/vc.py
+++ b/SCons/Tool/MSCommon/vc.py
@@ -60,9 +60,14 @@ from . import common
from .common import CONFIG_CACHE, debug
from .sdk import get_installed_sdks
+from . import MSVC
-class VisualCException(Exception):
- pass
+from .MSVC.Exceptions import (
+ VisualCException,
+ MSVCUserError,
+ MSVCArgumentError,
+ MSVCToolsetVersionNotFound,
+)
class UnsupportedVersion(VisualCException):
pass
@@ -82,12 +87,13 @@ class NoVersionFound(VisualCException):
class BatchFileExecutionError(VisualCException):
pass
-class MSVCScriptNotFound(VisualCException):
+class MSVCScriptNotFound(MSVCUserError):
pass
-class MSVCUseSettingsError(VisualCException):
+class MSVCUseSettingsError(MSVCUserError):
pass
+
# Dict to 'canonalize' the arch
_ARCH_TO_CANONICAL = {
"amd64" : "amd64",
@@ -417,7 +423,7 @@ def get_msvc_version_numeric(msvc_version):
str: the value converted to a numeric only string
"""
- return ''.join([x for x in msvc_version if x in string_digits + '.'])
+ return ''.join([x for x in msvc_version if x in string_digits + '.'])
def get_host_platform(host_platform):
@@ -512,7 +518,7 @@ def get_host_target(env, msvc_version, all_host_targets=False):
msg = "Unrecognized host architecture %s for version %s"
raise MSVCUnsupportedHostArch(msg % (repr(host_platform), msvc_version)) from None
- return (host_platform, target_platform, host_target_list)
+ return host_platform, target_platform, host_target_list
# If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the
# MSVC_VERSION documentation in Tool/msvc.xml.
@@ -615,11 +621,11 @@ def msvc_version_to_maj_min(msvc_version):
maj = int(t[0])
min = int(t[1])
return maj, min
- except ValueError as e:
+ except ValueError:
raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) from None
-VSWHERE_PATHS = [os.path.join(p,'vswhere.exe') for p in [
+VSWHERE_PATHS = [os.path.join(p,'vswhere.exe') for p in [
os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer"),
os.path.expandvars(r"%ProgramFiles%\Microsoft Visual Studio\Installer"),
os.path.expandvars(r"%ChocolateyInstall%\bin"),
@@ -675,7 +681,8 @@ def find_vc_pdir_vswhere(msvc_version, env=None):
debug("running: %s", vswhere_cmd)
- #cp = subprocess.run(vswhere_cmd, capture_output=True, check=True) # 3.7+ only
+ # TODO: Python 3.7
+ # cp = subprocess.run(vswhere_cmd, capture_output=True, check=True) # 3.7+ only
cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True)
if cp.stdout:
@@ -745,6 +752,9 @@ def find_vc_pdir(env, msvc_version):
except OSError:
debug('no VC registry key %s', repr(key))
else:
+ if msvc_version == '9.0' and key.lower().endswith('\\vcforpython\\9.0\\installdir'):
+ # Visual C++ for Python registry key is installdir (root) not productdir (vc)
+ comps = os.path.join(comps, 'VC')
debug('found VC in registry: %s', comps)
if os.path.exists(comps):
return comps
@@ -772,15 +782,21 @@ def find_batch_file(env, msvc_version, host_arch, target_arch):
vernum = float(msvc_ver_numeric)
arg = ''
+ vcdir = None
+
if vernum > 14:
# 14.1 (VS2017) and later
batfiledir = os.path.join(pdir, "Auxiliary", "Build")
batfile, _ = _GE2017_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)]
batfilename = os.path.join(batfiledir, batfile)
+ vcdir = pdir
elif 14 >= vernum >= 8:
# 14.0 (VS2015) to 8.0 (VS2005)
arg, _ = _LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[(host_arch, target_arch)]
batfilename = os.path.join(pdir, "vcvarsall.bat")
+ if msvc_version == '9.0' and not os.path.exists(batfilename):
+ # Visual C++ for Python batch file is in installdir (root) not productdir (vc)
+ batfilename = os.path.normpath(os.path.join(pdir, os.pardir, "vcvarsall.bat"))
else:
# 7.1 (VS2003) and earlier
pdir = os.path.join(pdir, "Bin")
@@ -799,9 +815,9 @@ def find_batch_file(env, msvc_version, host_arch, target_arch):
sdk_bat_file_path = os.path.join(pdir, sdk_bat_file)
if os.path.exists(sdk_bat_file_path):
debug('sdk_bat_file_path:%s', sdk_bat_file_path)
- return (batfilename, arg, sdk_bat_file_path)
+ return batfilename, arg, vcdir, sdk_bat_file_path
- return (batfilename, arg, None)
+ return batfilename, arg, vcdir, None
__INSTALLED_VCS_RUN = None
_VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt']
@@ -843,7 +859,7 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version):
# 2017 and newer allowed multiple versions of the VC toolset to be
# installed at the same time. This changes the layout.
# Just get the default tool version for now
- #TODO: support setting a specific minor VC version
+ # TODO: support setting a specific minor VC version
default_toolset_file = os.path.join(vc_dir, _VC_TOOLS_VERSION_FILE)
try:
with open(default_toolset_file) as f:
@@ -875,11 +891,6 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version):
elif 14 >= vernum >= 8:
# 14.0 (VS2015) to 8.0 (VS2005)
- cl_path_prefixes = [None]
- if msvc_version == '9.0':
- # Visual C++ for Python registry key is installdir (root) not productdir (vc)
- cl_path_prefixes.append(('VC',))
-
for host_platform, target_platform in host_target_list:
debug('host platform %s, target platform %s for version %s', host_platform, target_platform, msvc_version)
@@ -890,15 +901,12 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version):
continue
_, cl_path_comps = batcharg_clpathcomps
- for cl_path_prefix in cl_path_prefixes:
-
- cl_path_comps_adj = cl_path_prefix + cl_path_comps if cl_path_prefix else cl_path_comps
- cl_path = os.path.join(vc_dir, *cl_path_comps_adj, _CL_EXE_NAME)
- debug('checking for %s at %s', _CL_EXE_NAME, cl_path)
+ cl_path = os.path.join(vc_dir, *cl_path_comps, _CL_EXE_NAME)
+ debug('checking for %s at %s', _CL_EXE_NAME, cl_path)
- if os.path.exists(cl_path):
- debug('found %s', _CL_EXE_NAME)
- return True
+ if os.path.exists(cl_path):
+ debug('found %s', _CL_EXE_NAME)
+ return True
elif 8 > vernum >= 6:
# 7.1 (VS2003) to 6.0 (VS6)
@@ -959,6 +967,20 @@ def reset_installed_vcs():
"""Make it try again to find VC. This is just for the tests."""
global __INSTALLED_VCS_RUN
__INSTALLED_VCS_RUN = None
+ MSVC._reset()
+
+def msvc_default_version(env=None):
+ """Get default msvc version."""
+ vcs = get_installed_vcs(env)
+ msvc_version = vcs[0] if vcs else None
+ debug('msvc_version=%s', repr(msvc_version))
+ return msvc_version
+
+def get_installed_vcs_components(env=None):
+ """Test suite convenience function: return list of installed msvc version component tuples"""
+ vcs = get_installed_vcs(env)
+ msvc_version_component_defs = [MSVC.Util.msvc_version_components(vcver) for vcver in vcs]
+ return msvc_version_component_defs
# Running these batch files isn't cheap: most of the time spent in
# msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'"
@@ -980,7 +1002,7 @@ def reset_installed_vcs():
script_env_cache = None
-def script_env(script, args=None):
+def script_env(env, script, args=None):
global script_env_cache
if script_env_cache is None:
@@ -1006,16 +1028,41 @@ def script_env(script, args=None):
if cache_data is None:
stdout = common.get_output(script, args)
+ cache_data = common.parse_output(stdout)
- # Stupid batch files do not set return code: we take a look at the
- # beginning of the output for an error message instead
+ # debug(stdout)
olines = stdout.splitlines()
- if re_script_output_error.match(olines[0]):
- raise BatchFileExecutionError("\n".join(olines[:2]))
- cache_data = common.parse_output(stdout)
- script_env_cache[cache_key] = cache_data
+ # process stdout: batch file errors (not necessarily first line)
+ script_errlog = []
+ for line in olines:
+ if re_script_output_error.match(line):
+ if not script_errlog:
+ script_errlog.append('vc script errors detected:')
+ script_errlog.append(line)
+
+ if script_errlog:
+ script_errmsg = '\n'.join(script_errlog)
+
+ have_cl = False
+ if cache_data and 'PATH' in cache_data:
+ for p in cache_data['PATH']:
+ if os.path.exists(os.path.join(p, _CL_EXE_NAME)):
+ have_cl = True
+ break
+
+ debug(
+ 'script=%s args=%s have_cl=%s, errors=%s',
+ repr(script), repr(args), repr(have_cl), script_errmsg
+ )
+ MSVC.Policy.msvc_scripterror_handler(env, script_errmsg)
+
+ if not have_cl:
+ # detected errors, cl.exe not on path
+ raise BatchFileExecutionError(script_errmsg)
+
# once we updated cache, give a chance to write out if user wanted
+ script_env_cache[cache_key] = cache_data
common.write_script_env_cache(script_env_cache)
return cache_data
@@ -1034,36 +1081,42 @@ def get_default_version(env):
if not msvc_version == msvs_version:
SCons.Warnings.warn(
SCons.Warnings.VisualVersionMismatch,
- "Requested msvc version (%s) and msvs version (%s) do " \
- "not match: please use MSVC_VERSION only to request a " \
- "visual studio version, MSVS_VERSION is deprecated" \
+ "Requested msvc version (%s) and msvs version (%s) do "
+ "not match: please use MSVC_VERSION only to request a "
+ "visual studio version, MSVS_VERSION is deprecated"
% (msvc_version, msvs_version))
return msvs_version
if not msvc_version:
- installed_vcs = get_installed_vcs(env)
- debug('installed_vcs:%s', installed_vcs)
- if not installed_vcs:
+ msvc_version = msvc_default_version(env)
+ if not msvc_version:
#SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
debug('No installed VCs')
return None
- msvc_version = installed_vcs[0]
debug('using default installed MSVC version %s', repr(msvc_version))
else:
debug('using specified MSVC version %s', repr(msvc_version))
return msvc_version
-def msvc_setup_env_once(env):
+def msvc_setup_env_once(env, tool=None):
try:
- has_run = env["MSVC_SETUP_RUN"]
+ has_run = env["MSVC_SETUP_RUN"]
except KeyError:
has_run = False
if not has_run:
+ MSVC.SetupEnvDefault.register_setup(env, msvc_exists)
msvc_setup_env(env)
env["MSVC_SETUP_RUN"] = True
+ req_tools = MSVC.SetupEnvDefault.register_iserror(env, tool, msvc_exists)
+ if req_tools:
+ msg = "No versions of the MSVC compiler were found.\n" \
+ " Visual Studio C/C++ compilers may not be set correctly.\n" \
+ " Requested tool(s) are: {}".format(req_tools)
+ MSVC.Policy.msvc_notfound_handler(env, msg)
+
def msvc_find_valid_batch_script(env, version):
"""Find and execute appropriate batch script to set up build env.
@@ -1083,38 +1136,30 @@ def msvc_find_valid_batch_script(env, version):
host_platform, target_platform, host_target_list = platforms
d = None
+ version_installed = False
for host_arch, target_arch, in host_target_list:
# Set to current arch.
env['TARGET_ARCH'] = target_arch
+ arg = ''
# Try to locate a batch file for this host/target platform combo
try:
- (vc_script, arg, sdk_script) = find_batch_file(env, version, host_arch, target_arch)
+ (vc_script, arg, vc_dir, sdk_script) = find_batch_file(env, version, host_arch, target_arch)
debug('vc_script:%s vc_script_arg:%s sdk_script:%s', vc_script, arg, sdk_script)
+ version_installed = True
except VisualCException as e:
msg = str(e)
debug('Caught exception while looking for batch file (%s)', msg)
- warn_msg = "VC version %s not installed. " + \
- "C/C++ compilers are most likely not set correctly.\n" + \
- " Installed versions are: %s"
- warn_msg = warn_msg % (version, get_installed_vcs(env))
- SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ version_installed = False
continue
# Try to use the located batch file for this host/target platform combo
debug('use_script 2 %s, args:%s', repr(vc_script), arg)
found = None
if vc_script:
- # Get just version numbers
- maj, min = msvc_version_to_maj_min(version)
- # VS2015+
- if maj >= 14:
- if env.get('MSVC_UWP_APP') == '1':
- # Initialize environment variables with store/UWP paths
- arg = (arg + ' store').lstrip()
-
+ arg = MSVC.ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg)
try:
- d = script_env(vc_script, args=arg)
+ d = script_env(env, vc_script, args=arg)
found = vc_script
except BatchFileExecutionError as e:
debug('use_script 3: failed running VC script %s: %s: Error:%s', repr(vc_script), arg, e)
@@ -1123,7 +1168,7 @@ def msvc_find_valid_batch_script(env, version):
if not vc_script and sdk_script:
debug('use_script 4: trying sdk script: %s', sdk_script)
try:
- d = script_env(sdk_script)
+ d = script_env(env, sdk_script)
found = sdk_script
except BatchFileExecutionError as e:
debug('use_script 5: failed running SDK script %s: Error:%s', repr(sdk_script), e)
@@ -1140,15 +1185,30 @@ def msvc_find_valid_batch_script(env, version):
if not d:
env['TARGET_ARCH'] = target_platform
+ if version_installed:
+ msg = "MSVC version '{}' working host/target script was not found.\n" \
+ " Host = '{}', Target = '{}'\n" \
+ " Visual Studio C/C++ compilers may not be set correctly".format(
+ version, host_platform, target_platform
+ )
+ else:
+ installed_vcs = get_installed_vcs(env)
+ if installed_vcs:
+ msg = "MSVC version '{}' was not found.\n" \
+ " Visual Studio C/C++ compilers may not be set correctly.\n" \
+ " Installed versions are: {}".format(version, installed_vcs)
+ else:
+ msg = "MSVC version '{}' was not found.\n" \
+ " No versions of the MSVC compiler were found.\n" \
+ " Visual Studio C/C++ compilers may not be set correctly".format(version)
+
+ MSVC.Policy.msvc_notfound_handler(env, msg)
+
return d
-_undefined = None
+_UNDEFINED = object()
def get_use_script_use_settings(env):
- global _undefined
-
- if _undefined is None:
- _undefined = object()
# use_script use_settings return values action
# value ignored (value, None) use script or bypass detection
@@ -1157,30 +1217,28 @@ def get_use_script_use_settings(env):
# None (documentation) or evaluates False (code): bypass detection
# need to distinguish between undefined and None
- use_script = env.get('MSVC_USE_SCRIPT', _undefined)
+ use_script = env.get('MSVC_USE_SCRIPT', _UNDEFINED)
- if use_script != _undefined:
+ if use_script != _UNDEFINED:
# use_script defined, use_settings ignored (not type checked)
- return (use_script, None)
+ return use_script, None
# undefined or None: use_settings ignored
use_settings = env.get('MSVC_USE_SETTINGS', None)
if use_settings is not None:
# use script undefined, use_settings defined and not None (type checked)
- return (False, use_settings)
+ return False, use_settings
# use script undefined, use_settings undefined or None
- return (True, None)
-
+ return True, None
def msvc_setup_env(env):
debug('called')
version = get_default_version(env)
if version is None:
- warn_msg = "No version of Visual Studio compiler found - C/C++ " \
- "compilers most likely not set correctly"
- SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
+ if not msvc_setup_env_user(env):
+ MSVC.SetupEnvDefault.set_nodefault()
return None
# XXX: we set-up both MSVS version for backward
@@ -1189,7 +1247,6 @@ def msvc_setup_env(env):
env['MSVS_VERSION'] = version
env['MSVS'] = {}
-
use_script, use_settings = get_use_script_use_settings(env)
if SCons.Util.is_String(use_script):
use_script = use_script.strip()
@@ -1197,7 +1254,7 @@ def msvc_setup_env(env):
raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script))
args = env.subst('$MSVC_USE_SCRIPT_ARGS')
debug('use_script 1 %s %s', repr(use_script), repr(args))
- d = script_env(use_script, args)
+ d = script_env(env, use_script, args)
elif use_script:
d = msvc_find_valid_batch_script(env,version)
debug('use_script 2 %s', d)
@@ -1233,5 +1290,280 @@ def msvc_setup_env(env):
def msvc_exists(env=None, version=None):
vcs = get_installed_vcs(env)
if version is None:
- return len(vcs) > 0
- return version in vcs
+ rval = len(vcs) > 0
+ else:
+ rval = version in vcs
+ if not rval:
+ debug('version=%s, return=%s', repr(version), rval)
+ return rval
+
+def msvc_setup_env_user(env=None):
+ rval = False
+ if env:
+
+ # Intent is to use msvc tools:
+ # MSVC_VERSION: defined and evaluates True
+ # MSVS_VERSION: defined and evaluates True
+ # MSVC_USE_SCRIPT: defined and (is string or evaluates False)
+ # MSVC_USE_SETTINGS: defined and is not None
+
+ # defined and is True
+ for key in ['MSVC_VERSION', 'MSVS_VERSION']:
+ if key in env and env[key]:
+ rval = True
+ debug('key=%s, return=%s', repr(key), rval)
+ return rval
+
+ # defined and (is string or is False)
+ for key in ['MSVC_USE_SCRIPT']:
+ if key in env and (SCons.Util.is_String(env[key]) or not env[key]):
+ rval = True
+ debug('key=%s, return=%s', repr(key), rval)
+ return rval
+
+ # defined and is not None
+ for key in ['MSVC_USE_SETTINGS']:
+ if key in env and env[key] is not None:
+ rval = True
+ debug('key=%s, return=%s', repr(key), rval)
+ return rval
+
+ debug('return=%s', rval)
+ return rval
+
+def msvc_setup_env_tool(env=None, version=None, tool=None):
+ MSVC.SetupEnvDefault.register_tool(env, tool, msvc_exists)
+ rval = False
+ if not rval and msvc_exists(env, version):
+ rval = True
+ if not rval and msvc_setup_env_user(env):
+ rval = True
+ return rval
+
+def msvc_sdk_versions(version=None, msvc_uwp_app=False):
+ debug('version=%s, msvc_uwp_app=%s', repr(version), repr(msvc_uwp_app))
+
+ rval = []
+
+ if not version:
+ version = msvc_default_version()
+
+ if not version:
+ debug('no msvc versions detected')
+ return rval
+
+ version_def = MSVC.Util.msvc_extended_version_components(version)
+ if not version_def:
+ msg = 'Unsupported version {}'.format(repr(version))
+ raise MSVCArgumentError(msg)
+
+ rval = MSVC.WinSDK.get_msvc_sdk_version_list(version, msvc_uwp_app)
+ return rval
+
+def msvc_toolset_versions(msvc_version=None, full=True, sxs=False):
+ debug('msvc_version=%s, full=%s, sxs=%s', repr(msvc_version), repr(full), repr(sxs))
+
+ env = None
+ rval = []
+
+ if not msvc_version:
+ msvc_version = msvc_default_version()
+
+ if not msvc_version:
+ debug('no msvc versions detected')
+ return rval
+
+ if msvc_version not in _VCVER:
+ msg = 'Unsupported msvc version {}'.format(repr(msvc_version))
+ raise MSVCArgumentError(msg)
+
+ vc_dir = find_vc_pdir(env, msvc_version)
+ if not vc_dir:
+ debug('VC folder not found for version %s', repr(msvc_version))
+ return rval
+
+ rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs)
+ return rval
+
+def msvc_toolset_versions_spectre(msvc_version=None):
+ debug('msvc_version=%s', repr(msvc_version))
+
+ env = None
+ rval = []
+
+ if not msvc_version:
+ msvc_version = msvc_default_version()
+
+ if not msvc_version:
+ debug('no msvc versions detected')
+ return rval
+
+ if msvc_version not in _VCVER:
+ msg = 'Unsupported msvc version {}'.format(repr(msvc_version))
+ raise MSVCArgumentError(msg)
+
+ vc_dir = find_vc_pdir(env, msvc_version)
+ if not vc_dir:
+ debug('VC folder not found for version %s', repr(msvc_version))
+ return rval
+
+ rval = MSVC.ScriptArguments._msvc_toolset_versions_spectre_internal(msvc_version, vc_dir)
+ return rval
+
+def msvc_query_version_toolset(version=None, prefer_newest=True):
+ """
+ Returns an msvc version and a toolset version given a version
+ specification.
+
+ This is an EXPERIMENTAL proxy for using a toolset version to perform
+ msvc instance selection. This function will be removed when
+ toolset version is taken into account during msvc instance selection.
+
+ Search for an installed Visual Studio instance that supports the
+ specified version.
+
+ When the specified version contains a component suffix (e.g., Exp),
+ the msvc version is returned and the toolset version is None. No
+ search if performed.
+
+ When the specified version does not contain a component suffix, the
+ version is treated as a toolset version specification. A search is
+ performed for the first msvc instance that contains the toolset
+ version.
+
+ Only Visual Studio 2017 and later support toolset arguments. For
+ Visual Studio 2015 and earlier, the msvc version is returned and
+ the toolset version is None.
+
+ Args:
+
+ version: str
+ The version specification may be an msvc version or a toolset
+ version.
+
+ prefer_newest: bool
+ True: prefer newer Visual Studio instances.
+ False: prefer the "native" Visual Studio instance first. If
+ the native Visual Studio instance is not detected, prefer
+ newer Visual Studio instances.
+
+ Returns:
+ tuple: A tuple containing the msvc version and the msvc toolset version.
+ The msvc toolset version may be None.
+
+ Raises:
+ MSVCToolsetVersionNotFound: when the specified version is not found.
+ MSVCArgumentError: when argument validation fails.
+ """
+ debug('version=%s, prefer_newest=%s', repr(version), repr(prefer_newest))
+
+ env = None
+ msvc_version = None
+ msvc_toolset_version = None
+
+ if not version:
+ version = msvc_default_version()
+
+ if not version:
+ debug('no msvc versions detected')
+ return msvc_version, msvc_toolset_version
+
+ version_def = MSVC.Util.msvc_extended_version_components(version)
+
+ if not version_def:
+ msg = 'Unsupported msvc version {}'.format(repr(version))
+ raise MSVCArgumentError(msg)
+
+ if version_def.msvc_suffix:
+ if version_def.msvc_verstr != version_def.msvc_toolset_version:
+ # toolset version with component suffix
+ msg = 'Unsupported toolset version {}'.format(repr(version))
+ raise MSVCArgumentError(msg)
+
+ if version_def.msvc_vernum > 14.0:
+ # VS2017 and later
+ force_toolset_msvc_version = False
+ else:
+ # VS2015 and earlier
+ force_toolset_msvc_version = True
+ extended_version = version_def.msvc_verstr + '0.00000'
+ if not extended_version.startswith(version_def.msvc_toolset_version):
+ # toolset not equivalent to msvc version
+ msg = 'Unsupported toolset version {} (expected {})'.format(
+ repr(version), repr(extended_version)
+ )
+ raise MSVCArgumentError(msg)
+
+ msvc_version = version_def.msvc_version
+
+ if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP:
+ # VS2013 and earlier
+ debug(
+ 'ignore: msvc_version=%s, msvc_toolset_version=%s',
+ repr(msvc_version), repr(msvc_toolset_version)
+ )
+ return msvc_version, msvc_toolset_version
+
+ if force_toolset_msvc_version:
+ query_msvc_toolset_version = version_def.msvc_verstr
+ else:
+ query_msvc_toolset_version = version_def.msvc_toolset_version
+
+ if prefer_newest:
+ query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
+ else:
+ query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \
+ MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
+
+ seen_msvc_version = set()
+ for query_msvc_version in query_version_list:
+
+ if query_msvc_version in seen_msvc_version:
+ continue
+ seen_msvc_version.add(query_msvc_version)
+
+ vc_dir = find_vc_pdir(env, query_msvc_version)
+ if not vc_dir:
+ continue
+
+ if query_msvc_version.startswith('14.0'):
+ # VS2015 does not support toolset version argument
+ msvc_toolset_version = None
+ debug(
+ 'found: msvc_version=%s, msvc_toolset_version=%s',
+ repr(query_msvc_version), repr(msvc_toolset_version)
+ )
+ return query_msvc_version, msvc_toolset_version
+
+ try:
+ toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir)
+ if toolset_vcvars:
+ msvc_toolset_version = toolset_vcvars
+ debug(
+ 'found: msvc_version=%s, msvc_toolset_version=%s',
+ repr(query_msvc_version), repr(msvc_toolset_version)
+ )
+ return query_msvc_version, msvc_toolset_version
+
+ except MSVCToolsetVersionNotFound:
+ pass
+
+ msvc_toolset_version = query_msvc_toolset_version
+
+ debug(
+ 'not found: msvc_version=%s, msvc_toolset_version=%s',
+ repr(msvc_version), repr(msvc_toolset_version)
+ )
+
+ if version_def.msvc_verstr == msvc_toolset_version:
+ msg = 'MSVC version {} was not found'.format(repr(version))
+ MSVC.Policy.msvc_notfound_handler(None, msg)
+ return msvc_version, msvc_toolset_version
+
+ msg = 'MSVC toolset version {} not found'.format(repr(version))
+ raise MSVCToolsetVersionNotFound(msg)
+
+
+# internal consistency check (should be last)
+MSVC._verify()
+
diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py
index 2b6fbe5..3e37def 100644
--- a/SCons/Tool/MSCommon/vcTests.py
+++ b/SCons/Tool/MSCommon/vcTests.py
@@ -1,5 +1,6 @@
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -19,10 +20,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.
-#
-# from typing import Dict, Any
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
@@ -31,6 +29,7 @@ import unittest
import SCons.Node.FS
import SCons.Warnings
import SCons.Tool.MSCommon.vc
+from SCons.Tool import MSCommon
import TestCmd
@@ -43,14 +42,15 @@ os.chdir(test.workpath(''))
MSVCUnsupportedHostArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedHostArch
MSVCUnsupportedTargetArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedTargetArch
-MS_TOOLS_VERSION='1.1.1'
+MS_TOOLS_VERSION = '1.1.1'
+
class VswhereTestCase(unittest.TestCase):
@staticmethod
def _createVSWhere(path):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w') as f:
- f.write("Created:%s"%f)
+ f.write("Created:%s" % f)
def testDefaults(self):
"""
@@ -59,28 +59,25 @@ class VswhereTestCase(unittest.TestCase):
# import pdb; pdb.set_trace()
vswhere_dirs = [os.path.splitdrive(p)[1] for p in SCons.Tool.MSCommon.vc.VSWHERE_PATHS]
base_dir = test.workpath('fake_vswhere')
- test_vswhere_dirs = [os.path.join(base_dir,d[1:]) for d in vswhere_dirs]
+ test_vswhere_dirs = [os.path.join(base_dir,d[1:]) for d in vswhere_dirs]
SCons.Tool.MSCommon.vc.VSWHERE_PATHS = test_vswhere_dirs
for vsw in test_vswhere_dirs:
VswhereTestCase._createVSWhere(vsw)
find_path = SCons.Tool.MSCommon.vc.msvc_find_vswhere()
- self.assertTrue(vsw == find_path, "Didn't find vswhere in %s found in %s"%(vsw, find_path))
+ self.assertTrue(vsw == find_path, "Didn't find vswhere in %s found in %s" % (vsw, find_path))
os.remove(vsw)
# def specifiedVswherePathTest(self):
# "Verify that msvc.generate() respects VSWHERE Specified"
-
-
-
class MSVcTestCase(unittest.TestCase):
@staticmethod
def _createDummyCl(path, add_bin=True):
"""
- Creates a dummy cl.exe in the correct directory.
+ Creates a dummy cl.exe in the correct directory.
It will create all missing parent directories as well
Args:
@@ -96,14 +93,14 @@ class MSVcTestCase(unittest.TestCase):
create_path = path
if create_path and not os.path.isdir(create_path):
os.makedirs(create_path)
-
+
create_this = os.path.join(create_path,'cl.exe')
# print("Creating: %s"%create_this)
with open(create_this,'w') as ct:
ct.write('created')
-
+
def runTest(self):
"""
Check that all proper HOST_PLATFORM and TARGET_PLATFORM are handled.
@@ -117,7 +114,7 @@ class MSVcTestCase(unittest.TestCase):
_, clpathcomps = SCons.Tool.MSCommon.vc._LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[('x86','x86')]
path = os.path.join('.', *clpathcomps)
MSVcTestCase._createDummyCl(path, add_bin=False)
-
+
# print("retval:%s"%check(env, '.', '8.0'))
@@ -132,7 +129,7 @@ class MSVcTestCase(unittest.TestCase):
with open(tools_version_file, 'w') as tf:
tf.write(MS_TOOLS_VERSION)
except IOError as e:
- print("Failed trying to write :%s :%s"%(tools_version_file, e))
+ print("Failed trying to write :%s :%s" % (tools_version_file, e))
# Now walk all the valid combinations of host/target for 14.1 (VS2017) and later
@@ -158,7 +155,7 @@ class MSVcTestCase(unittest.TestCase):
except MSVCUnsupportedHostArch:
pass
else:
- self.fail('Did not fail when HOST_ARCH specified as: %s'%env['HOST_ARCH'])
+ self.fail('Did not fail when HOST_ARCH specified as: %s' % env['HOST_ARCH'])
# Now test bogus value for TARGET_ARCH
env={'TARGET_ARCH':'GARBAGE', 'HOST_ARCH':'x86'}
@@ -189,7 +186,7 @@ class MSVcTestCase(unittest.TestCase):
try:
result=check(env, '.', '9.0')
# print("for:%s got :%s"%(env, result))
- self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s"%(env['HOST_ARCH'], env['TARGET_ARCH']))
+ self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH']))
except MSVCUnsupportedHostArch:
pass
else:
@@ -200,7 +197,7 @@ class MSVcTestCase(unittest.TestCase):
try:
result=check(env, '.', '9.0')
# print("for:%s got :%s"%(env, result))
- self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s"%(env['HOST_ARCH'], env['TARGET_ARCH']))
+ self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH']))
except MSVCUnsupportedTargetArch:
pass
else:
@@ -241,6 +238,296 @@ class MSVcTestCase(unittest.TestCase):
self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH'])
+class Data:
+
+ HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False
+
+ INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components()
+
+ @classmethod
+ def _msvc_toolset_notfound_list(cls, toolset_seen, toolset_list):
+ new_toolset_list = []
+ if not toolset_list:
+ return new_toolset_list
+ for toolset_version in toolset_list:
+ version = toolset_version
+ comps = version.split('.')
+ if len(comps) != 3:
+ continue
+ # full versions only
+ nloop = 0
+ while nloop < 10:
+ ival = int(comps[-1])
+ if ival == 0:
+ ival = 1000000
+ ival -= 1
+ version = '{}.{}.{:05d}'.format(comps[0], comps[1], ival)
+ if version not in toolset_seen:
+ new_toolset_list.append(version)
+ break
+ nloop += 1
+ return new_toolset_list
+
+ _msvc_toolset_notfound_dict = None
+
+ @classmethod
+ def msvc_toolset_notfound_dict(cls):
+ if cls._msvc_toolset_notfound_dict is None:
+ toolset_seen = set()
+ toolset_dict = {}
+ for symbol in MSCommon.vc._VCVER:
+ toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False)
+ if not toolset_list:
+ continue
+ toolset_seen.update(toolset_list)
+ toolset_dict[symbol] = toolset_list
+ for key, val in toolset_dict.items():
+ toolset_dict[key] = cls._msvc_toolset_notfound_list(toolset_seen, val)
+ cls._msvc_toolset_notfound_dict = toolset_dict
+ return cls._msvc_toolset_notfound_dict
+
+class Patch:
+
+ class MSCommon:
+
+ class vc:
+
+ class msvc_default_version:
+
+ msvc_default_version = MSCommon.vc.msvc_default_version
+
+ @classmethod
+ def msvc_default_version_none(cls):
+ return None
+
+ @classmethod
+ def enable_none(cls):
+ hook = cls.msvc_default_version_none
+ MSCommon.vc.msvc_default_version = hook
+ return hook
+
+ @classmethod
+ def restore(cls):
+ MSCommon.vc.msvc_default_version = cls.msvc_default_version
+
+class MsvcSdkVersionsTests(unittest.TestCase):
+ """Test msvc_sdk_versions"""
+
+ def run_valid_default_msvc(self):
+ symbol = MSCommon.vc.msvc_default_version()
+ version_def = MSCommon.msvc_version_components(symbol)
+ for msvc_uwp_app in (True, False):
+ sdk_list = MSCommon.vc.msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app)
+ if symbol and version_def.msvc_vernum >= 14.0:
+ self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(None)))
+ else:
+ self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None)))
+
+ def test_valid_default_msvc(self):
+ if Data.HAVE_MSVC:
+ Patch.MSCommon.vc.msvc_default_version.enable_none()
+ self.run_valid_default_msvc()
+ Patch.MSCommon.vc.msvc_default_version.restore()
+ self.run_valid_default_msvc()
+
+ def test_valid_vcver(self):
+ for symbol in MSCommon.vc._VCVER:
+ version_def = MSCommon.msvc_version_components(symbol)
+ for msvc_uwp_app in (True, False):
+ sdk_list = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app)
+ if Data.HAVE_MSVC and version_def.msvc_vernum >= 14.0:
+ self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol)))
+ else:
+ self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol)))
+
+ def test_valid_vcver_toolsets(self):
+ for symbol in MSCommon.vc._VCVER:
+ toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True)
+ if toolset_list is None:
+ continue
+ for toolset in toolset_list:
+ extended_def = MSCommon.msvc_extended_version_components(toolset)
+ for msvc_uwp_app in (True, False):
+ sdk_list = MSCommon.vc.msvc_sdk_versions(
+ version=extended_def.msvc_toolset_version,
+ msvc_uwp_app=msvc_uwp_app
+ )
+ self.assertTrue(sdk_list, "SDK list is empty for msvc toolset version {}".format(repr(toolset)))
+
+ def test_invalid_vcver(self):
+ for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']:
+ for msvc_uwp_app in (True, False):
+ with self.assertRaises(MSCommon.vc.MSVCArgumentError):
+ _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app)
+
+ def test_invalid_vcver_toolsets(self):
+ for symbol in ['14.31.123456', '14.31.1.1']:
+ for msvc_uwp_app in (True, False):
+ with self.assertRaises(MSCommon.vc.MSVCArgumentError):
+ _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app)
+
+class MsvcToolsetVersionsTests(unittest.TestCase):
+ """Test msvc_toolset_versions"""
+
+ def run_valid_default_msvc(self):
+ symbol = MSCommon.vc.msvc_default_version()
+ version_def = MSCommon.msvc_version_components(symbol)
+ toolset_none_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=False)
+ toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=False)
+ toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=True)
+ toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=True)
+ if symbol and version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1:
+ # sxs list could be empty
+ self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None)))
+ self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None)))
+ else:
+ self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(None)))
+ self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(None)))
+ self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(None)))
+ self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(None)))
+
+ def test_valid_default_msvc(self):
+ if Data.HAVE_MSVC:
+ Patch.MSCommon.vc.msvc_default_version.enable_none()
+ self.run_valid_default_msvc()
+ Patch.MSCommon.vc.msvc_default_version.restore()
+ self.run_valid_default_msvc()
+
+ def test_valid_vcver(self):
+ for symbol in MSCommon.vc._VCVER:
+ version_def = MSCommon.msvc_version_components(symbol)
+ toolset_none_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=False, sxs=False)
+ toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False)
+ toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True)
+ toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True)
+ if version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1:
+ # sxs list could be empty
+ self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol)))
+ self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol)))
+ else:
+ self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(symbol)))
+ self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(symbol)))
+ self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(symbol)))
+ self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(symbol)))
+
+ def test_invalid_vcver(self):
+ for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']:
+ with self.assertRaises(MSCommon.vc.MSVCArgumentError):
+ _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol)
+
+class MsvcToolsetVersionsSpectreTests(unittest.TestCase):
+
+ def run_valid_default_msvc(self):
+ symbol = MSCommon.vc.msvc_default_version()
+ version_def = MSCommon.msvc_version_components(symbol)
+ spectre_toolset_list = MSCommon.vc.msvc_toolset_versions_spectre(msvc_version=None)
+ if symbol and version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1:
+ # spectre toolset list can empty (may not be installed)
+ pass
+ else:
+ self.assertFalse(spectre_toolset_list, "Toolset spectre list is not empty for msvc version {}".format(repr(None)))
+
+ def test_valid_default_msvc(self):
+ if Data.HAVE_MSVC:
+ Patch.MSCommon.vc.msvc_default_version.enable_none()
+ self.run_valid_default_msvc()
+ Patch.MSCommon.vc.msvc_default_version.restore()
+ self.run_valid_default_msvc()
+
+ def test_valid_vcver(self):
+ for symbol in MSCommon.vc._VCVER:
+ version_def = MSCommon.msvc_version_components(symbol)
+ spectre_toolset_list = MSCommon.vc.msvc_toolset_versions_spectre(msvc_version=symbol)
+ if version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1:
+ # spectre toolset list can empty (may not be installed)
+ pass
+ else:
+ self.assertFalse(spectre_toolset_list, "Toolset spectre list is not empty for msvc version {}".format(repr(symbol)))
+
+ def test_invalid_vcver(self):
+ for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']:
+ with self.assertRaises(MSCommon.vc.MSVCArgumentError):
+ _ = MSCommon.vc.msvc_toolset_versions_spectre(msvc_version=symbol)
+
+class MsvcQueryVersionToolsetTests(unittest.TestCase):
+ """Test msvc_query_toolset_version"""
+
+ def run_valid_default_msvc(self, have_msvc):
+ for prefer_newest in (True, False):
+ msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
+ version=None, prefer_newest=prefer_newest
+ )
+ expect = (have_msvc and msvc_version) or (not have_msvc and not msvc_version)
+ self.assertTrue(expect, "unexpected msvc_version {} for for msvc version {}".format(
+ repr(msvc_version), repr(None)
+ ))
+ version_def = MSCommon.msvc_version_components(msvc_version)
+ if have_msvc and version_def.msvc_vernum > 14.0:
+ # VS2017 and later for toolset version
+ self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(
+ repr(None)
+ ))
+
+ def test_valid_default_msvc(self):
+ if Data.HAVE_MSVC:
+ Patch.MSCommon.vc.msvc_default_version.enable_none()
+ self.run_valid_default_msvc(have_msvc=False)
+ Patch.MSCommon.vc.msvc_default_version.restore()
+ self.run_valid_default_msvc(have_msvc=Data.HAVE_MSVC)
+
+ def test_valid_vcver(self):
+ for symbol in MSCommon.vc._VCVER:
+ version_def = MSCommon.msvc_version_components(symbol)
+ for prefer_newest in (True, False):
+ msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
+ version=symbol, prefer_newest=prefer_newest
+ )
+ self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(symbol)))
+ if version_def.msvc_vernum > 14.0:
+ # VS2017 and later for toolset version
+ self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(
+ repr(symbol)
+ ))
+
+ def test_valid_vcver_toolsets(self):
+ for symbol in MSCommon.vc._VCVER:
+ toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True)
+ if toolset_list is None:
+ continue
+ for toolset in toolset_list:
+ extended_def = MSCommon.msvc_extended_version_components(toolset)
+ for prefer_newest in (True, False):
+ version = extended_def.msvc_toolset_version
+ msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
+ version=version, prefer_newest=prefer_newest
+ )
+ self.assertTrue(msvc_version, "msvc_version is undefined for msvc toolset version {}".format(repr(toolset)))
+ if extended_def.msvc_vernum > 14.0:
+ # VS2017 and later for toolset version
+ self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc toolset version {}".format(
+ repr(toolset)
+ ))
+
+ def test_msvc_query_version_toolset_notfound(self):
+ toolset_notfound_dict = Data.msvc_toolset_notfound_dict()
+ for toolset_notfound_list in toolset_notfound_dict.values():
+ for toolset in toolset_notfound_list[:1]:
+ for prefer_newest in (True, False):
+ with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound):
+ _ = MSCommon.vc.msvc_query_version_toolset(version=toolset, prefer_newest=prefer_newest)
+
+ def test_invalid_vcver(self):
+ for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']:
+ for prefer_newest in (True, False):
+ with self.assertRaises(MSCommon.vc.MSVCArgumentError):
+ _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest)
+
+ def test_invalid_vcver_toolsets(self):
+ for symbol in ['14.16.00000Exp', '14.00.00001', '14.31.123456', '14.31.1.1']:
+ for prefer_newest in (True, False):
+ with self.assertRaises(MSCommon.vc.MSVCArgumentError):
+ _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest)
+
if __name__ == "__main__":
unittest.main()
diff --git a/SCons/Tool/ToolTests.py b/SCons/Tool/ToolTests.py
index 7cff7c8..59d093b 100644
--- a/SCons/Tool/ToolTests.py
+++ b/SCons/Tool/ToolTests.py
@@ -1,5 +1,6 @@
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -19,9 +20,6 @@
# 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 unittest
@@ -99,14 +97,14 @@ class ToolTestCase(unittest.TestCase):
def test_pathfind(self):
- """Test that find_program_path() does not alter PATH"""
+ """Test that find_program_path() alters PATH only if add_path is true"""
env = DummyEnvironment()
PHONY_PATHS = [
r'C:\cygwin64\bin',
r'C:\cygwin\bin',
'/usr/local/dummy/bin',
- env.PHONY_PATH, # will be recognized by dummy WhereIs
+ env.PHONY_PATH, # will be recognized by dummy WhereIs
]
env['ENV'] = {}
env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin'
@@ -114,9 +112,14 @@ class ToolTestCase(unittest.TestCase):
_ = SCons.Tool.find_program_path(env, 'no_tool', default_paths=PHONY_PATHS)
assert env['ENV']['PATH'] == pre_path, env['ENV']['PATH']
+ _ = SCons.Tool.find_program_path(env, 'no_tool', default_paths=PHONY_PATHS, add_path=True)
+ assert env.PHONY_PATH in env['ENV']['PATH'], env['ENV']['PATH']
+
if __name__ == "__main__":
- suite = unittest.makeSuite(ToolTestCase, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ suite = loader.loadTestsFromTestCase(ToolTestCase)
TestUnit.run(suite)
# Local Variables:
diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py
index eda9b0d..33c1d33 100644
--- a/SCons/Tool/__init__.py
+++ b/SCons/Tool/__init__.py
@@ -21,13 +21,10 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-"""SCons.Tool
+"""SCons tool selection.
-SCons tool selection.
-
-This looks for modules that define a callable object that can modify
-a construction environment as appropriate for a given tool (or tool
-chain).
+Looks for modules that define a callable object that can modify a
+construction environment as appropriate for a given tool (or tool chain).
Note that because this subsystem just *selects* a callable that can
modify a construction environment, it's possible for people to define
@@ -36,11 +33,10 @@ one needs to use or tie in to this subsystem in order to roll their own
tool specifications.
"""
-
-
import sys
import os
import importlib.util
+from typing import Optional
import SCons.Builder
import SCons.Errors
@@ -125,78 +121,71 @@ class Tool:
if hasattr(module, 'options'):
self.options = module.options
- def _load_dotted_module_py2(self, short_name, full_name, searchpaths=None):
- import imp
-
- splitname = short_name.split('.')
- index = 0
- srchpths = searchpaths
- for item in splitname:
- file, path, desc = imp.find_module(item, srchpths)
- mod = imp.load_module(full_name, file, path, desc)
- srchpths = [path]
- return mod, file
-
def _tool_module(self):
+ """Try to load a tool module.
+
+ This will hunt in the toolpath for both a Python file (toolname.py)
+ and a Python module (toolname directory), then try the regular
+ import machinery, then fallback to try a zipfile.
+ """
oldpythonpath = sys.path
sys.path = self.toolpath + sys.path
- # sys.stderr.write("Tool:%s\nPATH:%s\n"%(self.name,sys.path))
-
- # From: http://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path/67692#67692
- # import importlib.util
- # spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
- # foo = importlib.util.module_from_spec(spec)
- # spec.loader.exec_module(foo)
- # foo.MyClass()
- # Py 3 code
-
-
- # sys.stderr.write("toolpath:%s\n" % self.toolpath)
- # sys.stderr.write("SCONS.TOOL path:%s\n" % sys.modules['SCons.Tool'].__path__)
+ # These could be enabled under "if debug:"
+ # sys.stderr.write(f"Tool: {self.name}\n")
+ # sys.stderr.write(f"PATH: {sys.path}\n")
+ # sys.stderr.write(f"toolpath: {self.toolpath}\n")
+ # sys.stderr.write(f"SCONS.TOOL path: {sys.modules['SCons.Tool'].__path__}\n")
debug = False
spec = None
found_name = self.name
add_to_scons_tools_namespace = False
+
+ # Search for the tool module, but don't import it, yet.
+ #
+ # First look in the toolpath: these take priority.
+ # TODO: any reason to not just use find_spec here?
for path in self.toolpath:
sepname = self.name.replace('.', os.path.sep)
- file_path = os.path.join(path, "%s.py" % sepname)
+ file_path = os.path.join(path, sepname + ".py")
file_package = os.path.join(path, sepname)
- if debug: sys.stderr.write("Trying:%s %s\n" % (file_path, file_package))
+ if debug: sys.stderr.write(f"Trying: {file_path} {file_package}\n")
if os.path.isfile(file_path):
spec = importlib.util.spec_from_file_location(self.name, file_path)
- if debug: print("file_Path:%s FOUND" % file_path)
+ if debug: sys.stderr.write(f"file_Path: {file_path} FOUND\n")
break
elif os.path.isdir(file_package):
file_package = os.path.join(file_package, '__init__.py')
spec = importlib.util.spec_from_file_location(self.name, file_package)
- if debug: print("PACKAGE:%s Found" % file_package)
+ if debug: sys.stderr.write(f"PACKAGE: {file_package} Found\n")
break
-
else:
continue
+ # Now look in the builtin tools (SCons.Tool package)
if spec is None:
- if debug: sys.stderr.write("NO SPEC :%s\n" % self.name)
+ if debug: sys.stderr.write(f"NO SPEC: {self.name}\n")
spec = importlib.util.find_spec("." + self.name, package='SCons.Tool')
if spec:
found_name = 'SCons.Tool.' + self.name
add_to_scons_tools_namespace = True
- if debug: sys.stderr.write("Spec Found? .%s :%s\n" % (self.name, spec))
+ if debug: sys.stderr.write(f"Spec Found? .{self.name}: {spec}\n")
if spec is None:
+ # we are going to bail out here, format up stuff for the msg
sconstools = os.path.normpath(sys.modules['SCons.Tool'].__path__[0])
if self.toolpath:
sconstools = ", ".join(self.toolpath) + ", " + sconstools
- error_string = "No tool module '%s' found in %s" % (self.name, sconstools)
- raise SCons.Errors.UserError(error_string)
+ msg = f"No tool module '{self.name}' found in {sconstools}"
+ raise SCons.Errors.UserError(msg)
+ # We have a module spec, so we're good to go.
module = importlib.util.module_from_spec(spec)
if module is None:
- if debug: print("MODULE IS NONE:%s" % self.name)
- error_string = "Tool module '%s' failed import" % self.name
- raise SCons.Errors.SConsEnvironmentError(error_string)
+ if debug: sys.stderr.write(f"MODULE IS NONE: {self.name}\n")
+ msg = f"Tool module '{self.name}' failed import"
+ raise SCons.Errors.SConsEnvironmentError(msg)
# Don't reload a tool we already loaded.
sys_modules_value = sys.modules.get(found_name, False)
@@ -208,13 +197,11 @@ class Tool:
# Not sure what to do in the case that there already
# exists sys.modules[self.name] but the source file is
# different.. ?
- module = spec.loader.load_module(spec.name)
-
sys.modules[found_name] = module
+ spec.loader.exec_module(module)
if add_to_scons_tools_namespace:
- # If we found it in SCons.Tool, then add it to the module
+ # If we found it in SCons.Tool, add it to the module
setattr(SCons.Tool, self.name, module)
-
found_module = module
if found_module is not None:
@@ -223,33 +210,36 @@ class Tool:
sys.path = oldpythonpath
+ # We try some other things here, but this is essentially dead code,
+ # because we bailed out above if we didn't find a module spec.
full_name = 'SCons.Tool.' + self.name
try:
return sys.modules[full_name]
except KeyError:
try:
- smpath = sys.modules['SCons.Tool'].__path__
- try:
- module, file = self._load_dotted_module_py2(self.name, full_name, smpath)
- setattr(SCons.Tool, self.name, module)
- if file:
- file.close()
- return module
- except ImportError as e:
- if str(e) != "No module named %s" % self.name:
- raise SCons.Errors.SConsEnvironmentError(e)
- try:
- import zipimport
- importer = zipimport.zipimporter(sys.modules['SCons.Tool'].__path__[0])
- module = importer.load_module(full_name)
- setattr(SCons.Tool, self.name, module)
- return module
- except ImportError as e:
- m = "No tool named '%s': %s" % (self.name, e)
- raise SCons.Errors.SConsEnvironmentError(m)
- except ImportError as e:
- m = "No tool named '%s': %s" % (self.name, e)
- raise SCons.Errors.SConsEnvironmentError(m)
+ # This support was added to enable running inside
+ # a py2exe bundle a long time ago - unclear if it's
+ # still needed. It is *not* intended to load individual
+ # tool modules stored in a zipfile.
+ import zipimport
+
+ tooldir = sys.modules['SCons.Tool'].__path__[0]
+ importer = zipimport.zipimporter(tooldir)
+ if not hasattr(importer, 'find_spec'):
+ # zipimport only added find_spec, exec_module in 3.10,
+ # unlike importlib, where they've been around since 3.4.
+ # If we don't have 'em, use the old way.
+ module = importer.load_module(full_name)
+ else:
+ spec = importer.find_spec(full_name)
+ module = importlib.util.module_from_spec(spec)
+ importer.exec_module(module)
+ sys.modules[full_name] = module
+ setattr(SCons.Tool, self.name, module)
+ return module
+ except zipimport.ZipImportError as e:
+ msg = "No tool named '{self.name}': {e}"
+ raise SCons.Errors.SConsEnvironmentError(msg)
def __call__(self, env, *args, **kw):
if self.init_kw is not None:
@@ -834,15 +824,17 @@ def tool_list(platform, env):
return [x for x in tools if x]
-def find_program_path(env, key_program, default_paths=None):
+def find_program_path(env, key_program, default_paths=None, add_path=False) -> Optional[str]:
"""
Find the location of a tool using various means.
Mainly for windows where tools aren't all installed in /usr/bin, etc.
- :param env: Current Construction Environment.
- :param key_program: Tool to locate.
- :param default_paths: List of additional paths this tool might be found in.
+ Args:
+ env: Current Construction Environment.
+ key_program: Tool to locate.
+ default_paths: List of additional paths this tool might be found in.
+ add_path: If true, add path found if it was from *default_paths*.
"""
# First search in the SCons path
path = env.WhereIs(key_program)
@@ -852,17 +844,25 @@ def find_program_path(env, key_program, default_paths=None):
# Then in the OS path
path = SCons.Util.WhereIs(key_program)
if path:
+ if add_path:
+ env.AppendENVPath('PATH', os.path.dirname(path))
return path
- # Finally, add the defaults and check again. Do not change
- # ['ENV']['PATH'] permananetly, the caller can do that if needed.
+ # Finally, add the defaults and check again.
if default_paths is None:
return path
+
save_path = env['ENV']['PATH']
for p in default_paths:
env.AppendENVPath('PATH', p)
path = env.WhereIs(key_program)
+
+ # By default, do not change ['ENV']['PATH'] permananetly
+ # leave that to the caller, unless add_path is true.
env['ENV']['PATH'] = save_path
+ if path and add_path:
+ env.AppendENVPath('PATH', os.path.dirname(path))
+
return path
# Local Variables:
diff --git a/SCons/Tool/compilation_db.py b/SCons/Tool/compilation_db.py
index d20d171..a4954c1 100644
--- a/SCons/Tool/compilation_db.py
+++ b/SCons/Tool/compilation_db.py
@@ -34,6 +34,8 @@ import itertools
import fnmatch
import SCons
+from SCons.Platform import TempFileMunge
+
from .cxx import CXXSuffixes
from .cc import CSuffixes
from .asm import ASSuffixes, ASPPSuffixes
@@ -53,6 +55,7 @@ class __CompilationDbNode(SCons.Node.Python.Value):
SCons.Node.Python.Value.__init__(self, value)
self.Decider(changed_since_last_build_node)
+
def changed_since_last_build_node(child, target, prev_ni, node):
""" Dummy decider to force always building"""
return True
@@ -103,6 +106,11 @@ def make_emit_compilation_DB_entry(comstr):
return emit_compilation_db_entry
+class CompDBTEMPFILE(TempFileMunge):
+ def __call__(self, target, source, env, for_signature):
+ return self.cmd
+
+
def compilation_db_entry_action(target, source, env, **kw):
"""
Create a dictionary with evaluated command line, target, source
@@ -119,6 +127,7 @@ def compilation_db_entry_action(target, source, env, **kw):
target=env["__COMPILATIONDB_UOUTPUT"],
source=env["__COMPILATIONDB_USOURCE"],
env=env["__COMPILATIONDB_ENV"],
+ overrides={'TEMPFILE': CompDBTEMPFILE}
)
entry = {
@@ -163,6 +172,7 @@ def write_compilation_db(target, source, env):
json.dump(
entries, output_file, sort_keys=True, indent=4, separators=(",", ": ")
)
+ output_file.write("\n")
def scan_compilation_db(node, env, path):
diff --git a/SCons/Tool/docbook/__init__.py b/SCons/Tool/docbook/__init__.py
index 4c3f60c..52e2911 100644
--- a/SCons/Tool/docbook/__init__.py
+++ b/SCons/Tool/docbook/__init__.py
@@ -66,6 +66,18 @@ re_manvolnum = re.compile(r"<manvolnum>([^<]*)</manvolnum>")
re_refname = re.compile(r"<refname>([^<]*)</refname>")
#
+# lxml etree XSLT global max traversal depth
+#
+
+lmxl_xslt_global_max_depth = 3600
+
+if has_lxml and lmxl_xslt_global_max_depth:
+ def __lxml_xslt_set_global_max_depth(max_depth):
+ from lxml import etree
+ etree.XSLT.set_global_max_depth(max_depth)
+ __lxml_xslt_set_global_max_depth(lmxl_xslt_global_max_depth)
+
+#
# Helper functions
#
def __extend_targets_sources(target, source):
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/extensions/docbook.py b/SCons/Tool/docbook/docbook-xsl-1.76.1/extensions/docbook.py
index d79ece3..17241bb 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/extensions/docbook.py
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/extensions/docbook.py
@@ -157,7 +157,7 @@ def convertLength(length):
global pixelsPerInch
global unitHash
- m = re.search('([+-]?[\d.]+)(\S+)', length)
+ m = re.search(r'([+-]?[\d.]+)(\S+)', length)
if m is not None and m.lastindex > 1:
unit = pixelsPerInch
if m.group(2) in unitHash:
@@ -204,11 +204,11 @@ def lookupVariable(tctxt, varName, default):
return default
# If it's a list, get the first element
- if type(varString) == type([]):
+ if isinstance(varString, list):
varString = varString[0]
# If it's not a string, it must be a node, get its content
- if type(varString) != type(""):
+ if not isinstance(varString, str):
varString = varString.content
return varString
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/fo/highlight.xsl b/SCons/Tool/docbook/docbook-xsl-1.76.1/fo/highlight.xsl
index 7843ad2..444aef6 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/fo/highlight.xsl
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/fo/highlight.xsl
@@ -1,77 +1,77 @@
-<?xml version='1.0'?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- xmlns:xslthl="http://xslthl.sf.net"
- exclude-result-prefixes="xslthl"
- version='1.0'>
-
-<!-- ********************************************************************
- $Id: highlight.xsl 8419 2009-04-29 20:37:52Z kosek $
- ********************************************************************
-
- This file is part of the XSL DocBook Stylesheet distribution.
- See ../README or http://docbook.sf.net/release/xsl/current/ for
- and other information.
-
- ******************************************************************** -->
-
-<xsl:import href="../highlighting/common.xsl"/>
-
-<xsl:template match='xslthl:keyword' mode="xslthl">
- <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<xsl:template match='xslthl:string' mode="xslthl">
- <fo:inline font-weight="bold" font-style="italic"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<xsl:template match='xslthl:comment' mode="xslthl">
- <fo:inline font-style="italic"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<xsl:template match='xslthl:tag' mode="xslthl">
- <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<xsl:template match='xslthl:attribute' mode="xslthl">
- <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<xsl:template match='xslthl:value' mode="xslthl">
- <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<!--
-<xsl:template match='xslthl:html'>
- <span style='background:#AFF'><font color='blue'><xsl:apply-templates/></font></span>
-</xsl:template>
-
-<xsl:template match='xslthl:xslt'>
- <span style='background:#AAA'><font color='blue'><xsl:apply-templates/></font></span>
-</xsl:template>
-
-<xsl:template match='xslthl:section'>
- <span style='background:yellow'><xsl:apply-templates/></span>
-</xsl:template>
--->
-
-<xsl:template match='xslthl:number' mode="xslthl">
- <xsl:apply-templates mode="xslthl"/>
-</xsl:template>
-
-<xsl:template match='xslthl:annotation' mode="xslthl">
- <fo:inline color="gray"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-<xsl:template match='xslthl:directive' mode="xslthl">
- <xsl:apply-templates mode="xslthl"/>
-</xsl:template>
-
-<!-- Not sure which element will be in final XSLTHL 2.0 -->
-<xsl:template match='xslthl:doccomment|xslthl:doctype' mode="xslthl">
- <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
-</xsl:template>
-
-
-</xsl:stylesheet>
-
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:xslthl="http://xslthl.sf.net"
+ exclude-result-prefixes="xslthl"
+ version='1.0'>
+
+<!-- ********************************************************************
+ $Id: highlight.xsl 8419 2009-04-29 20:37:52Z kosek $
+ ********************************************************************
+
+ This file is part of the XSL DocBook Stylesheet distribution.
+ See ../README or http://docbook.sf.net/release/xsl/current/ for
+ and other information.
+
+ ******************************************************************** -->
+
+<xsl:import href="../highlighting/common.xsl"/>
+
+<xsl:template match='xslthl:keyword' mode="xslthl">
+ <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<xsl:template match='xslthl:string' mode="xslthl">
+ <fo:inline font-weight="bold" font-style="italic"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<xsl:template match='xslthl:comment' mode="xslthl">
+ <fo:inline font-style="italic"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<xsl:template match='xslthl:tag' mode="xslthl">
+ <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<xsl:template match='xslthl:attribute' mode="xslthl">
+ <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<xsl:template match='xslthl:value' mode="xslthl">
+ <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<!--
+<xsl:template match='xslthl:html'>
+ <span style='background:#AFF'><font color='blue'><xsl:apply-templates/></font></span>
+</xsl:template>
+
+<xsl:template match='xslthl:xslt'>
+ <span style='background:#AAA'><font color='blue'><xsl:apply-templates/></font></span>
+</xsl:template>
+
+<xsl:template match='xslthl:section'>
+ <span style='background:yellow'><xsl:apply-templates/></span>
+</xsl:template>
+-->
+
+<xsl:template match='xslthl:number' mode="xslthl">
+ <xsl:apply-templates mode="xslthl"/>
+</xsl:template>
+
+<xsl:template match='xslthl:annotation' mode="xslthl">
+ <fo:inline color="gray"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+<xsl:template match='xslthl:directive' mode="xslthl">
+ <xsl:apply-templates mode="xslthl"/>
+</xsl:template>
+
+<!-- Not sure which element will be in final XSLTHL 2.0 -->
+<xsl:template match='xslthl:doccomment|xslthl:doctype' mode="xslthl">
+ <fo:inline font-weight="bold"><xsl:apply-templates mode="xslthl"/></fo:inline>
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/c-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/c-hl.xml
index 1503dd1..44ab02d 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/c-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/c-hl.xml
@@ -1,101 +1,101 @@
-<?xml version="1.0" encoding="UTF-8"?>
- <!--
- Syntax highlighting definition for C xslthl - XSLT Syntax Highlighting http://sourceforge.net/projects/xslthl/
- Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks This software is provided 'as-is', without any
- express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this
- software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and
- to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not
- be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an
- acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must
- be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be
- removed or altered from any source distribution. Michal Molhanec <mol1111 at users.sourceforge.net> Jirka Kosek <kosek
- at users.sourceforge.net> Michiel Hendriks <elmuerte at users.sourceforge.net>
- -->
-<highlighters>
- <highlighter type="multiline-comment">
- <start>/**</start>
- <end>*/</end>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="oneline-comment">
- <start>///</start>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>/*</start>
- <end>*/</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="oneline-comment">
- <!-- use the online-comment highlighter to detect directives -->
- <start>#</start>
- <lineBreakEscape>\</lineBreakEscape>
- <style>directive</style>
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <suffix>ul</suffix>
- <suffix>lu</suffix>
- <suffix>u</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <pointStarts />
- <exponent>e</exponent>
- <suffix>ul</suffix>
- <suffix>lu</suffix>
- <suffix>u</suffix>
- <suffix>f</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>auto</keyword>
- <keyword>_Bool</keyword>
- <keyword>break</keyword>
- <keyword>case</keyword>
- <keyword>char</keyword>
- <keyword>_Complex</keyword>
- <keyword>const</keyword>
- <keyword>continue</keyword>
- <keyword>default</keyword>
- <keyword>do</keyword>
- <keyword>double</keyword>
- <keyword>else</keyword>
- <keyword>enum</keyword>
- <keyword>extern</keyword>
- <keyword>float</keyword>
- <keyword>for</keyword>
- <keyword>goto</keyword>
- <keyword>if</keyword>
- <keyword>_Imaginary</keyword>
- <keyword>inline</keyword>
- <keyword>int</keyword>
- <keyword>long</keyword>
- <keyword>register</keyword>
- <keyword>restrict</keyword>
- <keyword>return</keyword>
- <keyword>short</keyword>
- <keyword>signed</keyword>
- <keyword>sizeof</keyword>
- <keyword>static</keyword>
- <keyword>struct</keyword>
- <keyword>switch</keyword>
- <keyword>typedef</keyword>
- <keyword>union</keyword>
- <keyword>unsigned</keyword>
- <keyword>void</keyword>
- <keyword>volatile</keyword>
- <keyword>while</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+ <!--
+ Syntax highlighting definition for C xslthl - XSLT Syntax Highlighting http://sourceforge.net/projects/xslthl/
+ Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks This software is provided 'as-is', without any
+ express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this
+ software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and
+ to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not
+ be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an
+ acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must
+ be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be
+ removed or altered from any source distribution. Michal Molhanec <mol1111 at users.sourceforge.net> Jirka Kosek <kosek
+ at users.sourceforge.net> Michiel Hendriks <elmuerte at users.sourceforge.net>
+ -->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <start>/**</start>
+ <end>*/</end>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="oneline-comment">
+ <start>///</start>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>/*</start>
+ <end>*/</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="oneline-comment">
+ <!-- use the online-comment highlighter to detect directives -->
+ <start>#</start>
+ <lineBreakEscape>\</lineBreakEscape>
+ <style>directive</style>
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <suffix>ul</suffix>
+ <suffix>lu</suffix>
+ <suffix>u</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <pointStarts />
+ <exponent>e</exponent>
+ <suffix>ul</suffix>
+ <suffix>lu</suffix>
+ <suffix>u</suffix>
+ <suffix>f</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>auto</keyword>
+ <keyword>_Bool</keyword>
+ <keyword>break</keyword>
+ <keyword>case</keyword>
+ <keyword>char</keyword>
+ <keyword>_Complex</keyword>
+ <keyword>const</keyword>
+ <keyword>continue</keyword>
+ <keyword>default</keyword>
+ <keyword>do</keyword>
+ <keyword>double</keyword>
+ <keyword>else</keyword>
+ <keyword>enum</keyword>
+ <keyword>extern</keyword>
+ <keyword>float</keyword>
+ <keyword>for</keyword>
+ <keyword>goto</keyword>
+ <keyword>if</keyword>
+ <keyword>_Imaginary</keyword>
+ <keyword>inline</keyword>
+ <keyword>int</keyword>
+ <keyword>long</keyword>
+ <keyword>register</keyword>
+ <keyword>restrict</keyword>
+ <keyword>return</keyword>
+ <keyword>short</keyword>
+ <keyword>signed</keyword>
+ <keyword>sizeof</keyword>
+ <keyword>static</keyword>
+ <keyword>struct</keyword>
+ <keyword>switch</keyword>
+ <keyword>typedef</keyword>
+ <keyword>union</keyword>
+ <keyword>unsigned</keyword>
+ <keyword>void</keyword>
+ <keyword>volatile</keyword>
+ <keyword>while</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/common.xsl b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/common.xsl
index e9b5650..1742377 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/common.xsl
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/common.xsl
@@ -1,120 +1,120 @@
-<?xml version='1.0'?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-
- xmlns:s6hl="http://net.sf.xslthl/ConnectorSaxon6"
- xmlns:sbhl="http://net.sf.xslthl/ConnectorSaxonB"
- xmlns:xhl="http://net.sf.xslthl/ConnectorXalan"
- xmlns:saxon6="http://icl.com/saxon"
- xmlns:saxonb="http://saxon.sf.net/"
- xmlns:xalan="http://xml.apache.org/xalan"
-
- xmlns:exsl="http://exslt.org/common"
- xmlns:xslthl="http://xslthl.sf.net"
- exclude-result-prefixes="exsl xslthl s6hl sbhl xhl"
- version='1.0'>
-
-<!-- ********************************************************************
- $Id: common.xsl 8257 2009-02-20 04:40:16Z abdelazer $
- ********************************************************************
-
- This file is part of the XSL DocBook Stylesheet distribution.
- See ../README or http://docbook.sf.net/release/xsl/current/ for
- and other information.
-
- ******************************************************************** -->
-
-<!-- this construction is needed to have the saxon and xalan connectors working alongside each other -->
-<xalan:component prefix="xhl" functions="highlight">
- <xalan:script lang="javaclass" src="xalan://net.sf.xslthl.ConnectorXalan" />
-</xalan:component>
-
-<!-- for saxon 6 -->
-<saxon6:script implements-prefix="s6hl" language="java" src="java:net.sf.xslthl.ConnectorSaxon6" />
-
-<!-- for saxon 8.5 and later -->
-<saxonb:script implements-prefix="sbhl" language="java" src="java:net.sf.xslthl.ConnectorSaxonB" />
-
-
-<!-- You can override this template to do more complex mapping of
- language attribute to highlighter language ID (see xslthl-config.xml) -->
-<xsl:template name="language.to.xslthl">
- <xsl:param name="context"/>
-
- <xsl:choose>
- <xsl:when test="$context/@language != ''">
- <xsl:value-of select="$context/@language"/>
- </xsl:when>
- <xsl:when test="$highlight.default.language != ''">
- <xsl:value-of select="$highlight.default.language"/>
- </xsl:when>
- </xsl:choose>
-</xsl:template>
-
-<xsl:template name="apply-highlighting">
- <xsl:choose>
- <!-- Do we want syntax highlighting -->
- <xsl:when test="$highlight.source != 0">
- <xsl:variable name="language">
- <xsl:call-template name="language.to.xslthl">
- <xsl:with-param name="context" select="."/>
- </xsl:call-template>
- </xsl:variable>
- <xsl:choose>
- <xsl:when test="$language != ''">
- <xsl:variable name="content">
- <xsl:apply-templates/>
- </xsl:variable>
- <xsl:choose>
- <xsl:when test="function-available('s6hl:highlight')">
- <xsl:apply-templates select="s6hl:highlight($language, exsl:node-set($content), $highlight.xslthl.config)"
- mode="xslthl"/>
- </xsl:when>
- <xsl:when test="function-available('sbhl:highlight')">
- <xsl:apply-templates select="sbhl:highlight($language, exsl:node-set($content), $highlight.xslthl.config)"
- mode="xslthl"/>
- </xsl:when>
- <xsl:when test="function-available('xhl:highlight')">
- <xsl:apply-templates select="xhl:highlight($language, exsl:node-set($content), $highlight.xslthl.config)"
- mode="xslthl"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:copy-of select="$content"/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <xsl:otherwise>
- <xsl:apply-templates/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:when>
- <!-- No syntax highlighting -->
- <xsl:otherwise>
- <xsl:apply-templates/>
- </xsl:otherwise>
- </xsl:choose>
-</xsl:template>
-
-<!-- A fallback when the specific style isn't recognized -->
-<xsl:template match="xslthl:*" mode="xslthl">
- <xsl:message>
- <xsl:text>unprocessed xslthl style: </xsl:text>
- <xsl:value-of select="local-name(.)" />
- </xsl:message>
- <xsl:apply-templates mode="xslthl"/>
-</xsl:template>
-
-<!-- Copy over already produced markup (FO/HTML) -->
-<xsl:template match="node()" mode="xslthl" priority="-1">
- <xsl:copy>
- <xsl:apply-templates select="node()" mode="xslthl"/>
- </xsl:copy>
-</xsl:template>
-
-<xsl:template match="*" mode="xslthl">
- <xsl:copy>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates select="node()" mode="xslthl"/>
- </xsl:copy>
-</xsl:template>
-
-</xsl:stylesheet>
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:s6hl="http://net.sf.xslthl/ConnectorSaxon6"
+ xmlns:sbhl="http://net.sf.xslthl/ConnectorSaxonB"
+ xmlns:xhl="http://net.sf.xslthl/ConnectorXalan"
+ xmlns:saxon6="http://icl.com/saxon"
+ xmlns:saxonb="http://saxon.sf.net/"
+ xmlns:xalan="http://xml.apache.org/xalan"
+
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:xslthl="http://xslthl.sf.net"
+ exclude-result-prefixes="exsl xslthl s6hl sbhl xhl"
+ version='1.0'>
+
+<!-- ********************************************************************
+ $Id: common.xsl 8257 2009-02-20 04:40:16Z abdelazer $
+ ********************************************************************
+
+ This file is part of the XSL DocBook Stylesheet distribution.
+ See ../README or http://docbook.sf.net/release/xsl/current/ for
+ and other information.
+
+ ******************************************************************** -->
+
+<!-- this construction is needed to have the saxon and xalan connectors working alongside each other -->
+<xalan:component prefix="xhl" functions="highlight">
+ <xalan:script lang="javaclass" src="xalan://net.sf.xslthl.ConnectorXalan" />
+</xalan:component>
+
+<!-- for saxon 6 -->
+<saxon6:script implements-prefix="s6hl" language="java" src="java:net.sf.xslthl.ConnectorSaxon6" />
+
+<!-- for saxon 8.5 and later -->
+<saxonb:script implements-prefix="sbhl" language="java" src="java:net.sf.xslthl.ConnectorSaxonB" />
+
+
+<!-- You can override this template to do more complex mapping of
+ language attribute to highlighter language ID (see xslthl-config.xml) -->
+<xsl:template name="language.to.xslthl">
+ <xsl:param name="context"/>
+
+ <xsl:choose>
+ <xsl:when test="$context/@language != ''">
+ <xsl:value-of select="$context/@language"/>
+ </xsl:when>
+ <xsl:when test="$highlight.default.language != ''">
+ <xsl:value-of select="$highlight.default.language"/>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="apply-highlighting">
+ <xsl:choose>
+ <!-- Do we want syntax highlighting -->
+ <xsl:when test="$highlight.source != 0">
+ <xsl:variable name="language">
+ <xsl:call-template name="language.to.xslthl">
+ <xsl:with-param name="context" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$language != ''">
+ <xsl:variable name="content">
+ <xsl:apply-templates/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="function-available('s6hl:highlight')">
+ <xsl:apply-templates select="s6hl:highlight($language, exsl:node-set($content), $highlight.xslthl.config)"
+ mode="xslthl"/>
+ </xsl:when>
+ <xsl:when test="function-available('sbhl:highlight')">
+ <xsl:apply-templates select="sbhl:highlight($language, exsl:node-set($content), $highlight.xslthl.config)"
+ mode="xslthl"/>
+ </xsl:when>
+ <xsl:when test="function-available('xhl:highlight')">
+ <xsl:apply-templates select="xhl:highlight($language, exsl:node-set($content), $highlight.xslthl.config)"
+ mode="xslthl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy-of select="$content"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <!-- No syntax highlighting -->
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- A fallback when the specific style isn't recognized -->
+<xsl:template match="xslthl:*" mode="xslthl">
+ <xsl:message>
+ <xsl:text>unprocessed xslthl style: </xsl:text>
+ <xsl:value-of select="local-name(.)" />
+ </xsl:message>
+ <xsl:apply-templates mode="xslthl"/>
+</xsl:template>
+
+<!-- Copy over already produced markup (FO/HTML) -->
+<xsl:template match="node()" mode="xslthl" priority="-1">
+ <xsl:copy>
+ <xsl:apply-templates select="node()" mode="xslthl"/>
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="*" mode="xslthl">
+ <xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates select="node()" mode="xslthl"/>
+ </xsl:copy>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/cpp-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/cpp-hl.xml
index db57d5e..2213b7c 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/cpp-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/cpp-hl.xml
@@ -1,150 +1,150 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for C++
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="multiline-comment">
- <start>/**</start>
- <end>*/</end>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="oneline-comment">
- <start>///</start>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>/*</start>
- <end>*/</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="oneline-comment">
- <!-- use the online-comment highlighter to detect directives -->
- <start>#</start>
- <lineBreakEscape>\</lineBreakEscape>
- <style>directive</style>
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <suffix>ul</suffix>
- <suffix>lu</suffix>
- <suffix>u</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <pointStarts />
- <exponent>e</exponent>
- <suffix>ul</suffix>
- <suffix>lu</suffix>
- <suffix>u</suffix>
- <suffix>f</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <!-- C keywords -->
- <keyword>auto</keyword>
- <keyword>_Bool</keyword>
- <keyword>break</keyword>
- <keyword>case</keyword>
- <keyword>char</keyword>
- <keyword>_Complex</keyword>
- <keyword>const</keyword>
- <keyword>continue</keyword>
- <keyword>default</keyword>
- <keyword>do</keyword>
- <keyword>double</keyword>
- <keyword>else</keyword>
- <keyword>enum</keyword>
- <keyword>extern</keyword>
- <keyword>float</keyword>
- <keyword>for</keyword>
- <keyword>goto</keyword>
- <keyword>if</keyword>
- <keyword>_Imaginary</keyword>
- <keyword>inline</keyword>
- <keyword>int</keyword>
- <keyword>long</keyword>
- <keyword>register</keyword>
- <keyword>restrict</keyword>
- <keyword>return</keyword>
- <keyword>short</keyword>
- <keyword>signed</keyword>
- <keyword>sizeof</keyword>
- <keyword>static</keyword>
- <keyword>struct</keyword>
- <keyword>switch</keyword>
- <keyword>typedef</keyword>
- <keyword>union</keyword>
- <keyword>unsigned</keyword>
- <keyword>void</keyword>
- <keyword>volatile</keyword>
- <keyword>while</keyword>
- <!-- C++ keywords -->
- <keyword>asm</keyword>
- <keyword>dynamic_cast</keyword>
- <keyword>namespace</keyword>
- <keyword>reinterpret_cast</keyword>
- <keyword>try</keyword>
- <keyword>bool</keyword>
- <keyword>explicit</keyword>
- <keyword>new</keyword>
- <keyword>static_cast</keyword>
- <keyword>typeid</keyword>
- <keyword>catch</keyword>
- <keyword>false</keyword>
- <keyword>operator</keyword>
- <keyword>template</keyword>
- <keyword>typename</keyword>
- <keyword>class</keyword>
- <keyword>friend</keyword>
- <keyword>private</keyword>
- <keyword>this</keyword>
- <keyword>using</keyword>
- <keyword>const_cast</keyword>
- <keyword>inline</keyword>
- <keyword>public</keyword>
- <keyword>throw</keyword>
- <keyword>virtual</keyword>
- <keyword>delete</keyword>
- <keyword>mutable</keyword>
- <keyword>protected</keyword>
- <keyword>true</keyword>
- <keyword>wchar_t</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for C++
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <start>/**</start>
+ <end>*/</end>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="oneline-comment">
+ <start>///</start>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>/*</start>
+ <end>*/</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="oneline-comment">
+ <!-- use the online-comment highlighter to detect directives -->
+ <start>#</start>
+ <lineBreakEscape>\</lineBreakEscape>
+ <style>directive</style>
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <suffix>ul</suffix>
+ <suffix>lu</suffix>
+ <suffix>u</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <pointStarts />
+ <exponent>e</exponent>
+ <suffix>ul</suffix>
+ <suffix>lu</suffix>
+ <suffix>u</suffix>
+ <suffix>f</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <!-- C keywords -->
+ <keyword>auto</keyword>
+ <keyword>_Bool</keyword>
+ <keyword>break</keyword>
+ <keyword>case</keyword>
+ <keyword>char</keyword>
+ <keyword>_Complex</keyword>
+ <keyword>const</keyword>
+ <keyword>continue</keyword>
+ <keyword>default</keyword>
+ <keyword>do</keyword>
+ <keyword>double</keyword>
+ <keyword>else</keyword>
+ <keyword>enum</keyword>
+ <keyword>extern</keyword>
+ <keyword>float</keyword>
+ <keyword>for</keyword>
+ <keyword>goto</keyword>
+ <keyword>if</keyword>
+ <keyword>_Imaginary</keyword>
+ <keyword>inline</keyword>
+ <keyword>int</keyword>
+ <keyword>long</keyword>
+ <keyword>register</keyword>
+ <keyword>restrict</keyword>
+ <keyword>return</keyword>
+ <keyword>short</keyword>
+ <keyword>signed</keyword>
+ <keyword>sizeof</keyword>
+ <keyword>static</keyword>
+ <keyword>struct</keyword>
+ <keyword>switch</keyword>
+ <keyword>typedef</keyword>
+ <keyword>union</keyword>
+ <keyword>unsigned</keyword>
+ <keyword>void</keyword>
+ <keyword>volatile</keyword>
+ <keyword>while</keyword>
+ <!-- C++ keywords -->
+ <keyword>asm</keyword>
+ <keyword>dynamic_cast</keyword>
+ <keyword>namespace</keyword>
+ <keyword>reinterpret_cast</keyword>
+ <keyword>try</keyword>
+ <keyword>bool</keyword>
+ <keyword>explicit</keyword>
+ <keyword>new</keyword>
+ <keyword>static_cast</keyword>
+ <keyword>typeid</keyword>
+ <keyword>catch</keyword>
+ <keyword>false</keyword>
+ <keyword>operator</keyword>
+ <keyword>template</keyword>
+ <keyword>typename</keyword>
+ <keyword>class</keyword>
+ <keyword>friend</keyword>
+ <keyword>private</keyword>
+ <keyword>this</keyword>
+ <keyword>using</keyword>
+ <keyword>const_cast</keyword>
+ <keyword>inline</keyword>
+ <keyword>public</keyword>
+ <keyword>throw</keyword>
+ <keyword>virtual</keyword>
+ <keyword>delete</keyword>
+ <keyword>mutable</keyword>
+ <keyword>protected</keyword>
+ <keyword>true</keyword>
+ <keyword>wchar_t</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/csharp-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/csharp-hl.xml
index 99c2e3e..8a8a76d 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/csharp-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/csharp-hl.xml
@@ -1,187 +1,187 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for C#
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="multiline-comment">
- <start>/**</start>
- <end>*/</end>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="oneline-comment">
- <start>///</start>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>/*</start>
- <end>*/</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="annotation">
- <!-- annotations are called (custom) "attributes" in .NET -->
- <start>[</start>
- <end>]</end>
- <valueStart>(</valueStart>
- <valueEnd>)</valueEnd>
- </highlighter>
- <highlighter type="oneline-comment">
- <!-- C# supports a couple of directives -->
- <start>#</start>
- <lineBreakEscape>\</lineBreakEscape>
- <style>directive</style>
- </highlighter>
- <highlighter type="string">
- <!-- strings starting with an "@" can span multiple lines -->
- <string>@"</string>
- <endString>"</endString>
- <escape>\</escape>
- <spanNewLines />
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <suffix>ul</suffix>
- <suffix>lu</suffix>
- <suffix>u</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <pointStarts />
- <exponent>e</exponent>
- <suffix>ul</suffix>
- <suffix>lu</suffix>
- <suffix>u</suffix>
- <suffix>f</suffix>
- <suffix>d</suffix>
- <suffix>m</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>abstract</keyword>
- <keyword>as</keyword>
- <keyword>base</keyword>
- <keyword>bool</keyword>
- <keyword>break</keyword>
- <keyword>byte</keyword>
- <keyword>case</keyword>
- <keyword>catch</keyword>
- <keyword>char</keyword>
- <keyword>checked</keyword>
- <keyword>class</keyword>
- <keyword>const</keyword>
- <keyword>continue</keyword>
- <keyword>decimal</keyword>
- <keyword>default</keyword>
- <keyword>delegate</keyword>
- <keyword>do</keyword>
- <keyword>double</keyword>
- <keyword>else</keyword>
- <keyword>enum</keyword>
- <keyword>event</keyword>
- <keyword>explicit</keyword>
- <keyword>extern</keyword>
- <keyword>false</keyword>
- <keyword>finally</keyword>
- <keyword>fixed</keyword>
- <keyword>float</keyword>
- <keyword>for</keyword>
- <keyword>foreach</keyword>
- <keyword>goto</keyword>
- <keyword>if</keyword>
- <keyword>implicit</keyword>
- <keyword>in</keyword>
- <keyword>int</keyword>
- <keyword>interface</keyword>
- <keyword>internal</keyword>
- <keyword>is</keyword>
- <keyword>lock</keyword>
- <keyword>long</keyword>
- <keyword>namespace</keyword>
- <keyword>new</keyword>
- <keyword>null</keyword>
- <keyword>object</keyword>
- <keyword>operator</keyword>
- <keyword>out</keyword>
- <keyword>override</keyword>
- <keyword>params</keyword>
- <keyword>private</keyword>
- <keyword>protected</keyword>
- <keyword>public</keyword>
- <keyword>readonly</keyword>
- <keyword>ref</keyword>
- <keyword>return</keyword>
- <keyword>sbyte</keyword>
- <keyword>sealed</keyword>
- <keyword>short</keyword>
- <keyword>sizeof</keyword>
- <keyword>stackalloc</keyword>
- <keyword>static</keyword>
- <keyword>string</keyword>
- <keyword>struct</keyword>
- <keyword>switch</keyword>
- <keyword>this</keyword>
- <keyword>throw</keyword>
- <keyword>true</keyword>
- <keyword>try</keyword>
- <keyword>typeof</keyword>
- <keyword>uint</keyword>
- <keyword>ulong</keyword>
- <keyword>unchecked</keyword>
- <keyword>unsafe</keyword>
- <keyword>ushort</keyword>
- <keyword>using</keyword>
- <keyword>virtual</keyword>
- <keyword>void</keyword>
- <keyword>volatile</keyword>
- <keyword>while</keyword>
- </highlighter>
- <highlighter type="keywords">
- <!-- special words, not really keywords -->
- <keyword>add</keyword>
- <keyword>alias</keyword>
- <keyword>get</keyword>
- <keyword>global</keyword>
- <keyword>partial</keyword>
- <keyword>remove</keyword>
- <keyword>set</keyword>
- <keyword>value</keyword>
- <keyword>where</keyword>
- <keyword>yield</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for C#
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <start>/**</start>
+ <end>*/</end>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="oneline-comment">
+ <start>///</start>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>/*</start>
+ <end>*/</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="annotation">
+ <!-- annotations are called (custom) "attributes" in .NET -->
+ <start>[</start>
+ <end>]</end>
+ <valueStart>(</valueStart>
+ <valueEnd>)</valueEnd>
+ </highlighter>
+ <highlighter type="oneline-comment">
+ <!-- C# supports a couple of directives -->
+ <start>#</start>
+ <lineBreakEscape>\</lineBreakEscape>
+ <style>directive</style>
+ </highlighter>
+ <highlighter type="string">
+ <!-- strings starting with an "@" can span multiple lines -->
+ <string>@"</string>
+ <endString>"</endString>
+ <escape>\</escape>
+ <spanNewLines />
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <suffix>ul</suffix>
+ <suffix>lu</suffix>
+ <suffix>u</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <pointStarts />
+ <exponent>e</exponent>
+ <suffix>ul</suffix>
+ <suffix>lu</suffix>
+ <suffix>u</suffix>
+ <suffix>f</suffix>
+ <suffix>d</suffix>
+ <suffix>m</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>abstract</keyword>
+ <keyword>as</keyword>
+ <keyword>base</keyword>
+ <keyword>bool</keyword>
+ <keyword>break</keyword>
+ <keyword>byte</keyword>
+ <keyword>case</keyword>
+ <keyword>catch</keyword>
+ <keyword>char</keyword>
+ <keyword>checked</keyword>
+ <keyword>class</keyword>
+ <keyword>const</keyword>
+ <keyword>continue</keyword>
+ <keyword>decimal</keyword>
+ <keyword>default</keyword>
+ <keyword>delegate</keyword>
+ <keyword>do</keyword>
+ <keyword>double</keyword>
+ <keyword>else</keyword>
+ <keyword>enum</keyword>
+ <keyword>event</keyword>
+ <keyword>explicit</keyword>
+ <keyword>extern</keyword>
+ <keyword>false</keyword>
+ <keyword>finally</keyword>
+ <keyword>fixed</keyword>
+ <keyword>float</keyword>
+ <keyword>for</keyword>
+ <keyword>foreach</keyword>
+ <keyword>goto</keyword>
+ <keyword>if</keyword>
+ <keyword>implicit</keyword>
+ <keyword>in</keyword>
+ <keyword>int</keyword>
+ <keyword>interface</keyword>
+ <keyword>internal</keyword>
+ <keyword>is</keyword>
+ <keyword>lock</keyword>
+ <keyword>long</keyword>
+ <keyword>namespace</keyword>
+ <keyword>new</keyword>
+ <keyword>null</keyword>
+ <keyword>object</keyword>
+ <keyword>operator</keyword>
+ <keyword>out</keyword>
+ <keyword>override</keyword>
+ <keyword>params</keyword>
+ <keyword>private</keyword>
+ <keyword>protected</keyword>
+ <keyword>public</keyword>
+ <keyword>readonly</keyword>
+ <keyword>ref</keyword>
+ <keyword>return</keyword>
+ <keyword>sbyte</keyword>
+ <keyword>sealed</keyword>
+ <keyword>short</keyword>
+ <keyword>sizeof</keyword>
+ <keyword>stackalloc</keyword>
+ <keyword>static</keyword>
+ <keyword>string</keyword>
+ <keyword>struct</keyword>
+ <keyword>switch</keyword>
+ <keyword>this</keyword>
+ <keyword>throw</keyword>
+ <keyword>true</keyword>
+ <keyword>try</keyword>
+ <keyword>typeof</keyword>
+ <keyword>uint</keyword>
+ <keyword>ulong</keyword>
+ <keyword>unchecked</keyword>
+ <keyword>unsafe</keyword>
+ <keyword>ushort</keyword>
+ <keyword>using</keyword>
+ <keyword>virtual</keyword>
+ <keyword>void</keyword>
+ <keyword>volatile</keyword>
+ <keyword>while</keyword>
+ </highlighter>
+ <highlighter type="keywords">
+ <!-- special words, not really keywords -->
+ <keyword>add</keyword>
+ <keyword>alias</keyword>
+ <keyword>get</keyword>
+ <keyword>global</keyword>
+ <keyword>partial</keyword>
+ <keyword>remove</keyword>
+ <keyword>set</keyword>
+ <keyword>value</keyword>
+ <keyword>where</keyword>
+ <keyword>yield</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/delphi-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/delphi-hl.xml
index d5b4d1a..2a45d29 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/delphi-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/delphi-hl.xml
@@ -1,200 +1,200 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for Delphi (also suitable for Pascal)
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="multiline-comment">
- <!-- multiline comments starting with an $ are directives -->
- <start>{$</start>
- <end>}</end>
- <style>directive</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <!-- multiline comments starting with an $ are directives -->
- <start>(*$</start>
- <end>)</end>
- <style>directive</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>{</start>
- <end>}</end>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>(*</start>
- <end>*)</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="string">
- <string>'</string>
- <doubleEscapes />
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>#$</prefix>
- <ignoreCase />
- <style>string</style>
- </highlighter>
- <highlighter type="number">
- <prefix>#</prefix>
- <ignoreCase />
- <style>string</style>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>$</prefix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <exponent>e</exponent>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <!-- Reserved words -->
- <keyword>and</keyword>
- <keyword>else</keyword>
- <keyword>inherited</keyword>
- <keyword>packed</keyword>
- <keyword>then</keyword>
- <keyword>array</keyword>
- <keyword>end</keyword>
- <keyword>initialization</keyword>
- <keyword>procedure</keyword>
- <keyword>threadvar</keyword>
- <keyword>as</keyword>
- <keyword>except</keyword>
- <keyword>inline</keyword>
- <keyword>program</keyword>
- <keyword>to</keyword>
- <keyword>asm</keyword>
- <keyword>exports</keyword>
- <keyword>interface</keyword>
- <keyword>property</keyword>
- <keyword>try</keyword>
- <keyword>begin</keyword>
- <keyword>file</keyword>
- <keyword>is</keyword>
- <keyword>raise</keyword>
- <keyword>type</keyword>
- <keyword>case</keyword>
- <keyword>final</keyword>
- <keyword>label</keyword>
- <keyword>record</keyword>
- <keyword>unit</keyword>
- <keyword>class</keyword>
- <keyword>finalization</keyword>
- <keyword>library</keyword>
- <keyword>repeat</keyword>
- <keyword>unsafe</keyword>
- <keyword>const</keyword>
- <keyword>finally</keyword>
- <keyword>mod</keyword>
- <keyword>resourcestring</keyword>
- <keyword>until</keyword>
- <keyword>constructor</keyword>
- <keyword>for</keyword>
- <keyword>nil</keyword>
- <keyword>sealed</keyword>
- <keyword>uses</keyword>
- <keyword>destructor</keyword>
- <keyword>function</keyword>
- <keyword>not</keyword>
- <keyword>set</keyword>
- <keyword>var</keyword>
- <keyword>dispinterface</keyword>
- <keyword>goto</keyword>
- <keyword>object</keyword>
- <keyword>shl</keyword>
- <keyword>while</keyword>
- <keyword>div</keyword>
- <keyword>if</keyword>
- <keyword>of</keyword>
- <keyword>shr</keyword>
- <keyword>with</keyword>
- <keyword>do</keyword>
- <keyword>implementation</keyword>
- <keyword>or</keyword>
- <keyword>static</keyword>
- <keyword>xor</keyword>
- <keyword>downto</keyword>
- <keyword>in</keyword>
- <keyword>out</keyword>
- <keyword>string</keyword>
-
- <!-- Special meaning -->
- <keyword>at</keyword>
- <keyword>on</keyword>
-
- <!-- Directives -->
- <keyword>absolute</keyword>
- <keyword>dynamic</keyword>
- <keyword>local</keyword>
- <keyword>platform</keyword>
- <keyword>requires</keyword>
- <keyword>abstract</keyword>
- <keyword>export</keyword>
- <keyword>message</keyword>
- <keyword>private</keyword>
- <keyword>resident</keyword>
- <keyword>assembler</keyword>
- <keyword>external</keyword>
- <keyword>name</keyword>
- <keyword>protected</keyword>
- <keyword>safecall</keyword>
- <keyword>automated</keyword>
- <keyword>far</keyword>
- <keyword>near</keyword>
- <keyword>public</keyword>
- <keyword>stdcall</keyword>
- <keyword>cdecl</keyword>
- <keyword>forward</keyword>
- <keyword>nodefault</keyword>
- <keyword>published</keyword>
- <keyword>stored</keyword>
- <keyword>contains</keyword>
- <keyword>implements</keyword>
- <keyword>overload</keyword>
- <keyword>read</keyword>
- <keyword>varargs</keyword>
- <keyword>default</keyword>
- <keyword>index</keyword>
- <keyword>override</keyword>
- <keyword>readonly</keyword>
- <keyword>virtual</keyword>
- <keyword>deprecated</keyword>
- <keyword>inline</keyword>
- <keyword>package</keyword>
- <keyword>register</keyword>
- <keyword>write</keyword>
- <keyword>dispid</keyword>
- <keyword>library</keyword>
- <keyword>pascal</keyword>
- <keyword>reintroduce</keyword>
- <keyword>writeonly</keyword>
- <ignoreCase />
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for Delphi (also suitable for Pascal)
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <!-- multiline comments starting with an $ are directives -->
+ <start>{$</start>
+ <end>}</end>
+ <style>directive</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <!-- multiline comments starting with an $ are directives -->
+ <start>(*$</start>
+ <end>)</end>
+ <style>directive</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>{</start>
+ <end>}</end>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>(*</start>
+ <end>*)</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <doubleEscapes />
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>#$</prefix>
+ <ignoreCase />
+ <style>string</style>
+ </highlighter>
+ <highlighter type="number">
+ <prefix>#</prefix>
+ <ignoreCase />
+ <style>string</style>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>$</prefix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <exponent>e</exponent>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <!-- Reserved words -->
+ <keyword>and</keyword>
+ <keyword>else</keyword>
+ <keyword>inherited</keyword>
+ <keyword>packed</keyword>
+ <keyword>then</keyword>
+ <keyword>array</keyword>
+ <keyword>end</keyword>
+ <keyword>initialization</keyword>
+ <keyword>procedure</keyword>
+ <keyword>threadvar</keyword>
+ <keyword>as</keyword>
+ <keyword>except</keyword>
+ <keyword>inline</keyword>
+ <keyword>program</keyword>
+ <keyword>to</keyword>
+ <keyword>asm</keyword>
+ <keyword>exports</keyword>
+ <keyword>interface</keyword>
+ <keyword>property</keyword>
+ <keyword>try</keyword>
+ <keyword>begin</keyword>
+ <keyword>file</keyword>
+ <keyword>is</keyword>
+ <keyword>raise</keyword>
+ <keyword>type</keyword>
+ <keyword>case</keyword>
+ <keyword>final</keyword>
+ <keyword>label</keyword>
+ <keyword>record</keyword>
+ <keyword>unit</keyword>
+ <keyword>class</keyword>
+ <keyword>finalization</keyword>
+ <keyword>library</keyword>
+ <keyword>repeat</keyword>
+ <keyword>unsafe</keyword>
+ <keyword>const</keyword>
+ <keyword>finally</keyword>
+ <keyword>mod</keyword>
+ <keyword>resourcestring</keyword>
+ <keyword>until</keyword>
+ <keyword>constructor</keyword>
+ <keyword>for</keyword>
+ <keyword>nil</keyword>
+ <keyword>sealed</keyword>
+ <keyword>uses</keyword>
+ <keyword>destructor</keyword>
+ <keyword>function</keyword>
+ <keyword>not</keyword>
+ <keyword>set</keyword>
+ <keyword>var</keyword>
+ <keyword>dispinterface</keyword>
+ <keyword>goto</keyword>
+ <keyword>object</keyword>
+ <keyword>shl</keyword>
+ <keyword>while</keyword>
+ <keyword>div</keyword>
+ <keyword>if</keyword>
+ <keyword>of</keyword>
+ <keyword>shr</keyword>
+ <keyword>with</keyword>
+ <keyword>do</keyword>
+ <keyword>implementation</keyword>
+ <keyword>or</keyword>
+ <keyword>static</keyword>
+ <keyword>xor</keyword>
+ <keyword>downto</keyword>
+ <keyword>in</keyword>
+ <keyword>out</keyword>
+ <keyword>string</keyword>
+
+ <!-- Special meaning -->
+ <keyword>at</keyword>
+ <keyword>on</keyword>
+
+ <!-- Directives -->
+ <keyword>absolute</keyword>
+ <keyword>dynamic</keyword>
+ <keyword>local</keyword>
+ <keyword>platform</keyword>
+ <keyword>requires</keyword>
+ <keyword>abstract</keyword>
+ <keyword>export</keyword>
+ <keyword>message</keyword>
+ <keyword>private</keyword>
+ <keyword>resident</keyword>
+ <keyword>assembler</keyword>
+ <keyword>external</keyword>
+ <keyword>name</keyword>
+ <keyword>protected</keyword>
+ <keyword>safecall</keyword>
+ <keyword>automated</keyword>
+ <keyword>far</keyword>
+ <keyword>near</keyword>
+ <keyword>public</keyword>
+ <keyword>stdcall</keyword>
+ <keyword>cdecl</keyword>
+ <keyword>forward</keyword>
+ <keyword>nodefault</keyword>
+ <keyword>published</keyword>
+ <keyword>stored</keyword>
+ <keyword>contains</keyword>
+ <keyword>implements</keyword>
+ <keyword>overload</keyword>
+ <keyword>read</keyword>
+ <keyword>varargs</keyword>
+ <keyword>default</keyword>
+ <keyword>index</keyword>
+ <keyword>override</keyword>
+ <keyword>readonly</keyword>
+ <keyword>virtual</keyword>
+ <keyword>deprecated</keyword>
+ <keyword>inline</keyword>
+ <keyword>package</keyword>
+ <keyword>register</keyword>
+ <keyword>write</keyword>
+ <keyword>dispid</keyword>
+ <keyword>library</keyword>
+ <keyword>pascal</keyword>
+ <keyword>reintroduce</keyword>
+ <keyword>writeonly</keyword>
+ <ignoreCase />
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ini-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ini-hl.xml
index 8a938f3..84be9c6 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ini-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ini-hl.xml
@@ -1,45 +1,45 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for ini files
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="oneline-comment">;</highlighter>
- <highlighter type="regex">
- <!-- ini sections -->
- <pattern>^(\[.+\]\s*)$</pattern>
- <style>keyword</style>
- <flags>MULTILINE</flags>
- </highlighter>
- <highlighter type="regex">
- <!-- the keys in an ini section -->
- <pattern>^(.+)(?==)</pattern>
- <style>attribute</style>
- <flags>MULTILINE</flags>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for ini files
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="oneline-comment">;</highlighter>
+ <highlighter type="regex">
+ <!-- ini sections -->
+ <pattern>^(\[.+\]\s*)$</pattern>
+ <style>keyword</style>
+ <flags>MULTILINE</flags>
+ </highlighter>
+ <highlighter type="regex">
+ <!-- the keys in an ini section -->
+ <pattern>^(.+)(?==)</pattern>
+ <style>attribute</style>
+ <flags>MULTILINE</flags>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/java-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/java-hl.xml
index 672d518..d499d83 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/java-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/java-hl.xml
@@ -1,117 +1,117 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for Java
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="multiline-comment">
- <start>/**</start>
- <end>*/</end>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>/*</start>
- <end>*/</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="annotation">
- <start>@</start>
- <valueStart>(</valueStart>
- <valueEnd>)</valueEnd>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <exponent>e</exponent>
- <suffix>f</suffix>
- <suffix>d</suffix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>abstract</keyword>
- <keyword>boolean</keyword>
- <keyword>break</keyword>
- <keyword>byte</keyword>
- <keyword>case</keyword>
- <keyword>catch</keyword>
- <keyword>char</keyword>
- <keyword>class</keyword>
- <keyword>const</keyword>
- <keyword>continue</keyword>
- <keyword>default</keyword>
- <keyword>do</keyword>
- <keyword>double</keyword>
- <keyword>else</keyword>
- <keyword>extends</keyword>
- <keyword>final</keyword>
- <keyword>finally</keyword>
- <keyword>float</keyword>
- <keyword>for</keyword>
- <keyword>goto</keyword>
- <keyword>if</keyword>
- <keyword>implements</keyword>
- <keyword>import</keyword>
- <keyword>instanceof</keyword>
- <keyword>int</keyword>
- <keyword>interface</keyword>
- <keyword>long</keyword>
- <keyword>native</keyword>
- <keyword>new</keyword>
- <keyword>package</keyword>
- <keyword>private</keyword>
- <keyword>protected</keyword>
- <keyword>public</keyword>
- <keyword>return</keyword>
- <keyword>short</keyword>
- <keyword>static</keyword>
- <keyword>strictfp</keyword>
- <keyword>super</keyword>
- <keyword>switch</keyword>
- <keyword>synchronized</keyword>
- <keyword>this</keyword>
- <keyword>throw</keyword>
- <keyword>throws</keyword>
- <keyword>transient</keyword>
- <keyword>try</keyword>
- <keyword>void</keyword>
- <keyword>volatile</keyword>
- <keyword>while</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for Java
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <start>/**</start>
+ <end>*/</end>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>/*</start>
+ <end>*/</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="annotation">
+ <start>@</start>
+ <valueStart>(</valueStart>
+ <valueEnd>)</valueEnd>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <exponent>e</exponent>
+ <suffix>f</suffix>
+ <suffix>d</suffix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>abstract</keyword>
+ <keyword>boolean</keyword>
+ <keyword>break</keyword>
+ <keyword>byte</keyword>
+ <keyword>case</keyword>
+ <keyword>catch</keyword>
+ <keyword>char</keyword>
+ <keyword>class</keyword>
+ <keyword>const</keyword>
+ <keyword>continue</keyword>
+ <keyword>default</keyword>
+ <keyword>do</keyword>
+ <keyword>double</keyword>
+ <keyword>else</keyword>
+ <keyword>extends</keyword>
+ <keyword>final</keyword>
+ <keyword>finally</keyword>
+ <keyword>float</keyword>
+ <keyword>for</keyword>
+ <keyword>goto</keyword>
+ <keyword>if</keyword>
+ <keyword>implements</keyword>
+ <keyword>import</keyword>
+ <keyword>instanceof</keyword>
+ <keyword>int</keyword>
+ <keyword>interface</keyword>
+ <keyword>long</keyword>
+ <keyword>native</keyword>
+ <keyword>new</keyword>
+ <keyword>package</keyword>
+ <keyword>private</keyword>
+ <keyword>protected</keyword>
+ <keyword>public</keyword>
+ <keyword>return</keyword>
+ <keyword>short</keyword>
+ <keyword>static</keyword>
+ <keyword>strictfp</keyword>
+ <keyword>super</keyword>
+ <keyword>switch</keyword>
+ <keyword>synchronized</keyword>
+ <keyword>this</keyword>
+ <keyword>throw</keyword>
+ <keyword>throws</keyword>
+ <keyword>transient</keyword>
+ <keyword>try</keyword>
+ <keyword>void</keyword>
+ <keyword>volatile</keyword>
+ <keyword>while</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/javascript-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/javascript-hl.xml
index 08c90ba..5749b88 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/javascript-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/javascript-hl.xml
@@ -1,147 +1,147 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for JavaScript
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="multiline-comment">
- <start>/*</start>
- <end>*/</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <exponent>e</exponent>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>break</keyword>
- <keyword>case</keyword>
- <keyword>catch</keyword>
- <keyword>continue</keyword>
- <keyword>default</keyword>
- <keyword>delete</keyword>
- <keyword>do</keyword>
- <keyword>else</keyword>
- <keyword>finally</keyword>
- <keyword>for</keyword>
- <keyword>function</keyword>
- <keyword>if</keyword>
- <keyword>in</keyword>
- <keyword>instanceof</keyword>
- <keyword>new</keyword>
- <keyword>return</keyword>
- <keyword>switch</keyword>
- <keyword>this</keyword>
- <keyword>throw</keyword>
- <keyword>try</keyword>
- <keyword>typeof</keyword>
- <keyword>var</keyword>
- <keyword>void</keyword>
- <keyword>while</keyword>
- <keyword>with</keyword>
- <!-- future keywords -->
- <keyword>abstract</keyword>
- <keyword>boolean</keyword>
- <keyword>byte</keyword>
- <keyword>char</keyword>
- <keyword>class</keyword>
- <keyword>const</keyword>
- <keyword>debugger</keyword>
- <keyword>double</keyword>
- <keyword>enum</keyword>
- <keyword>export</keyword>
- <keyword>extends</keyword>
- <keyword>final</keyword>
- <keyword>float</keyword>
- <keyword>goto</keyword>
- <keyword>implements</keyword>
- <keyword>import</keyword>
- <keyword>int</keyword>
- <keyword>interface</keyword>
- <keyword>long</keyword>
- <keyword>native</keyword>
- <keyword>package</keyword>
- <keyword>private</keyword>
- <keyword>protected</keyword>
- <keyword>public</keyword>
- <keyword>short</keyword>
- <keyword>static</keyword>
- <keyword>super</keyword>
- <keyword>synchronized</keyword>
- <keyword>throws</keyword>
- <keyword>transient</keyword>
- <keyword>volatile</keyword>
- </highlighter>
- <highlighter type="keywords">
- <keyword>prototype</keyword>
- <!-- Global Objects -->
- <keyword>Array</keyword>
- <keyword>Boolean</keyword>
- <keyword>Date</keyword>
- <keyword>Error</keyword>
- <keyword>EvalError</keyword>
- <keyword>Function</keyword>
- <keyword>Math</keyword>
- <keyword>Number</keyword>
- <keyword>Object</keyword>
- <keyword>RangeError</keyword>
- <keyword>ReferenceError</keyword>
- <keyword>RegExp</keyword>
- <keyword>String</keyword>
- <keyword>SyntaxError</keyword>
- <keyword>TypeError</keyword>
- <keyword>URIError</keyword>
- <!-- Global functions -->
- <keyword>decodeURI</keyword>
- <keyword>decodeURIComponent</keyword>
- <keyword>encodeURI</keyword>
- <keyword>encodeURIComponent</keyword>
- <keyword>eval</keyword>
- <keyword>isFinite</keyword>
- <keyword>isNaN</keyword>
- <keyword>parseFloat</keyword>
- <keyword>parseInt</keyword>
- <!-- Global properties -->
- <keyword>Infinity</keyword>
- <keyword>NaN</keyword>
- <keyword>undefined</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for JavaScript
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <start>/*</start>
+ <end>*/</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <exponent>e</exponent>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>break</keyword>
+ <keyword>case</keyword>
+ <keyword>catch</keyword>
+ <keyword>continue</keyword>
+ <keyword>default</keyword>
+ <keyword>delete</keyword>
+ <keyword>do</keyword>
+ <keyword>else</keyword>
+ <keyword>finally</keyword>
+ <keyword>for</keyword>
+ <keyword>function</keyword>
+ <keyword>if</keyword>
+ <keyword>in</keyword>
+ <keyword>instanceof</keyword>
+ <keyword>new</keyword>
+ <keyword>return</keyword>
+ <keyword>switch</keyword>
+ <keyword>this</keyword>
+ <keyword>throw</keyword>
+ <keyword>try</keyword>
+ <keyword>typeof</keyword>
+ <keyword>var</keyword>
+ <keyword>void</keyword>
+ <keyword>while</keyword>
+ <keyword>with</keyword>
+ <!-- future keywords -->
+ <keyword>abstract</keyword>
+ <keyword>boolean</keyword>
+ <keyword>byte</keyword>
+ <keyword>char</keyword>
+ <keyword>class</keyword>
+ <keyword>const</keyword>
+ <keyword>debugger</keyword>
+ <keyword>double</keyword>
+ <keyword>enum</keyword>
+ <keyword>export</keyword>
+ <keyword>extends</keyword>
+ <keyword>final</keyword>
+ <keyword>float</keyword>
+ <keyword>goto</keyword>
+ <keyword>implements</keyword>
+ <keyword>import</keyword>
+ <keyword>int</keyword>
+ <keyword>interface</keyword>
+ <keyword>long</keyword>
+ <keyword>native</keyword>
+ <keyword>package</keyword>
+ <keyword>private</keyword>
+ <keyword>protected</keyword>
+ <keyword>public</keyword>
+ <keyword>short</keyword>
+ <keyword>static</keyword>
+ <keyword>super</keyword>
+ <keyword>synchronized</keyword>
+ <keyword>throws</keyword>
+ <keyword>transient</keyword>
+ <keyword>volatile</keyword>
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>prototype</keyword>
+ <!-- Global Objects -->
+ <keyword>Array</keyword>
+ <keyword>Boolean</keyword>
+ <keyword>Date</keyword>
+ <keyword>Error</keyword>
+ <keyword>EvalError</keyword>
+ <keyword>Function</keyword>
+ <keyword>Math</keyword>
+ <keyword>Number</keyword>
+ <keyword>Object</keyword>
+ <keyword>RangeError</keyword>
+ <keyword>ReferenceError</keyword>
+ <keyword>RegExp</keyword>
+ <keyword>String</keyword>
+ <keyword>SyntaxError</keyword>
+ <keyword>TypeError</keyword>
+ <keyword>URIError</keyword>
+ <!-- Global functions -->
+ <keyword>decodeURI</keyword>
+ <keyword>decodeURIComponent</keyword>
+ <keyword>encodeURI</keyword>
+ <keyword>encodeURIComponent</keyword>
+ <keyword>eval</keyword>
+ <keyword>isFinite</keyword>
+ <keyword>isNaN</keyword>
+ <keyword>parseFloat</keyword>
+ <keyword>parseInt</keyword>
+ <!-- Global properties -->
+ <keyword>Infinity</keyword>
+ <keyword>NaN</keyword>
+ <keyword>undefined</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/m2-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/m2-hl.xml
index b145f74..a3ef314 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/m2-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/m2-hl.xml
@@ -1,90 +1,90 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for Modulo-2
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="nested-multiline-comment">
- <start>(*</start>
- <end>*)</end>
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <exponent>e</exponent>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>and</keyword>
- <keyword>array</keyword>
- <keyword>begin</keyword>
- <keyword>by</keyword>
- <keyword>case</keyword>
- <keyword>const</keyword>
- <keyword>definition</keyword>
- <keyword>div</keyword>
- <keyword>do</keyword>
- <keyword>else</keyword>
- <keyword>elsif</keyword>
- <keyword>end</keyword>
- <keyword>exit</keyword>
- <keyword>export</keyword>
- <keyword>for</keyword>
- <keyword>from</keyword>
- <keyword>if</keyword>
- <keyword>implementation</keyword>
- <keyword>import</keyword>
- <keyword>in</keyword>
- <keyword>loop</keyword>
- <keyword>mod</keyword>
- <keyword>module</keyword>
- <keyword>not</keyword>
- <keyword>of</keyword>
- <keyword>or</keyword>
- <keyword>pointer</keyword>
- <keyword>procedure</keyword>
- <keyword>qualified</keyword>
- <keyword>record</keyword>
- <keyword>repeat</keyword>
- <keyword>return</keyword>
- <keyword>set</keyword>
- <keyword>then</keyword>
- <keyword>to</keyword>
- <keyword>type</keyword>
- <keyword>until</keyword>
- <keyword>var</keyword>
- <keyword>while</keyword>
- <keyword>with</keyword>
- <ignoreCase />
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for Modulo-2
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="nested-multiline-comment">
+ <start>(*</start>
+ <end>*)</end>
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <exponent>e</exponent>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>and</keyword>
+ <keyword>array</keyword>
+ <keyword>begin</keyword>
+ <keyword>by</keyword>
+ <keyword>case</keyword>
+ <keyword>const</keyword>
+ <keyword>definition</keyword>
+ <keyword>div</keyword>
+ <keyword>do</keyword>
+ <keyword>else</keyword>
+ <keyword>elsif</keyword>
+ <keyword>end</keyword>
+ <keyword>exit</keyword>
+ <keyword>export</keyword>
+ <keyword>for</keyword>
+ <keyword>from</keyword>
+ <keyword>if</keyword>
+ <keyword>implementation</keyword>
+ <keyword>import</keyword>
+ <keyword>in</keyword>
+ <keyword>loop</keyword>
+ <keyword>mod</keyword>
+ <keyword>module</keyword>
+ <keyword>not</keyword>
+ <keyword>of</keyword>
+ <keyword>or</keyword>
+ <keyword>pointer</keyword>
+ <keyword>procedure</keyword>
+ <keyword>qualified</keyword>
+ <keyword>record</keyword>
+ <keyword>repeat</keyword>
+ <keyword>return</keyword>
+ <keyword>set</keyword>
+ <keyword>then</keyword>
+ <keyword>to</keyword>
+ <keyword>type</keyword>
+ <keyword>until</keyword>
+ <keyword>var</keyword>
+ <keyword>while</keyword>
+ <keyword>with</keyword>
+ <ignoreCase />
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/myxml-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/myxml-hl.xml
index afa4be7..efa90d7 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/myxml-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/myxml-hl.xml
@@ -1,116 +1,116 @@
-<?xml version='1.0'?>
-<!--
-
- Bakalarska prace: Zvyraznovani syntaxe v XSLT
- Michal Molhanec 2005
-
- myxml-hl.xml - konfigurace zvyraznovace XML, ktera zvlast zvyrazni
- HTML elementy a XSL elementy
-
--->
-<highlighters>
-
-<wholehighlighter type='xml'>
- <elementSet>
- <style>html</style>
- <element>A</element>
- <element>ABBR</element>
- <element>ACRONYM</element>
- <element>ADDRESS</element>
- <element>APPLET</element>
- <element>AREA</element>
- <element>B</element>
- <element>BASE</element>
- <element>BASEFONT</element>
- <element>BDO</element>
- <element>BIG</element>
- <element>BLOCKQUOTE</element>
- <element>BODY</element>
- <element>BR</element>
- <element>BUTTON</element>
- <element>CAPTION</element>
- <element>CENTER</element>
- <element>CITE</element>
- <element>CODE</element>
- <element>COL</element>
- <element>COLGROUP</element>
- <element>DD</element>
- <element>DEL</element>
- <element>DFN</element>
- <element>DIR</element>
- <element>DIV</element>
- <element>DL</element>
- <element>DT</element>
- <element>EM</element>
- <element>FIELDSET</element>
- <element>FONT</element>
- <element>FORM</element>
- <element>FRAME</element>
- <element>FRAMESET</element>
- <element>H1</element>
- <element>H2</element>
- <element>H3</element>
- <element>H4</element>
- <element>H5</element>
- <element>H6</element>
- <element>HEAD</element>
- <element>HR</element>
- <element>HTML</element>
- <element>I</element>
- <element>IFRAME</element>
- <element>IMG</element>
- <element>INPUT</element>
- <element>INS</element>
- <element>ISINDEX</element>
- <element>KBD</element>
- <element>LABEL</element>
- <element>LEGEND</element>
- <element>LI</element>
- <element>LINK</element>
- <element>MAP</element>
- <element>MENU</element>
- <element>META</element>
- <element>NOFRAMES</element>
- <element>NOSCRIPT</element>
- <element>OBJECT</element>
- <element>OL</element>
- <element>OPTGROUP</element>
- <element>OPTION</element>
- <element>P</element>
- <element>PARAM</element>
- <element>PRE</element>
- <element>Q</element>
- <element>S</element>
- <element>SAMP</element>
- <element>SCRIPT</element>
- <element>SELECT</element>
- <element>SMALL</element>
- <element>SPAN</element>
- <element>STRIKE</element>
- <element>STRONG</element>
- <element>STYLE</element>
- <element>SUB</element>
- <element>SUP</element>
- <element>TABLE</element>
- <element>TBODY</element>
- <element>TD</element>
- <element>TEXTAREA</element>
- <element>TFOOT</element>
- <element>TH</element>
- <element>THEAD</element>
- <element>TITLE</element>
- <element>TR</element>
- <element>TT</element>
- <element>U</element>
- <element>UL</element>
- <element>VAR</element>
- <element>XMP</element>
- <ignoreCase/>
- </elementSet>
- <elementPrefix>
- <style>xslt</style>
- <prefix>xsl:</prefix>
- </elementPrefix>
-</wholehighlighter>
-
+<?xml version='1.0'?>
+<!--
+
+ Bakalarska prace: Zvyraznovani syntaxe v XSLT
+ Michal Molhanec 2005
+
+ myxml-hl.xml - konfigurace zvyraznovace XML, ktera zvlast zvyrazni
+ HTML elementy a XSL elementy
+
+-->
+<highlighters>
+
+<wholehighlighter type='xml'>
+ <elementSet>
+ <style>html</style>
+ <element>A</element>
+ <element>ABBR</element>
+ <element>ACRONYM</element>
+ <element>ADDRESS</element>
+ <element>APPLET</element>
+ <element>AREA</element>
+ <element>B</element>
+ <element>BASE</element>
+ <element>BASEFONT</element>
+ <element>BDO</element>
+ <element>BIG</element>
+ <element>BLOCKQUOTE</element>
+ <element>BODY</element>
+ <element>BR</element>
+ <element>BUTTON</element>
+ <element>CAPTION</element>
+ <element>CENTER</element>
+ <element>CITE</element>
+ <element>CODE</element>
+ <element>COL</element>
+ <element>COLGROUP</element>
+ <element>DD</element>
+ <element>DEL</element>
+ <element>DFN</element>
+ <element>DIR</element>
+ <element>DIV</element>
+ <element>DL</element>
+ <element>DT</element>
+ <element>EM</element>
+ <element>FIELDSET</element>
+ <element>FONT</element>
+ <element>FORM</element>
+ <element>FRAME</element>
+ <element>FRAMESET</element>
+ <element>H1</element>
+ <element>H2</element>
+ <element>H3</element>
+ <element>H4</element>
+ <element>H5</element>
+ <element>H6</element>
+ <element>HEAD</element>
+ <element>HR</element>
+ <element>HTML</element>
+ <element>I</element>
+ <element>IFRAME</element>
+ <element>IMG</element>
+ <element>INPUT</element>
+ <element>INS</element>
+ <element>ISINDEX</element>
+ <element>KBD</element>
+ <element>LABEL</element>
+ <element>LEGEND</element>
+ <element>LI</element>
+ <element>LINK</element>
+ <element>MAP</element>
+ <element>MENU</element>
+ <element>META</element>
+ <element>NOFRAMES</element>
+ <element>NOSCRIPT</element>
+ <element>OBJECT</element>
+ <element>OL</element>
+ <element>OPTGROUP</element>
+ <element>OPTION</element>
+ <element>P</element>
+ <element>PARAM</element>
+ <element>PRE</element>
+ <element>Q</element>
+ <element>S</element>
+ <element>SAMP</element>
+ <element>SCRIPT</element>
+ <element>SELECT</element>
+ <element>SMALL</element>
+ <element>SPAN</element>
+ <element>STRIKE</element>
+ <element>STRONG</element>
+ <element>STYLE</element>
+ <element>SUB</element>
+ <element>SUP</element>
+ <element>TABLE</element>
+ <element>TBODY</element>
+ <element>TD</element>
+ <element>TEXTAREA</element>
+ <element>TFOOT</element>
+ <element>TH</element>
+ <element>THEAD</element>
+ <element>TITLE</element>
+ <element>TR</element>
+ <element>TT</element>
+ <element>U</element>
+ <element>UL</element>
+ <element>VAR</element>
+ <element>XMP</element>
+ <ignoreCase/>
+ </elementSet>
+ <elementPrefix>
+ <style>xslt</style>
+ <prefix>xsl:</prefix>
+ </elementPrefix>
+</wholehighlighter>
+
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/perl-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/perl-hl.xml
index da1924a..23fdfd8 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/perl-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/perl-hl.xml
@@ -1,120 +1,120 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for Perl
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="oneline-comment">#</highlighter>
- <highlighter type="heredoc">
- <start>&lt;&lt;</start>
- <quote>'</quote>
- <quote>"</quote>
- <noWhiteSpace/>
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- <spanNewLines/>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <pointStarts />
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>if</keyword>
- <keyword>unless</keyword>
- <keyword>while</keyword>
- <keyword>until</keyword>
- <keyword>foreach</keyword>
- <keyword>else</keyword>
- <keyword>elsif</keyword>
- <keyword>for</keyword>
- <keyword>when</keyword>
- <keyword>default</keyword>
- <keyword>given</keyword>
- <!-- Keywords related to the control flow of your perl program -->
- <keyword>caller</keyword>
- <keyword>continue</keyword>
- <keyword>die</keyword>
- <keyword>do</keyword>
- <keyword>dump</keyword>
- <keyword>eval</keyword>
- <keyword>exit</keyword>
- <keyword>goto</keyword>
- <keyword>last</keyword>
- <keyword>next</keyword>
- <keyword>redo</keyword>
- <keyword>return</keyword>
- <keyword>sub</keyword>
- <keyword>wantarray</keyword>
- <!-- Keywords related to scoping -->
- <keyword>caller</keyword>
- <keyword>import</keyword>
- <keyword>local</keyword>
- <keyword>my</keyword>
- <keyword>package</keyword>
- <keyword>use</keyword>
- <!-- Keywords related to perl modules -->
- <keyword>do</keyword>
- <keyword>import</keyword>
- <keyword>no</keyword>
- <keyword>package</keyword>
- <keyword>require</keyword>
- <keyword>use</keyword>
- <!-- Keywords related to classes and object-orientedness -->
- <keyword>bless</keyword>
- <keyword>dbmclose</keyword>
- <keyword>dbmopen</keyword>
- <keyword>package</keyword>
- <keyword>ref</keyword>
- <keyword>tie</keyword>
- <keyword>tied</keyword>
- <keyword>untie</keyword>
- <keyword>use</keyword>
- <!-- operators -->
- <keyword>and</keyword>
- <keyword>or</keyword>
- <keyword>not</keyword>
- <keyword>eq</keyword>
- <keyword>ne</keyword>
- <keyword>lt</keyword>
- <keyword>gt</keyword>
- <keyword>le</keyword>
- <keyword>ge</keyword>
- <keyword>cmp</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for Perl
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="oneline-comment">#</highlighter>
+ <highlighter type="heredoc">
+ <start>&lt;&lt;</start>
+ <quote>'</quote>
+ <quote>"</quote>
+ <noWhiteSpace/>
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ <spanNewLines/>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <pointStarts />
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>if</keyword>
+ <keyword>unless</keyword>
+ <keyword>while</keyword>
+ <keyword>until</keyword>
+ <keyword>foreach</keyword>
+ <keyword>else</keyword>
+ <keyword>elsif</keyword>
+ <keyword>for</keyword>
+ <keyword>when</keyword>
+ <keyword>default</keyword>
+ <keyword>given</keyword>
+ <!-- Keywords related to the control flow of your perl program -->
+ <keyword>caller</keyword>
+ <keyword>continue</keyword>
+ <keyword>die</keyword>
+ <keyword>do</keyword>
+ <keyword>dump</keyword>
+ <keyword>eval</keyword>
+ <keyword>exit</keyword>
+ <keyword>goto</keyword>
+ <keyword>last</keyword>
+ <keyword>next</keyword>
+ <keyword>redo</keyword>
+ <keyword>return</keyword>
+ <keyword>sub</keyword>
+ <keyword>wantarray</keyword>
+ <!-- Keywords related to scoping -->
+ <keyword>caller</keyword>
+ <keyword>import</keyword>
+ <keyword>local</keyword>
+ <keyword>my</keyword>
+ <keyword>package</keyword>
+ <keyword>use</keyword>
+ <!-- Keywords related to perl modules -->
+ <keyword>do</keyword>
+ <keyword>import</keyword>
+ <keyword>no</keyword>
+ <keyword>package</keyword>
+ <keyword>require</keyword>
+ <keyword>use</keyword>
+ <!-- Keywords related to classes and object-orientedness -->
+ <keyword>bless</keyword>
+ <keyword>dbmclose</keyword>
+ <keyword>dbmopen</keyword>
+ <keyword>package</keyword>
+ <keyword>ref</keyword>
+ <keyword>tie</keyword>
+ <keyword>tied</keyword>
+ <keyword>untie</keyword>
+ <keyword>use</keyword>
+ <!-- operators -->
+ <keyword>and</keyword>
+ <keyword>or</keyword>
+ <keyword>not</keyword>
+ <keyword>eq</keyword>
+ <keyword>ne</keyword>
+ <keyword>lt</keyword>
+ <keyword>gt</keyword>
+ <keyword>le</keyword>
+ <keyword>ge</keyword>
+ <keyword>cmp</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/php-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/php-hl.xml
index 4a70225..0daa348 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/php-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/php-hl.xml
@@ -1,149 +1,149 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for PHP
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="multiline-comment">
- <start>/**</start>
- <end>*/</end>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="oneline-comment">
- <start>///</start>
- <style>doccomment</style>
- </highlighter>
- <highlighter type="multiline-comment">
- <start>/*</start>
- <end>*/</end>
- </highlighter>
- <highlighter type="oneline-comment">//</highlighter>
- <highlighter type="oneline-comment">#</highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- <spanNewLines />
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- <spanNewLines />
- </highlighter>
- <highlighter type="heredoc">
- <start>&lt;&lt;&lt;</start>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <exponent>e</exponent>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>and</keyword>
- <keyword>or</keyword>
- <keyword>xor</keyword>
- <keyword>__FILE__</keyword>
- <keyword>exception</keyword>
- <keyword>__LINE__</keyword>
- <keyword>array</keyword>
- <keyword>as</keyword>
- <keyword>break</keyword>
- <keyword>case</keyword>
- <keyword>class</keyword>
- <keyword>const</keyword>
- <keyword>continue</keyword>
- <keyword>declare</keyword>
- <keyword>default</keyword>
- <keyword>die</keyword>
- <keyword>do</keyword>
- <keyword>echo</keyword>
- <keyword>else</keyword>
- <keyword>elseif</keyword>
- <keyword>empty</keyword>
- <keyword>enddeclare</keyword>
- <keyword>endfor</keyword>
- <keyword>endforeach</keyword>
- <keyword>endif</keyword>
- <keyword>endswitch</keyword>
- <keyword>endwhile</keyword>
- <keyword>eval</keyword>
- <keyword>exit</keyword>
- <keyword>extends</keyword>
- <keyword>for</keyword>
- <keyword>foreach</keyword>
- <keyword>function</keyword>
- <keyword>global</keyword>
- <keyword>if</keyword>
- <keyword>include</keyword>
- <keyword>include_once</keyword>
- <keyword>isset</keyword>
- <keyword>list</keyword>
- <keyword>new</keyword>
- <keyword>print</keyword>
- <keyword>require</keyword>
- <keyword>require_once</keyword>
- <keyword>return</keyword>
- <keyword>static</keyword>
- <keyword>switch</keyword>
- <keyword>unset</keyword>
- <keyword>use</keyword>
- <keyword>var</keyword>
- <keyword>while</keyword>
- <keyword>__FUNCTION__</keyword>
- <keyword>__CLASS__</keyword>
- <keyword>__METHOD__</keyword>
- <keyword>final</keyword>
- <keyword>php_user_filter</keyword>
- <keyword>interface</keyword>
- <keyword>implements</keyword>
- <keyword>extends</keyword>
- <keyword>public</keyword>
- <keyword>private</keyword>
- <keyword>protected</keyword>
- <keyword>abstract</keyword>
- <keyword>clone</keyword>
- <keyword>try</keyword>
- <keyword>catch</keyword>
- <keyword>throw</keyword>
- <keyword>cfunction</keyword>
- <keyword>old_function</keyword>
- <keyword>true</keyword>
- <keyword>false</keyword>
- <ignoreCase />
- </highlighter>
- <highlighter type="word">
- <!-- highlight the php open and close tags as directives -->
- <word>?&gt;</word>
- <word>&lt;?php</word>
- <word>&lt;?=</word>
- <style>directive</style>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for PHP
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="multiline-comment">
+ <start>/**</start>
+ <end>*/</end>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="oneline-comment">
+ <start>///</start>
+ <style>doccomment</style>
+ </highlighter>
+ <highlighter type="multiline-comment">
+ <start>/*</start>
+ <end>*/</end>
+ </highlighter>
+ <highlighter type="oneline-comment">//</highlighter>
+ <highlighter type="oneline-comment">#</highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ <spanNewLines />
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ <spanNewLines />
+ </highlighter>
+ <highlighter type="heredoc">
+ <start>&lt;&lt;&lt;</start>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <exponent>e</exponent>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>and</keyword>
+ <keyword>or</keyword>
+ <keyword>xor</keyword>
+ <keyword>__FILE__</keyword>
+ <keyword>exception</keyword>
+ <keyword>__LINE__</keyword>
+ <keyword>array</keyword>
+ <keyword>as</keyword>
+ <keyword>break</keyword>
+ <keyword>case</keyword>
+ <keyword>class</keyword>
+ <keyword>const</keyword>
+ <keyword>continue</keyword>
+ <keyword>declare</keyword>
+ <keyword>default</keyword>
+ <keyword>die</keyword>
+ <keyword>do</keyword>
+ <keyword>echo</keyword>
+ <keyword>else</keyword>
+ <keyword>elseif</keyword>
+ <keyword>empty</keyword>
+ <keyword>enddeclare</keyword>
+ <keyword>endfor</keyword>
+ <keyword>endforeach</keyword>
+ <keyword>endif</keyword>
+ <keyword>endswitch</keyword>
+ <keyword>endwhile</keyword>
+ <keyword>eval</keyword>
+ <keyword>exit</keyword>
+ <keyword>extends</keyword>
+ <keyword>for</keyword>
+ <keyword>foreach</keyword>
+ <keyword>function</keyword>
+ <keyword>global</keyword>
+ <keyword>if</keyword>
+ <keyword>include</keyword>
+ <keyword>include_once</keyword>
+ <keyword>isset</keyword>
+ <keyword>list</keyword>
+ <keyword>new</keyword>
+ <keyword>print</keyword>
+ <keyword>require</keyword>
+ <keyword>require_once</keyword>
+ <keyword>return</keyword>
+ <keyword>static</keyword>
+ <keyword>switch</keyword>
+ <keyword>unset</keyword>
+ <keyword>use</keyword>
+ <keyword>var</keyword>
+ <keyword>while</keyword>
+ <keyword>__FUNCTION__</keyword>
+ <keyword>__CLASS__</keyword>
+ <keyword>__METHOD__</keyword>
+ <keyword>final</keyword>
+ <keyword>php_user_filter</keyword>
+ <keyword>interface</keyword>
+ <keyword>implements</keyword>
+ <keyword>extends</keyword>
+ <keyword>public</keyword>
+ <keyword>private</keyword>
+ <keyword>protected</keyword>
+ <keyword>abstract</keyword>
+ <keyword>clone</keyword>
+ <keyword>try</keyword>
+ <keyword>catch</keyword>
+ <keyword>throw</keyword>
+ <keyword>cfunction</keyword>
+ <keyword>old_function</keyword>
+ <keyword>true</keyword>
+ <keyword>false</keyword>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="word">
+ <!-- highlight the php open and close tags as directives -->
+ <word>?&gt;</word>
+ <word>&lt;?php</word>
+ <word>&lt;?=</word>
+ <style>directive</style>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/python-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/python-hl.xml
index 791bc7a..1b1f087 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/python-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/python-hl.xml
@@ -1,100 +1,100 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for Python
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="annotation">
- <!-- these are actually called decorators -->
- <start>@</start>
- <valueStart>(</valueStart>
- <valueEnd>)</valueEnd>
- </highlighter>
- <highlighter type="oneline-comment">#</highlighter>
- <highlighter type="string">
- <string>"""</string>
- <spanNewLines />
- </highlighter>
- <highlighter type="string">
- <string>'''</string>
- <spanNewLines />
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <pointStarts />
- <exponent>e</exponent>
- <suffix>l</suffix>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>and</keyword>
- <keyword>del</keyword>
- <keyword>from</keyword>
- <keyword>not</keyword>
- <keyword>while</keyword>
- <keyword>as</keyword>
- <keyword>elif</keyword>
- <keyword>global</keyword>
- <keyword>or</keyword>
- <keyword>with</keyword>
- <keyword>assert</keyword>
- <keyword>else</keyword>
- <keyword>if</keyword>
- <keyword>pass</keyword>
- <keyword>yield</keyword>
- <keyword>break</keyword>
- <keyword>except</keyword>
- <keyword>import</keyword>
- <keyword>print</keyword>
- <keyword>class</keyword>
- <keyword>exec</keyword>
- <keyword>in</keyword>
- <keyword>raise</keyword>
- <keyword>continue</keyword>
- <keyword>finally</keyword>
- <keyword>is</keyword>
- <keyword>return</keyword>
- <keyword>def</keyword>
- <keyword>for</keyword>
- <keyword>lambda</keyword>
- <keyword>try</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for Python
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="annotation">
+ <!-- these are actually called decorators -->
+ <start>@</start>
+ <valueStart>(</valueStart>
+ <valueEnd>)</valueEnd>
+ </highlighter>
+ <highlighter type="oneline-comment">#</highlighter>
+ <highlighter type="string">
+ <string>"""</string>
+ <spanNewLines />
+ </highlighter>
+ <highlighter type="string">
+ <string>'''</string>
+ <spanNewLines />
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <pointStarts />
+ <exponent>e</exponent>
+ <suffix>l</suffix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>and</keyword>
+ <keyword>del</keyword>
+ <keyword>from</keyword>
+ <keyword>not</keyword>
+ <keyword>while</keyword>
+ <keyword>as</keyword>
+ <keyword>elif</keyword>
+ <keyword>global</keyword>
+ <keyword>or</keyword>
+ <keyword>with</keyword>
+ <keyword>assert</keyword>
+ <keyword>else</keyword>
+ <keyword>if</keyword>
+ <keyword>pass</keyword>
+ <keyword>yield</keyword>
+ <keyword>break</keyword>
+ <keyword>except</keyword>
+ <keyword>import</keyword>
+ <keyword>print</keyword>
+ <keyword>class</keyword>
+ <keyword>exec</keyword>
+ <keyword>in</keyword>
+ <keyword>raise</keyword>
+ <keyword>continue</keyword>
+ <keyword>finally</keyword>
+ <keyword>is</keyword>
+ <keyword>return</keyword>
+ <keyword>def</keyword>
+ <keyword>for</keyword>
+ <keyword>lambda</keyword>
+ <keyword>try</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ruby-hl.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ruby-hl.xml
index 78189b0..2f74352 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ruby-hl.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/ruby-hl.xml
@@ -1,109 +1,109 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-Syntax highlighting definition for Ruby
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<highlighters>
- <highlighter type="oneline-comment">#</highlighter>
- <highlighter type="heredoc">
- <start>&lt;&lt;</start>
- <noWhiteSpace/>
- </highlighter>
- <highlighter type="string">
- <string>"</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>%Q{</string>
- <endString>}</endString>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>%/</string>
- <endString>/</endString>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>'</string>
- <escape>\</escape>
- </highlighter>
- <highlighter type="string">
- <string>%q{</string>
- <endString>}</endString>
- <escape>\</escape>
- </highlighter>
- <highlighter type="hexnumber">
- <prefix>0x</prefix>
- <ignoreCase />
- </highlighter>
- <highlighter type="number">
- <point>.</point>
- <exponent>e</exponent>
- <ignoreCase />
- </highlighter>
- <highlighter type="keywords">
- <keyword>alias</keyword>
- <keyword>and</keyword>
- <keyword>BEGIN</keyword>
- <keyword>begin</keyword>
- <keyword>break</keyword>
- <keyword>case</keyword>
- <keyword>class</keyword>
- <keyword>def</keyword>
- <keyword>defined</keyword>
- <keyword>do</keyword>
- <keyword>else</keyword>
- <keyword>elsif</keyword>
- <keyword>END</keyword>
- <keyword>end</keyword>
- <keyword>ensure</keyword>
- <keyword>false</keyword>
- <keyword>for</keyword>
- <keyword>if</keyword>
- <keyword>in</keyword>
- <keyword>module</keyword>
- <keyword>next</keyword>
- <keyword>nil</keyword>
- <keyword>not</keyword>
- <keyword>or</keyword>
- <keyword>redo</keyword>
- <keyword>rescue</keyword>
- <keyword>retry</keyword>
- <keyword>return</keyword>
- <keyword>self</keyword>
- <keyword>super</keyword>
- <keyword>then</keyword>
- <keyword>true</keyword>
- <keyword>undef</keyword>
- <keyword>unless</keyword>
- <keyword>until</keyword>
- <keyword>when</keyword>
- <keyword>while</keyword>
- <keyword>yield</keyword>
- </highlighter>
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Syntax highlighting definition for Ruby
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<highlighters>
+ <highlighter type="oneline-comment">#</highlighter>
+ <highlighter type="heredoc">
+ <start>&lt;&lt;</start>
+ <noWhiteSpace/>
+ </highlighter>
+ <highlighter type="string">
+ <string>"</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>%Q{</string>
+ <endString>}</endString>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>%/</string>
+ <endString>/</endString>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>'</string>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="string">
+ <string>%q{</string>
+ <endString>}</endString>
+ <escape>\</escape>
+ </highlighter>
+ <highlighter type="hexnumber">
+ <prefix>0x</prefix>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="number">
+ <point>.</point>
+ <exponent>e</exponent>
+ <ignoreCase />
+ </highlighter>
+ <highlighter type="keywords">
+ <keyword>alias</keyword>
+ <keyword>and</keyword>
+ <keyword>BEGIN</keyword>
+ <keyword>begin</keyword>
+ <keyword>break</keyword>
+ <keyword>case</keyword>
+ <keyword>class</keyword>
+ <keyword>def</keyword>
+ <keyword>defined</keyword>
+ <keyword>do</keyword>
+ <keyword>else</keyword>
+ <keyword>elsif</keyword>
+ <keyword>END</keyword>
+ <keyword>end</keyword>
+ <keyword>ensure</keyword>
+ <keyword>false</keyword>
+ <keyword>for</keyword>
+ <keyword>if</keyword>
+ <keyword>in</keyword>
+ <keyword>module</keyword>
+ <keyword>next</keyword>
+ <keyword>nil</keyword>
+ <keyword>not</keyword>
+ <keyword>or</keyword>
+ <keyword>redo</keyword>
+ <keyword>rescue</keyword>
+ <keyword>retry</keyword>
+ <keyword>return</keyword>
+ <keyword>self</keyword>
+ <keyword>super</keyword>
+ <keyword>then</keyword>
+ <keyword>true</keyword>
+ <keyword>undef</keyword>
+ <keyword>unless</keyword>
+ <keyword>until</keyword>
+ <keyword>when</keyword>
+ <keyword>while</keyword>
+ <keyword>yield</keyword>
+ </highlighter>
</highlighters> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/xslthl-config.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/xslthl-config.xml
index b24e469..910289a 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/xslthl-config.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/highlighting/xslthl-config.xml
@@ -1,46 +1,46 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-
-xslthl - XSLT Syntax Highlighting
-http://sourceforge.net/projects/xslthl/
-Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
-
-Michal Molhanec <mol1111 at users.sourceforge.net>
-Jirka Kosek <kosek at users.sourceforge.net>
-Michiel Hendriks <elmuerte at users.sourceforge.net>
-
--->
-<xslthl-config>
- <highlighter id="java" file="java-hl.xml" />
- <highlighter id="delphi" file="delphi-hl.xml" />
- <highlighter id="pascal" file="delphi-hl.xml" />
- <highlighter id="ini" file="ini-hl.xml" />
- <highlighter id="php" file="php-hl.xml" />
- <highlighter id="myxml" file="myxml-hl.xml" />
- <highlighter id="m2" file="m2-hl.xml" />
- <highlighter id="tcl" file="tcl-hl.xml" />
- <highlighter id="c" file="c-hl.xml" />
- <highlighter id="cpp" file="cpp-hl.xml" />
- <highlighter id="csharp" file="csharp-hl.xml" />
- <highlighter id="python" file="python-hl.xml" />
- <highlighter id="ruby" file="ruby-hl.xml" />
- <highlighter id="perl" file="perl-hl.xml" />
- <highlighter id="javascript" file="javascript-hl.xml" />
- <namespace prefix="xslthl" uri="http://xslthl.sf.net" />
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+xslthl - XSLT Syntax Highlighting
+http://sourceforge.net/projects/xslthl/
+Copyright (C) 2005-2008 Michal Molhanec, Jirka Kosek, Michiel Hendriks
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
+Michal Molhanec <mol1111 at users.sourceforge.net>
+Jirka Kosek <kosek at users.sourceforge.net>
+Michiel Hendriks <elmuerte at users.sourceforge.net>
+
+-->
+<xslthl-config>
+ <highlighter id="java" file="java-hl.xml" />
+ <highlighter id="delphi" file="delphi-hl.xml" />
+ <highlighter id="pascal" file="delphi-hl.xml" />
+ <highlighter id="ini" file="ini-hl.xml" />
+ <highlighter id="php" file="php-hl.xml" />
+ <highlighter id="myxml" file="myxml-hl.xml" />
+ <highlighter id="m2" file="m2-hl.xml" />
+ <highlighter id="tcl" file="tcl-hl.xml" />
+ <highlighter id="c" file="c-hl.xml" />
+ <highlighter id="cpp" file="cpp-hl.xml" />
+ <highlighter id="csharp" file="csharp-hl.xml" />
+ <highlighter id="python" file="python-hl.xml" />
+ <highlighter id="ruby" file="ruby-hl.xml" />
+ <highlighter id="perl" file="perl-hl.xml" />
+ <highlighter id="javascript" file="javascript-hl.xml" />
+ <namespace prefix="xslthl" uri="http://xslthl.sf.net" />
</xslthl-config> \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/html/highlight.xsl b/SCons/Tool/docbook/docbook-xsl-1.76.1/html/highlight.xsl
index f7307a4..d6fc969 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/html/highlight.xsl
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/html/highlight.xsl
@@ -1,86 +1,86 @@
-<?xml version="1.0"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslthl="http://xslthl.sf.net" exclude-result-prefixes="xslthl" version="1.0">
- <!-- ********************************************************************
- $Id: highlight.xsl 8911 2010-09-28 17:02:06Z abdelazer $
- ********************************************************************
-
- This file is part of the XSL DocBook Stylesheet distribution.
- See ../README or http://docbook.sf.net/release/xsl/current/ for
- and other information.
-
- ******************************************************************** -->
- <xsl:import href="../highlighting/common.xsl"/>
- <xsl:template match="xslthl:keyword" mode="xslthl">
- <strong class="hl-keyword">
- <xsl:apply-templates mode="xslthl"/>
- </strong>
- </xsl:template>
- <xsl:template match="xslthl:string" mode="xslthl">
- <strong class="hl-string">
- <em style="color:red">
- <xsl:apply-templates mode="xslthl"/>
- </em>
- </strong>
- </xsl:template>
- <xsl:template match="xslthl:comment" mode="xslthl">
- <em class="hl-comment" style="color: silver">
- <xsl:apply-templates mode="xslthl"/>
- </em>
- </xsl:template>
- <xsl:template match="xslthl:directive" mode="xslthl">
- <span class="hl-directive" style="color: maroon">
- <xsl:apply-templates mode="xslthl"/>
- </span>
- </xsl:template>
- <xsl:template match="xslthl:tag" mode="xslthl">
- <strong class="hl-tag" style="color: #000096">
- <xsl:apply-templates mode="xslthl"/>
- </strong>
- </xsl:template>
- <xsl:template match="xslthl:attribute" mode="xslthl">
- <span class="hl-attribute" style="color: #F5844C">
- <xsl:apply-templates mode="xslthl"/>
- </span>
- </xsl:template>
- <xsl:template match="xslthl:value" mode="xslthl">
- <span class="hl-value" style="color: #993300">
- <xsl:apply-templates mode="xslthl"/>
- </span>
- </xsl:template>
- <xsl:template match="xslthl:html" mode="xslthl">
- <strong>
- <em style="color: red">
- <xsl:apply-templates mode="xslthl"/>
- </em>
- </strong>
- </xsl:template>
- <xsl:template match="xslthl:xslt" mode="xslthl">
- <strong style="color: #0066FF">
- <xsl:apply-templates mode="xslthl"/>
- </strong>
- </xsl:template>
- <!-- Not emitted since XSLTHL 2.0 -->
- <xsl:template match="xslthl:section" mode="xslthl">
- <strong>
- <xsl:apply-templates mode="xslthl"/>
- </strong>
- </xsl:template>
- <xsl:template match="xslthl:number" mode="xslthl">
- <span class="hl-number">
- <xsl:apply-templates mode="xslthl"/>
- </span>
- </xsl:template>
- <xsl:template match="xslthl:annotation" mode="xslthl">
- <em>
- <span class="hl-annotation" style="color: gray">
- <xsl:apply-templates mode="xslthl"/>
- </span>
- </em>
- </xsl:template>
- <!-- Not sure which element will be in final XSLTHL 2.0 -->
- <xsl:template match="xslthl:doccomment|xslthl:doctype" mode="xslthl">
- <strong class="hl-tag" style="color: blue">
- <xsl:apply-templates mode="xslthl"/>
- </strong>
- </xsl:template>
-</xsl:stylesheet>
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslthl="http://xslthl.sf.net" exclude-result-prefixes="xslthl" version="1.0">
+ <!-- ********************************************************************
+ $Id: highlight.xsl 8911 2010-09-28 17:02:06Z abdelazer $
+ ********************************************************************
+
+ This file is part of the XSL DocBook Stylesheet distribution.
+ See ../README or http://docbook.sf.net/release/xsl/current/ for
+ and other information.
+
+ ******************************************************************** -->
+ <xsl:import href="../highlighting/common.xsl"/>
+ <xsl:template match="xslthl:keyword" mode="xslthl">
+ <strong class="hl-keyword">
+ <xsl:apply-templates mode="xslthl"/>
+ </strong>
+ </xsl:template>
+ <xsl:template match="xslthl:string" mode="xslthl">
+ <strong class="hl-string">
+ <em style="color:red">
+ <xsl:apply-templates mode="xslthl"/>
+ </em>
+ </strong>
+ </xsl:template>
+ <xsl:template match="xslthl:comment" mode="xslthl">
+ <em class="hl-comment" style="color: silver">
+ <xsl:apply-templates mode="xslthl"/>
+ </em>
+ </xsl:template>
+ <xsl:template match="xslthl:directive" mode="xslthl">
+ <span class="hl-directive" style="color: maroon">
+ <xsl:apply-templates mode="xslthl"/>
+ </span>
+ </xsl:template>
+ <xsl:template match="xslthl:tag" mode="xslthl">
+ <strong class="hl-tag" style="color: #000096">
+ <xsl:apply-templates mode="xslthl"/>
+ </strong>
+ </xsl:template>
+ <xsl:template match="xslthl:attribute" mode="xslthl">
+ <span class="hl-attribute" style="color: #F5844C">
+ <xsl:apply-templates mode="xslthl"/>
+ </span>
+ </xsl:template>
+ <xsl:template match="xslthl:value" mode="xslthl">
+ <span class="hl-value" style="color: #993300">
+ <xsl:apply-templates mode="xslthl"/>
+ </span>
+ </xsl:template>
+ <xsl:template match="xslthl:html" mode="xslthl">
+ <strong>
+ <em style="color: red">
+ <xsl:apply-templates mode="xslthl"/>
+ </em>
+ </strong>
+ </xsl:template>
+ <xsl:template match="xslthl:xslt" mode="xslthl">
+ <strong style="color: #0066FF">
+ <xsl:apply-templates mode="xslthl"/>
+ </strong>
+ </xsl:template>
+ <!-- Not emitted since XSLTHL 2.0 -->
+ <xsl:template match="xslthl:section" mode="xslthl">
+ <strong>
+ <xsl:apply-templates mode="xslthl"/>
+ </strong>
+ </xsl:template>
+ <xsl:template match="xslthl:number" mode="xslthl">
+ <span class="hl-number">
+ <xsl:apply-templates mode="xslthl"/>
+ </span>
+ </xsl:template>
+ <xsl:template match="xslthl:annotation" mode="xslthl">
+ <em>
+ <span class="hl-annotation" style="color: gray">
+ <xsl:apply-templates mode="xslthl"/>
+ </span>
+ </em>
+ </xsl:template>
+ <!-- Not sure which element will be in final XSLTHL 2.0 -->
+ <xsl:template match="xslthl:doccomment|xslthl:doctype" mode="xslthl">
+ <strong class="hl-tag" style="color: blue">
+ <xsl:apply-templates mode="xslthl"/>
+ </strong>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/params/bibliography.style.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/params/bibliography.style.xml
index fa44582..363e980 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/params/bibliography.style.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/params/bibliography.style.xml
@@ -1,35 +1,35 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:xi="http://www.w3.org/2001/XInclude"
- xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="5.0" xml:id="bibliography.style">
-<refmeta>
-<refentrytitle>bibliography.style</refentrytitle>
-<refmiscinfo class="other" otherclass="datatype">list</refmiscinfo>
-<refmiscinfo class="other" otherclass="value">normal</refmiscinfo>
-<refmiscinfo class="other" otherclass="value">iso690</refmiscinfo>
-</refmeta>
-<refnamediv>
-<refname>bibliography.style</refname>
-<refpurpose>Style used for formatting of biblioentries.</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-<src:fragment xml:id='bibliography.style.frag'>
-<xsl:param name="bibliography.style">normal</xsl:param>
-</src:fragment>
-</refsynopsisdiv>
-
-<refsection><info><title>Description</title></info>
-
-<para>Currently only <literal>normal</literal> and
-<literal>iso690</literal> styles are supported.</para>
-
-<para>In order to use ISO690 style to the full extent you might need
-to use additional markup described on <link
-xlink:href="http://wiki.docbook.org/topic/ISO690Bibliography">the
-following WiKi page</link>.</para>
-
-</refsection>
-</refentry>
+<refentry xmlns="http://docbook.org/ns/docbook"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="5.0" xml:id="bibliography.style">
+<refmeta>
+<refentrytitle>bibliography.style</refentrytitle>
+<refmiscinfo class="other" otherclass="datatype">list</refmiscinfo>
+<refmiscinfo class="other" otherclass="value">normal</refmiscinfo>
+<refmiscinfo class="other" otherclass="value">iso690</refmiscinfo>
+</refmeta>
+<refnamediv>
+<refname>bibliography.style</refname>
+<refpurpose>Style used for formatting of biblioentries.</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+<src:fragment xml:id='bibliography.style.frag'>
+<xsl:param name="bibliography.style">normal</xsl:param>
+</src:fragment>
+</refsynopsisdiv>
+
+<refsection><info><title>Description</title></info>
+
+<para>Currently only <literal>normal</literal> and
+<literal>iso690</literal> styles are supported.</para>
+
+<para>In order to use ISO690 style to the full extent you might need
+to use additional markup described on <link
+xlink:href="http://wiki.docbook.org/topic/ISO690Bibliography">the
+following WiKi page</link>.</para>
+
+</refsection>
+</refentry>
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/params/highlight.source.xml b/SCons/Tool/docbook/docbook-xsl-1.76.1/params/highlight.source.xml
index 41d7b2f..c4eb1a8 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/params/highlight.source.xml
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/params/highlight.source.xml
@@ -1,82 +1,82 @@
-<refentry xmlns="http://docbook.org/ns/docbook"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:xi="http://www.w3.org/2001/XInclude"
- xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="5.0" xml:id="highlight.source">
-<refmeta>
-<refentrytitle>highlight.source</refentrytitle>
-<refmiscinfo class="other" otherclass="datatype">boolean</refmiscinfo>
-</refmeta>
-<refnamediv>
-<refname>highlight.source</refname>
-<refpurpose>Should the content of <tag>programlisting</tag>
-be syntactically highlighted?</refpurpose>
-</refnamediv>
-
-<refsynopsisdiv>
-<src:fragment xml:id="highlight.source.frag">
-<xsl:param name="highlight.source" select="0"/>
-</src:fragment>
-</refsynopsisdiv>
-
-<refsection><info><title>Description</title></info>
-
-<para>When this parameter is non-zero, the stylesheets will try to do syntax highlighting of the
-content of <tag>programlisting</tag> elements. You specify the language for each programlisting
-by using the <tag class="attribute">language</tag> attribute. The <parameter>highlight.default.language</parameter>
-parameter can be used to specify the language for programlistings without a <tag class="attribute">language</tag>
-attribute. Syntax highlighting also works for <tag>screen</tag> and <tag>synopsis</tag> elements.</para>
-
-<para>The actual highlighting work is done by the XSLTHL extension module. This is an external Java library that has to be
-downloaded separately (see below).</para>
-
-<itemizedlist>
-<para>In order to use this extension, you must</para>
-
-<listitem><para>add <filename>xslthl-2.x.x.jar</filename> to your Java classpath. The latest version is available
-from <link xlink:href="http://sourceforge.net/projects/xslthl">the XSLT syntax highlighting project</link>
-at SourceForge.</para>
-</listitem>
-<listitem>
-<para>use a customization layer in which you import one of the following stylesheet modules:
-<itemizedlist>
- <listitem>
- <para><filename>html/highlight.xsl</filename>
- </para>
- </listitem>
-<listitem>
- <para><filename>xhtml/highlight.xsl</filename>
- </para>
- </listitem>
-<listitem>
- <para><filename>xhtml-1_1/highlight.xsl</filename>
- </para>
- </listitem>
-<listitem>
- <para><filename>fo/highlight.xsl</filename>
- </para>
-</listitem>
-</itemizedlist>
-</para>
-</listitem>
-<listitem><para>let either the <literal>xslthl.config</literal> Java system property or the
-<parameter>highlight.xslthl.config</parameter> parameter point to the configuration file for syntax
-highlighting (using URL syntax). DocBook XSL comes with a ready-to-use configuration file,
-<filename>highlighting/xslthl-config.xml</filename>.</para>
-</listitem>
-</itemizedlist>
-
-<para>The extension works with Saxon 6.5.x and Xalan-J. (Saxon 8.5 or later is also supported, but since it is
-an XSLT 2.0 processor it is not guaranteed to work with DocBook XSL in all circumstances.)</para>
-
-<para>The following is an example of a Saxon 6 command adapted for syntax highlighting, to be used on Windows:</para>
-
-<informalexample>
-<para><command>java -cp c:/Java/saxon.jar;c:/Java/xslthl-2.0.1.jar
--Dxslthl.config=file:///c:/docbook-xsl/highlighting/xslthl-config.xml com.icl.saxon.StyleSheet
--o test.html test.xml myhtml.xsl</command></para>
-</informalexample>
-
-</refsection>
-</refentry>
+<refentry xmlns="http://docbook.org/ns/docbook"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:xi="http://www.w3.org/2001/XInclude"
+ xmlns:src="http://nwalsh.com/xmlns/litprog/fragment"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="5.0" xml:id="highlight.source">
+<refmeta>
+<refentrytitle>highlight.source</refentrytitle>
+<refmiscinfo class="other" otherclass="datatype">boolean</refmiscinfo>
+</refmeta>
+<refnamediv>
+<refname>highlight.source</refname>
+<refpurpose>Should the content of <tag>programlisting</tag>
+be syntactically highlighted?</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+<src:fragment xml:id="highlight.source.frag">
+<xsl:param name="highlight.source" select="0"/>
+</src:fragment>
+</refsynopsisdiv>
+
+<refsection><info><title>Description</title></info>
+
+<para>When this parameter is non-zero, the stylesheets will try to do syntax highlighting of the
+content of <tag>programlisting</tag> elements. You specify the language for each programlisting
+by using the <tag class="attribute">language</tag> attribute. The <parameter>highlight.default.language</parameter>
+parameter can be used to specify the language for programlistings without a <tag class="attribute">language</tag>
+attribute. Syntax highlighting also works for <tag>screen</tag> and <tag>synopsis</tag> elements.</para>
+
+<para>The actual highlighting work is done by the XSLTHL extension module. This is an external Java library that has to be
+downloaded separately (see below).</para>
+
+<itemizedlist>
+<para>In order to use this extension, you must</para>
+
+<listitem><para>add <filename>xslthl-2.x.x.jar</filename> to your Java classpath. The latest version is available
+from <link xlink:href="http://sourceforge.net/projects/xslthl">the XSLT syntax highlighting project</link>
+at SourceForge.</para>
+</listitem>
+<listitem>
+<para>use a customization layer in which you import one of the following stylesheet modules:
+<itemizedlist>
+ <listitem>
+ <para><filename>html/highlight.xsl</filename>
+ </para>
+ </listitem>
+<listitem>
+ <para><filename>xhtml/highlight.xsl</filename>
+ </para>
+ </listitem>
+<listitem>
+ <para><filename>xhtml-1_1/highlight.xsl</filename>
+ </para>
+ </listitem>
+<listitem>
+ <para><filename>fo/highlight.xsl</filename>
+ </para>
+</listitem>
+</itemizedlist>
+</para>
+</listitem>
+<listitem><para>let either the <literal>xslthl.config</literal> Java system property or the
+<parameter>highlight.xslthl.config</parameter> parameter point to the configuration file for syntax
+highlighting (using URL syntax). DocBook XSL comes with a ready-to-use configuration file,
+<filename>highlighting/xslthl-config.xml</filename>.</para>
+</listitem>
+</itemizedlist>
+
+<para>The extension works with Saxon 6.5.x and Xalan-J. (Saxon 8.5 or later is also supported, but since it is
+an XSLT 2.0 processor it is not guaranteed to work with DocBook XSL in all circumstances.)</para>
+
+<para>The following is an example of a Saxon 6 command adapted for syntax highlighting, to be used on Windows:</para>
+
+<informalexample>
+<para><command>java -cp c:/Java/saxon.jar;c:/Java/xslthl-2.0.1.jar
+-Dxslthl.config=file:///c:/docbook-xsl/highlighting/xslthl-config.xml com.icl.saxon.StyleSheet
+-o test.html test.xml myhtml.xsl</command></para>
+</informalexample>
+
+</refsection>
+</refentry>
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.async.js b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.async.js
index 2597dde..c91a9c6 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.async.js
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.async.js
@@ -1,72 +1,72 @@
-/*
- * Async Treeview 0.1 - Lazy-loading extension for Treeview
- *
- * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
- *
- * Copyright (c) 2007 Jörn Zaefferer
- *
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * Revision: $Id$
- *
- */
-
-;(function($) {
-
-function load(settings, root, child, container) {
- $.getJSON(settings.url, {root: root}, function(response) {
- function createNode(parent) {
- var current = $("<li/>").attr("id", this.id || "").html("<span>" + this.text + "</span>").appendTo(parent);
- if (this.classes) {
- current.children("span").addClass(this.classes);
- }
- if (this.expanded) {
- current.addClass("open");
- }
- if (this.hasChildren || this.children && this.children.length) {
- var branch = $("<ul/>").appendTo(current);
- if (this.hasChildren) {
- current.addClass("hasChildren");
- createNode.call({
- text:"placeholder",
- id:"placeholder",
- children:[]
- }, branch);
- }
- if (this.children && this.children.length) {
- $.each(this.children, createNode, [branch])
- }
- }
- }
- $.each(response, createNode, [child]);
- $(container).treeview({add: child});
- });
-}
-
-var proxied = $.fn.treeview;
-$.fn.treeview = function(settings) {
- if (!settings.url) {
- return proxied.apply(this, arguments);
- }
- var container = this;
- load(settings, "source", this, container);
- var userToggle = settings.toggle;
- return proxied.call(this, $.extend({}, settings, {
- collapsed: true,
- toggle: function() {
- var $this = $(this);
- if ($this.hasClass("hasChildren")) {
- var childList = $this.removeClass("hasChildren").find("ul");
- childList.empty();
- load(settings, this.id, childList, container);
- }
- if (userToggle) {
- userToggle.apply(this, arguments);
- }
- }
- }));
-};
-
+/*
+ * Async Treeview 0.1 - Lazy-loading extension for Treeview
+ *
+ * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
+ *
+ * Copyright (c) 2007 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id$
+ *
+ */
+
+;(function($) {
+
+function load(settings, root, child, container) {
+ $.getJSON(settings.url, {root: root}, function(response) {
+ function createNode(parent) {
+ var current = $("<li/>").attr("id", this.id || "").html("<span>" + this.text + "</span>").appendTo(parent);
+ if (this.classes) {
+ current.children("span").addClass(this.classes);
+ }
+ if (this.expanded) {
+ current.addClass("open");
+ }
+ if (this.hasChildren || this.children && this.children.length) {
+ var branch = $("<ul/>").appendTo(current);
+ if (this.hasChildren) {
+ current.addClass("hasChildren");
+ createNode.call({
+ text:"placeholder",
+ id:"placeholder",
+ children:[]
+ }, branch);
+ }
+ if (this.children && this.children.length) {
+ $.each(this.children, createNode, [branch])
+ }
+ }
+ }
+ $.each(response, createNode, [child]);
+ $(container).treeview({add: child});
+ });
+}
+
+var proxied = $.fn.treeview;
+$.fn.treeview = function(settings) {
+ if (!settings.url) {
+ return proxied.apply(this, arguments);
+ }
+ var container = this;
+ load(settings, "source", this, container);
+ var userToggle = settings.toggle;
+ return proxied.call(this, $.extend({}, settings, {
+ collapsed: true,
+ toggle: function() {
+ var $this = $(this);
+ if ($this.hasClass("hasChildren")) {
+ var childList = $this.removeClass("hasChildren").find("ul");
+ childList.empty();
+ load(settings, this.id, childList, container);
+ }
+ if (userToggle) {
+ userToggle.apply(this, arguments);
+ }
+ }
+ }));
+};
+
})(jQuery); \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.css b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.css
index dbf425b..d7e2c00 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.css
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/common/jquery/treeview/jquery.treeview.css
@@ -1,85 +1,85 @@
-.treeview, .treeview ul {
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
-.treeview ul {
- background-color: white;
- margin-top: 4px;
-}
-
-.treeview .hitarea {
- background: url(images/treeview-default.gif) -64px -25px no-repeat;
- height: 16px;
- width: 16px;
- margin-left: -16px;
- float: left;
- cursor: pointer;
-}
-/* fix for IE6 */
-* html .hitarea {
- display: inline;
- float:none;
-}
-
-.treeview li {
- margin: 0;
- padding: 3px 0 3px 16px;
-}
-
-.treeview a.selected {
- background-color: #eee;
-}
-
-#treecontrol { margin: 1em 0; display: none; }
-
-.treeview .hover { color: red; cursor: pointer; }
-
-.treeview li { background: url(images/treeview-default-line.gif) 0 0 no-repeat; }
-.treeview li.collapsable, .treeview li.expandable { background-position: 0 -176px; }
-
-.treeview .expandable-hitarea { background-position: -80px -3px; }
-
-.treeview li.last { background-position: 0 -1766px }
-.treeview li.lastCollapsable, .treeview li.lastExpandable { background-image: url(images/treeview-default.gif); }
-.treeview li.lastCollapsable { background-position: 0 -111px }
-.treeview li.lastExpandable { background-position: -32px -67px }
-
-.treeview div.lastCollapsable-hitarea, .treeview div.lastExpandable-hitarea { background-position: 0; }
-
-.treeview-red li { background-image: url(images/treeview-red-line.gif); }
-.treeview-red .hitarea, .treeview-red li.lastCollapsable, .treeview-red li.lastExpandable { background-image: url(images/treeview-red.gif); }
-
-.treeview-black li { background-image: url(images/treeview-black-line.gif); }
-.treeview-black .hitarea, .treeview-black li.lastCollapsable, .treeview-black li.lastExpandable { background-image: url(images/treeview-black.gif); }
-
-.treeview-gray li { background-image: url(images/treeview-gray-line.gif); }
-.treeview-gray .hitarea, .treeview-gray li.lastCollapsable, .treeview-gray li.lastExpandable { background-image: url(images/treeview-gray.gif); }
-
-.treeview-famfamfam li { background-image: url(images/treeview-famfamfam-line.gif); }
-.treeview-famfamfam .hitarea, .treeview-famfamfam li.lastCollapsable, .treeview-famfamfam li.lastExpandable { background-image: url(images/treeview-famfamfam.gif); }
-
-
-.filetree li { padding: 3px 0 2px 16px; }
-.filetree span.folder, .filetree span.file { padding: 1px 0 1px 16px; display: block; }
-.filetree span.folder { background: url(images/folder.gif) 0 0 no-repeat; }
-.filetree li.expandable span.folder { background: url(images/folder-closed.gif) 0 0 no-repeat; }
-.filetree span.file { background: url(images/file.gif) 0 0 no-repeat; }
-
-html, body {height:100%; margin: 0; padding: 0; }
-
-/*
-html>body {
- font-size: 16px;
- font-size: 68.75%;
-} Reset Base Font Size */
- /*
-body {
- font-family: Verdana, helvetica, arial, sans-serif;
- font-size: 68.75%;
- background: #fff;
- color: #333;
-} */
-
+.treeview, .treeview ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+}
+
+.treeview ul {
+ background-color: white;
+ margin-top: 4px;
+}
+
+.treeview .hitarea {
+ background: url(images/treeview-default.gif) -64px -25px no-repeat;
+ height: 16px;
+ width: 16px;
+ margin-left: -16px;
+ float: left;
+ cursor: pointer;
+}
+/* fix for IE6 */
+* html .hitarea {
+ display: inline;
+ float:none;
+}
+
+.treeview li {
+ margin: 0;
+ padding: 3px 0 3px 16px;
+}
+
+.treeview a.selected {
+ background-color: #eee;
+}
+
+#treecontrol { margin: 1em 0; display: none; }
+
+.treeview .hover { color: red; cursor: pointer; }
+
+.treeview li { background: url(images/treeview-default-line.gif) 0 0 no-repeat; }
+.treeview li.collapsable, .treeview li.expandable { background-position: 0 -176px; }
+
+.treeview .expandable-hitarea { background-position: -80px -3px; }
+
+.treeview li.last { background-position: 0 -1766px }
+.treeview li.lastCollapsable, .treeview li.lastExpandable { background-image: url(images/treeview-default.gif); }
+.treeview li.lastCollapsable { background-position: 0 -111px }
+.treeview li.lastExpandable { background-position: -32px -67px }
+
+.treeview div.lastCollapsable-hitarea, .treeview div.lastExpandable-hitarea { background-position: 0; }
+
+.treeview-red li { background-image: url(images/treeview-red-line.gif); }
+.treeview-red .hitarea, .treeview-red li.lastCollapsable, .treeview-red li.lastExpandable { background-image: url(images/treeview-red.gif); }
+
+.treeview-black li { background-image: url(images/treeview-black-line.gif); }
+.treeview-black .hitarea, .treeview-black li.lastCollapsable, .treeview-black li.lastExpandable { background-image: url(images/treeview-black.gif); }
+
+.treeview-gray li { background-image: url(images/treeview-gray-line.gif); }
+.treeview-gray .hitarea, .treeview-gray li.lastCollapsable, .treeview-gray li.lastExpandable { background-image: url(images/treeview-gray.gif); }
+
+.treeview-famfamfam li { background-image: url(images/treeview-famfamfam-line.gif); }
+.treeview-famfamfam .hitarea, .treeview-famfamfam li.lastCollapsable, .treeview-famfamfam li.lastExpandable { background-image: url(images/treeview-famfamfam.gif); }
+
+
+.filetree li { padding: 3px 0 2px 16px; }
+.filetree span.folder, .filetree span.file { padding: 1px 0 1px 16px; display: block; }
+.filetree span.folder { background: url(images/folder.gif) 0 0 no-repeat; }
+.filetree li.expandable span.folder { background: url(images/folder-closed.gif) 0 0 no-repeat; }
+.filetree span.file { background: url(images/file.gif) 0 0 no-repeat; }
+
+html, body {height:100%; margin: 0; padding: 0; }
+
+/*
+html>body {
+ font-size: 16px;
+ font-size: 68.75%;
+} Reset Base Font Size */
+ /*
+body {
+ font-family: Verdana, helvetica, arial, sans-serif;
+ font-size: 68.75%;
+ background: #fff;
+ color: #333;
+} */
+
a img { border: none; } \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/content/search/nwSearchFnt.js b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/content/search/nwSearchFnt.js
index 0111559..0c4336d 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/content/search/nwSearchFnt.js
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/docs/content/search/nwSearchFnt.js
@@ -1,513 +1,513 @@
-/*----------------------------------------------------------------------------
- * JavaScript for webhelp search
- *----------------------------------------------------------------------------
- This file is part of the webhelpsearch plugin for DocBook WebHelp
- Copyright (c) 2007-2008 NexWave Solutions All Rights Reserved.
- www.nexwave.biz Nadege Quaine
- http://kasunbg.blogspot.com/ Kasun Gajasinghe
- */
-
-//string initialization
-var htmlfileList = "htmlFileList.js";
-var htmlfileinfoList = "htmlFileInfoList.js";
-var useCJKTokenizing = false;
-
-/* Cette fonction verifie la validite de la recherche entrre par l utilisateur */
-function Verifie(ditaSearch_Form) {
-
- // Check browser compatibitily
- if (navigator.userAgent.indexOf("Konquerer") > -1) {
-
- alert(txt_browser_not_supported);
- return;
- }
-
-
- var expressionInput = document.ditaSearch_Form.textToSearch.value;
- //Set a cookie to store the searched keywords
- $.cookie('textToSearch', expressionInput);
-
-
- if (expressionInput.length < 1) {
-
- // expression is invalid
- alert(txt_enter_at_least_1_char);
- // reactive la fenetre de search (utile car cadres)
- document.ditaSearch_Form.textToSearch.focus();
- }
- else {
-
- // Effectuer la recherche
- Effectuer_recherche(expressionInput);
-
- // reactive la fenetre de search (utile car cadres)
- document.ditaSearch_Form.textToSearch.focus();
- }
-}
-
-var stemQueryMap = new Array(); // A hashtable which maps stems to query words
-
-/* This function parses the search expression, loads the indices and displays the results*/
-function Effectuer_recherche(expressionInput) {
-
- /* Display a waiting message */
- //DisplayWaitingMessage();
-
- /*data initialisation*/
- var searchFor = ""; // expression en lowercase et sans les caracte res speciaux
- //w = new Object(); // hashtable, key=word, value = list of the index of the html files
- scriptLetterTab = new Scriptfirstchar(); // Array containing the first letter of each word to look for
- var wordsList = new Array(); // Array with the words to look for
- var finalWordsList = new Array(); // Array with the words to look for after removing spaces
- var linkTab = new Array();
- var fileAndWordList = new Array();
- var txt_wordsnotfound = "";
-
-
- /*nqu: expressionInput, la recherche est lower cased, plus remplacement des char speciaux*/
- searchFor = expressionInput.toLowerCase().replace(/<\//g, "_st_").replace(/\$_/g, "_di_").replace(/\.|%2C|%3B|%21|%3A|@|\/|\*/g, " ").replace(/(%20)+/g, " ").replace(/_st_/g, "</").replace(/_di_/g, "%24_");
-
- searchFor = searchFor.replace(/ +/g, " ");
- searchFor = searchFor.replace(/ $/, "").replace(/^ /, "");
-
- wordsList = searchFor.split(" ");
- wordsList.sort();
-
- //set the tokenizing method
- if(typeof indexerLanguage != "undefined" && (indexerLanguage=="zh" || indexerLanguage=="ja" ||indexerLanguage=="ko")){
- useCJKTokenizing=true;
- } else {
- useCJKTokenizing=false;
- }
- //If Lucene CJKTokenizer was used as the indexer, then useCJKTokenizing will be true. Else, do normal tokenizing.
- // 2-gram tokenizinghappens in CJKTokenizing,
- if(useCJKTokenizing){
- finalWordsList = cjkTokenize(wordsList);
- } else {
- finalWordsList = tokenize(wordsList);
- }
-
- //load the scripts with the indices: the following lines do not work on the server. To be corrected
- /*if (IEBrowser) {
- scriptsarray = loadTheIndexScripts (scriptLetterTab);
- } */
-
- /**
- * Compare with the indexed words (in the w[] array), and push words that are in it to tempTab.
- */
- var tempTab = new Array();
- for (var t in finalWordsList) {
- if (w[finalWordsList[t].toString()] == undefined) {
- txt_wordsnotfound += finalWordsList[t] + " ";
- } else {
- tempTab.push(finalWordsList[t]);
- }
- }
- finalWordsList = tempTab;
-
- if (finalWordsList.length) {
-
- //search 'and' and 'or' one time
- fileAndWordList = SortResults(finalWordsList);
-
- var cpt = fileAndWordList.length;
- for (var i = cpt - 1; i >= 0; i--) {
- if (fileAndWordList[i] != undefined) {
- linkTab.push("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + fileAndWordList[i][0].motslisteDisplay + "</span>" + "</p>");
-
- linkTab.push("<ul class='searchresult'>");
- for (t in fileAndWordList[i]) {
- //DEBUG: alert(": "+ fileAndWordList[i][t].filenb+" " +fileAndWordList[i][t].motsliste);
- //linkTab.push("<li><a href=\"../"+fl[fileAndWordList[i][t].filenb]+"\">"+fl[fileAndWordList[i][t].filenb]+"</a></li>");
- var tempInfo = fil[fileAndWordList[i][t].filenb];
- var pos1 = tempInfo.indexOf("@@@");
- var pos2 = tempInfo.lastIndexOf("@@@");
- var tempPath = tempInfo.substring(0, pos1);
- var tempTitle = tempInfo.substring(pos1 + 3, pos2);
- var tempShortdesc = tempInfo.substring(pos2 + 3, tempInfo.length);
-
- //file:///home/kasun/docbook/WEBHELP/webhelp-draft-output-format-idea/src/main/resources/web/webhelp/installation.html
- var linkString = "<li><a href=" + tempPath + ">" + tempTitle + "</a>";
- // var linkString = "<li><a href=\"installation.html\">" + tempTitle + "</a>";
- if ((tempShortdesc != "null")) {
- linkString += "\n<div class=\"shortdesclink\">" + tempShortdesc + "</div>";
- }
- linkString += "</li>";
- linkTab.push(linkString);
- }
- linkTab.push("</ul>");
- }
- }
- }
-
- var results = "";
- if (linkTab.length > 0) {
- /*writeln ("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + cleanwordsList + "</span>" + "<br/>"+"</p>");*/
- results = "<p>";
- //write("<ul class='searchresult'>");
- for (t in linkTab) {
- results += linkTab[t].toString();
- }
- results += "</p>";
- } else {
- results = "<p>" + "Your search returned no results for " + "<span class=\"searchExpression\">" + txt_wordsnotfound + "</span>" + "</p>";
- }
- //alert(results);
- document.getElementById('searchResults').innerHTML = results;
-}
-
-function tokenize(wordsList){
- var stemmedWordsList = new Array(); // Array with the words to look for after removing spaces
- var cleanwordsList = new Array(); // Array with the words to look for
- for(var j in wordsList){
- var word = wordsList[j];
- if(typeof stemmer != "undefined" ){
- stemQueryMap[stemmer(word)] = word;
- } else {
- stemQueryMap[word] = word;
- }
- }
- //stemmedWordsList is the stemmed list of words separated by spaces.
- for (var t in wordsList) {
- wordsList[t] = wordsList[t].replace(/(%22)|^-/g, "");
- if (wordsList[t] != "%20") {
- scriptLetterTab.add(wordsList[t].charAt(0));
- cleanwordsList.push(wordsList[t]);
- }
- }
-
- if(typeof stemmer != "undefined" ){
- //Do the stemming using Porter's stemming algorithm
- for (var i = 0; i < cleanwordsList.length; i++) {
- var stemWord = stemmer(cleanwordsList[i]);
- stemmedWordsList.push(stemWord);
- }
- } else {
- stemmedWordsList = cleanwordsList;
- }
- return stemmedWordsList;
-}
-
-//Invoker of CJKTokenizer class methods.
-function cjkTokenize(wordsList){
- var allTokens= new Array();
- var notCJKTokens= new Array();
- var j=0;
- for(j=0;j<wordsList.length;j++){
- var word = wordsList[j];
- if(getAvgAsciiValue(word) < 127){
- notCJKTokens.push(word);
- } else {
- var tokenizer = new CJKTokenizer(word);
- var tokensTmp = tokenizer.getAllTokens();
- allTokens = allTokens.concat(tokensTmp);
- }
- }
- allTokens = allTokens.concat(tokenize(notCJKTokens));
- return allTokens;
-}
-
-//A simple way to determine whether the query is in english or not.
-function getAvgAsciiValue(word){
- var tmp = 0;
- var num = word.length < 5 ? word.length:5;
- for(var i=0;i<num;i++){
- if(i==5) break;
- tmp += word.charCodeAt(i);
- }
- return tmp/num;
-}
-
-//CJKTokenizer
-function CJKTokenizer(input){
- this.input = input;
- this.offset=-1;
- this.tokens = new Array();
- this.incrementToken = incrementToken;
- this.tokenize = tokenize;
- this.getAllTokens = getAllTokens;
- this.unique = unique;
-
- function incrementToken(){
- if(this.input.length - 2 <= this.offset){
- // console.log("false "+offset);
- return false;
- }
- else {
- this.offset+=1;
- return true;
- }
- }
-
- function tokenize(){
- //document.getElementById("content").innerHTML += x.substring(offset,offset+2)+"<br>";
- return this.input.substring(this.offset,this.offset+2);
- }
-
- function getAllTokens(){
- while(this.incrementToken()){
- var tmp = this.tokenize();
- this.tokens.push(tmp);
- }
- return this.unique(this.tokens);
-// document.getElementById("content").innerHTML += tokens+" ";
-// document.getElementById("content").innerHTML += "<br>dada"+sortedTokens+" ";
-// console.log(tokens.length+"dsdsds");
- /*for(i=0;i<tokens.length;i++){
- console.log(tokens[i]);
- var ss = tokens[i] == sortedTokens[i];
-
-// document.getElementById("content").innerHTML += "<br>dada"+un[i]+"- "+stems[i]+"&nbsp;&nbsp;&nbsp;"+ ss;
- document.getElementById("content").innerHTML += "<br>"+sortedTokens[i];
- }*/
- }
-
- function unique(a)
- {
- var r = new Array();
- o:for(var i = 0, n = a.length; i < n; i++)
- {
- for(var x = 0, y = r.length; x < y; x++)
- {
- if(r[x]==a[i]) continue o;
- }
- r[r.length] = a[i];
- }
- return r;
- }
-}
-
-
-/* Scriptfirstchar: to gather the first letter of index js files to upload */
-function Scriptfirstchar() {
- this.strLetters = "";
- this.add = addLettre;
-}
-
-function addLettre(caract) {
-
- if (this.strLetters == 'undefined') {
- this.strLetters = caract;
- } else if (this.strLetters.indexOf(caract) < 0) {
- this.strLetters += caract;
- }
-
- return 0;
-}
-/* end of scriptfirstchar */
-
-/*main loader function*/
-/*tab contains the first letters of each word looked for*/
-function loadTheIndexScripts(tab) {
-
- //alert (tab.strLetters);
- var scriptsarray = new Array();
-
- for (var i = 0; i < tab.strLetters.length; i++) {
-
- scriptsarray[i] = "..\/search" + "\/" + tab.strLetters.charAt(i) + ".js";
- }
- // add the list of html files
- i++;
- scriptsarray[i] = "..\/search" + "\/" + htmlfileList;
-
- //debug
- for (var t in scriptsarray) {
- //alert (scriptsarray[t]);
- }
-
- tab = new ScriptLoader();
- for (t in scriptsarray) {
- tab.add(scriptsarray[t]);
- }
- tab.load();
- //alert ("scripts loaded");
- return (scriptsarray);
-}
-
-/* ScriptLoader: to load the scripts and wait that it's finished */
-function ScriptLoader() {
- this.cpt = 0;
- this.scriptTab = new Array();
- this.add = addAScriptInTheList;
- this.load = loadTheScripts;
- this.onScriptLoaded = onScriptLoadedFunc;
-}
-
-function addAScriptInTheList(scriptPath) {
- this.scriptTab.push(scriptPath);
-}
-
-function loadTheScripts() {
- var script;
- var head;
-
- head = document.getElementsByTagName('head').item(0);
-
- //script = document.createElement('script');
-
- for (var el in this.scriptTab) {
- //alert (el+this.scriptTab[el]);
- script = document.createElement('script');
- script.src = this.scriptTab[el];
- script.type = 'text/javascript';
- script.defer = false;
-
- head.appendChild(script);
- }
-
-}
-
-function onScriptLoadedFunc(e) {
- e = e || window.event;
- var target = e.target || e.srcElement;
- var isComplete = true;
- if (typeof target.readyState != undefined) {
-
- isComplete = (target.readyState == "complete" || target.readyState == "loaded");
- }
- if (isComplete) {
- ScriptLoader.cpt++;
- if (ScriptLoader.cpt == ScriptLoader.scripts.length) {
- ScriptLoader.onLoadComplete();
- }
- }
-}
-
-/*
-function onLoadComplete() {
- alert("loaded !!");
-} */
-
-/* End of scriptloader functions */
-
-// Array.unique( strict ) - Remove duplicate values
-function unique(tab) {
- var a = new Array();
- var i;
- var l = tab.length;
-
- if (tab[0] != undefined) {
- a[0] = tab[0];
- }
- else {
- return -1
- }
-
- for (i = 1; i < l; i++) {
- if (indexof(a, tab[i], 0) < 0) {
- a.push(tab[i]);
- }
- }
- return a;
-}
-function indexof(tab, element, begin) {
- for (var i = begin; i < tab.length; i++) {
- if (tab[i] == element) {
- return i;
- }
- }
- return -1;
-
-}
-/* end of Array functions */
-
-
-/*
- Param: mots= list of words to look for.
- This function creates an hashtable:
- - The key is the index of a html file which contains a word to look for.
- - The value is the list of all words contained in the html file.
-
- Return value: the hashtable fileAndWordList
- */
-function SortResults(mots) {
-
- var fileAndWordList = new Object();
- if (mots.length == 0) {
- return null;
- }
-
- for (var t in mots) {
- // get the list of the indices of the files.
- var listNumerosDesFicStr = w[mots[t].toString()];
- //alert ("listNumerosDesFicStr "+listNumerosDesFicStr);
- var tab = listNumerosDesFicStr.split(",");
-
- //for each file (file's index):
- for (var t2 in tab) {
- var temp = tab[t2].toString();
- if (fileAndWordList[temp] == undefined) {
-
- fileAndWordList[temp] = "" + mots[t];
- } else {
-
- fileAndWordList[temp] += "," + mots[t];
- }
- }
- }
-
- var fileAndWordListValuesOnly = new Array();
-
- // sort results according to values
- var temptab = new Array();
- for (t in fileAndWordList) {
- tab = fileAndWordList[t].split(',');
-
- var tempDisplay = new Array();
- for (var x in tab) {
- if(stemQueryMap[tab[x]] != undefined){
- tempDisplay.push(stemQueryMap[tab[x]]); //get the original word from the stem word.
- } else {
- tempDisplay.push(tab[x]); //no stem is available. (probably a CJK language)
- }
- }
- var tempDispString = tempDisplay.join(", ");
-
- temptab.push(new resultPerFile(t, fileAndWordList[t], tab.length, tempDispString));
- fileAndWordListValuesOnly.push(fileAndWordList[t]);
- }
-
-
- //alert("t"+fileAndWordListValuesOnly.toString());
-
- fileAndWordListValuesOnly = unique(fileAndWordListValuesOnly);
- fileAndWordListValuesOnly = fileAndWordListValuesOnly.sort(compare_nbMots);
- //alert("t: "+fileAndWordListValuesOnly.join(';'));
-
- var listToOutput = new Array();
-
- for (var j in fileAndWordListValuesOnly) {
- for (t in temptab) {
- if (temptab[t].motsliste == fileAndWordListValuesOnly[j]) {
- if (listToOutput[j] == undefined) {
- listToOutput[j] = new Array(temptab[t]);
- } else {
- listToOutput[j].push(temptab[t]);
- }
- }
- }
- }
- return listToOutput;
-}
-
-function resultPerFile(filenb, motsliste, motsnb, motslisteDisplay) {
- this.filenb = filenb;
- this.motsliste = motsliste;
- this.motsnb = motsnb;
- this.motslisteDisplay= motslisteDisplay;
-}
-
-function compare_nbMots(s1, s2) {
- var t1 = s1.split(',');
- var t2 = s2.split(',');
- //alert ("s1:"+t1.length + " " +t2.length)
- if (t1.length == t2.length) {
- return 0;
- } else if (t1.length > t2.length) {
- return 1;
- } else {
- return -1;
- }
- //return t1.length - t2.length);
+/*----------------------------------------------------------------------------
+ * JavaScript for webhelp search
+ *----------------------------------------------------------------------------
+ This file is part of the webhelpsearch plugin for DocBook WebHelp
+ Copyright (c) 2007-2008 NexWave Solutions All Rights Reserved.
+ www.nexwave.biz Nadege Quaine
+ http://kasunbg.blogspot.com/ Kasun Gajasinghe
+ */
+
+//string initialization
+var htmlfileList = "htmlFileList.js";
+var htmlfileinfoList = "htmlFileInfoList.js";
+var useCJKTokenizing = false;
+
+/* Cette fonction verifie la validite de la recherche entrre par l utilisateur */
+function Verifie(ditaSearch_Form) {
+
+ // Check browser compatibitily
+ if (navigator.userAgent.indexOf("Konquerer") > -1) {
+
+ alert(txt_browser_not_supported);
+ return;
+ }
+
+
+ var expressionInput = document.ditaSearch_Form.textToSearch.value;
+ //Set a cookie to store the searched keywords
+ $.cookie('textToSearch', expressionInput);
+
+
+ if (expressionInput.length < 1) {
+
+ // expression is invalid
+ alert(txt_enter_at_least_1_char);
+ // reactive la fenetre de search (utile car cadres)
+ document.ditaSearch_Form.textToSearch.focus();
+ }
+ else {
+
+ // Effectuer la recherche
+ Effectuer_recherche(expressionInput);
+
+ // reactive la fenetre de search (utile car cadres)
+ document.ditaSearch_Form.textToSearch.focus();
+ }
+}
+
+var stemQueryMap = new Array(); // A hashtable which maps stems to query words
+
+/* This function parses the search expression, loads the indices and displays the results*/
+function Effectuer_recherche(expressionInput) {
+
+ /* Display a waiting message */
+ //DisplayWaitingMessage();
+
+ /*data initialisation*/
+ var searchFor = ""; // expression en lowercase et sans les caracte res speciaux
+ //w = new Object(); // hashtable, key=word, value = list of the index of the html files
+ scriptLetterTab = new Scriptfirstchar(); // Array containing the first letter of each word to look for
+ var wordsList = new Array(); // Array with the words to look for
+ var finalWordsList = new Array(); // Array with the words to look for after removing spaces
+ var linkTab = new Array();
+ var fileAndWordList = new Array();
+ var txt_wordsnotfound = "";
+
+
+ /*nqu: expressionInput, la recherche est lower cased, plus remplacement des char speciaux*/
+ searchFor = expressionInput.toLowerCase().replace(/<\//g, "_st_").replace(/\$_/g, "_di_").replace(/\.|%2C|%3B|%21|%3A|@|\/|\*/g, " ").replace(/(%20)+/g, " ").replace(/_st_/g, "</").replace(/_di_/g, "%24_");
+
+ searchFor = searchFor.replace(/ +/g, " ");
+ searchFor = searchFor.replace(/ $/, "").replace(/^ /, "");
+
+ wordsList = searchFor.split(" ");
+ wordsList.sort();
+
+ //set the tokenizing method
+ if(typeof indexerLanguage != "undefined" && (indexerLanguage=="zh" || indexerLanguage=="ja" ||indexerLanguage=="ko")){
+ useCJKTokenizing=true;
+ } else {
+ useCJKTokenizing=false;
+ }
+ //If Lucene CJKTokenizer was used as the indexer, then useCJKTokenizing will be true. Else, do normal tokenizing.
+ // 2-gram tokenizinghappens in CJKTokenizing,
+ if(useCJKTokenizing){
+ finalWordsList = cjkTokenize(wordsList);
+ } else {
+ finalWordsList = tokenize(wordsList);
+ }
+
+ //load the scripts with the indices: the following lines do not work on the server. To be corrected
+ /*if (IEBrowser) {
+ scriptsarray = loadTheIndexScripts (scriptLetterTab);
+ } */
+
+ /**
+ * Compare with the indexed words (in the w[] array), and push words that are in it to tempTab.
+ */
+ var tempTab = new Array();
+ for (var t in finalWordsList) {
+ if (w[finalWordsList[t].toString()] == undefined) {
+ txt_wordsnotfound += finalWordsList[t] + " ";
+ } else {
+ tempTab.push(finalWordsList[t]);
+ }
+ }
+ finalWordsList = tempTab;
+
+ if (finalWordsList.length) {
+
+ //search 'and' and 'or' one time
+ fileAndWordList = SortResults(finalWordsList);
+
+ var cpt = fileAndWordList.length;
+ for (var i = cpt - 1; i >= 0; i--) {
+ if (fileAndWordList[i] != undefined) {
+ linkTab.push("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + fileAndWordList[i][0].motslisteDisplay + "</span>" + "</p>");
+
+ linkTab.push("<ul class='searchresult'>");
+ for (t in fileAndWordList[i]) {
+ //DEBUG: alert(": "+ fileAndWordList[i][t].filenb+" " +fileAndWordList[i][t].motsliste);
+ //linkTab.push("<li><a href=\"../"+fl[fileAndWordList[i][t].filenb]+"\">"+fl[fileAndWordList[i][t].filenb]+"</a></li>");
+ var tempInfo = fil[fileAndWordList[i][t].filenb];
+ var pos1 = tempInfo.indexOf("@@@");
+ var pos2 = tempInfo.lastIndexOf("@@@");
+ var tempPath = tempInfo.substring(0, pos1);
+ var tempTitle = tempInfo.substring(pos1 + 3, pos2);
+ var tempShortdesc = tempInfo.substring(pos2 + 3, tempInfo.length);
+
+ //file:///home/kasun/docbook/WEBHELP/webhelp-draft-output-format-idea/src/main/resources/web/webhelp/installation.html
+ var linkString = "<li><a href=" + tempPath + ">" + tempTitle + "</a>";
+ // var linkString = "<li><a href=\"installation.html\">" + tempTitle + "</a>";
+ if ((tempShortdesc != "null")) {
+ linkString += "\n<div class=\"shortdesclink\">" + tempShortdesc + "</div>";
+ }
+ linkString += "</li>";
+ linkTab.push(linkString);
+ }
+ linkTab.push("</ul>");
+ }
+ }
+ }
+
+ var results = "";
+ if (linkTab.length > 0) {
+ /*writeln ("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + cleanwordsList + "</span>" + "<br/>"+"</p>");*/
+ results = "<p>";
+ //write("<ul class='searchresult'>");
+ for (t in linkTab) {
+ results += linkTab[t].toString();
+ }
+ results += "</p>";
+ } else {
+ results = "<p>" + "Your search returned no results for " + "<span class=\"searchExpression\">" + txt_wordsnotfound + "</span>" + "</p>";
+ }
+ //alert(results);
+ document.getElementById('searchResults').innerHTML = results;
+}
+
+function tokenize(wordsList){
+ var stemmedWordsList = new Array(); // Array with the words to look for after removing spaces
+ var cleanwordsList = new Array(); // Array with the words to look for
+ for(var j in wordsList){
+ var word = wordsList[j];
+ if(typeof stemmer != "undefined" ){
+ stemQueryMap[stemmer(word)] = word;
+ } else {
+ stemQueryMap[word] = word;
+ }
+ }
+ //stemmedWordsList is the stemmed list of words separated by spaces.
+ for (var t in wordsList) {
+ wordsList[t] = wordsList[t].replace(/(%22)|^-/g, "");
+ if (wordsList[t] != "%20") {
+ scriptLetterTab.add(wordsList[t].charAt(0));
+ cleanwordsList.push(wordsList[t]);
+ }
+ }
+
+ if(typeof stemmer != "undefined" ){
+ //Do the stemming using Porter's stemming algorithm
+ for (var i = 0; i < cleanwordsList.length; i++) {
+ var stemWord = stemmer(cleanwordsList[i]);
+ stemmedWordsList.push(stemWord);
+ }
+ } else {
+ stemmedWordsList = cleanwordsList;
+ }
+ return stemmedWordsList;
+}
+
+//Invoker of CJKTokenizer class methods.
+function cjkTokenize(wordsList){
+ var allTokens= new Array();
+ var notCJKTokens= new Array();
+ var j=0;
+ for(j=0;j<wordsList.length;j++){
+ var word = wordsList[j];
+ if(getAvgAsciiValue(word) < 127){
+ notCJKTokens.push(word);
+ } else {
+ var tokenizer = new CJKTokenizer(word);
+ var tokensTmp = tokenizer.getAllTokens();
+ allTokens = allTokens.concat(tokensTmp);
+ }
+ }
+ allTokens = allTokens.concat(tokenize(notCJKTokens));
+ return allTokens;
+}
+
+//A simple way to determine whether the query is in english or not.
+function getAvgAsciiValue(word){
+ var tmp = 0;
+ var num = word.length < 5 ? word.length:5;
+ for(var i=0;i<num;i++){
+ if(i==5) break;
+ tmp += word.charCodeAt(i);
+ }
+ return tmp/num;
+}
+
+//CJKTokenizer
+function CJKTokenizer(input){
+ this.input = input;
+ this.offset=-1;
+ this.tokens = new Array();
+ this.incrementToken = incrementToken;
+ this.tokenize = tokenize;
+ this.getAllTokens = getAllTokens;
+ this.unique = unique;
+
+ function incrementToken(){
+ if(this.input.length - 2 <= this.offset){
+ // console.log("false "+offset);
+ return false;
+ }
+ else {
+ this.offset+=1;
+ return true;
+ }
+ }
+
+ function tokenize(){
+ //document.getElementById("content").innerHTML += x.substring(offset,offset+2)+"<br>";
+ return this.input.substring(this.offset,this.offset+2);
+ }
+
+ function getAllTokens(){
+ while(this.incrementToken()){
+ var tmp = this.tokenize();
+ this.tokens.push(tmp);
+ }
+ return this.unique(this.tokens);
+// document.getElementById("content").innerHTML += tokens+" ";
+// document.getElementById("content").innerHTML += "<br>dada"+sortedTokens+" ";
+// console.log(tokens.length+"dsdsds");
+ /*for(i=0;i<tokens.length;i++){
+ console.log(tokens[i]);
+ var ss = tokens[i] == sortedTokens[i];
+
+// document.getElementById("content").innerHTML += "<br>dada"+un[i]+"- "+stems[i]+"&nbsp;&nbsp;&nbsp;"+ ss;
+ document.getElementById("content").innerHTML += "<br>"+sortedTokens[i];
+ }*/
+ }
+
+ function unique(a)
+ {
+ var r = new Array();
+ o:for(var i = 0, n = a.length; i < n; i++)
+ {
+ for(var x = 0, y = r.length; x < y; x++)
+ {
+ if(r[x]==a[i]) continue o;
+ }
+ r[r.length] = a[i];
+ }
+ return r;
+ }
+}
+
+
+/* Scriptfirstchar: to gather the first letter of index js files to upload */
+function Scriptfirstchar() {
+ this.strLetters = "";
+ this.add = addLettre;
+}
+
+function addLettre(caract) {
+
+ if (this.strLetters == 'undefined') {
+ this.strLetters = caract;
+ } else if (this.strLetters.indexOf(caract) < 0) {
+ this.strLetters += caract;
+ }
+
+ return 0;
+}
+/* end of scriptfirstchar */
+
+/*main loader function*/
+/*tab contains the first letters of each word looked for*/
+function loadTheIndexScripts(tab) {
+
+ //alert (tab.strLetters);
+ var scriptsarray = new Array();
+
+ for (var i = 0; i < tab.strLetters.length; i++) {
+
+ scriptsarray[i] = "..\/search" + "\/" + tab.strLetters.charAt(i) + ".js";
+ }
+ // add the list of html files
+ i++;
+ scriptsarray[i] = "..\/search" + "\/" + htmlfileList;
+
+ //debug
+ for (var t in scriptsarray) {
+ //alert (scriptsarray[t]);
+ }
+
+ tab = new ScriptLoader();
+ for (t in scriptsarray) {
+ tab.add(scriptsarray[t]);
+ }
+ tab.load();
+ //alert ("scripts loaded");
+ return (scriptsarray);
+}
+
+/* ScriptLoader: to load the scripts and wait that it's finished */
+function ScriptLoader() {
+ this.cpt = 0;
+ this.scriptTab = new Array();
+ this.add = addAScriptInTheList;
+ this.load = loadTheScripts;
+ this.onScriptLoaded = onScriptLoadedFunc;
+}
+
+function addAScriptInTheList(scriptPath) {
+ this.scriptTab.push(scriptPath);
+}
+
+function loadTheScripts() {
+ var script;
+ var head;
+
+ head = document.getElementsByTagName('head').item(0);
+
+ //script = document.createElement('script');
+
+ for (var el in this.scriptTab) {
+ //alert (el+this.scriptTab[el]);
+ script = document.createElement('script');
+ script.src = this.scriptTab[el];
+ script.type = 'text/javascript';
+ script.defer = false;
+
+ head.appendChild(script);
+ }
+
+}
+
+function onScriptLoadedFunc(e) {
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+ var isComplete = true;
+ if (typeof target.readyState != undefined) {
+
+ isComplete = (target.readyState == "complete" || target.readyState == "loaded");
+ }
+ if (isComplete) {
+ ScriptLoader.cpt++;
+ if (ScriptLoader.cpt == ScriptLoader.scripts.length) {
+ ScriptLoader.onLoadComplete();
+ }
+ }
+}
+
+/*
+function onLoadComplete() {
+ alert("loaded !!");
+} */
+
+/* End of scriptloader functions */
+
+// Array.unique( strict ) - Remove duplicate values
+function unique(tab) {
+ var a = new Array();
+ var i;
+ var l = tab.length;
+
+ if (tab[0] != undefined) {
+ a[0] = tab[0];
+ }
+ else {
+ return -1
+ }
+
+ for (i = 1; i < l; i++) {
+ if (indexof(a, tab[i], 0) < 0) {
+ a.push(tab[i]);
+ }
+ }
+ return a;
+}
+function indexof(tab, element, begin) {
+ for (var i = begin; i < tab.length; i++) {
+ if (tab[i] == element) {
+ return i;
+ }
+ }
+ return -1;
+
+}
+/* end of Array functions */
+
+
+/*
+ Param: mots= list of words to look for.
+ This function creates an hashtable:
+ - The key is the index of a html file which contains a word to look for.
+ - The value is the list of all words contained in the html file.
+
+ Return value: the hashtable fileAndWordList
+ */
+function SortResults(mots) {
+
+ var fileAndWordList = new Object();
+ if (mots.length == 0) {
+ return null;
+ }
+
+ for (var t in mots) {
+ // get the list of the indices of the files.
+ var listNumerosDesFicStr = w[mots[t].toString()];
+ //alert ("listNumerosDesFicStr "+listNumerosDesFicStr);
+ var tab = listNumerosDesFicStr.split(",");
+
+ //for each file (file's index):
+ for (var t2 in tab) {
+ var temp = tab[t2].toString();
+ if (fileAndWordList[temp] == undefined) {
+
+ fileAndWordList[temp] = "" + mots[t];
+ } else {
+
+ fileAndWordList[temp] += "," + mots[t];
+ }
+ }
+ }
+
+ var fileAndWordListValuesOnly = new Array();
+
+ // sort results according to values
+ var temptab = new Array();
+ for (t in fileAndWordList) {
+ tab = fileAndWordList[t].split(',');
+
+ var tempDisplay = new Array();
+ for (var x in tab) {
+ if(stemQueryMap[tab[x]] != undefined){
+ tempDisplay.push(stemQueryMap[tab[x]]); //get the original word from the stem word.
+ } else {
+ tempDisplay.push(tab[x]); //no stem is available. (probably a CJK language)
+ }
+ }
+ var tempDispString = tempDisplay.join(", ");
+
+ temptab.push(new resultPerFile(t, fileAndWordList[t], tab.length, tempDispString));
+ fileAndWordListValuesOnly.push(fileAndWordList[t]);
+ }
+
+
+ //alert("t"+fileAndWordListValuesOnly.toString());
+
+ fileAndWordListValuesOnly = unique(fileAndWordListValuesOnly);
+ fileAndWordListValuesOnly = fileAndWordListValuesOnly.sort(compare_nbMots);
+ //alert("t: "+fileAndWordListValuesOnly.join(';'));
+
+ var listToOutput = new Array();
+
+ for (var j in fileAndWordListValuesOnly) {
+ for (t in temptab) {
+ if (temptab[t].motsliste == fileAndWordListValuesOnly[j]) {
+ if (listToOutput[j] == undefined) {
+ listToOutput[j] = new Array(temptab[t]);
+ } else {
+ listToOutput[j].push(temptab[t]);
+ }
+ }
+ }
+ }
+ return listToOutput;
+}
+
+function resultPerFile(filenb, motsliste, motsnb, motslisteDisplay) {
+ this.filenb = filenb;
+ this.motsliste = motsliste;
+ this.motsnb = motsnb;
+ this.motslisteDisplay= motslisteDisplay;
+}
+
+function compare_nbMots(s1, s2) {
+ var t1 = s1.split(',');
+ var t2 = s2.split(',');
+ //alert ("s1:"+t1.length + " " +t2.length)
+ if (t1.length == t2.length) {
+ return 0;
+ } else if (t1.length > t2.length) {
+ return 1;
+ } else {
+ return -1;
+ }
+ //return t1.length - t2.length);
} \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.async.js b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.async.js
index 2597dde..c91a9c6 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.async.js
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.async.js
@@ -1,72 +1,72 @@
-/*
- * Async Treeview 0.1 - Lazy-loading extension for Treeview
- *
- * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
- *
- * Copyright (c) 2007 Jörn Zaefferer
- *
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * Revision: $Id$
- *
- */
-
-;(function($) {
-
-function load(settings, root, child, container) {
- $.getJSON(settings.url, {root: root}, function(response) {
- function createNode(parent) {
- var current = $("<li/>").attr("id", this.id || "").html("<span>" + this.text + "</span>").appendTo(parent);
- if (this.classes) {
- current.children("span").addClass(this.classes);
- }
- if (this.expanded) {
- current.addClass("open");
- }
- if (this.hasChildren || this.children && this.children.length) {
- var branch = $("<ul/>").appendTo(current);
- if (this.hasChildren) {
- current.addClass("hasChildren");
- createNode.call({
- text:"placeholder",
- id:"placeholder",
- children:[]
- }, branch);
- }
- if (this.children && this.children.length) {
- $.each(this.children, createNode, [branch])
- }
- }
- }
- $.each(response, createNode, [child]);
- $(container).treeview({add: child});
- });
-}
-
-var proxied = $.fn.treeview;
-$.fn.treeview = function(settings) {
- if (!settings.url) {
- return proxied.apply(this, arguments);
- }
- var container = this;
- load(settings, "source", this, container);
- var userToggle = settings.toggle;
- return proxied.call(this, $.extend({}, settings, {
- collapsed: true,
- toggle: function() {
- var $this = $(this);
- if ($this.hasClass("hasChildren")) {
- var childList = $this.removeClass("hasChildren").find("ul");
- childList.empty();
- load(settings, this.id, childList, container);
- }
- if (userToggle) {
- userToggle.apply(this, arguments);
- }
- }
- }));
-};
-
+/*
+ * Async Treeview 0.1 - Lazy-loading extension for Treeview
+ *
+ * http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
+ *
+ * Copyright (c) 2007 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id$
+ *
+ */
+
+;(function($) {
+
+function load(settings, root, child, container) {
+ $.getJSON(settings.url, {root: root}, function(response) {
+ function createNode(parent) {
+ var current = $("<li/>").attr("id", this.id || "").html("<span>" + this.text + "</span>").appendTo(parent);
+ if (this.classes) {
+ current.children("span").addClass(this.classes);
+ }
+ if (this.expanded) {
+ current.addClass("open");
+ }
+ if (this.hasChildren || this.children && this.children.length) {
+ var branch = $("<ul/>").appendTo(current);
+ if (this.hasChildren) {
+ current.addClass("hasChildren");
+ createNode.call({
+ text:"placeholder",
+ id:"placeholder",
+ children:[]
+ }, branch);
+ }
+ if (this.children && this.children.length) {
+ $.each(this.children, createNode, [branch])
+ }
+ }
+ }
+ $.each(response, createNode, [child]);
+ $(container).treeview({add: child});
+ });
+}
+
+var proxied = $.fn.treeview;
+$.fn.treeview = function(settings) {
+ if (!settings.url) {
+ return proxied.apply(this, arguments);
+ }
+ var container = this;
+ load(settings, "source", this, container);
+ var userToggle = settings.toggle;
+ return proxied.call(this, $.extend({}, settings, {
+ collapsed: true,
+ toggle: function() {
+ var $this = $(this);
+ if ($this.hasClass("hasChildren")) {
+ var childList = $this.removeClass("hasChildren").find("ul");
+ childList.empty();
+ load(settings, this.id, childList, container);
+ }
+ if (userToggle) {
+ userToggle.apply(this, arguments);
+ }
+ }
+ }));
+};
+
})(jQuery); \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.css b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.css
index dbf425b..d7e2c00 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.css
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/common/jquery/treeview/jquery.treeview.css
@@ -1,85 +1,85 @@
-.treeview, .treeview ul {
- padding: 0;
- margin: 0;
- list-style: none;
-}
-
-.treeview ul {
- background-color: white;
- margin-top: 4px;
-}
-
-.treeview .hitarea {
- background: url(images/treeview-default.gif) -64px -25px no-repeat;
- height: 16px;
- width: 16px;
- margin-left: -16px;
- float: left;
- cursor: pointer;
-}
-/* fix for IE6 */
-* html .hitarea {
- display: inline;
- float:none;
-}
-
-.treeview li {
- margin: 0;
- padding: 3px 0 3px 16px;
-}
-
-.treeview a.selected {
- background-color: #eee;
-}
-
-#treecontrol { margin: 1em 0; display: none; }
-
-.treeview .hover { color: red; cursor: pointer; }
-
-.treeview li { background: url(images/treeview-default-line.gif) 0 0 no-repeat; }
-.treeview li.collapsable, .treeview li.expandable { background-position: 0 -176px; }
-
-.treeview .expandable-hitarea { background-position: -80px -3px; }
-
-.treeview li.last { background-position: 0 -1766px }
-.treeview li.lastCollapsable, .treeview li.lastExpandable { background-image: url(images/treeview-default.gif); }
-.treeview li.lastCollapsable { background-position: 0 -111px }
-.treeview li.lastExpandable { background-position: -32px -67px }
-
-.treeview div.lastCollapsable-hitarea, .treeview div.lastExpandable-hitarea { background-position: 0; }
-
-.treeview-red li { background-image: url(images/treeview-red-line.gif); }
-.treeview-red .hitarea, .treeview-red li.lastCollapsable, .treeview-red li.lastExpandable { background-image: url(images/treeview-red.gif); }
-
-.treeview-black li { background-image: url(images/treeview-black-line.gif); }
-.treeview-black .hitarea, .treeview-black li.lastCollapsable, .treeview-black li.lastExpandable { background-image: url(images/treeview-black.gif); }
-
-.treeview-gray li { background-image: url(images/treeview-gray-line.gif); }
-.treeview-gray .hitarea, .treeview-gray li.lastCollapsable, .treeview-gray li.lastExpandable { background-image: url(images/treeview-gray.gif); }
-
-.treeview-famfamfam li { background-image: url(images/treeview-famfamfam-line.gif); }
-.treeview-famfamfam .hitarea, .treeview-famfamfam li.lastCollapsable, .treeview-famfamfam li.lastExpandable { background-image: url(images/treeview-famfamfam.gif); }
-
-
-.filetree li { padding: 3px 0 2px 16px; }
-.filetree span.folder, .filetree span.file { padding: 1px 0 1px 16px; display: block; }
-.filetree span.folder { background: url(images/folder.gif) 0 0 no-repeat; }
-.filetree li.expandable span.folder { background: url(images/folder-closed.gif) 0 0 no-repeat; }
-.filetree span.file { background: url(images/file.gif) 0 0 no-repeat; }
-
-html, body {height:100%; margin: 0; padding: 0; }
-
-/*
-html>body {
- font-size: 16px;
- font-size: 68.75%;
-} Reset Base Font Size */
- /*
-body {
- font-family: Verdana, helvetica, arial, sans-serif;
- font-size: 68.75%;
- background: #fff;
- color: #333;
-} */
-
+.treeview, .treeview ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+}
+
+.treeview ul {
+ background-color: white;
+ margin-top: 4px;
+}
+
+.treeview .hitarea {
+ background: url(images/treeview-default.gif) -64px -25px no-repeat;
+ height: 16px;
+ width: 16px;
+ margin-left: -16px;
+ float: left;
+ cursor: pointer;
+}
+/* fix for IE6 */
+* html .hitarea {
+ display: inline;
+ float:none;
+}
+
+.treeview li {
+ margin: 0;
+ padding: 3px 0 3px 16px;
+}
+
+.treeview a.selected {
+ background-color: #eee;
+}
+
+#treecontrol { margin: 1em 0; display: none; }
+
+.treeview .hover { color: red; cursor: pointer; }
+
+.treeview li { background: url(images/treeview-default-line.gif) 0 0 no-repeat; }
+.treeview li.collapsable, .treeview li.expandable { background-position: 0 -176px; }
+
+.treeview .expandable-hitarea { background-position: -80px -3px; }
+
+.treeview li.last { background-position: 0 -1766px }
+.treeview li.lastCollapsable, .treeview li.lastExpandable { background-image: url(images/treeview-default.gif); }
+.treeview li.lastCollapsable { background-position: 0 -111px }
+.treeview li.lastExpandable { background-position: -32px -67px }
+
+.treeview div.lastCollapsable-hitarea, .treeview div.lastExpandable-hitarea { background-position: 0; }
+
+.treeview-red li { background-image: url(images/treeview-red-line.gif); }
+.treeview-red .hitarea, .treeview-red li.lastCollapsable, .treeview-red li.lastExpandable { background-image: url(images/treeview-red.gif); }
+
+.treeview-black li { background-image: url(images/treeview-black-line.gif); }
+.treeview-black .hitarea, .treeview-black li.lastCollapsable, .treeview-black li.lastExpandable { background-image: url(images/treeview-black.gif); }
+
+.treeview-gray li { background-image: url(images/treeview-gray-line.gif); }
+.treeview-gray .hitarea, .treeview-gray li.lastCollapsable, .treeview-gray li.lastExpandable { background-image: url(images/treeview-gray.gif); }
+
+.treeview-famfamfam li { background-image: url(images/treeview-famfamfam-line.gif); }
+.treeview-famfamfam .hitarea, .treeview-famfamfam li.lastCollapsable, .treeview-famfamfam li.lastExpandable { background-image: url(images/treeview-famfamfam.gif); }
+
+
+.filetree li { padding: 3px 0 2px 16px; }
+.filetree span.folder, .filetree span.file { padding: 1px 0 1px 16px; display: block; }
+.filetree span.folder { background: url(images/folder.gif) 0 0 no-repeat; }
+.filetree li.expandable span.folder { background: url(images/folder-closed.gif) 0 0 no-repeat; }
+.filetree span.file { background: url(images/file.gif) 0 0 no-repeat; }
+
+html, body {height:100%; margin: 0; padding: 0; }
+
+/*
+html>body {
+ font-size: 16px;
+ font-size: 68.75%;
+} Reset Base Font Size */
+ /*
+body {
+ font-family: Verdana, helvetica, arial, sans-serif;
+ font-size: 68.75%;
+ background: #fff;
+ color: #333;
+} */
+
a img { border: none; } \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/en-us.props b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/en-us.props
index da284ce..f7ed270 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/en-us.props
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/en-us.props
@@ -1,45 +1,45 @@
-DEF01=this
-DEF02=is
-DEF03=the
-DEF04=in
-DEF05=i
-DEF06=on
-DEF07=a
-DEF08=about
-DEF09=an
-DEF10=are
-DEF11=as
-DEF12=at
-DEF13=be
-DEF14=by
-DEF15=com
-DEF16=de
-DEF17=en
-DEF18=for
-DEF19=from
-DEF20=how
-DEF21=it
-DEF22=la
-DEF23=of
-DEF24=on
-DEF25=or
-DEF26=that
-DEF27=to
-DEF28=was
-DEF29=what
-DEF30=when
-DEF31=where
-DEF32=who
-DEF33=will
-DEF34=with
-DEF35=und
-DEF36=Next
-DEF37=Prev
-DEF38=Home
-DEF39=Motive
-DEF40=Inc
-DEF41=Copyright
-DEF42=All
-DEF43=rights
-DEF44=reserved
+DEF01=this
+DEF02=is
+DEF03=the
+DEF04=in
+DEF05=i
+DEF06=on
+DEF07=a
+DEF08=about
+DEF09=an
+DEF10=are
+DEF11=as
+DEF12=at
+DEF13=be
+DEF14=by
+DEF15=com
+DEF16=de
+DEF17=en
+DEF18=for
+DEF19=from
+DEF20=how
+DEF21=it
+DEF22=la
+DEF23=of
+DEF24=on
+DEF25=or
+DEF26=that
+DEF27=to
+DEF28=was
+DEF29=what
+DEF30=when
+DEF31=where
+DEF32=who
+DEF33=will
+DEF34=with
+DEF35=und
+DEF36=Next
+DEF37=Prev
+DEF38=Home
+DEF39=Motive
+DEF40=Inc
+DEF41=Copyright
+DEF42=All
+DEF43=rights
+DEF44=reserved
DEF45=Up \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/es-es.props b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/es-es.props
index fb73bdc..b1d0b40 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/es-es.props
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/es-es.props
@@ -1,179 +1,179 @@
-DEF01=un
-DEF02=una
-DEF03=unas
-DEF04=unos
-DEF05=uno
-DEF06=sobre
-DEF07=todo
-DEF08=también
-DEF09=tras
-DEF10=otro
-DEF11=algún
-DEF12=alguno
-DEF13=alguna
-DEF14=algunos
-DEF15=algunas
-DEF16=ser
-DEF17=es
-DEF18=soy
-DEF19=eres
-DEF20=somos
-DEF21=sois
-DEF22=estoy
-DEF23=esta
-DEF24=estamos
-DEF25=estais
-DEF26=estan
-DEF27=como
-DEF28=en
-DEF29=para
-DEF30=atras
-DEF31=porque
-DEF32=por
-DEF33=estado
-DEF34=estaba
-DEF35=ante
-DEF36=antes
-DEF37=siendo
-DEF38=ambos
-DEF39=pero
-DEF40=por
-DEF41=poder
-DEF42=puede
-DEF43=puedo
-DEF44=podemos
-DEF45=podeis
-DEF46=pueden
-DEF47=fui
-DEF48=fue
-DEF49=fuimos
-DEF50=fueron
-DEF51=hacer
-DEF52=hago
-DEF53=hace
-DEF54=hacemos
-DEF55=haceis
-DEF56=hacen
-DEF57=cada
-DEF58=fin
-DEF59=incluso
-DEF60=primero
-DEF61=desde
-DEF62=conseguir
-DEF63=consigo
-DEF64=consigue
-DEF65=consigues
-DEF66=conseguimos
-DEF67=consiguen
-DEF68=ir
-DEF69=voy
-DEF70=va
-DEF71=vamos
-DEF72=vais
-DEF73=van
-DEF74=vaya
-DEF75=gueno
-DEF76=ha
-DEF77=tener
-DEF78=tengo
-DEF79=tiene
-DEF80=tenemos
-DEF81=teneis
-DEF82=tienen
-DEF83=el
-DEF84=la
-DEF85=lo
-DEF86=las
-DEF87=los
-DEF88=su
-DEF89=aqui
-DEF90=mio
-DEF91=tuyo
-DEF92=ellos
-DEF93=ellas
-DEF94=nos
-DEF95=nosotros
-DEF96=vosotros
-DEF97=vosotras
-DEF98=si
-DEF99=dentro
-DEF100=solo
-DEF101=solamente
-DEF102=saber
-DEF103=sabes
-DEF104=sabe
-DEF105=sabemos
-DEF106=sabeis
-DEF107=saben
-DEF108=ultimo
-DEF109=largo
-DEF110=bastante
-DEF111=haces
-DEF112=muchos
-DEF113=aquellos
-DEF114=aquellas
-DEF115=sus
-DEF116=entonces
-DEF117=tiempo
-DEF118=verdad
-DEF119=verdadero
-DEF120=verdadera
-DEF121=cierto
-DEF122=ciertos
-DEF123=cierta
-DEF124=ciertas
-DEF125=intentar
-DEF126=intento
-DEF127=intenta
-DEF128=intentas
-DEF129=intentamos
-DEF130=intentais
-DEF131=intentan
-DEF132=dos
-DEF133=bajo
-DEF134=arriba
-DEF135=encima
-DEF136=usar
-DEF137=uso
-DEF138=usas
-DEF139=usa
-DEF140=usamos
-DEF141=usais
-DEF142=usan
-DEF143=emplear
-DEF144=empleo
-DEF145=empleas
-DEF146=emplean
-DEF147=ampleamos
-DEF148=empleais
-DEF149=valor
-DEF150=muy
-DEF151=era
-DEF152=eras
-DEF153=eramos
-DEF154=eran
-DEF155=modo
-DEF156=bien
-DEF157=cual
-DEF158=cuando
-DEF159=donde
-DEF160=mientras
-DEF161=quien
-DEF162=con
-DEF163=entre
-DEF164=sin
-DEF165=trabajo
-DEF166=trabajar
-DEF167=trabajas
-DEF168=trabaja
-DEF169=trabajamos
-DEF170=trabajais
-DEF171=trabajan
-DEF172=podria
-DEF173=podrias
-DEF174=podriamos
-DEF175=podrian
-DEF176=podriais
-DEF177=yo
-DEF178=aquel
+DEF01=un
+DEF02=una
+DEF03=unas
+DEF04=unos
+DEF05=uno
+DEF06=sobre
+DEF07=todo
+DEF08=también
+DEF09=tras
+DEF10=otro
+DEF11=algún
+DEF12=alguno
+DEF13=alguna
+DEF14=algunos
+DEF15=algunas
+DEF16=ser
+DEF17=es
+DEF18=soy
+DEF19=eres
+DEF20=somos
+DEF21=sois
+DEF22=estoy
+DEF23=esta
+DEF24=estamos
+DEF25=estais
+DEF26=estan
+DEF27=como
+DEF28=en
+DEF29=para
+DEF30=atras
+DEF31=porque
+DEF32=por
+DEF33=estado
+DEF34=estaba
+DEF35=ante
+DEF36=antes
+DEF37=siendo
+DEF38=ambos
+DEF39=pero
+DEF40=por
+DEF41=poder
+DEF42=puede
+DEF43=puedo
+DEF44=podemos
+DEF45=podeis
+DEF46=pueden
+DEF47=fui
+DEF48=fue
+DEF49=fuimos
+DEF50=fueron
+DEF51=hacer
+DEF52=hago
+DEF53=hace
+DEF54=hacemos
+DEF55=haceis
+DEF56=hacen
+DEF57=cada
+DEF58=fin
+DEF59=incluso
+DEF60=primero
+DEF61=desde
+DEF62=conseguir
+DEF63=consigo
+DEF64=consigue
+DEF65=consigues
+DEF66=conseguimos
+DEF67=consiguen
+DEF68=ir
+DEF69=voy
+DEF70=va
+DEF71=vamos
+DEF72=vais
+DEF73=van
+DEF74=vaya
+DEF75=gueno
+DEF76=ha
+DEF77=tener
+DEF78=tengo
+DEF79=tiene
+DEF80=tenemos
+DEF81=teneis
+DEF82=tienen
+DEF83=el
+DEF84=la
+DEF85=lo
+DEF86=las
+DEF87=los
+DEF88=su
+DEF89=aqui
+DEF90=mio
+DEF91=tuyo
+DEF92=ellos
+DEF93=ellas
+DEF94=nos
+DEF95=nosotros
+DEF96=vosotros
+DEF97=vosotras
+DEF98=si
+DEF99=dentro
+DEF100=solo
+DEF101=solamente
+DEF102=saber
+DEF103=sabes
+DEF104=sabe
+DEF105=sabemos
+DEF106=sabeis
+DEF107=saben
+DEF108=ultimo
+DEF109=largo
+DEF110=bastante
+DEF111=haces
+DEF112=muchos
+DEF113=aquellos
+DEF114=aquellas
+DEF115=sus
+DEF116=entonces
+DEF117=tiempo
+DEF118=verdad
+DEF119=verdadero
+DEF120=verdadera
+DEF121=cierto
+DEF122=ciertos
+DEF123=cierta
+DEF124=ciertas
+DEF125=intentar
+DEF126=intento
+DEF127=intenta
+DEF128=intentas
+DEF129=intentamos
+DEF130=intentais
+DEF131=intentan
+DEF132=dos
+DEF133=bajo
+DEF134=arriba
+DEF135=encima
+DEF136=usar
+DEF137=uso
+DEF138=usas
+DEF139=usa
+DEF140=usamos
+DEF141=usais
+DEF142=usan
+DEF143=emplear
+DEF144=empleo
+DEF145=empleas
+DEF146=emplean
+DEF147=ampleamos
+DEF148=empleais
+DEF149=valor
+DEF150=muy
+DEF151=era
+DEF152=eras
+DEF153=eramos
+DEF154=eran
+DEF155=modo
+DEF156=bien
+DEF157=cual
+DEF158=cuando
+DEF159=donde
+DEF160=mientras
+DEF161=quien
+DEF162=con
+DEF163=entre
+DEF164=sin
+DEF165=trabajo
+DEF166=trabajar
+DEF167=trabajas
+DEF168=trabaja
+DEF169=trabajamos
+DEF170=trabajais
+DEF171=trabajan
+DEF172=podria
+DEF173=podrias
+DEF174=podriamos
+DEF175=podrian
+DEF176=podriais
+DEF177=yo
+DEF178=aquel
DEF179=qué \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/nwSearchFnt.js b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/nwSearchFnt.js
index 0111559..0c4336d 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/nwSearchFnt.js
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/nwSearchFnt.js
@@ -1,513 +1,513 @@
-/*----------------------------------------------------------------------------
- * JavaScript for webhelp search
- *----------------------------------------------------------------------------
- This file is part of the webhelpsearch plugin for DocBook WebHelp
- Copyright (c) 2007-2008 NexWave Solutions All Rights Reserved.
- www.nexwave.biz Nadege Quaine
- http://kasunbg.blogspot.com/ Kasun Gajasinghe
- */
-
-//string initialization
-var htmlfileList = "htmlFileList.js";
-var htmlfileinfoList = "htmlFileInfoList.js";
-var useCJKTokenizing = false;
-
-/* Cette fonction verifie la validite de la recherche entrre par l utilisateur */
-function Verifie(ditaSearch_Form) {
-
- // Check browser compatibitily
- if (navigator.userAgent.indexOf("Konquerer") > -1) {
-
- alert(txt_browser_not_supported);
- return;
- }
-
-
- var expressionInput = document.ditaSearch_Form.textToSearch.value;
- //Set a cookie to store the searched keywords
- $.cookie('textToSearch', expressionInput);
-
-
- if (expressionInput.length < 1) {
-
- // expression is invalid
- alert(txt_enter_at_least_1_char);
- // reactive la fenetre de search (utile car cadres)
- document.ditaSearch_Form.textToSearch.focus();
- }
- else {
-
- // Effectuer la recherche
- Effectuer_recherche(expressionInput);
-
- // reactive la fenetre de search (utile car cadres)
- document.ditaSearch_Form.textToSearch.focus();
- }
-}
-
-var stemQueryMap = new Array(); // A hashtable which maps stems to query words
-
-/* This function parses the search expression, loads the indices and displays the results*/
-function Effectuer_recherche(expressionInput) {
-
- /* Display a waiting message */
- //DisplayWaitingMessage();
-
- /*data initialisation*/
- var searchFor = ""; // expression en lowercase et sans les caracte res speciaux
- //w = new Object(); // hashtable, key=word, value = list of the index of the html files
- scriptLetterTab = new Scriptfirstchar(); // Array containing the first letter of each word to look for
- var wordsList = new Array(); // Array with the words to look for
- var finalWordsList = new Array(); // Array with the words to look for after removing spaces
- var linkTab = new Array();
- var fileAndWordList = new Array();
- var txt_wordsnotfound = "";
-
-
- /*nqu: expressionInput, la recherche est lower cased, plus remplacement des char speciaux*/
- searchFor = expressionInput.toLowerCase().replace(/<\//g, "_st_").replace(/\$_/g, "_di_").replace(/\.|%2C|%3B|%21|%3A|@|\/|\*/g, " ").replace(/(%20)+/g, " ").replace(/_st_/g, "</").replace(/_di_/g, "%24_");
-
- searchFor = searchFor.replace(/ +/g, " ");
- searchFor = searchFor.replace(/ $/, "").replace(/^ /, "");
-
- wordsList = searchFor.split(" ");
- wordsList.sort();
-
- //set the tokenizing method
- if(typeof indexerLanguage != "undefined" && (indexerLanguage=="zh" || indexerLanguage=="ja" ||indexerLanguage=="ko")){
- useCJKTokenizing=true;
- } else {
- useCJKTokenizing=false;
- }
- //If Lucene CJKTokenizer was used as the indexer, then useCJKTokenizing will be true. Else, do normal tokenizing.
- // 2-gram tokenizinghappens in CJKTokenizing,
- if(useCJKTokenizing){
- finalWordsList = cjkTokenize(wordsList);
- } else {
- finalWordsList = tokenize(wordsList);
- }
-
- //load the scripts with the indices: the following lines do not work on the server. To be corrected
- /*if (IEBrowser) {
- scriptsarray = loadTheIndexScripts (scriptLetterTab);
- } */
-
- /**
- * Compare with the indexed words (in the w[] array), and push words that are in it to tempTab.
- */
- var tempTab = new Array();
- for (var t in finalWordsList) {
- if (w[finalWordsList[t].toString()] == undefined) {
- txt_wordsnotfound += finalWordsList[t] + " ";
- } else {
- tempTab.push(finalWordsList[t]);
- }
- }
- finalWordsList = tempTab;
-
- if (finalWordsList.length) {
-
- //search 'and' and 'or' one time
- fileAndWordList = SortResults(finalWordsList);
-
- var cpt = fileAndWordList.length;
- for (var i = cpt - 1; i >= 0; i--) {
- if (fileAndWordList[i] != undefined) {
- linkTab.push("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + fileAndWordList[i][0].motslisteDisplay + "</span>" + "</p>");
-
- linkTab.push("<ul class='searchresult'>");
- for (t in fileAndWordList[i]) {
- //DEBUG: alert(": "+ fileAndWordList[i][t].filenb+" " +fileAndWordList[i][t].motsliste);
- //linkTab.push("<li><a href=\"../"+fl[fileAndWordList[i][t].filenb]+"\">"+fl[fileAndWordList[i][t].filenb]+"</a></li>");
- var tempInfo = fil[fileAndWordList[i][t].filenb];
- var pos1 = tempInfo.indexOf("@@@");
- var pos2 = tempInfo.lastIndexOf("@@@");
- var tempPath = tempInfo.substring(0, pos1);
- var tempTitle = tempInfo.substring(pos1 + 3, pos2);
- var tempShortdesc = tempInfo.substring(pos2 + 3, tempInfo.length);
-
- //file:///home/kasun/docbook/WEBHELP/webhelp-draft-output-format-idea/src/main/resources/web/webhelp/installation.html
- var linkString = "<li><a href=" + tempPath + ">" + tempTitle + "</a>";
- // var linkString = "<li><a href=\"installation.html\">" + tempTitle + "</a>";
- if ((tempShortdesc != "null")) {
- linkString += "\n<div class=\"shortdesclink\">" + tempShortdesc + "</div>";
- }
- linkString += "</li>";
- linkTab.push(linkString);
- }
- linkTab.push("</ul>");
- }
- }
- }
-
- var results = "";
- if (linkTab.length > 0) {
- /*writeln ("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + cleanwordsList + "</span>" + "<br/>"+"</p>");*/
- results = "<p>";
- //write("<ul class='searchresult'>");
- for (t in linkTab) {
- results += linkTab[t].toString();
- }
- results += "</p>";
- } else {
- results = "<p>" + "Your search returned no results for " + "<span class=\"searchExpression\">" + txt_wordsnotfound + "</span>" + "</p>";
- }
- //alert(results);
- document.getElementById('searchResults').innerHTML = results;
-}
-
-function tokenize(wordsList){
- var stemmedWordsList = new Array(); // Array with the words to look for after removing spaces
- var cleanwordsList = new Array(); // Array with the words to look for
- for(var j in wordsList){
- var word = wordsList[j];
- if(typeof stemmer != "undefined" ){
- stemQueryMap[stemmer(word)] = word;
- } else {
- stemQueryMap[word] = word;
- }
- }
- //stemmedWordsList is the stemmed list of words separated by spaces.
- for (var t in wordsList) {
- wordsList[t] = wordsList[t].replace(/(%22)|^-/g, "");
- if (wordsList[t] != "%20") {
- scriptLetterTab.add(wordsList[t].charAt(0));
- cleanwordsList.push(wordsList[t]);
- }
- }
-
- if(typeof stemmer != "undefined" ){
- //Do the stemming using Porter's stemming algorithm
- for (var i = 0; i < cleanwordsList.length; i++) {
- var stemWord = stemmer(cleanwordsList[i]);
- stemmedWordsList.push(stemWord);
- }
- } else {
- stemmedWordsList = cleanwordsList;
- }
- return stemmedWordsList;
-}
-
-//Invoker of CJKTokenizer class methods.
-function cjkTokenize(wordsList){
- var allTokens= new Array();
- var notCJKTokens= new Array();
- var j=0;
- for(j=0;j<wordsList.length;j++){
- var word = wordsList[j];
- if(getAvgAsciiValue(word) < 127){
- notCJKTokens.push(word);
- } else {
- var tokenizer = new CJKTokenizer(word);
- var tokensTmp = tokenizer.getAllTokens();
- allTokens = allTokens.concat(tokensTmp);
- }
- }
- allTokens = allTokens.concat(tokenize(notCJKTokens));
- return allTokens;
-}
-
-//A simple way to determine whether the query is in english or not.
-function getAvgAsciiValue(word){
- var tmp = 0;
- var num = word.length < 5 ? word.length:5;
- for(var i=0;i<num;i++){
- if(i==5) break;
- tmp += word.charCodeAt(i);
- }
- return tmp/num;
-}
-
-//CJKTokenizer
-function CJKTokenizer(input){
- this.input = input;
- this.offset=-1;
- this.tokens = new Array();
- this.incrementToken = incrementToken;
- this.tokenize = tokenize;
- this.getAllTokens = getAllTokens;
- this.unique = unique;
-
- function incrementToken(){
- if(this.input.length - 2 <= this.offset){
- // console.log("false "+offset);
- return false;
- }
- else {
- this.offset+=1;
- return true;
- }
- }
-
- function tokenize(){
- //document.getElementById("content").innerHTML += x.substring(offset,offset+2)+"<br>";
- return this.input.substring(this.offset,this.offset+2);
- }
-
- function getAllTokens(){
- while(this.incrementToken()){
- var tmp = this.tokenize();
- this.tokens.push(tmp);
- }
- return this.unique(this.tokens);
-// document.getElementById("content").innerHTML += tokens+" ";
-// document.getElementById("content").innerHTML += "<br>dada"+sortedTokens+" ";
-// console.log(tokens.length+"dsdsds");
- /*for(i=0;i<tokens.length;i++){
- console.log(tokens[i]);
- var ss = tokens[i] == sortedTokens[i];
-
-// document.getElementById("content").innerHTML += "<br>dada"+un[i]+"- "+stems[i]+"&nbsp;&nbsp;&nbsp;"+ ss;
- document.getElementById("content").innerHTML += "<br>"+sortedTokens[i];
- }*/
- }
-
- function unique(a)
- {
- var r = new Array();
- o:for(var i = 0, n = a.length; i < n; i++)
- {
- for(var x = 0, y = r.length; x < y; x++)
- {
- if(r[x]==a[i]) continue o;
- }
- r[r.length] = a[i];
- }
- return r;
- }
-}
-
-
-/* Scriptfirstchar: to gather the first letter of index js files to upload */
-function Scriptfirstchar() {
- this.strLetters = "";
- this.add = addLettre;
-}
-
-function addLettre(caract) {
-
- if (this.strLetters == 'undefined') {
- this.strLetters = caract;
- } else if (this.strLetters.indexOf(caract) < 0) {
- this.strLetters += caract;
- }
-
- return 0;
-}
-/* end of scriptfirstchar */
-
-/*main loader function*/
-/*tab contains the first letters of each word looked for*/
-function loadTheIndexScripts(tab) {
-
- //alert (tab.strLetters);
- var scriptsarray = new Array();
-
- for (var i = 0; i < tab.strLetters.length; i++) {
-
- scriptsarray[i] = "..\/search" + "\/" + tab.strLetters.charAt(i) + ".js";
- }
- // add the list of html files
- i++;
- scriptsarray[i] = "..\/search" + "\/" + htmlfileList;
-
- //debug
- for (var t in scriptsarray) {
- //alert (scriptsarray[t]);
- }
-
- tab = new ScriptLoader();
- for (t in scriptsarray) {
- tab.add(scriptsarray[t]);
- }
- tab.load();
- //alert ("scripts loaded");
- return (scriptsarray);
-}
-
-/* ScriptLoader: to load the scripts and wait that it's finished */
-function ScriptLoader() {
- this.cpt = 0;
- this.scriptTab = new Array();
- this.add = addAScriptInTheList;
- this.load = loadTheScripts;
- this.onScriptLoaded = onScriptLoadedFunc;
-}
-
-function addAScriptInTheList(scriptPath) {
- this.scriptTab.push(scriptPath);
-}
-
-function loadTheScripts() {
- var script;
- var head;
-
- head = document.getElementsByTagName('head').item(0);
-
- //script = document.createElement('script');
-
- for (var el in this.scriptTab) {
- //alert (el+this.scriptTab[el]);
- script = document.createElement('script');
- script.src = this.scriptTab[el];
- script.type = 'text/javascript';
- script.defer = false;
-
- head.appendChild(script);
- }
-
-}
-
-function onScriptLoadedFunc(e) {
- e = e || window.event;
- var target = e.target || e.srcElement;
- var isComplete = true;
- if (typeof target.readyState != undefined) {
-
- isComplete = (target.readyState == "complete" || target.readyState == "loaded");
- }
- if (isComplete) {
- ScriptLoader.cpt++;
- if (ScriptLoader.cpt == ScriptLoader.scripts.length) {
- ScriptLoader.onLoadComplete();
- }
- }
-}
-
-/*
-function onLoadComplete() {
- alert("loaded !!");
-} */
-
-/* End of scriptloader functions */
-
-// Array.unique( strict ) - Remove duplicate values
-function unique(tab) {
- var a = new Array();
- var i;
- var l = tab.length;
-
- if (tab[0] != undefined) {
- a[0] = tab[0];
- }
- else {
- return -1
- }
-
- for (i = 1; i < l; i++) {
- if (indexof(a, tab[i], 0) < 0) {
- a.push(tab[i]);
- }
- }
- return a;
-}
-function indexof(tab, element, begin) {
- for (var i = begin; i < tab.length; i++) {
- if (tab[i] == element) {
- return i;
- }
- }
- return -1;
-
-}
-/* end of Array functions */
-
-
-/*
- Param: mots= list of words to look for.
- This function creates an hashtable:
- - The key is the index of a html file which contains a word to look for.
- - The value is the list of all words contained in the html file.
-
- Return value: the hashtable fileAndWordList
- */
-function SortResults(mots) {
-
- var fileAndWordList = new Object();
- if (mots.length == 0) {
- return null;
- }
-
- for (var t in mots) {
- // get the list of the indices of the files.
- var listNumerosDesFicStr = w[mots[t].toString()];
- //alert ("listNumerosDesFicStr "+listNumerosDesFicStr);
- var tab = listNumerosDesFicStr.split(",");
-
- //for each file (file's index):
- for (var t2 in tab) {
- var temp = tab[t2].toString();
- if (fileAndWordList[temp] == undefined) {
-
- fileAndWordList[temp] = "" + mots[t];
- } else {
-
- fileAndWordList[temp] += "," + mots[t];
- }
- }
- }
-
- var fileAndWordListValuesOnly = new Array();
-
- // sort results according to values
- var temptab = new Array();
- for (t in fileAndWordList) {
- tab = fileAndWordList[t].split(',');
-
- var tempDisplay = new Array();
- for (var x in tab) {
- if(stemQueryMap[tab[x]] != undefined){
- tempDisplay.push(stemQueryMap[tab[x]]); //get the original word from the stem word.
- } else {
- tempDisplay.push(tab[x]); //no stem is available. (probably a CJK language)
- }
- }
- var tempDispString = tempDisplay.join(", ");
-
- temptab.push(new resultPerFile(t, fileAndWordList[t], tab.length, tempDispString));
- fileAndWordListValuesOnly.push(fileAndWordList[t]);
- }
-
-
- //alert("t"+fileAndWordListValuesOnly.toString());
-
- fileAndWordListValuesOnly = unique(fileAndWordListValuesOnly);
- fileAndWordListValuesOnly = fileAndWordListValuesOnly.sort(compare_nbMots);
- //alert("t: "+fileAndWordListValuesOnly.join(';'));
-
- var listToOutput = new Array();
-
- for (var j in fileAndWordListValuesOnly) {
- for (t in temptab) {
- if (temptab[t].motsliste == fileAndWordListValuesOnly[j]) {
- if (listToOutput[j] == undefined) {
- listToOutput[j] = new Array(temptab[t]);
- } else {
- listToOutput[j].push(temptab[t]);
- }
- }
- }
- }
- return listToOutput;
-}
-
-function resultPerFile(filenb, motsliste, motsnb, motslisteDisplay) {
- this.filenb = filenb;
- this.motsliste = motsliste;
- this.motsnb = motsnb;
- this.motslisteDisplay= motslisteDisplay;
-}
-
-function compare_nbMots(s1, s2) {
- var t1 = s1.split(',');
- var t2 = s2.split(',');
- //alert ("s1:"+t1.length + " " +t2.length)
- if (t1.length == t2.length) {
- return 0;
- } else if (t1.length > t2.length) {
- return 1;
- } else {
- return -1;
- }
- //return t1.length - t2.length);
+/*----------------------------------------------------------------------------
+ * JavaScript for webhelp search
+ *----------------------------------------------------------------------------
+ This file is part of the webhelpsearch plugin for DocBook WebHelp
+ Copyright (c) 2007-2008 NexWave Solutions All Rights Reserved.
+ www.nexwave.biz Nadege Quaine
+ http://kasunbg.blogspot.com/ Kasun Gajasinghe
+ */
+
+//string initialization
+var htmlfileList = "htmlFileList.js";
+var htmlfileinfoList = "htmlFileInfoList.js";
+var useCJKTokenizing = false;
+
+/* Cette fonction verifie la validite de la recherche entrre par l utilisateur */
+function Verifie(ditaSearch_Form) {
+
+ // Check browser compatibitily
+ if (navigator.userAgent.indexOf("Konquerer") > -1) {
+
+ alert(txt_browser_not_supported);
+ return;
+ }
+
+
+ var expressionInput = document.ditaSearch_Form.textToSearch.value;
+ //Set a cookie to store the searched keywords
+ $.cookie('textToSearch', expressionInput);
+
+
+ if (expressionInput.length < 1) {
+
+ // expression is invalid
+ alert(txt_enter_at_least_1_char);
+ // reactive la fenetre de search (utile car cadres)
+ document.ditaSearch_Form.textToSearch.focus();
+ }
+ else {
+
+ // Effectuer la recherche
+ Effectuer_recherche(expressionInput);
+
+ // reactive la fenetre de search (utile car cadres)
+ document.ditaSearch_Form.textToSearch.focus();
+ }
+}
+
+var stemQueryMap = new Array(); // A hashtable which maps stems to query words
+
+/* This function parses the search expression, loads the indices and displays the results*/
+function Effectuer_recherche(expressionInput) {
+
+ /* Display a waiting message */
+ //DisplayWaitingMessage();
+
+ /*data initialisation*/
+ var searchFor = ""; // expression en lowercase et sans les caracte res speciaux
+ //w = new Object(); // hashtable, key=word, value = list of the index of the html files
+ scriptLetterTab = new Scriptfirstchar(); // Array containing the first letter of each word to look for
+ var wordsList = new Array(); // Array with the words to look for
+ var finalWordsList = new Array(); // Array with the words to look for after removing spaces
+ var linkTab = new Array();
+ var fileAndWordList = new Array();
+ var txt_wordsnotfound = "";
+
+
+ /*nqu: expressionInput, la recherche est lower cased, plus remplacement des char speciaux*/
+ searchFor = expressionInput.toLowerCase().replace(/<\//g, "_st_").replace(/\$_/g, "_di_").replace(/\.|%2C|%3B|%21|%3A|@|\/|\*/g, " ").replace(/(%20)+/g, " ").replace(/_st_/g, "</").replace(/_di_/g, "%24_");
+
+ searchFor = searchFor.replace(/ +/g, " ");
+ searchFor = searchFor.replace(/ $/, "").replace(/^ /, "");
+
+ wordsList = searchFor.split(" ");
+ wordsList.sort();
+
+ //set the tokenizing method
+ if(typeof indexerLanguage != "undefined" && (indexerLanguage=="zh" || indexerLanguage=="ja" ||indexerLanguage=="ko")){
+ useCJKTokenizing=true;
+ } else {
+ useCJKTokenizing=false;
+ }
+ //If Lucene CJKTokenizer was used as the indexer, then useCJKTokenizing will be true. Else, do normal tokenizing.
+ // 2-gram tokenizinghappens in CJKTokenizing,
+ if(useCJKTokenizing){
+ finalWordsList = cjkTokenize(wordsList);
+ } else {
+ finalWordsList = tokenize(wordsList);
+ }
+
+ //load the scripts with the indices: the following lines do not work on the server. To be corrected
+ /*if (IEBrowser) {
+ scriptsarray = loadTheIndexScripts (scriptLetterTab);
+ } */
+
+ /**
+ * Compare with the indexed words (in the w[] array), and push words that are in it to tempTab.
+ */
+ var tempTab = new Array();
+ for (var t in finalWordsList) {
+ if (w[finalWordsList[t].toString()] == undefined) {
+ txt_wordsnotfound += finalWordsList[t] + " ";
+ } else {
+ tempTab.push(finalWordsList[t]);
+ }
+ }
+ finalWordsList = tempTab;
+
+ if (finalWordsList.length) {
+
+ //search 'and' and 'or' one time
+ fileAndWordList = SortResults(finalWordsList);
+
+ var cpt = fileAndWordList.length;
+ for (var i = cpt - 1; i >= 0; i--) {
+ if (fileAndWordList[i] != undefined) {
+ linkTab.push("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + fileAndWordList[i][0].motslisteDisplay + "</span>" + "</p>");
+
+ linkTab.push("<ul class='searchresult'>");
+ for (t in fileAndWordList[i]) {
+ //DEBUG: alert(": "+ fileAndWordList[i][t].filenb+" " +fileAndWordList[i][t].motsliste);
+ //linkTab.push("<li><a href=\"../"+fl[fileAndWordList[i][t].filenb]+"\">"+fl[fileAndWordList[i][t].filenb]+"</a></li>");
+ var tempInfo = fil[fileAndWordList[i][t].filenb];
+ var pos1 = tempInfo.indexOf("@@@");
+ var pos2 = tempInfo.lastIndexOf("@@@");
+ var tempPath = tempInfo.substring(0, pos1);
+ var tempTitle = tempInfo.substring(pos1 + 3, pos2);
+ var tempShortdesc = tempInfo.substring(pos2 + 3, tempInfo.length);
+
+ //file:///home/kasun/docbook/WEBHELP/webhelp-draft-output-format-idea/src/main/resources/web/webhelp/installation.html
+ var linkString = "<li><a href=" + tempPath + ">" + tempTitle + "</a>";
+ // var linkString = "<li><a href=\"installation.html\">" + tempTitle + "</a>";
+ if ((tempShortdesc != "null")) {
+ linkString += "\n<div class=\"shortdesclink\">" + tempShortdesc + "</div>";
+ }
+ linkString += "</li>";
+ linkTab.push(linkString);
+ }
+ linkTab.push("</ul>");
+ }
+ }
+ }
+
+ var results = "";
+ if (linkTab.length > 0) {
+ /*writeln ("<p>" + txt_results_for + " " + "<span class=\"searchExpression\">" + cleanwordsList + "</span>" + "<br/>"+"</p>");*/
+ results = "<p>";
+ //write("<ul class='searchresult'>");
+ for (t in linkTab) {
+ results += linkTab[t].toString();
+ }
+ results += "</p>";
+ } else {
+ results = "<p>" + "Your search returned no results for " + "<span class=\"searchExpression\">" + txt_wordsnotfound + "</span>" + "</p>";
+ }
+ //alert(results);
+ document.getElementById('searchResults').innerHTML = results;
+}
+
+function tokenize(wordsList){
+ var stemmedWordsList = new Array(); // Array with the words to look for after removing spaces
+ var cleanwordsList = new Array(); // Array with the words to look for
+ for(var j in wordsList){
+ var word = wordsList[j];
+ if(typeof stemmer != "undefined" ){
+ stemQueryMap[stemmer(word)] = word;
+ } else {
+ stemQueryMap[word] = word;
+ }
+ }
+ //stemmedWordsList is the stemmed list of words separated by spaces.
+ for (var t in wordsList) {
+ wordsList[t] = wordsList[t].replace(/(%22)|^-/g, "");
+ if (wordsList[t] != "%20") {
+ scriptLetterTab.add(wordsList[t].charAt(0));
+ cleanwordsList.push(wordsList[t]);
+ }
+ }
+
+ if(typeof stemmer != "undefined" ){
+ //Do the stemming using Porter's stemming algorithm
+ for (var i = 0; i < cleanwordsList.length; i++) {
+ var stemWord = stemmer(cleanwordsList[i]);
+ stemmedWordsList.push(stemWord);
+ }
+ } else {
+ stemmedWordsList = cleanwordsList;
+ }
+ return stemmedWordsList;
+}
+
+//Invoker of CJKTokenizer class methods.
+function cjkTokenize(wordsList){
+ var allTokens= new Array();
+ var notCJKTokens= new Array();
+ var j=0;
+ for(j=0;j<wordsList.length;j++){
+ var word = wordsList[j];
+ if(getAvgAsciiValue(word) < 127){
+ notCJKTokens.push(word);
+ } else {
+ var tokenizer = new CJKTokenizer(word);
+ var tokensTmp = tokenizer.getAllTokens();
+ allTokens = allTokens.concat(tokensTmp);
+ }
+ }
+ allTokens = allTokens.concat(tokenize(notCJKTokens));
+ return allTokens;
+}
+
+//A simple way to determine whether the query is in english or not.
+function getAvgAsciiValue(word){
+ var tmp = 0;
+ var num = word.length < 5 ? word.length:5;
+ for(var i=0;i<num;i++){
+ if(i==5) break;
+ tmp += word.charCodeAt(i);
+ }
+ return tmp/num;
+}
+
+//CJKTokenizer
+function CJKTokenizer(input){
+ this.input = input;
+ this.offset=-1;
+ this.tokens = new Array();
+ this.incrementToken = incrementToken;
+ this.tokenize = tokenize;
+ this.getAllTokens = getAllTokens;
+ this.unique = unique;
+
+ function incrementToken(){
+ if(this.input.length - 2 <= this.offset){
+ // console.log("false "+offset);
+ return false;
+ }
+ else {
+ this.offset+=1;
+ return true;
+ }
+ }
+
+ function tokenize(){
+ //document.getElementById("content").innerHTML += x.substring(offset,offset+2)+"<br>";
+ return this.input.substring(this.offset,this.offset+2);
+ }
+
+ function getAllTokens(){
+ while(this.incrementToken()){
+ var tmp = this.tokenize();
+ this.tokens.push(tmp);
+ }
+ return this.unique(this.tokens);
+// document.getElementById("content").innerHTML += tokens+" ";
+// document.getElementById("content").innerHTML += "<br>dada"+sortedTokens+" ";
+// console.log(tokens.length+"dsdsds");
+ /*for(i=0;i<tokens.length;i++){
+ console.log(tokens[i]);
+ var ss = tokens[i] == sortedTokens[i];
+
+// document.getElementById("content").innerHTML += "<br>dada"+un[i]+"- "+stems[i]+"&nbsp;&nbsp;&nbsp;"+ ss;
+ document.getElementById("content").innerHTML += "<br>"+sortedTokens[i];
+ }*/
+ }
+
+ function unique(a)
+ {
+ var r = new Array();
+ o:for(var i = 0, n = a.length; i < n; i++)
+ {
+ for(var x = 0, y = r.length; x < y; x++)
+ {
+ if(r[x]==a[i]) continue o;
+ }
+ r[r.length] = a[i];
+ }
+ return r;
+ }
+}
+
+
+/* Scriptfirstchar: to gather the first letter of index js files to upload */
+function Scriptfirstchar() {
+ this.strLetters = "";
+ this.add = addLettre;
+}
+
+function addLettre(caract) {
+
+ if (this.strLetters == 'undefined') {
+ this.strLetters = caract;
+ } else if (this.strLetters.indexOf(caract) < 0) {
+ this.strLetters += caract;
+ }
+
+ return 0;
+}
+/* end of scriptfirstchar */
+
+/*main loader function*/
+/*tab contains the first letters of each word looked for*/
+function loadTheIndexScripts(tab) {
+
+ //alert (tab.strLetters);
+ var scriptsarray = new Array();
+
+ for (var i = 0; i < tab.strLetters.length; i++) {
+
+ scriptsarray[i] = "..\/search" + "\/" + tab.strLetters.charAt(i) + ".js";
+ }
+ // add the list of html files
+ i++;
+ scriptsarray[i] = "..\/search" + "\/" + htmlfileList;
+
+ //debug
+ for (var t in scriptsarray) {
+ //alert (scriptsarray[t]);
+ }
+
+ tab = new ScriptLoader();
+ for (t in scriptsarray) {
+ tab.add(scriptsarray[t]);
+ }
+ tab.load();
+ //alert ("scripts loaded");
+ return (scriptsarray);
+}
+
+/* ScriptLoader: to load the scripts and wait that it's finished */
+function ScriptLoader() {
+ this.cpt = 0;
+ this.scriptTab = new Array();
+ this.add = addAScriptInTheList;
+ this.load = loadTheScripts;
+ this.onScriptLoaded = onScriptLoadedFunc;
+}
+
+function addAScriptInTheList(scriptPath) {
+ this.scriptTab.push(scriptPath);
+}
+
+function loadTheScripts() {
+ var script;
+ var head;
+
+ head = document.getElementsByTagName('head').item(0);
+
+ //script = document.createElement('script');
+
+ for (var el in this.scriptTab) {
+ //alert (el+this.scriptTab[el]);
+ script = document.createElement('script');
+ script.src = this.scriptTab[el];
+ script.type = 'text/javascript';
+ script.defer = false;
+
+ head.appendChild(script);
+ }
+
+}
+
+function onScriptLoadedFunc(e) {
+ e = e || window.event;
+ var target = e.target || e.srcElement;
+ var isComplete = true;
+ if (typeof target.readyState != undefined) {
+
+ isComplete = (target.readyState == "complete" || target.readyState == "loaded");
+ }
+ if (isComplete) {
+ ScriptLoader.cpt++;
+ if (ScriptLoader.cpt == ScriptLoader.scripts.length) {
+ ScriptLoader.onLoadComplete();
+ }
+ }
+}
+
+/*
+function onLoadComplete() {
+ alert("loaded !!");
+} */
+
+/* End of scriptloader functions */
+
+// Array.unique( strict ) - Remove duplicate values
+function unique(tab) {
+ var a = new Array();
+ var i;
+ var l = tab.length;
+
+ if (tab[0] != undefined) {
+ a[0] = tab[0];
+ }
+ else {
+ return -1
+ }
+
+ for (i = 1; i < l; i++) {
+ if (indexof(a, tab[i], 0) < 0) {
+ a.push(tab[i]);
+ }
+ }
+ return a;
+}
+function indexof(tab, element, begin) {
+ for (var i = begin; i < tab.length; i++) {
+ if (tab[i] == element) {
+ return i;
+ }
+ }
+ return -1;
+
+}
+/* end of Array functions */
+
+
+/*
+ Param: mots= list of words to look for.
+ This function creates an hashtable:
+ - The key is the index of a html file which contains a word to look for.
+ - The value is the list of all words contained in the html file.
+
+ Return value: the hashtable fileAndWordList
+ */
+function SortResults(mots) {
+
+ var fileAndWordList = new Object();
+ if (mots.length == 0) {
+ return null;
+ }
+
+ for (var t in mots) {
+ // get the list of the indices of the files.
+ var listNumerosDesFicStr = w[mots[t].toString()];
+ //alert ("listNumerosDesFicStr "+listNumerosDesFicStr);
+ var tab = listNumerosDesFicStr.split(",");
+
+ //for each file (file's index):
+ for (var t2 in tab) {
+ var temp = tab[t2].toString();
+ if (fileAndWordList[temp] == undefined) {
+
+ fileAndWordList[temp] = "" + mots[t];
+ } else {
+
+ fileAndWordList[temp] += "," + mots[t];
+ }
+ }
+ }
+
+ var fileAndWordListValuesOnly = new Array();
+
+ // sort results according to values
+ var temptab = new Array();
+ for (t in fileAndWordList) {
+ tab = fileAndWordList[t].split(',');
+
+ var tempDisplay = new Array();
+ for (var x in tab) {
+ if(stemQueryMap[tab[x]] != undefined){
+ tempDisplay.push(stemQueryMap[tab[x]]); //get the original word from the stem word.
+ } else {
+ tempDisplay.push(tab[x]); //no stem is available. (probably a CJK language)
+ }
+ }
+ var tempDispString = tempDisplay.join(", ");
+
+ temptab.push(new resultPerFile(t, fileAndWordList[t], tab.length, tempDispString));
+ fileAndWordListValuesOnly.push(fileAndWordList[t]);
+ }
+
+
+ //alert("t"+fileAndWordListValuesOnly.toString());
+
+ fileAndWordListValuesOnly = unique(fileAndWordListValuesOnly);
+ fileAndWordListValuesOnly = fileAndWordListValuesOnly.sort(compare_nbMots);
+ //alert("t: "+fileAndWordListValuesOnly.join(';'));
+
+ var listToOutput = new Array();
+
+ for (var j in fileAndWordListValuesOnly) {
+ for (t in temptab) {
+ if (temptab[t].motsliste == fileAndWordListValuesOnly[j]) {
+ if (listToOutput[j] == undefined) {
+ listToOutput[j] = new Array(temptab[t]);
+ } else {
+ listToOutput[j].push(temptab[t]);
+ }
+ }
+ }
+ }
+ return listToOutput;
+}
+
+function resultPerFile(filenb, motsliste, motsnb, motslisteDisplay) {
+ this.filenb = filenb;
+ this.motsliste = motsliste;
+ this.motsnb = motsnb;
+ this.motslisteDisplay= motslisteDisplay;
+}
+
+function compare_nbMots(s1, s2) {
+ var t1 = s1.split(',');
+ var t2 = s2.split(',');
+ //alert ("s1:"+t1.length + " " +t2.length)
+ if (t1.length == t2.length) {
+ return 0;
+ } else if (t1.length > t2.length) {
+ return 1;
+ } else {
+ return -1;
+ }
+ //return t1.length - t2.length);
} \ No newline at end of file
diff --git a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/punctuation.props b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/punctuation.props
index d3e3fcd..edb3413 100644
--- a/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/punctuation.props
+++ b/SCons/Tool/docbook/docbook-xsl-1.76.1/webhelp/template/content/search/punctuation.props
@@ -1,31 +1,31 @@
-Punct01=\\u3002
-Punct02=\\u3003
-Punct03=\\u300C
-Punct04=\\u300D
-Punct05=\\u300E
-Punct06=\\u300F
-Punct07=\\u301D
-Punct08=\\u301E
-Punct09=\\u301F
-Punct10=\\u309B
-Punct11=\\u2018
-Punct12=\\u2019
-Punct13=\\u201A
-Punct14=\\u201C
-Punct15=\\u201D
-Punct16=\\u201E
-Punct17=\\u2032
-Punct18=\\u2033
-Punct19=\\u2035
-Punct20=\\u2039
-Punct21=\\u203A
-Punct22=\\u201E
-Punct23=\\u00BB
-Punct24=\\u00AB
-Punct25=©
-Punct26=’
-Punct27=\\u00A0
-Punct28=\\u2014
-
-
-
+Punct01=\\u3002
+Punct02=\\u3003
+Punct03=\\u300C
+Punct04=\\u300D
+Punct05=\\u300E
+Punct06=\\u300F
+Punct07=\\u301D
+Punct08=\\u301E
+Punct09=\\u301F
+Punct10=\\u309B
+Punct11=\\u2018
+Punct12=\\u2019
+Punct13=\\u201A
+Punct14=\\u201C
+Punct15=\\u201D
+Punct16=\\u201E
+Punct17=\\u2032
+Punct18=\\u2033
+Punct19=\\u2035
+Punct20=\\u2039
+Punct21=\\u203A
+Punct22=\\u201E
+Punct23=\\u00BB
+Punct24=\\u00AB
+Punct25=©
+Punct26=’
+Punct27=\\u00A0
+Punct28=\\u2014
+
+
+
diff --git a/SCons/Tool/docbook/docs/html.xsl b/SCons/Tool/docbook/docs/html.xsl
index f53e1b3..407e18d 100644
--- a/SCons/Tool/docbook/docs/html.xsl
+++ b/SCons/Tool/docbook/docs/html.xsl
@@ -1,55 +1,55 @@
-<?xml version='1.0'?>
-<!--
-
- Copyright (c) 2001-2010 The SCons Foundation
-
- 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.
-
--->
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
- <xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl"/>
-
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"/>
-<xsl:param name="html.stylesheet" select="'scons.css'"/>
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-</xsl:stylesheet>
-
+<?xml version='1.0'?>
+<!--
+
+ Copyright (c) 2001-2010 The SCons Foundation
+
+ 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.
+
+-->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+ <xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl"/>
+
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="html.stylesheet" select="'scons.css'"/>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+</xsl:stylesheet>
+
diff --git a/SCons/Tool/docbook/docs/pdf.xsl b/SCons/Tool/docbook/docs/pdf.xsl
index 25b4433..8703ced 100644
--- a/SCons/Tool/docbook/docs/pdf.xsl
+++ b/SCons/Tool/docbook/docs/pdf.xsl
@@ -1,62 +1,62 @@
-<?xml version='1.0'?>
-<!--
-
- Copyright (c) 2001-2010 The SCons Foundation
-
- 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.
-
--->
-
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
- <xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl"/>
-
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"></xsl:param>
-<xsl:param name="toc.indent.width" select="0"></xsl:param>
-<xsl:param name="body.start.indent">0pt</xsl:param>
-<xsl:param name="shade.verbatim" select="1"></xsl:param>
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-<xsl:template match="varlistentry/term">
- <xsl:call-template name="inline.boldseq"/>
-</xsl:template>
-
-</xsl:stylesheet>
-
+<?xml version='1.0'?>
+<!--
+
+ Copyright (c) 2001-2010 The SCons Foundation
+
+ 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.
+
+-->
+
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+ <xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl"/>
+
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"></xsl:param>
+<xsl:param name="toc.indent.width" select="0"></xsl:param>
+<xsl:param name="body.start.indent">0pt</xsl:param>
+<xsl:param name="shade.verbatim" select="1"></xsl:param>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+<xsl:template match="varlistentry/term">
+ <xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/SCons/Tool/f03.xml b/SCons/Tool/f03.xml
index fc14ace..c989385 100644
--- a/SCons/Tool/f03.xml
+++ b/SCons/Tool/f03.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -45,6 +45,7 @@ Set construction variables for generic POSIX Fortran 03 compilers.
<item>F03PPCOMSTR</item>
<item>SHF03COMSTR</item>
<item>SHF03PPCOMSTR</item>
+<item>FORTRANCOMMONFLAGS</item>
</uses>
</tool>
@@ -271,7 +272,7 @@ Options that are passed to the Fortran 03 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF03FLAGS; if you need to define specific
user options for Fortran 03 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
diff --git a/SCons/Tool/f08.xml b/SCons/Tool/f08.xml
index fa7f633..8f69b93 100644
--- a/SCons/Tool/f08.xml
+++ b/SCons/Tool/f08.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -45,6 +45,7 @@ Set construction variables for generic POSIX Fortran 08 compilers.
<item>F08PPCOMSTR</item>
<item>SHF08COMSTR</item>
<item>SHF08PPCOMSTR</item>
+<item>FORTRANCOMMONFLAGS</item>
</uses>
</tool>
@@ -271,7 +272,7 @@ 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,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
diff --git a/SCons/Tool/f77.xml b/SCons/Tool/f77.xml
index cade57f..f60d617 100644
--- a/SCons/Tool/f77.xml
+++ b/SCons/Tool/f77.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -58,6 +58,9 @@ Set construction variables for generic POSIX Fortran 77 compilers.
<item>SHF77PPCOMSTR</item>
<item>SHFORTRANCOMSTR</item>
<item>SHFORTRANPPCOMSTR</item>
+<item>FORTRANFLAGS</item>
+<item>FORTRANCOMMONFLAGS</item>
+<item>SHFORTRANFLAGS</item>
</uses>
</tool>
@@ -284,7 +287,7 @@ Options that are passed to the Fortran 77 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF77FLAGS; if you need to define specific
user options for Fortran 77 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
diff --git a/SCons/Tool/f90.xml b/SCons/Tool/f90.xml
index 343aefe..ab76def 100644
--- a/SCons/Tool/f90.xml
+++ b/SCons/Tool/f90.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -45,6 +45,7 @@ Set construction variables for generic POSIX Fortran 90 compilers.
<item>F90PPCOMSTR</item>
<item>SHF90COMSTR</item>
<item>SHF90PPCOMSTR</item>
+<item>FORTRANCOMMONFLAGS</item>
</uses>
</tool>
@@ -270,7 +271,7 @@ Options that are passed to the Fortran 90 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF90FLAGS; if you need to define specific
user options for Fortran 90 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
diff --git a/SCons/Tool/f95.xml b/SCons/Tool/f95.xml
index 3f38030..59cfd78 100644
--- a/SCons/Tool/f95.xml
+++ b/SCons/Tool/f95.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -45,6 +45,7 @@ Set construction variables for generic POSIX Fortran 95 compilers.
<item>F95PPCOMSTR</item>
<item>SHF95COMSTR</item>
<item>SHF95PPCOMSTR</item>
+<item>FORTRANCOMMONFLAGS</item>
</uses>
</tool>
@@ -271,7 +272,7 @@ Options that are passed to the Fortran 95 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF95FLAGS; if you need to define specific
user options for Fortran 95 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
diff --git a/SCons/Tool/fortran.py b/SCons/Tool/fortran.py
index 9a095ae..d21b930 100644
--- a/SCons/Tool/fortran.py
+++ b/SCons/Tool/fortran.py
@@ -32,6 +32,7 @@ from SCons.Tool.FortranCommon import add_all_to_env, add_fortran_to_env
compilers = ['f95', 'f90', 'f77']
+
def generate(env):
add_all_to_env(env)
add_fortran_to_env(env)
@@ -41,6 +42,7 @@ def generate(env):
if 'SHFORTRAN' not in env:
env['SHFORTRAN'] = '$FORTRAN'
+
def exists(env):
return env.Detect(compilers)
diff --git a/SCons/Tool/fortran.xml b/SCons/Tool/fortran.xml
index a4f0cec..4a092ec 100644
--- a/SCons/Tool/fortran.xml
+++ b/SCons/Tool/fortran.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -26,7 +26,7 @@ See its __doc__ string for a discussion of the format.
<tool name="fortran">
<summary>
<para>
-Set construction variables for generic POSIX Fortran compilers.
+Set &consvars; for generic POSIX Fortran compilers.
</para>
</summary>
<sets>
@@ -43,6 +43,8 @@ Set construction variables for generic POSIX Fortran compilers.
<item>FORTRANPPCOMSTR</item>
<item>SHFORTRANCOMSTR</item>
<item>SHFORTRANPPCOMSTR</item>
+<item>CPPFLAGS</item>
+<item>_CPPDEFFLAGS</item>
</uses>
</tool>
@@ -61,11 +63,9 @@ for all versions of Fortran.
The command line used to compile a Fortran source file to an object file.
By default, any options specified
in the &cv-link-FORTRANFLAGS;,
-&cv-link-CPPFLAGS;,
-&cv-link-_CPPDEFFLAGS;,
&cv-link-_FORTRANMODFLAG;, and
-&cv-link-_FORTRANINCFLAGS; construction variables
-are included on this command line.
+&cv-link-_FORTRANINCFLAGS;
+&consvars; are included on this command line.
</para>
</summary>
</cvar>
@@ -102,7 +102,8 @@ FORTRAN dialect will be used. By default, this is <literal>['.fpp', '.FPP']</lit
<cvar name="FORTRANFLAGS">
<summary>
<para>
-General user-specified options that are passed to the Fortran compiler.
+General user-specified options for the FORTRAN dialect
+that are passed to the Fortran compiler.
Note that this variable does
<emphasis>not</emphasis>
contain
@@ -110,17 +111,27 @@ contain
(or similar) include or module search path options
that scons generates automatically from &cv-link-FORTRANPATH;.
See
-&cv-link-_FORTRANINCFLAGS; and &cv-link-_FORTRANMODFLAG;,
-below,
-for the variables that expand those options.
+&cv-link-_FORTRANINCFLAGS; and &cv-link-_FORTRANMODFLAG;
+for the &consvars; that expand those options.
+</para>
+</summary>
+</cvar>
+
+<cvar name="FORTRANCOMMONFLAGS">
+<summary>
+<para>
+General user-specified options that are passed to the Fortran compiler.
+Similar to &cv-link-FORTRANFLAGS;,
+but this &consvar; is applied to all dialects.
</para>
+<para><emphasis>New in version 4.4.</emphasis></para>
</summary>
</cvar>
<cvar name="_FORTRANINCFLAGS">
<summary>
<para>
-An automatically-generated construction variable
+An automatically-generated &consvar;
containing the Fortran compiler command-line options
for specifying directories to be searched for include
files and module files.
@@ -150,7 +161,7 @@ for module files, as well.
The prefix used to specify a module directory on the Fortran compiler command
line.
This will be prepended to the beginning of the directory
-in the &cv-link-FORTRANMODDIR; construction variables
+in the &cv-link-FORTRANMODDIR; &consvars;
when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
</para>
</summary>
@@ -162,7 +173,7 @@ when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
The suffix used to specify a module directory on the Fortran compiler command
line.
This will be appended to the end of the directory
-in the &cv-link-FORTRANMODDIR; construction variables
+in the &cv-link-FORTRANMODDIR; &consvars;
when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
</para>
</summary>
@@ -171,7 +182,7 @@ when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
<cvar name="_FORTRANMODFLAG">
<summary>
<para>
-An automatically-generated construction variable
+An automatically-generated &consvar;
containing the Fortran compiler command-line option
for specifying the directory location where the Fortran
compiler should place any module files that happen to get
@@ -249,11 +260,11 @@ env = Environment(FORTRANPATH=include)
The directory list will be added to command lines
through the automatically-generated
&cv-link-_FORTRANINCFLAGS;
-construction variable,
+&consvar;,
which is constructed by
respectively prepending and appending the values of the
&cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
-construction variables
+&consvars;
to the beginning and end
of each directory in &cv-link-FORTRANPATH;.
Any command lines you define that need
@@ -277,7 +288,7 @@ By default, any options specified in the &cv-link-FORTRANFLAGS;,
&cv-link-_CPPDEFFLAGS;,
&cv-link-_FORTRANMODFLAG;, and
&cv-link-_FORTRANINCFLAGS;
-construction variables are included on this command line.
+&consvars; are included on this command line.
</para>
</summary>
</cvar>
@@ -323,6 +334,12 @@ The default Fortran compiler used for generating shared-library objects.
<para>
The command line used to compile a Fortran source file
to a shared-library object file.
+By default, any options specified
+in the &cv-link-SHFORTRANFLAGS;,
+&cv-link-_FORTRANMODFLAG;, and
+&cv-link-_FORTRANINCFLAGS;
+&consvars; are included on this command line.
+See also &cv-link-FORTRANCOM;.
</para>
</summary>
</cvar>
@@ -353,10 +370,13 @@ to generate shared-library objects.
The command line used to compile a Fortran source file to a
shared-library object file
after first running the file through the C preprocessor.
-Any options specified
-in the &cv-link-SHFORTRANFLAGS; and
-&cv-link-CPPFLAGS; construction variables
-are included on this command line.
+By default, any options specified in the &cv-link-SHFORTRANFLAGS;,
+&cv-link-CPPFLAGS;,
+&cv-link-_CPPDEFFLAGS;,
+&cv-link-_FORTRANMODFLAG;, and
+&cv-link-_FORTRANINCFLAGS;
+&consvars; are included on this command line.
+See also &cv-link-SHFORTRANCOM;.
</para>
</summary>
</cvar>
diff --git a/SCons/Tool/g77.py b/SCons/Tool/g77.py
index 764235e..aea419a 100644
--- a/SCons/Tool/g77.py
+++ b/SCons/Tool/g77.py
@@ -1,15 +1,6 @@
-"""SCons.Tool.g77
-
-Tool-specific initialization for g77.
-
-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.
-
-"""
-
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -29,15 +20,21 @@ selection method.
# 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__"
+"""
+Tool-specific initialization for g77.
+
+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.
+"""
import SCons.Util
from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env
compilers = ['g77', 'f77']
+
def generate(env):
"""Add Builders and construction variables for g77 to an Environment."""
add_all_to_env(env)
@@ -63,6 +60,7 @@ def generate(env):
env['INCF77PREFIX'] = "-I"
env['INCF77SUFFIX'] = ""
+
def exists(env):
return env.Detect(compilers)
diff --git a/SCons/Tool/g77.xml b/SCons/Tool/g77.xml
index 40516ba..febfeb3 100644
--- a/SCons/Tool/g77.xml
+++ b/SCons/Tool/g77.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -27,10 +27,31 @@ See its __doc__ string for a discussion of the format.
<summary>
<para>
Set construction variables for the &g77; Fortran compiler.
-Calls the &t-f77; Tool module
-to set variables.
</para>
</summary>
+<sets>
+<item>F77</item>
+<item>F77COM</item>
+<item>F77PPCOM</item>
+<item>F77FILESUFFIXES</item>
+<item>F77PPFILESUFFIXES</item>
+<item>FORTRAN</item>
+<item>FORTRANCOM</item>
+<item>FORTRANPPCOM</item>
+<item>SHF77</item>
+<item>SHF77COM</item>
+<item>SHF77PPCOM</item>
+<item>SHF77FLAGS</item>
+<item>SHFORTRAN</item>
+<item>SHFORTRANCOM</item>
+<item>SHFORTRANPPCOM</item>
+<item>SHFORTRANFLAGS</item>
+</sets>
+<uses>
+<item>F77FLAGS</item>
+<item>FORTRANFLAGS</item>
+<item>FORTRANCOMMONFLAGS</item>
+</uses>
</tool>
</sconsdoc>
diff --git a/SCons/Tool/gfortran.py b/SCons/Tool/gfortran.py
index 45750d6..f9c0a45 100644
--- a/SCons/Tool/gfortran.py
+++ b/SCons/Tool/gfortran.py
@@ -1,16 +1,6 @@
-"""SCons.Tool.gfortran
-
-Tool-specific initialization for gfortran, the GNU Fortran 95/Fortran
-2003 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.
-
-"""
-
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -30,32 +20,42 @@ selection method.
# 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__"
+"""
+Tool-specific initialization for gfortran, the GNU 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.
+"""
-import SCons.Util
+from SCons.Util import CLVar
from . import fortran
+
def generate(env):
- """Add Builders and construction variables for gfortran to an
- Environment."""
+ """Add Builders and construction variables for gfortran."""
fortran.generate(env)
- 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']:
- env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS' % dialect)
- else:
- env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect)
+ # fill in other dialects (FORTRAN dialect set by fortran.generate(),
+ # but don't overwrite if they have been set manually.
+ for dialect in ['F77', 'F90', 'F95', 'F03', 'F08']:
+ if dialect not in env:
+ env[f'{dialect}'] = 'gfortran'
+ if f'SH{dialect}' not in env:
+ env[f'SH{dialect}'] = f'${dialect}'
- env['INC%sPREFIX' % dialect] = "-I"
- env['INC%sSUFFIX' % dialect] = ""
+ # The fortran module always sets the shlib FLAGS, but does not
+ # include -fPIC, which is needed for the GNU tools. Rewrite if needed.
+ if env['PLATFORM'] not in ['cygwin', 'win32']:
+ env[f'SH{dialect}FLAGS'] = CLVar(f'${dialect}FLAGS -fPIC')
+ env[f'INC{dialect}PREFIX'] = "-I"
+ env[f'INC{dialect}SUFFIX'] = ""
env['FORTRANMODDIRPREFIX'] = "-J"
+
def exists(env):
return env.Detect('gfortran')
diff --git a/SCons/Tool/gfortran.xml b/SCons/Tool/gfortran.xml
index b5bad1a..95458fc 100644
--- a/SCons/Tool/gfortran.xml
+++ b/SCons/Tool/gfortran.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -26,7 +26,8 @@ See its __doc__ string for a discussion of the format.
<tool name="gfortran">
<summary>
<para>
-Sets construction variables for the GNU F95/F2003 GNU compiler.
+Sets construction variables for the GNU Fortran compiler.
+Calls the &t-link-fortran; Tool module to set variables.
</para>
</summary>
<sets>
diff --git a/SCons/Tool/ifl.py b/SCons/Tool/ifl.py
index 865d2ba..5746950 100644
--- a/SCons/Tool/ifl.py
+++ b/SCons/Tool/ifl.py
@@ -1,15 +1,6 @@
-"""SCons.Tool.ifl
-
-Tool-specific initialization for the Intel 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.
-
-"""
-
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -29,9 +20,13 @@ selection method.
# 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__"
+"""Tool-specific initialization for the Intel 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.
+"""
import SCons.Defaults
from SCons.Scanner.Fortran import FortranScan
diff --git a/SCons/Tool/ifort.py b/SCons/Tool/ifort.py
index 638bd12..bf39b7f 100644
--- a/SCons/Tool/ifort.py
+++ b/SCons/Tool/ifort.py
@@ -1,16 +1,6 @@
-"""SCons.Tool.ifort
-
-Tool-specific initialization for newer versions of the Intel Fortran Compiler
-for Linux/Windows (and possibly Mac OS X).
-
-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.
-
-"""
-
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -30,14 +20,21 @@ selection method.
# 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__"
+"""
+Tool-specific initialization for newer versions of the Intel Fortran Compiler
+for Linux/Windows (and possibly Mac OS X).
+
+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.
+"""
import SCons.Defaults
from SCons.Scanner.Fortran import FortranScan
from .FortranCommon import add_all_to_env
+
def generate(env):
"""Add Builders and construction variables for ifort to an Environment."""
# ifort supports Fortran 90 and Fortran 95
@@ -61,23 +58,24 @@ def generate(env):
fc = 'ifort'
for dialect in ['F77', 'F90', 'FORTRAN', 'F95']:
- env['%s' % dialect] = fc
- env['SH%s' % dialect] = '$%s' % dialect
+ env[f'{dialect}'] = fc
+ env[f'SH{dialect}'] = f'${dialect}'
if env['PLATFORM'] == 'posix':
- env['SH%sFLAGS' % dialect] = SCons.Util.CLVar('$%sFLAGS -fPIC' % dialect)
+ env[f'SH{dialect}FLAGS'] = SCons.Util.CLVar(f'${dialect}FLAGS -fPIC')
if env['PLATFORM'] == 'win32':
# On Windows, the ifort compiler specifies the object on the
# command line with -object:, not -o. Massage the necessary
# command-line construction variables.
for dialect in ['F77', 'F90', 'FORTRAN', 'F95']:
- for var in ['%sCOM' % dialect, '%sPPCOM' % dialect,
- 'SH%sCOM' % dialect, 'SH%sPPCOM' % dialect]:
+ for var in [f'{dialect}COM', f'{dialect}PPCOM',
+ f'SH{dialect}COM', f'SH{dialect}PPCOM']:
env[var] = env[var].replace('-o $TARGET', '-object:$TARGET')
env['FORTRANMODDIRPREFIX'] = "/module:"
else:
env['FORTRANMODDIRPREFIX'] = "-module "
+
def exists(env):
return env.Detect('ifort')
diff --git a/SCons/Tool/jar.xml b/SCons/Tool/jar.xml
index 1713495..403aacb 100644
--- a/SCons/Tool/jar.xml
+++ b/SCons/Tool/jar.xml
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
diff --git a/SCons/Tool/javac.py b/SCons/Tool/javac.py
index 9e18964..1b33125 100644
--- a/SCons/Tool/javac.py
+++ b/SCons/Tool/javac.py
@@ -231,13 +231,15 @@ def generate(env):
JAVABOOTCLASSPATH=[],
JAVACLASSPATH=[],
JAVASOURCEPATH=[],
+ JAVAPROCESSORPATH=[],
)
env['_javapathopt'] = pathopt
env['_JAVABOOTCLASSPATH'] = '${_javapathopt("-bootclasspath", "JAVABOOTCLASSPATH")} '
+ env['_JAVAPROCESSORPATH'] = '${_javapathopt("-processorpath", "JAVAPROCESSORPATH")} '
env['_JAVACLASSPATH'] = '${_javapathopt("-classpath", "JAVACLASSPATH")} '
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'] = '$JAVAC $JAVACFLAGS $_JAVABOOTCLASSPATH $_JAVAPROCESSORPATH $_JAVACLASSPATH -d ${TARGET.attributes.java_classdir} $_JAVASOURCEPATH $SOURCES'
env['JAVACCOM'] = "${TEMPFILE('$_JAVACCOM','$JAVACCOMSTR')}"
def exists(env):
diff --git a/SCons/Tool/javac.xml b/SCons/Tool/javac.xml
index 36b8785..6f48356 100644
--- a/SCons/Tool/javac.xml
+++ b/SCons/Tool/javac.xml
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -95,9 +97,9 @@ See its __doc__ string for a discussion of the format.
</para>
<example_commands>
-env.Java(target = 'classes', source = 'src')
-env.Java(target = 'classes', source = ['src1', 'src2'])
-env.Java(target = 'classes', source = ['File1.java', 'File2.java'])
+env.Java(target='classes', source='src')
+env.Java(target='classes', source=['src1', 'src2'])
+env.Java(target='classes', source=['File1.java', 'File2.java'])
</example_commands>
<para>
@@ -123,15 +125,57 @@ env['ENV']['LANG'] = 'en_GB.UTF-8'
<cvar name="JAVABOOTCLASSPATH">
<summary>
<para>
- Specifies the list of directories that
- will be added to the
- &javac; command line
- via the <option>-bootclasspath</option> option.
- The individual directory names will be
- separated by the operating system's path separate character
- (<filename>:</filename> on UNIX/Linux/POSIX,
- <filename>;</filename>
- on Windows).
+ Specifies the location of the bootstrap class files.
+ Can be specified as a string or Node object,
+ or as a list of strings or Node objects.
+ </para>
+ <para>
+ The value will be added to the JDK command lines
+ via the <option>-bootclasspath</option> option,
+ which requires a system-specific search path separator.
+ This will be supplied by &SCons; as needed when it
+ constructs the command line if &cv-JAVABOOTCLASSPATH; is
+ provided in list form.
+ If &cv-JAVABOOTCLASSPATH; is a single string containing
+ search path separator characters
+ (<literal>:</literal> for POSIX systems or
+ <literal>;</literal> for Windows), it will not be modified;
+ and so is inherently system-specific;
+ to supply the path in a system-independent manner,
+ give &cv-JAVABOOTCLASSPATH; as a list of paths instead.
+ </para>
+ <note>
+ <para>
+ Can only be used when compiling for releases prior to JDK 9.
+ </para>
+ </note>
+ </summary>
+ </cvar>
+
+ <cvar name="JAVAPROCESSORPATH">
+ <summary>
+ <para>
+ Specifies the location of the annotation processor class files.
+ Can be specified as a string or Node object,
+ or as a list of strings or Node objects.
+ </para>
+ <para>
+ The value will be added to the JDK command lines
+ via the <option>-processorpath</option> option,
+ which requires a system-specific search path separator.
+ This will be supplied by &SCons; as needed when it
+ constructs the command line if &cv-JAVAPROCESSORPATH; is
+ provided in list form.
+ If &cv-JAVAPROCESSORPATH; is a single string containing
+ search path separator characters
+ (<literal>:</literal> for POSIX systems or
+ <literal>;</literal> for Windows), it will not be modified;
+ and so is inherently system-specific;
+ to supply the path in a system-independent manner,
+ give &cv-JAVAPROCESSORPATH; as a list of paths instead.
+ </para>
+ <para>
+ <emphasis>New in version 4.5.0</emphasis>
</para>
</summary>
</cvar>
@@ -139,7 +183,8 @@ env['ENV']['LANG'] = 'en_GB.UTF-8'
<cvar name="JAVAINCLUDES">
<summary>
<para>
- Include path for Java header files (such as jni.h)
+ Include path for Java header files
+ (such as <filename>jni.h</filename>).
</para>
</summary>
</cvar>
@@ -191,10 +236,9 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
<summary>
<para>
The directory in which Java class files may be found.
- This is stripped from the beginning of any Java .class
- file names supplied to the
- <literal>JavaH</literal>
- builder.
+ This is stripped from the beginning of any Java
+ <filename>.class</filename>
+ file names supplied to the &b-link-JavaH; builder.
</para>
</summary>
</cvar>
@@ -202,19 +246,54 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
<cvar name="JAVACLASSPATH">
<summary>
<para>
- Specifies the list of directories that
- will be searched for Java
- <filename>.class</filename>
- file.
- The directories in this list will be added to the
- &javac; and &javah; command lines
- via the <option>-classpath</option> option.
- The individual directory names will be
- separated by the operating system's path separate character
- (<filename>:</filename> on UNIX/Linux/POSIX,
- <filename>;</filename>
- on Windows).
+ Specifies the class search path for the JDK tools.
+ Can be specified as a string or Node object,
+ or as a list of strings or Node objects.
+ Class path entries may be directory names to search
+ for class files or packages, pathnames to archives
+ (<filename>.jar</filename> or <filename>.zip</filename>)
+ containing classes, or paths ending in a "base name wildcard"
+ character (<literal>*</literal>), which matches files
+ in that directory with a <filename>.jar</filename> suffix.
+ See the Java documentation for more details.
</para>
+ <para>
+ The value will be added to the JDK command lines
+ via the <option>-classpath</option> option,
+ which requires a system-specific search path separator.
+ This will be supplied by &SCons; as needed when it
+ constructs the command line if &cv-JAVACLASSPATH; is
+ provided in list form.
+ If &cv-JAVACLASSPATH; is a single string containing
+ search path separator characters
+ (<literal>:</literal> for POSIX systems or
+ <literal>;</literal> for Windows),
+ it will be split on the separator into a list of individual
+ paths for dependency scanning purposes.
+ It will not be modified for JDK command-line usage,
+ so such a string is inherently system-specific;
+ to supply the path in a system-independent manner,
+ give &cv-JAVACLASSPATH; as a list of paths instead.
+ </para>
+ <note>
+ <para>
+ &SCons; <emphasis role="bold">always</emphasis>
+ supplies a <option>-sourcepath</option>
+ when invoking the Java compiler &javac;,
+ regardless of the setting of &cv-link-JAVASOURCEPATH;,
+ as it passes the path(s) to the source(s) supplied
+ in the call to the &b-link-Java; builder via
+ <option>-sourcepath</option> .
+ From the documentation of the standard Java toolkit for &javac;:
+ <quote>If not compiling code for modules, if the
+ <option>--source-path</option> or <option>-sourcepath</option>
+ option is not specified, then the user class path is also
+ searched for source files.</quote>
+ Since <option>-sourcepath</option> is always supplied,
+ &javac; will not use the contents of the value of
+ &cv-JAVACLASSPATH; when searching for sources.
+ </para>
+ </note>
</summary>
</cvar>
@@ -232,24 +311,31 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
<summary>
<para>
Specifies the list of directories that
- will be searched for input
- <filename>.java</filename>
- file.
- The directories in this list will be added to the
- &javac; command line
- via the <option>-sourcepath</option> option.
- The individual directory names will be
- separated by the operating system's path separate character
- (<filename>:</filename> on UNIX/Linux/POSIX,
- <filename>;</filename>
- on Windows).
+ will be searched for input (source)
+ <filename>.java</filename> files.
+ Can be specified as a string or Node object,
+ or as a list of strings or Node objects.
+ </para>
+ <para>
+ The value will be added to the JDK command lines
+ via the <option>-sourcepath</option> option,
+ which requires a system-specific search path separator,
+ This will be supplied by &SCons; as needed when it
+ constructs the command line if &cv-JAVASOURCEPATH; is
+ provided in list form.
+ If &cv-JAVASOURCEPATH; is a single string containing
+ search path separator characters
+ (<literal>:</literal> for POSIX systems or
+ <literal>;</literal> for Windows), it will not be modified,
+ and so is inherently system-specific;
+ to supply the path in a system-independent manner,
+ give &cv-JAVASOURCEPATH; as a list of paths instead.
</para>
-
<para>
- Note that this currently just adds the specified
- directory via the <option>-sourcepath</option> option.
+ Note that the specified directories are only added to
+ the command line via the <option>-sourcepath</option> option.
&SCons; does not currently search the
- &cv-JAVASOURCEPATH; directories for dependency
+ &cv-JAVASOURCEPATH; directories for dependent
<filename>.java</filename>
files.
</para>
@@ -297,4 +383,4 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
</summary>
</cvar>
- </sconsdoc>
+</sconsdoc>
diff --git a/SCons/Tool/javah.xml b/SCons/Tool/javah.xml
index 2bb66b4..16e969a 100644
--- a/SCons/Tool/javah.xml
+++ b/SCons/Tool/javah.xml
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
diff --git a/SCons/Tool/lex.py b/SCons/Tool/lex.py
index d8d8de4..3f1a3ad 100644
--- a/SCons/Tool/lex.py
+++ b/SCons/Tool/lex.py
@@ -23,6 +23,9 @@
"""Tool-specific initialization for lex.
+This tool should support multiple lex implementations,
+but is in actuality biased towards GNU Flex.
+
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.
@@ -30,14 +33,17 @@ selection method.
import os.path
import sys
+from typing import Optional
import SCons.Action
import SCons.Tool
-import SCons.Util
import SCons.Warnings
from SCons.Platform.mingw import MINGW_DEFAULT_PATHS
from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS
from SCons.Platform.win32 import CHOCO_DEFAULT_PATH
+from SCons.Util import CLVar, to_String
+
+DEFAULT_PATHS = CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS
LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR")
@@ -46,48 +52,68 @@ if sys.platform == 'win32':
else:
BINS = ["flex", "lex"]
-def lexEmitter(target, source, env):
- sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0]))
+def lexEmitter(target, source, env) -> tuple:
+ """Adds extra files generated by lex program to target list."""
+
+ sourceBase, sourceExt = os.path.splitext(to_String(source[0]))
if sourceExt == ".lm": # If using Objective-C
target = [sourceBase + ".m"] # the extension is ".m".
- # This emitter essentially tries to add to the target all extra
- # files generated by flex.
-
- # Different options that are used to trigger the creation of extra files.
- fileGenOptions = ["--header-file=", "--tables-file="]
-
- lexflags = env.subst("$LEXFLAGS", target=target, source=source)
- for option in SCons.Util.CLVar(lexflags):
- for fileGenOption in fileGenOptions:
+ # With --header-file and ----tables-file, the file to write is defined
+ # by the option argument. Extract this and include in the list of targets.
+ # NOTE: a filename passed to the command this way is not modified by SCons,
+ # and so will be interpreted relative to the project top directory at
+ # execution time, while the name added to the target list will be
+ # interpreted relative to the SConscript directory - a possible mismatch.
+ #
+ # These are GNU flex-only options.
+ # TODO: recognize --outfile also?
+ file_gen_options = ["--header-file=", "--tables-file="]
+ lexflags = env.subst_list("$LEXFLAGS", target=target, source=source)
+ for option in lexflags[0]:
+ for fileGenOption in file_gen_options:
l = len(fileGenOption)
if option[:l] == fileGenOption:
# A file generating option is present, so add the
# file name to the target list.
- fileName = option[l:].strip()
- target.append(fileName)
- return (target, source)
+ file_name = option[l:].strip()
+ target.append(file_name)
+
+ lexheaderfile = env.subst("$LEX_HEADER_FILE", target=target, source=source)
+ if lexheaderfile:
+ target.append(lexheaderfile)
+ # rewrite user-supplied file string with a node, we need later
+ env.Replace(LEX_HEADER_FILE=env.File(lexheaderfile))
+
+ lextablesfile = env.subst("$LEX_TABLES_FILE", target=target, source=source)
+ if lextablesfile:
+ target.append(lextablesfile)
+ # rewrite user-supplied file string with a node, we need later
+ env.Replace(LEX_TABLES_FILE=env.File(lextablesfile))
+
+ return target, source
-def get_lex_path(env, append_paths=False):
+
+def get_lex_path(env, append_paths=False) -> Optional[str]:
"""
- Find the path to the lex tool, searching several possible names
+ Returns the path to the lex tool, searching several possible names.
- Only called in the Windows case, so the default_path
- can be Windows-specific
+ Only called in the Windows case, so the `default_path` argument to
+ :func:`find_program_path` can be Windows-specific.
- :param env: current construction environment
- :param append_paths: if set, add the path to the tool to PATH
- :return: path to lex tool, if found
+ Args:
+ env: current construction environment
+ append_paths: if set, add the path to the tool to PATH
"""
for prog in BINS:
bin_path = SCons.Tool.find_program_path(
env,
prog,
- default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS )
+ default_paths=DEFAULT_PATHS,
+ add_path=append_paths,
+ )
if bin_path:
- if append_paths:
- env.AppendENVPath('PATH', os.path.dirname(bin_path))
return bin_path
SCons.Warnings.warn(
@@ -96,7 +122,7 @@ def get_lex_path(env, append_paths=False):
)
-def generate(env):
+def generate(env) -> None:
"""Add Builders and construction variables for lex to an Environment."""
c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
@@ -115,20 +141,28 @@ def generate(env):
cxx_file.add_action(".ll", LexAction)
cxx_file.add_emitter(".ll", lexEmitter)
- env["LEXFLAGS"] = SCons.Util.CLVar("")
-
if sys.platform == 'win32':
- # ignore the return - we do not need the full path here
+ # ignore the return, all we need is for the path to be added
_ = get_lex_path(env, append_paths=True)
- env["LEX"] = env.Detect(BINS)
- if not env.get("LEXUNISTD"):
- env["LEXUNISTD"] = SCons.Util.CLVar("")
- env["LEXCOM"] = "$LEX $LEXUNISTD $LEXFLAGS -t $SOURCES > $TARGET"
+
+ env.SetDefault(
+ LEX=env.Detect(BINS),
+ LEXFLAGS=CLVar(""),
+ LEX_HEADER_FILE="",
+ LEX_TABLES_FILE="",
+ )
+
+ if sys.platform == 'win32':
+ env.SetDefault(LEXUNISTD=CLVar(""))
+ env["LEXCOM"] = "$LEX $LEXUNISTD $LEXFLAGS $_LEX_HEADER $_LEX_TABLES -t $SOURCES > $TARGET"
else:
- env["LEX"] = env.Detect(BINS)
- env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET"
+ env["LEXCOM"] = "$LEX $LEXFLAGS $_LEX_HEADER $_LEX_TABLES -t $SOURCES > $TARGET"
+
+ env['_LEX_HEADER'] = '${LEX_HEADER_FILE and "--header-file=" + str(LEX_HEADER_FILE)}'
+ env['_LEX_TABLES'] = '${LEX_TABLES_FILE and "--tables-file=" + str(LEX_TABLES_FILE)}'
+
-def exists(env):
+def exists(env) -> Optional[str]:
if sys.platform == 'win32':
return get_lex_path(env)
else:
diff --git a/SCons/Tool/lex.xml b/SCons/Tool/lex.xml
index 5afb754..391e429 100644
--- a/SCons/Tool/lex.xml
+++ b/SCons/Tool/lex.xml
@@ -1,6 +1,28 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+ MIT License
+
+ Copyright The SCons Foundation
+
+ 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.
+
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -37,6 +59,9 @@ Sets construction variables for the &lex; lexical analyser.
</sets>
<uses>
<item>LEXCOMSTR</item>
+<item>LEXFLAGS</item>
+<item>LEX_HEADER_FILE</item>
+<item>LEX_TABLES_FILE</item>
</uses>
</tool>
@@ -66,7 +91,7 @@ If this is not set, then &cv-link-LEXCOM; (the command line) is displayed.
</para>
<example_commands>
-env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES")
+env = Environment(LEXCOMSTR="Lex'ing $TARGET from $SOURCES")
</example_commands>
</summary>
</cvar>
@@ -75,6 +100,43 @@ env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES")
<summary>
<para>
General options passed to the lexical analyzer generator.
+In addition to passing the value on during invocation,
+the &t-link-lex; tool also examines this &consvar; for options
+which cause additional output files to be generated,
+and adds those to the target list.
+Recognized for this purpose are GNU &flex; options
+<option>--header-file=</option> and
+<option>--tables-file=</option>;
+the output file is named by the option argument.
+</para>
+<para>
+Note that files specified by <option>--header-file=</option> and
+<option>--tables-file=</option> may not be properly handled
+by &SCons; in all situations. Consider using
+&cv-link-LEX_HEADER_FILE; and &cv-link-LEX_TABLES_FILE; instead.
+</para>
+</summary>
+</cvar>
+
+<cvar name="LEX_HEADER_FILE">
+<summary>
+<para>
+If supplied, generate a C header file with the name taken from this variable.
+Will be emitted as a <option>--header-file=</option>
+command-line option. Use this in preference to including
+<option>--header-file=</option> in &cv-link-LEXFLAGS; directly.
+</para>
+</summary>
+</cvar>
+
+<cvar name="LEX_TABLES_FILE">
+<summary>
+<para>
+If supplied, write the lex tables to a file with the name
+taken from this variable.
+Will be emitted as a <option>--tables-file=</option>
+command-line option. Use this in preference to including
+<option>--tables-file=</option> in &cv-link-LEXFLAGS; directly.
</para>
</summary>
</cvar>
diff --git a/SCons/Tool/linkCommon/__init__.py b/SCons/Tool/linkCommon/__init__.py
index 7aaffab..5461ad3 100644
--- a/SCons/Tool/linkCommon/__init__.py
+++ b/SCons/Tool/linkCommon/__init__.py
@@ -137,11 +137,14 @@ def smart_link(source, target, env, for_signature):
if has_cplusplus and has_fortran and not has_d:
global issued_mixed_link_warning
if not issued_mixed_link_warning:
- msg = "Using $CXX to link Fortran and C++ code together.\n\t" + \
- "This may generate a buggy executable if the '%s'\n\t" + \
- "compiler does not know how to deal with Fortran runtimes."
- SCons.Warnings.warn(SCons.Warnings.FortranCxxMixWarning,
- msg % env.subst('$CXX'))
+ msg = (
+ "Using $CXX to link Fortran and C++ code together.\n"
+ " This may generate a buggy executable if the '%s'\n"
+ " compiler does not know how to deal with Fortran runtimes."
+ )
+ SCons.Warnings.warn(
+ SCons.Warnings.FortranCxxMixWarning, msg % env.subst('$CXX')
+ )
issued_mixed_link_warning = True
return '$CXX'
elif has_d:
diff --git a/SCons/Tool/midl.py b/SCons/Tool/midl.py
index 2757c34..c25ce69 100644
--- a/SCons/Tool/midl.py
+++ b/SCons/Tool/midl.py
@@ -1,15 +1,6 @@
-"""SCons.Tool.midl
-
-Tool-specific initialization for midl (Microsoft IDL 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.
-
-"""
-
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -29,9 +20,16 @@ selection method.
# 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__"
+"""SCons.Tool.midl
+
+Tool-specific initialization for midl (Microsoft IDL 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.
+
+"""
import SCons.Action
import SCons.Builder
@@ -39,7 +37,10 @@ import SCons.Defaults
import SCons.Scanner.IDL
import SCons.Util
-from .MSCommon import msvc_exists
+from .MSCommon import msvc_setup_env_tool
+
+tool_name = 'midl'
+
def midl_emitter(target, source, env):
"""Produces a list of outputs from the MIDL compiler"""
@@ -58,28 +59,31 @@ def midl_emitter(target, source, env):
dlldata = base + '_data.c'
targets.append(dlldata)
- return (targets, source)
+ return targets, source
+
idl_scanner = SCons.Scanner.IDL.IDLScan()
midl_action = SCons.Action.Action('$MIDLCOM', '$MIDLCOMSTR')
-midl_builder = SCons.Builder.Builder(action = midl_action,
- src_suffix = '.idl',
+midl_builder = SCons.Builder.Builder(action=midl_action,
+ src_suffix='.idl',
suffix='.tlb',
- emitter = midl_emitter,
- source_scanner = idl_scanner)
+ emitter=midl_emitter,
+ source_scanner=idl_scanner)
+
def generate(env):
"""Add Builders and construction variables for midl to an Environment."""
- env['MIDL'] = 'MIDL.EXE'
- env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo')
- env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL'
+ env['MIDL'] = 'MIDL.EXE'
+ env['MIDLFLAGS'] = SCons.Util.CLVar('/nologo')
+ env['MIDLCOM'] = '$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} /h ${TARGETS[1]} /iid ${TARGETS[2]} /proxy ${TARGETS[3]} /dlldata ${TARGETS[4]} $SOURCE 2> NUL'
env['BUILDERS']['TypeLibrary'] = midl_builder
+
def exists(env):
- return msvc_exists(env)
+ return msvc_setup_env_tool(env, tool=tool_name)
# Local Variables:
# tab-width:4
diff --git a/SCons/Tool/mslib.py b/SCons/Tool/mslib.py
index 354f5cf..be4088b 100644
--- a/SCons/Tool/mslib.py
+++ b/SCons/Tool/mslib.py
@@ -41,14 +41,16 @@ import SCons.Tool.msvs
import SCons.Tool.msvc
import SCons.Util
-from .MSCommon import msvc_exists, msvc_setup_env_once
+from .MSCommon import msvc_setup_env_tool, msvc_setup_env_once
+
+tool_name = 'mslib'
def generate(env):
"""Add Builders and construction variables for lib to an Environment."""
SCons.Tool.createStaticLibBuilder(env)
# Set-up ms tools paths
- msvc_setup_env_once(env)
+ msvc_setup_env_once(env, tool=tool_name)
env['AR'] = 'lib'
env['ARFLAGS'] = SCons.Util.CLVar('/nologo')
@@ -64,7 +66,7 @@ def generate(env):
def exists(env):
- return msvc_exists(env)
+ return msvc_setup_env_tool(env, tool=tool_name)
# Local Variables:
# tab-width:4
diff --git a/SCons/Tool/mslink.py b/SCons/Tool/mslink.py
index 3dac7f0..2a90e17 100644
--- a/SCons/Tool/mslink.py
+++ b/SCons/Tool/mslink.py
@@ -43,9 +43,11 @@ import SCons.Tool.msvc
import SCons.Tool.msvs
import SCons.Util
-from .MSCommon import msvc_setup_env_once, msvc_exists
+from .MSCommon import msvc_setup_env_once, msvc_setup_env_tool
from .MSCommon.common import get_pch_node
+tool_name = 'mslink'
+
def pdbGenerator(env, target, source, for_signature):
try:
return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG']
@@ -307,7 +309,7 @@ def generate(env):
env['_MANIFEST_SOURCES'] = None # _windowsManifestSources
# Set-up ms tools paths
- msvc_setup_env_once(env)
+ msvc_setup_env_once(env, tool=tool_name)
# Loadable modules are on Windows the same as shared libraries, but they
# are subject to different build parameters (LDMODULE* variables).
@@ -330,7 +332,7 @@ def generate(env):
env['TEMPFILEARGJOIN'] = os.linesep
def exists(env):
- return msvc_exists(env)
+ return msvc_setup_env_tool(env, tool=tool_name)
# Local Variables:
# tab-width:4
diff --git a/SCons/Tool/msvc.py b/SCons/Tool/msvc.py
index 58b7463..191d2cc 100644
--- a/SCons/Tool/msvc.py
+++ b/SCons/Tool/msvc.py
@@ -44,9 +44,11 @@ import SCons.Util
import SCons.Warnings
import SCons.Scanner.RC
-from .MSCommon import msvc_exists, msvc_setup_env_once, msvc_version_to_maj_min, msvc_find_vswhere
+from .MSCommon import msvc_setup_env_tool, msvc_setup_env_once, msvc_version_to_maj_min, msvc_find_vswhere
from .MSCommon.common import get_pch_node
+tool_name = 'msvc'
+
CSuffixes = ['.c', '.C']
CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
@@ -293,7 +295,7 @@ def generate(env):
env['VSWHERE'] = env.get('VSWHERE', msvc_find_vswhere())
# Set-up ms tools paths
- msvc_setup_env_once(env)
+ msvc_setup_env_once(env, tool=tool_name)
env['CFILESUFFIX'] = '.c'
env['CXXFILESUFFIX'] = '.cc'
@@ -320,7 +322,7 @@ def generate(env):
def exists(env):
- return msvc_exists(env)
+ return msvc_setup_env_tool(env, tool=tool_name)
# Local Variables:
# tab-width:4
diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml
index 4a45d26..bf2e267 100644
--- a/SCons/Tool/msvc.xml
+++ b/SCons/Tool/msvc.xml
@@ -1,9 +1,10 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+SPDX-License-Identifier: MIT
+SPDX-FileCopyrightText: Copyright The SCons Foundation
+SPDX-FileType: DOCUMENTATION
This file is processed by the bin/SConsDoc.py module.
-See its __doc__ string for a discussion of the format.
-->
<!DOCTYPE sconsdoc [
@@ -26,7 +27,7 @@ See its __doc__ string for a discussion of the format.
<tool name="msvc">
<summary>
<para>
-Sets construction variables for the Microsoft Visual C/C++ compiler.
+Sets &consvars; for the Microsoft Visual C/C++ compiler.
</para>
</summary>
<sets>
@@ -74,6 +75,17 @@ Sets construction variables for the Microsoft Visual C/C++ compiler.
<item>PCH</item>
<item>PCHSTOP</item>
<item>PDB</item>
+<item>MSVC_VERSION</item>
+<item>MSVC_USE_SCRIPT</item>
+<item>MSVC_USE_SCRIPT_ARGS</item>
+<item>MSVC_USE_SETTINGS</item>
+<item>MSVC_NOTFOUND_POLICY</item>
+<item>MSVC_SCRIPTERROR_POLICY</item>
+<item>MSVC_SCRIPT_ARGS</item>
+<item>MSVC_SDK_VERSION</item>
+<item>MSVC_TOOLSET_VERSION</item>
+<item>MSVC_SPECTRE_LIBS</item>
+
</uses>
</tool>
@@ -87,7 +99,7 @@ file as the second element. Normally the object file is ignored.
This builder is only
provided when Microsoft Visual C++ is being used as the compiler.
The &b-PCH; builder is generally used in
-conjunction with the &cv-link-PCH; construction variable to force object files to use
+conjunction with the &cv-link-PCH; &consvar; to force object files to use
the precompiled header:
</para>
@@ -125,7 +137,7 @@ Options added to the compiler command line
to support building with precompiled headers.
The default value expands expands to the appropriate
Microsoft Visual C++ command-line options
-when the &cv-link-PCH; construction variable is set.
+when the &cv-link-PCH; &consvar; is set.
</para>
</summary>
</cvar>
@@ -138,7 +150,7 @@ to support storing debugging information in a
Microsoft Visual C++ PDB file.
The default value expands expands to appropriate
Microsoft Visual C++ command-line options
-when the &cv-link-PDB; construction variable is set.
+when the &cv-link-PDB; &consvar; is set.
</para>
<para>
@@ -185,11 +197,11 @@ compilation of object files
when calling the Microsoft Visual C/C++ compiler.
All compilations of source files from the same source directory
that generate target files in a same output directory
-and were configured in SCons using the same construction environment
+and were configured in SCons using the same &consenv;
will be built in a single call to the compiler.
Only source files that have changed since their
object files were built will be passed to each compiler invocation
-(via the &cv-link-CHANGED_SOURCES; construction variable).
+(via the &cv-link-CHANGED_SOURCES; &consvar;).
Any compilations where the object (target) file base name
(minus the <filename>.obj</filename>)
does not match the source file base name
@@ -238,9 +250,9 @@ If this is not set, then &cv-link-PCHCOM; (the command line) is displayed.
<cvar name="PCHPDBFLAGS">
<summary>
<para>
-A construction variable that, when expanded,
+A &consvar; that, when expanded,
adds the <option>/yD</option> flag to the command line
-only if the &cv-link-PDB; construction variable is set.
+only if the &cv-link-PDB; &consvar; is set.
</para>
</summary>
</cvar>
@@ -301,7 +313,7 @@ The flags passed to the resource compiler by the &b-link-RES; builder.
<cvar name="RCINCFLAGS">
<summary>
<para>
-An automatically-generated construction variable
+An automatically-generated &consvar;
containing the command-line options
for specifying directories to be searched
by the resource compiler.
@@ -320,7 +332,7 @@ of each directory in &cv-link-CPPPATH;.
The prefix (flag) used to specify an include directory
on the resource compiler command line.
This will be prepended to the beginning of each directory
-in the &cv-link-CPPPATH; construction variable
+in the &cv-link-CPPPATH; &consvar;
when the &cv-link-RCINCFLAGS; variable is expanded.
</para>
</summary>
@@ -332,7 +344,7 @@ when the &cv-link-RCINCFLAGS; variable is expanded.
The suffix used to specify an include directory
on the resource compiler command line.
This will be appended to the end of each directory
-in the &cv-link-CPPPATH; construction variable
+in the &cv-link-CPPPATH; &consvar;
when the &cv-link-RCINCFLAGS; variable is expanded.
</para>
</summary>
@@ -342,39 +354,202 @@ when the &cv-link-RCINCFLAGS; variable is expanded.
<summary>
<para>
Sets the preferred version of Microsoft Visual C/C++ to use.
+If the specified version is unavailable (not installed,
+or not discoverable), tool initialization will fail.
+If &cv-MSVC_VERSION; is not set, SCons will (by default) select the
+latest version of Visual C/C++ installed on your system.
</para>
<para>
-If &cv-MSVC_VERSION; is not set, SCons will (by default) select the
-latest version of Visual C/C++ installed on your system. If the
-specified version isn't installed, tool initialization will fail.
-This variable must be passed as an argument to the &f-link-Environment;
-constructor; setting it later has no effect.
-</para>
-
-<para>
-Valid values for Windows are
-<literal>14.3</literal>,
-<literal>14.2</literal>,
-<literal>14.1</literal>,
-<literal>14.1Exp</literal>,
-<literal>14.0</literal>,
-<literal>14.0Exp</literal>,
-<literal>12.0</literal>,
-<literal>12.0Exp</literal>,
-<literal>11.0</literal>,
-<literal>11.0Exp</literal>,
-<literal>10.0</literal>,
-<literal>10.0Exp</literal>,
-<literal>9.0</literal>,
-<literal>9.0Exp</literal>,
-<literal>8.0</literal>,
-<literal>8.0Exp</literal>,
-<literal>7.1</literal>,
-<literal>7.0</literal>,
-and <literal>6.0</literal>.
-Versions ending in <literal>Exp</literal> refer to "Express" or
-"Express for Desktop" editions.
+&cv-MSVC_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is
+loaded into the environment.
+</para>
+
+<para>
+The valid values for &cv-MSVC_VERSION; represent major versions
+of the compiler, except that versions ending in <literal>Exp</literal>
+refer to "Express" or "Express for Desktop" Visual Studio editions,
+which require distict entries because they use a different
+filesystem layout and have some feature limitations compared to
+the full version.
+The following table shows correspondence
+of the selector string to various version indicators
+('x' is used as a placeholder for
+a single digit that can vary).
+Note that it is not necessary to install Visual Studio
+to build with &SCons; (for example, you can install only
+Build Tools), but if Visual Studio is installed,
+additional builders such as &b-link-MSVSSolution; and
+&b-link-MSVSProject; become avaialable and will
+correspond to the indicated versions.
+</para>
+
+<informaltable>
+ <tgroup cols="5">
+ <colspec align="left" />
+ <colspec align="center" />
+ <colspec align="center" />
+ <colspec align="left" />
+ <colspec align="center" />
+ <thead>
+ <row>
+ <entry> SCons Key </entry>
+ <entry> MSVC++ Version </entry>
+ <entry> <literal>_MSVC_VER</literal> </entry>
+ <entry> VS Product </entry>
+ <entry> MSBuild/VS Version </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry> <literal>14.3</literal> </entry>
+ <entry> 14.3x </entry>
+ <entry> 193x </entry>
+ <entry> Visual Studio 2022 </entry>
+ <entry> 17.x </entry>
+ </row>
+ <row>
+ <entry> <literal>14.2</literal> </entry>
+ <entry> 14.2x </entry>
+ <entry> 192x </entry>
+ <entry> Visual Studio 2019 </entry>
+ <entry> 16.x, 16.1x </entry>
+ </row>
+ <row>
+ <entry> <literal>14.1</literal> </entry>
+ <entry> 14.1 or 14.1x </entry>
+ <entry> 191x </entry>
+ <entry> Visual Studio 2017 </entry>
+ <entry> 15.x </entry>
+ </row>
+ <row>
+ <entry> <literal>14.1Exp</literal> </entry>
+ <entry> 14.1 </entry>
+ <entry> 1910 </entry>
+ <entry> Visual Studio 2017 Express </entry>
+ <entry> 15.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>14.0</literal> </entry>
+ <entry> 14.0 </entry>
+ <entry> 1900 </entry>
+ <entry> Visual Studio 2015 </entry>
+ <entry> 14.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>14.0Exp</literal> </entry>
+ <entry> 14.0 </entry>
+ <entry> 1900 </entry>
+ <entry> Visual Studio 2015 Express </entry>
+ <entry> 14.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>12.0</literal> </entry>
+ <entry> 12.0 </entry>
+ <entry> 1800 </entry>
+ <entry> Visual Studio 2013 </entry>
+ <entry> 12.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>12.0Exp</literal> </entry>
+ <entry> 12.0 </entry>
+ <entry> 1800 </entry>
+ <entry> Visual Studio 2013 Express </entry>
+ <entry> 12.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>11.0</literal> </entry>
+ <entry> 11.0 </entry>
+ <entry> 1700 </entry>
+ <entry> Visual Studio 2012 </entry>
+ <entry> 11.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>11.0Exp</literal> </entry>
+ <entry> 11.0 </entry>
+ <entry> 1700 </entry>
+ <entry> Visual Studio 2012 Express </entry>
+ <entry> 11.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>10.0</literal> </entry>
+ <entry> 10.0 </entry>
+ <entry> 1600 </entry>
+ <entry> Visual Studio 2010 </entry>
+ <entry> 10.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>10.0Exp</literal> </entry>
+ <entry> 10.0 </entry>
+ <entry> 1600 </entry>
+ <entry> Visual C++ Express 2010 </entry>
+ <entry> 10.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>9.0</literal> </entry>
+ <entry> 9.0 </entry>
+ <entry> 1500 </entry>
+ <entry> Visual Studio 2008 </entry>
+ <entry> 9.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>9.0Exp</literal> </entry>
+ <entry> 9.0 </entry>
+ <entry> 1500 </entry>
+ <entry> Visual C++ Express 2008 </entry>
+ <entry> 9.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>8.0</literal> </entry>
+ <entry> 8.0 </entry>
+ <entry> 1400 </entry>
+ <entry> Visual Studio 2005 </entry>
+ <entry> 8.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>8.0Exp</literal> </entry>
+ <entry> 8.0 </entry>
+ <entry> 1400 </entry>
+ <entry> Visual C++ Express 2005 </entry>
+ <entry> 8.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>7.1</literal> </entry>
+ <entry> 7.1 </entry>
+ <entry> 1300 </entry>
+ <entry> Visual Studio .NET 2003 </entry>
+ <entry> 7.1 </entry>
+ </row>
+ <row>
+ <entry> <literal>7.0</literal> </entry>
+ <entry> 7.0 </entry>
+ <entry> 1200 </entry>
+ <entry> Visual Studio .NET 2002 </entry>
+ <entry> 7.0 </entry>
+ </row>
+ <row>
+ <entry> <literal>6.0</literal> </entry>
+ <entry> 6.0 </entry>
+ <entry> 1100 </entry>
+ <entry> Visual Studio 6.0 </entry>
+ <entry> 6.0 </entry>
+ </row>
+ </tbody>
+ </tgroup>
+</informaltable>
+
+<para>
+The compilation environment can be further or more precisely specified through the
+use of several other &consvars;: see the descriptions of
+&cv-link-MSVC_TOOLSET_VERSION;,
+&cv-link-MSVC_SDK_VERSION;,
+&cv-link-MSVC_USE_SCRIPT;,
+&cv-link-MSVC_USE_SCRIPT_ARGS;,
+and &cv-link-MSVC_USE_SETTINGS;.
</para>
</summary>
@@ -403,14 +578,20 @@ This can be useful to force the use of a compiler version that
Setting
&cv-MSVC_USE_SCRIPT; to <constant>None</constant> bypasses the
Visual Studio autodetection entirely;
-use this if you are running SCons in a Visual Studio <command>cmd</command>
+use this if you are running &SCons; in a Visual Studio <command>cmd</command>
window and importing the shell's environment variables - that
is, if you are sure everything is set correctly already and
you don't want &SCons; to change anything.
</para>
<para>
-&cv-MSVC_USE_SCRIPT; overrides &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
+&cv-MSVC_USE_SCRIPT; ignores &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
+</para>
+
+<para><emphasis>Changed in version 4.4:</emphasis>
+new &cv-link-MSVC_USE_SCRIPT_ARGS; provides a
+way to pass arguments.
</para>
+
</summary>
</cvar>
@@ -419,6 +600,9 @@ you don't want &SCons; to change anything.
<para>
Provides arguments passed to the script &cv-link-MSVC_USE_SCRIPT;.
</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
</summary>
</cvar>
@@ -480,11 +664,34 @@ env = Environment(MSVC_VERSION='8.0', MSVC_USE_SETTINGS=msvc_use_settings)
</para>
<para>
-Note: the dictionary content requirements are based on the internal msvc implementation and
-therefore may change at any time. The burden is on the user to ensure the dictionary contents
-are minimally sufficient to ensure successful builds.
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_USE_SETTINGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_USE_SETTINGS; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The dictionary content requirements are based on the internal msvc implementation and
+therefore may change at any time.
+</emphasis>
+The burden is on the user to ensure the dictionary contents are minimally sufficient to
+ensure successful builds.
</para>
+</listitem>
+
+</itemizedlist>
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
</summary>
</cvar>
@@ -509,21 +716,69 @@ are minimally sufficient to ensure successful builds.
<cvar name="MSVC_UWP_APP">
<summary>
<para>
-Build libraries for a Universal Windows Platform (UWP) Application.
+Build with the Universal Windows Platform (UWP) application Visual C++ libraries.
</para>
<para>
-If &cv-MSVC_UWP_APP; is set, the Visual C++ environment will be set up to point
+The valid values for &cv-MSVC_UWP_APP; are: <literal>True</literal>,
+<literal>'1'</literal>, <literal>False</literal>, <literal>'0'</literal>,
+or <literal>None</literal>.
+</para>
+
+<para>
+When &cv-MSVC_UWP_APP; is enabled (i.e., <literal>True</literal> or
+<literal>'1'</literal>), the Visual C++ environment will be set up to point
to the Windows Store compatible libraries and Visual C++ runtimes. In doing so,
any libraries that are built will be able to be used in a UWP App and published
to the Windows Store.
-This flag will only have an effect with Visual Studio 2015 or later.
-This variable must be passed as an argument to the Environment()
-constructor; setting it later has no effect.
+<!-- This flag will only have an effect with Visual Studio 2015 or later. -->
+<!-- This variable must be passed as an argument to the Environment()
+constructor; setting it later has no effect. -->
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+<listitem><para>
+&cv-MSVC_UWP_APP; is enabled for Visual Studio 2013 and earlier.
+</para></listitem>
+<listitem><para>
+&cv-MSVC_UWP_APP; is enabled and a UWP argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via &cv-MSVC_UWP_APP;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+</itemizedlist>
</para>
<para>
-Valid values are '1' or '0'
+Example - A Visual Studio 2022 build for the Universal Windows Platform:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_UWP_APP=True)
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_UWP_APP; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_UWP_APP; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The existence of the UWP libraries is not verified when &cv-MSVC_UWP_APP; is enabled
+which could result in build failures.
+</emphasis>
+The burden is on the user to ensure the requisite UWP libraries are installed.
+</para></listitem>
+
+</itemizedlist>
</para>
</summary>
@@ -578,5 +833,756 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools:
</summary>
</cvar>
+<cvar name="MSVC_NOTFOUND_POLICY">
+<summary>
+<para>
+Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not detected.
+</para>
+
+<para>
+ The &cv-MSVC_NOTFOUND_POLICY; specifies the &scons; behavior when no msvc versions are detected or
+ when the requested msvc version is not detected.
+</para>
+
+<para>
+The valid values for &cv-MSVC_NOTFOUND_POLICY; and the corresponding &scons; behavior are:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><parameter>'Error' or 'Exception'</parameter></term>
+<listitem>
+<para>
+Raise an exception when no msvc versions are detected or when the requested msvc version is not detected.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Warning' or 'Warn'</parameter></term>
+<listitem>
+<para>
+Issue a warning and continue when no msvc versions are detected or when the requested msvc version is not detected.
+Depending on usage, this could result in build failure(s).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Ignore' or 'Suppress'</parameter></term>
+<listitem>
+<para>
+Take no action and continue when no msvc versions are detected or when the requested msvc version is not detected.
+Depending on usage, this could result in build failure(s).
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>
+Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well.
+</para>
+
+<para>
+The &cv-MSVC_NOTFOUND_POLICY; is applied when any of the following conditions are satisfied:
+<itemizedlist>
+<listitem><para>
+&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified),
+and the default tools list contains one or more of the msvc tools.
+</para></listitem>
+<listitem><para>
+&cv-MSVC_VERSION; is specified, the default tools list is explicitly specified (e.g., <literal>tools=['default']</literal>),
+and the default tools list contains one or more of the msvc tools.
+</para></listitem>
+<listitem><para>
+A non-default tools list is specified that contains one or more of the msvc tools (e.g., <literal>tools=['msvc', 'mslink']</literal>).
+</para></listitem>
+</itemizedlist>
+</para>
+
+<para>
+The &cv-MSVC_NOTFOUND_POLICY; is ignored when any of the following conditions are satisfied:
+<itemizedlist>
+<listitem><para>
+&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
+</para></listitem>
+<listitem><para>
+&cv-MSVC_VERSION; is not specified and the default tools list is explicitly specified (e.g., <literal>tools=['default']</literal>).
+</para></listitem>
+<listitem><para>
+A non-default tool list is specified that does not contain any of the msvc tools (e.g., <literal>tools=['mingw']</literal>).
+</para></listitem>
+</itemizedlist>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_NOTFOUND_POLICY; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_NOTFOUND_POLICY; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+When &cv-MSVC_NOTFOUND_POLICY; is not specified, the default &scons; behavior is to issue a warning and continue
+subject to the conditions listed above. The default &scons; behavior may change in the future.
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</summary>
+</cvar>
+
+<cvar name="MSVC_SCRIPTERROR_POLICY">
+<summary>
+<para>
+Specify the &scons; behavior when Microsoft Visual C/C++ batch file errors are detected.
+</para>
+
+<para>
+The &cv-MSVC_SCRIPTERROR_POLICY; specifies the &scons; behavior when msvc batch file errors are
+detected.
+When &cv-MSVC_SCRIPTERROR_POLICY; is not specified, the default &scons; behavior is to suppress
+msvc batch file error messages.
+</para>
+<para>
+The root cause of msvc build failures may be difficult to diagnose. In these situations, setting
+the &scons; behavior to issue a warning when msvc batch file errors are detected <emphasis>may</emphasis>
+produce additional diagnostic information.
+</para>
+
+<para>
+The valid values for &cv-MSVC_SCRIPTERROR_POLICY; and the corresponding &scons; behavior are:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><parameter>'Error' or 'Exception'</parameter></term>
+<listitem>
+<para>
+Raise an exception when msvc batch file errors are detected.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Warning' or 'Warn'</parameter></term>
+<listitem>
+<para>
+Issue a warning when msvc batch file errors are detected.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Ignore' or 'Suppress'</parameter></term>
+<listitem>
+<para>
+Suppress msvc batch file error messages.
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>
+Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well.
+</para>
+
+<para>
+Example 1 - A Visual Studio 2022 build with user-defined script arguments:
+<example_commands>
+env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1'])
+env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject'])
+</example_commands>
+</para>
+
+<para>
+Example 1 - Output fragment:
+<example_commands>
+...
+link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj
+LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib'
+...
+</example_commands>
+</para>
+
+<para>
+Example 2 - A Visual Studio 2022 build with user-defined script arguments and the script error policy set
+to issue a warning when msvc batch file errors are detected:
+<example_commands>
+env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1'], MSVC_SCRIPTERROR_POLICY='warn')
+env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject'])
+</example_commands>
+</para>
+
+<para>
+Example 2 - Output fragment:
+<example_commands>
+...
+scons: warning: vc script errors detected:
+[ERROR:vcvars.bat] The UWP Application Platform requires a Windows 10 SDK.
+[ERROR:vcvars.bat] WindowsSdkDir = "C:\Program Files (x86)\Windows Kits\8.1\"
+[ERROR:vcvars.bat] host/target architecture is not supported : { x64 , x64 }
+...
+link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj
+LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib'
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SCRIPTERROR_POLICY; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SCRIPTERROR_POLICY; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+Due to &scons; implementation details, not all Windows system environment variables are propagated
+to the environment in which the msvc batch file is executed. Depending on Visual Studio version
+and installation options, non-fatal msvc batch file error messages may be generated for ancillary
+tools which may not affect builds with the msvc compiler. For this reason, caution is recommended
+when setting the script error policy to raise an exception (e.g., <literal>'Error'</literal>).
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</summary>
+</cvar>
+
+<cvar name="MSVC_SCRIPT_ARGS">
+<summary>
+<para>
+Pass user-defined arguments to the Visual C++ batch file determined via autodetection.
+</para>
+
+<para>
+&cv-MSVC_SCRIPT_ARGS; is available for msvc batch file arguments that do not have first-class support
+via &consvars; or when there is an issue with the appropriate &consvar; validation.
+When available, it is recommended to use the appropriate &consvars; (e.g., &cv-link-MSVC_TOOLSET_VERSION;)
+rather than &cv-MSVC_SCRIPT_ARGS; arguments.
+</para>
+
+<para>
+The valid values for &cv-MSVC_SCRIPT_ARGS; are: <literal>None</literal>, a string,
+or a list of strings.
+</para>
+
+<para>
+The &cv-MSVC_SCRIPT_ARGS; value is converted to a scalar string (i.e., "flattened").
+The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined
+via autodetection subject to the validation conditions listed below.
+</para>
+
+<para>
+&cv-MSVC_SCRIPT_ARGS; is ignored when the value is <literal>None</literal> and when the
+result from argument conversion is an empty string. The validation conditions below do not apply.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SCRIPT_ARGS; is specified for Visual Studio 2013 and earlier.
+</para></listitem>
+
+<listitem><para>
+Multiple SDK version arguments (e.g., <literal>'10.0.20348.0'</literal>) are specified
+in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument
+(e.g., <literal>'10.0.20348.0'</literal>) is specified in &cv-MSVC_SCRIPT_ARGS;.
+Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and &cv-MSVC_SCRIPT_ARGS;
+are not allowed.
+</para></listitem>
+
+<listitem><para>
+Multiple toolset version arguments (e.g., <literal>'-vcvars_ver=14.29'</literal>)
+are specified in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument
+(e.g., <literal>'-vcvars_ver=14.29'</literal>) is specified in &cv-MSVC_SCRIPT_ARGS;.
+Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and
+&cv-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+Multiple spectre library arguments (e.g., <literal>'-vcvars_spectre_libs=spectre'</literal>)
+are specified in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument
+(e.g., <literal>'-vcvars_spectre_libs=spectre'</literal>) is specified in
+&cv-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
+and &cv-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+Multiple UWP arguments (e.g., <literal>uwp</literal> or <literal>store</literal>) are specified
+in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., <literal>uwp</literal> or
+<literal>store</literal>) is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple UWP declarations
+via &cv-link-MSVC_UWP_APP; and &cv-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example 1 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a string argument:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS='10.0.20348.0 -vcvars_ver=14.29.30133')
+</example_commands>
+</para>
+
+<para>
+Example 2 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a list argument:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['10.0.20348.0', '-vcvars_ver=14.29.30133'])
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SCRIPT_ARGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SCRIPT_ARGS; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+Other than checking for multiple declarations as described above, &cv-MSVC_SCRIPT_ARGS; arguments
+are not validated.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+Erroneous, inconsistent, and/or version incompatible &cv-MSVC_SCRIPT_ARGS; arguments are likely
+to result in build failures for reasons that are not readily apparent and may be difficult to diagnose.
+</emphasis>
+The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent
+and compatible with the version of msvc selected.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</summary>
+</cvar>
+
+<cvar name="MSVC_SDK_VERSION">
+<summary>
+<para>
+Build with a specific version of the Microsoft Software Development Kit (SDK).
+</para>
+
+<para>
+The valid values for &cv-MSVC_SDK_VERSION; are: <literal>None</literal>
+or a string containing the requested SDK version (e.g., <literal>'10.0.20348.0'</literal>).
+</para>
+
+<para>
+&cv-MSVC_SDK_VERSION; is ignored when the value is <literal>None</literal> and when
+the value is an empty string. The validation conditions below do not apply.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SDK_VERSION; is specified for Visual Studio 2013 and earlier.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_SDK_VERSION; is specified and an SDK version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via &cv-MSVC_SDK_VERSION;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+The &cv-MSVC_SDK_VERSION; specified does not match any of the supported formats:
+<itemizedlist>
+<listitem><para>
+<literal>'10.0.XXXXX.Y'</literal> [SDK 10.0]
+</para></listitem>
+<listitem><para>
+<literal>'8.1'</literal> [SDK 8.1]
+</para></listitem>
+</itemizedlist>
+</para></listitem>
+
+<listitem><para>
+The system folder for the corresponding &cv-MSVC_SDK_VERSION; version is not found.
+The requested SDK version does not appear to be installed.
+</para></listitem>
+
+<listitem><para>
+The &cv-MSVC_SDK_VERSION; version does not appear to support the requested platform
+type (i.e., <literal>UWP</literal> or <literal>Desktop</literal>). The requested SDK version
+platform type components do not appear to be installed.
+</para></listitem>
+
+<listitem><para>
+The &cv-MSVC_SDK_VERSION; version is <literal>8.1</literal>, the platform type is
+<literal>UWP</literal>, and the build tools selected are from Visual Studio 2017
+and later (i.e., &cv-link-MSVC_VERSION; must be '14.0' or &cv-link-MSVC_TOOLSET_VERSION;
+must be '14.0').
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example 1 - A Visual Studio 2022 build with a specific Windows SDK version:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0')
+</example_commands>
+</para>
+
+<para>
+Example 2 - A Visual Studio 2022 build with a specific SDK version for the Universal Windows Platform:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0', MSVC_UWP_APP=True)
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SDK_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SDK_VERSION; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem>
+<para><emphasis>
+Should a SDK 10.0 version be installed that does not follow the naming scheme above, the
+SDK version will need to be specified via &cv-link-MSVC_SCRIPT_ARGS; until the version number
+validation format can be extended.
+</emphasis></para>
+</listitem>
+
+<listitem><para>
+Should an exception be raised indicating that the SDK version is not found, verify that
+the requested SDK version is installed with the necessary platform type components.
+</para></listitem>
+
+<listitem><para>
+There is a known issue with the Microsoft libraries when the target architecture is
+<literal>ARM64</literal> and a Windows 11 SDK (version <literal>'10.0.22000.0'</literal> and later) is used
+with the <literal>v141</literal> build tools and older <literal>v142</literal> toolsets
+(versions <literal>'14.28.29333'</literal> and earlier). Should build failures arise with these combinations
+of settings due to unresolved symbols in the Microsoft libraries, &cv-MSVC_SDK_VERSION; may be employed to
+specify a Windows 10 SDK (e.g., <literal>'10.0.20348.0'</literal>) for the build.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</summary>
+</cvar>
+
+<cvar name="MSVC_TOOLSET_VERSION">
+<summary>
+<para>
+Build with a specific Visual C++ toolset version.
+</para>
+
+<para><emphasis>
+Specifying &cv-MSVC_TOOLSET_VERSION; does not affect the autodetection and selection
+of msvc instances. The &cv-MSVC_TOOLSET_VERSION; is applied <emphasis>after</emphasis>
+an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION;
+is not specified.
+</emphasis></para>
+
+<para>
+The valid values for &cv-MSVC_TOOLSET_VERSION; are: <literal>None</literal>
+or a string containing the requested toolset version (e.g., <literal>'14.29'</literal>).
+</para>
+
+<para>
+&cv-MSVC_TOOLSET_VERSION; is ignored when the value is <literal>None</literal> and when
+the value is an empty string. The validation conditions below do not apply.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_TOOLSET_VERSION; is specified for Visual Studio 2015 and earlier.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_TOOLSET_VERSION; is specified and a toolset version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-MSVC_TOOLSET_VERSION;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem>
+<para>
+The &cv-MSVC_TOOLSET_VERSION; specified does not match any of the supported formats:
+</para>
+
+<itemizedlist>
+
+<listitem><para>
+<literal>'XX.Y'</literal>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY'</literal>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.ZZZZZ'</literal>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.Z'</literal> to <literal>'XX.YY.ZZZZ'</literal>
+<emphasis>
+[&scons; extension not directly supported by the msvc batch files and may be removed in the future]
+</emphasis>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.ZZ.N'</literal> [SxS format]
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.ZZ.NN'</literal> [SxS format]
+</para></listitem>
+
+</itemizedlist>
+
+</listitem>
+
+<listitem><para>
+The major msvc version prefix (i.e., <literal>'XX.Y'</literal>) of the &cv-MSVC_TOOLSET_VERSION; specified
+is for Visual Studio 2013 and earlier (e.g., <literal>'12.0'</literal>).
+</para></listitem>
+
+<listitem><para>
+The major msvc version prefix (i.e., <literal>'XX.Y'</literal>) of the &cv-MSVC_TOOLSET_VERSION; specified
+is greater than the msvc version selected (e.g., <literal>'99.0'</literal>).
+</para></listitem>
+
+<listitem><para>
+A system folder for the corresponding &cv-MSVC_TOOLSET_VERSION; version is not found.
+The requested toolset version does not appear to be installed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Toolset selection details:
+<itemizedlist>
+
+<listitem><para>
+When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number:
+the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION;
+prefix is selected.
+</para></listitem>
+
+<listitem><para>
+When &cv-MSVC_TOOLSET_VERSION; is specified using the major msvc version prefix
+(i.e., <literal>'XX.Y'</literal>) and the major msvc version is that of the latest release of
+Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version.
+</para><para>
+In the latest release of Visual Studio, the default Visual C++ toolset version is not necessarily the
+toolset with the largest version number.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example 1 - A default Visual Studio build with a partial toolset version specified:
+<example_commands>
+env = Environment(MSVC_TOOLSET_VERSION='14.2')
+</example_commands>
+</para>
+
+<para>
+Example 2 - A default Visual Studio build with a partial toolset version specified:
+<example_commands>
+env = Environment(MSVC_TOOLSET_VERSION='14.29')
+</example_commands>
+</para>
+
+<para>
+Example 3 - A Visual Studio 2022 build with a full toolset version specified:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
+</example_commands>
+</para>
+
+<para>
+Example 4 - A Visual Studio 2022 build with an SxS toolset version specified:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_TOOLSET_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_TOOLSET_VERSION; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The existence of the toolset host architecture and target architecture folders are not verified
+when &cv-MSVC_TOOLSET_VERSION; is specified which could result in build failures.
+</emphasis>
+The burden is on the user to ensure the requisite toolset target architecture build tools are installed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</summary>
+</cvar>
+
+<cvar name="MSVC_SPECTRE_LIBS">
+<summary>
+<para>
+Build with the spectre-mitigated Visual C++ libraries.
+</para>
+
+<para>
+The valid values for &cv-MSVC_SPECTRE_LIBS; are: <literal>True</literal>,
+<literal>False</literal>, or <literal>None</literal>.
+</para>
+
+<para>
+When &cv-MSVC_SPECTRE_LIBS; is enabled (i.e., <literal>True</literal>),
+the Visual C++ environment will include the paths to the spectre-mitigated implementations
+of the Microsoft Visual C++ libraries.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; is enabled for Visual Studio 2015 and earlier.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-MSVC_SPECTRE_LIBS;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; is enabled and the platform type is <literal>UWP</literal>. There
+are no spectre-mitigated libraries for Universal Windows Platform (UWP) applications or
+components.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example - A Visual Studio 2022 build with spectre mitigated Visual C++ libraries:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True)
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SPECTRE_LIBS; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+Additional compiler switches (e.g., <literal>/Qspectre</literal>) are necessary for including
+spectre mitigations when building user artifacts. Refer to the Visual Studio documentation for
+details.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The existence of the spectre libraries host architecture and target architecture folders are not
+verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures.
+</emphasis>
+The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para><emphasis>New in version 4.4</emphasis></para>
+
+</summary>
+</cvar>
</sconsdoc>
diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py
index 887cb59..86df1ef 100644
--- a/SCons/Tool/msvs.py
+++ b/SCons/Tool/msvs.py
@@ -45,7 +45,9 @@ import SCons.Util
import SCons.Warnings
from SCons.Defaults import processDefines
from SCons.compat import PICKLE_PROTOCOL
-from .MSCommon import msvc_exists, msvc_setup_env_once
+from .MSCommon import msvc_setup_env_tool, msvc_setup_env_once
+
+tool_name = 'msvs'
##############################################################################
# Below here are the classes and functions for generation of
@@ -2077,7 +2079,7 @@ def generate(env):
env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"'
# Set-up ms tools paths for default version
- msvc_setup_env_once(env)
+ msvc_setup_env_once(env, tool=tool_name)
if 'MSVS_VERSION' in env:
version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
@@ -2107,7 +2109,7 @@ def generate(env):
env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
def exists(env):
- return msvc_exists(env)
+ return msvc_setup_env_tool(env, tool=tool_name)
# Local Variables:
# tab-width:4
diff --git a/SCons/Tool/msvs.xml b/SCons/Tool/msvs.xml
index 55f0401..864d9a2 100644
--- a/SCons/Tool/msvs.xml
+++ b/SCons/Tool/msvs.xml
@@ -1,10 +1,12 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+SPDX-License-Identifier: MIT
+SPDX-FileCopyrightText: Copyright The SCons Foundation
+SPDX-FileType: DOCUMENTATION
-This file is processed by the bin/SConsDoc.py module.
-See its __doc__ string for a discussion of the format.
+This file is processed by the bin/SConsDoc.py module
-->
+
<!DOCTYPE sconsdoc [
<!ENTITY % scons SYSTEM "../../doc/scons.mod">
%scons;
@@ -20,7 +22,7 @@ See its __doc__ string for a discussion of the format.
<sconsdoc xmlns="http://www.scons.org/dbxsd/v1.0" xmlns:ns="http://www.scons.org/dbxsd/v1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" 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>
+ <para>Sets &consvars; for Microsoft Visual Studio.</para>
</summary>
<sets>
<item>MSVSPROJECTCOM</item>
@@ -39,70 +41,106 @@ See its __doc__ string for a discussion of the format.
<builder name="MSVSProject">
<summary>
<para>
- Builds a Microsoft Visual Studio project file, and by default
- builds a solution file as well.
+ Build a Microsoft Visual C++ project file and solution file.
</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, 8, and 9, it will
- generate a <filename>.vcproj</filename> file. For Visual
- Studio 10 and later, it will generate a
- <filename>.vcxproj</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 and later. 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
+ Builds a C++ project file based on the
+ version of Visual Studio (or to be more precise, of MSBuild)
+ that is configured: either the latest installed version,
+ or the version specified by
+ &cv-link-MSVC_VERSION; in the current &consenv;.
+ For Visual Studio 6.0 a <filename>.dsp</filename> file is generated.
+ For Visual Studio versions 2002-2008,
+ a <filename>.vcproj</filename> file is generated.
+ For Visual Studio 2010 and later a <filename>.vcxproj</filename>
+ file is generated.
+ Note there are multiple versioning schemes involved in
+ the Microsoft compilation environment -
+ see the description of &cv-link-MSVC_VERSION; for equivalences.
+ &SCons; does not know how to construct project files for
+ other languages (such as <filename>.csproj</filename> for C#,
+ <filename>.vbproj</filename> for Visual Basic or
+ <filename>.pyproject</filename> for Python)).
+ </para>
+ <para>
+ For the <filename>.vcxproj</filename> file, the underlying
+ format is the MSBuild XML Schema, and the details conform to:
+ <ulink url="https://learn.microsoft.com/en-us/cpp/build/reference/vcxproj-file-structure">
+ https://learn.microsoft.com/en-us/cpp/build/reference/vcxproj-file-structure</ulink>.
+ The generated solution file enables Visual Studio to
+ understand the project structure, and allows building it
+ using MSBuild to call back to &SCons;.
+ The project file encodes a toolset version that has been
+ selected by &SCons; as described above. Since recent Visual
+ Studio versions support multiple concurrent toolsets,
+ use &cv-link-MSVC_VERSION; to select the desired one if
+ it does not match the &SCons; default.
+ The project file also includes entries which describe
+ how to call &SCons; to build the project from within Visual Studio
+ (or from an MSBuild command line).
+ In some situations &SCons; may generate this incorrectly -
+ notably when using the <emphasis>scons-local</emphasis>
+ distribution, which is not installed in a way that that
+ matches the default invocation line.
+ If so, the &cv-link-SCONS_HOME; &consvar; can be used to describe
+ the right way to locate the &SCons; code so that it can be imported.
+ </para>
+ <para>
+ By default, a matching solution file for the project is also generated.
+ This behavior may be disabled by
+ specifying <parameter>auto_build_solution=0</parameter>
+ to the &b-MSVSProject; builder.
+ The solution file can also be independently
+ generated by calling the &b-MSVSSolution; builder,
+ such as in the case where a solution should describe
+ multiple projects.
+ See the &b-link-MSVSSolution; description for further information.
+ </para>
+ <para>
+ The &b-MSVSProject; builder accepts several keyword arguments
+ describing lists of filenames to be placed into the project file.
+ Currently,
+ <parameter>srcs</parameter>,
+ <parameter>incs</parameter>,
+ <parameter>localincs</parameter>,
+ <parameter>resources</parameter>,
+ and <parameter>misc</parameter>
+ are recognized.
+ The names are intended to be self-explanatory, but note that the
+ filenames need to be specified as strings, <emphasis>not</emphasis>
+ as &SCons; File Nodes
+ (for example if you generate files for inclusion by using the
+ &f-link-Glob; function, the results should be converted to
+ a list of strings before passing them to &b-MSVSProject;).
+ This is because Visual Studio and MSBuild know nothing about &SCons;
+ Node types.
+ Each of the filename lists are individually optional, but at
+ least one list 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:
+ may be specified as keyword arguments:
</para>
<variablelist>
<varlistentry>
- <term>target</term>
+ <term><parameter>target</parameter></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
+ &consvar; will be defined to the correct
value (see example below).
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>variant</term>
+ <term><parameter>variant</parameter></term>
<listitem>
<para>
- The name of this particular variant. For Visual Studio 7
+ The name of this particular variant. Except for Visual Studio 6
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
@@ -117,145 +155,184 @@ See its __doc__ string for a discussion of the format.
</listitem>
</varlistentry>
<varlistentry>
- <term>cmdargs</term>
+ <term><parameter>cmdargs</parameter></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
+ <parameter>cmdargs</parameter> entries must match the number
+ of <parameter>variant</parameter> entries, or be empty (not
specified). If you give only one, it will automatically
be propagated to all variants.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>cppdefines</term>
+ <term><parameter>cppdefines</parameter></term>
<listitem>
<para>
Preprocessor definitions for the different variants.
- The number of <literal>cppdefines</literal> entries
- must match the number of <literal>variant</literal>
+ The number of <parameter>cppdefines</parameter> entries
+ must match the number of <parameter>variant</parameter>
entries, or be empty (not specified). If you give
only one, it will automatically be propagated to all
- variants. If you don't give this parameter, SCons
+ variants. If you don't give this parameter, &SCons;
will use the invoking environment's
- <literal>CPPDEFINES</literal> entry for all variants.
+ &cv-link-CPPDEFINES; entry for all variants.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>cppflags</term>
+ <term><parameter>cppflags</parameter></term>
<listitem>
<para>
Compiler flags for the different variants.
- If a /std:c++ flag is found then /Zc:__cplusplus is
- appended to the flags if not already found, this
- ensures that intellisense uses the /std:c++ switch.
- The number of <literal>cppflags</literal> entries
- must match the number of <literal>variant</literal>
+ If a <option>/std:c++</option> flag is found then
+ <option>/Zc:__cplusplus</option> is appended to the
+ flags if not already found, this ensures that Intellisense
+ uses the <option>/std:c++</option> switch.
+ The number of <parameter>cppflags</parameter> entries
+ must match the number of <parameter>variant</parameter>
entries, or be empty (not specified). If you give
only one, it will automatically be propagated to all
variants. If you don't give this parameter, SCons
will combine the invoking environment's
- <literal>CCFLAGS</literal>, <literal>CXXFLAGS</literal>,
- <literal>CPPFLAGS</literal> entries for all variants.
+ &cv-link-CCFLAGS;, &cv-link-CXXFLAGS;,
+ &cv-link-CPPFLAGS; entries for all variants.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>cpppaths</term>
+ <term><parameter>cpppaths</parameter></term>
<listitem>
<para>
Compiler include paths for the different variants.
- The number of <literal>cpppaths</literal> entries
- must match the number of <literal>variant</literal>
+ The number of <parameter>cpppaths</parameter> entries
+ must match the number of <parameter>variant</parameter>
entries, or be empty (not specified). If you give
only one, it will automatically be propagated to all
variants. If you don't give this parameter, SCons
will use the invoking environment's
- <literal>CPPPATH</literal> entry for all variants.
+ &cv-link-CPPPATH; entry for all variants.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>buildtarget</term>
+ <term><parameter>buildtarget</parameter></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.
+ <parameter>buildtarget</parameter> entries must match the
+ number of <parameter>variant</parameter> entries.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term>runfile</term>
+ <term><parameter>runfile</parameter></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
+ value of the <parameter>Output</parameter> field in the
+ resulting Visual C++ project file. If this is not
specified, the default is the same as the specified
- <literal>buildtarget</literal> value.
+ <parameter>buildtarget</parameter> value.
</para>
</listitem>
</varlistentry>
</variablelist>
+ <note>
+ <para>
+ &SCons; and Microsoft Visual Studio understand projects in
+ different ways, and the mapping is sometimes imperfect:
+ </para>
<para>
- Note that because &SCons; always executes its build commands
+ 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
+ than the directory of the &SConstruct; file, 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>
+ be remedied by adding the Visual C/C++ <option>/FC</option>
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>
+ If the project file is only used to teach the Visual Studio
+ project browser about the file layout there should be no issues,
+ However, Visual Studio should not be used to make changes
+ to the project structure, build options, etc. as these will
+ (a) not feed back to the &SCons; description of the project
+ and (b) be lost if &SCons; regenerates the project file.
+ The SConscript files should remain the definitive description
+ of the build.
+ </para>
+ <para>
+ If the project file is used to drive MSBuild (such as selecting
+ "build" from the Visual Studio interface) you lose the direct
+ control of target selection and command-line options you would
+ have if launching the build directly from &SCons;,
+ because these will be hardcoded in the project file to the
+ values specified in the &b-MSVSProject; call.
+ You can regain some of this control by defining multiple variants,
+ using multiple &b-MSVSProject; calls to arrange different build
+ targets, arguments, defines, flags and paths for different variants.
+ </para>
+ <para>
+ If the build is divided into a solution with multiple MSBuild
+ projects the mapping is further strained. In this case,
+ it is important not to set Visual Studio to do parallel builds,
+ as it will then launch the separate project builds in parallel,
+ and &SCons; does not work well if called that way.
+ Instead you can set up the &SCons; build for parallel building -
+ see the &f-link-SetOption; function for how to do this with
+ <parameter>num_jobs</parameter>.
+ </para>
+ </note>
+
<para>Example usage:</para>
<example_commands>
barsrcs = ['bar.cpp']
barincs = ['bar.h']
barlocalincs = ['StdAfx.h']
-barresources = ['bar.rc','resource.h']
+barresources = ['bar.rc', 'resource.h']
barmisc = ['bar_readme.txt']
-dll = env.SharedLibrary(target='bar.dll',
- source=barsrcs)
+dll = env.SharedLibrary(target='bar.dll', source=barsrcs)
buildtarget = [s for s in dll if str(s).endswith('dll')]
-env.MSVSProject(target='Bar' + env['MSVSPROJECTSUFFIX'],
- srcs=barsrcs,
- incs=barincs,
- localincs=barlocalincs,
- resources=barresources,
- misc=barmisc,
- buildtarget=buildtarget,
- variant='Release')
+env.MSVSProject(
+ target='Bar' + env['MSVSPROJECTSUFFIX'],
+ srcs=barsrcs,
+ incs=barincs,
+ localincs=barlocalincs,
+ resources=barresources,
+ misc=barmisc,
+ buildtarget=buildtarget,
+ variant='Release',
+)
</example_commands>
- <para>
- Starting with version 2.4 of SCons it is
- also possible to specify the optional argument
- <parameter>DebugSettings</parameter>, which creates files
- for debugging under Visual Studio:
- </para>
+
<variablelist>
<varlistentry>
- <term>DebugSettings</term>
+ <term><parameter>DebugSettings</parameter></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),
+ version installed. As for <parameter>cmdargs</parameter>,
you can specify a <parameter>DebugSettings</parameter>
dictionary per variant. If you give only one, it will
be propagated to all variants.
</para>
+ <para>
+ <emphasis>Changed in version 2.4:</emphasis>
+ Added the optional <parameter>DebugSettings</parameter> parameter.
+ </para>
</listitem>
</varlistentry>
</variablelist>
@@ -279,12 +356,17 @@ 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)
+ env = Environment(MSVC_VERSION=msvcver + '.0', MSVC_BATCH=False)
else:
- env = Environment()
+ env = Environment()
-AddOption('--userfile', action='store_true', dest='userfile', default=False,
- help="Create Visual Studio Project user file")
+AddOption(
+ '--userfile',
+ action='store_true',
+ dest='userfile',
+ default=False,
+ help="Create Visual C++ project file",
+)
#
# 1. Configure your Debug Setting dictionary with options you want in the list
@@ -292,28 +374,28 @@ AddOption('--userfile', action='store_true', dest='userfile', default=False,
# a specific application for testing your dll with Microsoft Visual Studio 2008 (v9):
#
V9DebugSettings = {
- 'Command':'c:\\myapp\\using\\thisdll.exe',
+ '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,
+ # '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,
}
#
@@ -327,28 +409,28 @@ 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,
+ # '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,
}
#
@@ -370,71 +452,85 @@ else:
barsrcs = ['bar.cpp', 'dllmain.cpp', 'stdafx.cpp']
barincs = ['targetver.h']
barlocalincs = ['StdAfx.h']
-barresources = ['bar.rc','resource.h']
+barresources = ['bar.rc', 'resource.h']
barmisc = ['ReadMe.txt']
-dll = env.SharedLibrary(target='bar.dll',
- source=barsrcs)
+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, {}))
+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=f'vc={msvcver}',
+ DebugSettings=(dbgSettings, {}),
+)
</example_commands>
</summary>
</builder>
<builder name="MSVSSolution">
<summary>
- <para>Builds a Microsoft Visual Studio solution file.</para>
+ <para>Build 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
+ 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.
+ &cv-link-MSVC_VERSION; in the &consenv;. For
+ Visual Studio 6, a <filename>.dsw</filename> file is generated.
+ For Visual Studio .NET 2002 and later,
+ it will generate a <filename>.sln</filename> file.
+ Note there are multiple versioning schemes involved in
+ the Microsoft compilation environment -
+ see the description of &cv-link-MSVC_VERSION; for equivalences.
+ </para>
+ <para>
+ The solution file is a container for one or more projects,
+ and follows the format described at
+ <ulink url="https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/solution-dot-sln-file">
+ https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/solution-dot-sln-file</ulink>.
</para>
<para>The following values must be specified:</para>
<variablelist>
<varlistentry>
- <term>target</term>
+ <term><parameter>target</parameter></term>
<listitem>
<para>
- The name of the target .dsw or .sln file. The correct
+ The name of the target <filename>.dsw</filename> or
+ <filename>.sln</filename> 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>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>variant</parameter></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.
+ <literal>"Debug|Xbox"</literal>. Default platform is Win32.
</para>
</listitem>
- </varlistentry> <varlistentry>
- <term>projects</term> <listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>projects</parameter></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.
+ by calls to the &b-link-MSVSProject; Builder, to be placed
+ into the solution file.
+ Note that these filenames need to be specified as strings,
+ NOT as &SCons; File Nodes.
+ This is because the solution file will be interpreted by MSBuild
+ and by Visual Studio, which know nothing about &SCons; Node types.
</para>
</listitem>
</varlistentry>
@@ -456,28 +552,39 @@ env.MSVSSolution(
</para>
<variablelist>
<varlistentry>
- <term>VERSION</term> <listitem>
+ <term>VERSION</term>
+ <listitem>
<para>the version of MSVS being used (can be set via
- &cv-link-MSVS_VERSION;)</para>
+ &cv-link-MSVC_VERSION;)</para>
</listitem>
- </varlistentry> <varlistentry>
- <term>VERSIONS</term> <listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>VERSIONS</term>
+ <listitem>
<para>the available versions of MSVS installed</para>
</listitem>
- </varlistentry> <varlistentry>
- <term>VCINSTALLDIR</term> <listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>VCINSTALLDIR</term>
+ <listitem>
<para>installed directory of Visual C++</para>
</listitem>
- </varlistentry> <varlistentry>
- <term>VSINSTALLDIR</term> <listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>VSINSTALLDIR</term>
+ <listitem>
<para>installed directory of Visual Studio</para>
</listitem>
- </varlistentry> <varlistentry>
- <term>FRAMEWORKDIR</term> <listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>FRAMEWORKDIR</term>
+ <listitem>
<para>installed directory of the .NET framework</para>
</listitem>
- </varlistentry> <varlistentry>
- <term>FRAMEWORKVERSIONS</term> <listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>FRAMEWORKVERSIONS</term>
+ <listitem>
<para>
list of installed versions of the .NET framework,
sorted latest to oldest.
@@ -514,7 +621,12 @@ env.MSVSSolution(
</listitem>
</varlistentry>
</variablelist>
- <para>If a value is not set, it was not available in the registry.</para>
+ <para>If a value is not set, it was not available in the registry.
+ Visual Studio 2017 and later do not use the registry for
+ primary storage of this information, so typically for these
+ versions only <literal>PROJECTSUFFIX</literal> and
+ <literal>SOLUTIONSUFFIX</literal> will be set.
+ </para>
</summary>
</cvar> <cvar name="MSVS_ARCH">
<summary>
@@ -534,7 +646,7 @@ env.MSVSSolution(
<summary>
<para>
The string placed in a generated
-Microsoft Visual Studio project file as the value of the
+Microsoft Visual C++ project file as the value of the
<literal>ProjectGUID</literal> attribute. There is no default
value. If not
defined, a new GUID is generated.
@@ -545,9 +657,9 @@ defined, a new GUID is generated.
<summary>
<para>
The path name placed in a generated
-Microsoft Visual Studio project file as the value of the
+Microsoft Visual C++ project file as the value of the
<literal>SccAuxPath</literal> attribute if the
- <envar>MSVS_SCC_PROVIDER</envar> construction variable is
+ <envar>MSVS_SCC_PROVIDER</envar> &consvar; is
also set. There is
no default value.
@@ -559,7 +671,7 @@ no default value.
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
+ relative paths of the generated Microsoft Visual C++ 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
@@ -572,7 +684,7 @@ no default value.
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
+ the <envar>MSVS_SCC_PROVIDER</envar> &consvar; is
also set. The default value is the current working directory.
</para>
</summary>
@@ -580,9 +692,9 @@ no default value.
<summary>
<para>
The project name placed in a generated Microsoft
- Visual Studio project file as the value of the
+ Visual C++ project file as the value of the
<literal>SccProjectName</literal> attribute if the
- <envar>MSVS_SCC_PROVIDER</envar> construction variable
+ <envar>MSVS_SCC_PROVIDER</envar> &consvar;
is also set. In this case the string is also placed in
the <literal>SccProjectName0</literal> attribute of the
<literal>GlobalSection(SourceCodeControl)</literal> section
@@ -594,7 +706,7 @@ no default value.
<summary>
<para>
The string placed in a generated Microsoft
- Visual Studio project file as the value of the
+ Visual C++ 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>
@@ -604,23 +716,25 @@ no default value.
</summary>
</cvar> <cvar name="MSVS_VERSION">
<summary>
- <para>Sets the preferred version of Microsoft Visual Studio to use.</para>
+ <para>Set 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
+ specifying the &cv-link-MSVS_VERSION; variable when
+ initializing the Environment, 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.
+ <emphasis>Deprecated since 1.3.0:</emphasis>
+ &cv-MSVS_VERSION; is deprecated in favor of &cv-link-MSVC_VERSION;.
+ As a transitional aid, if &cv-MSVS_VERSION; is set
+ and &cv-MSVC_VERSION; is not,
+ &cv-MSVC_VERSION; will be initialized to the value
+ of &cv-MSVS_VERSION;.
+ An error is raised if If both are set and have different values,
</para>
</summary>
</cvar>
@@ -628,8 +742,8 @@ no default value.
<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.
+ C++ project file. The default is to have Visual Studio
+ invoke &SCons; with any specified build targets.
</para>
</summary>
</cvar>
@@ -637,33 +751,34 @@ no default value.
<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.
+ C++ project file. The default is to have Visual Studio
+ invoke &SCons; with the <option>-c</option> 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
+ Visual C++ 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>
+ <para>The action used to generate Microsoft Visual C++ 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.
+ The suffix used for Microsoft Visual C++ project (DSP)
+ files. The default value is
+ <filename>.vcxproj</filename> when using Visual Studio 2010
+ and later, <filename>.vcproj</filename>
+ when using Visual Studio versions between 2002 and 2008,
+ and <filename>.dsp</filename> when using Visual Studio 6.0.
</para>
</summary>
</cvar>
@@ -671,8 +786,8 @@ no default value.
<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.
+ Visual C++ project file. The default is to have Visual
+ Studio invoke &SCons; with any specified rebuild targets.
</para>
</summary>
@@ -680,8 +795,8 @@ no default value.
<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
+ The &SCons; used in generated Microsoft Visual C++ project
+ files. The default is the version of &SCons; being used to
generate the project file.
</para>
</summary>
@@ -689,15 +804,15 @@ no default value.
<cvar name="MSVSSCONSFLAGS">
<summary>
<para>
- The SCons flags used in generated Microsoft Visual Studio project files.
+ The &SCons; flags used in generated Microsoft Visual C++ project files.
</para>
</summary>
</cvar>
<cvar name="MSVSSCONSCOM">
<summary>
<para>
- The default SCons command used in generated Microsoft Visual
- Studio project files.
+ The default &SCons; command used in generated Microsoft Visual
+ C++ project files.
</para>
</summary>
</cvar>
@@ -705,10 +820,10 @@ no default value.
<summary>
<para>
The sconscript file (that is, &SConstruct; or &SConscript;
- file) that will be invoked by Visual Studio project files
+ file) that will be invoked by Visual C++ 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.
+ &b-link-MSVSProject; to build the project file.
</para>
</summary>
</cvar>
@@ -721,20 +836,19 @@ no default value.
<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.
+ when using Visual Studio version 7.x (.NET 2002) and later,
+ and <filename>.dsw</filename> when using Visual Studio 6.0.
</para>
</summary>
</cvar>
<cvar name="SCONS_HOME">
<summary>
<para>
- The (optional) path to the SCons library directory,
+ 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.
+ Visual C++ project files.
</para>
</summary>
</cvar>
diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py
index 4cbaf0e..c4d2e98 100644
--- a/SCons/Tool/msvsTests.py
+++ b/SCons/Tool/msvsTests.py
@@ -982,7 +982,9 @@ if __name__ == "__main__":
if k in os.environ:
del os.environ[k]
- suite = unittest.makeSuite(test_class, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ suite = loader.loadTestsFromTestCase(test_class)
if not TestUnit.cli.get_runner()().run(suite).wasSuccessful():
exit_val = 1
finally:
diff --git a/SCons/Tool/ninja/Globals.py b/SCons/Tool/ninja/Globals.py
index 4d1d38b..6b079ef 100644
--- a/SCons/Tool/ninja/Globals.py
+++ b/SCons/Tool/ninja/Globals.py
@@ -29,6 +29,8 @@ NINJA_CUSTOM_HANDLERS = "__NINJA_CUSTOM_HANDLERS"
NINJA_BUILD = "NINJA_BUILD"
NINJA_WHEREIS_MEMO = {}
NINJA_STAT_MEMO = {}
+NINJA_DEFAULT_TARGETS = []
+NINJA_CMDLINE_TARGETS = []
__NINJA_RULE_MAPPING = {}
# These are the types that get_command can do something with
diff --git a/SCons/Tool/ninja/Methods.py b/SCons/Tool/ninja/Methods.py
index 2be576f..c0afab8 100644
--- a/SCons/Tool/ninja/Methods.py
+++ b/SCons/Tool/ninja/Methods.py
@@ -81,7 +81,7 @@ def get_generic_shell_command(env, node, action, targets, sources, executor=None
"GENERATED_CMD",
{
"cmd": generate_command(env, node, action, targets, sources, executor=executor),
- "env": get_command_env(env),
+ "env": get_command_env(env, targets, sources),
},
# Since this function is a rule mapping provider, it must return a list of dependencies,
# and usually this would be the path to a tool, such as a compiler, used for this rule.
@@ -266,7 +266,7 @@ def gen_get_response_file_command(env, rule, tool, tool_is_dynamic=False, custom
variables = {"rspc": rsp_content, rule: cmd}
if use_command_env:
- variables["env"] = get_command_env(env)
+ variables["env"] = get_command_env(env, targets, sources)
for key, value in custom_env.items():
variables["env"] += env.subst(
diff --git a/SCons/Tool/ninja/NinjaState.py b/SCons/Tool/ninja/NinjaState.py
index 63ea3a1..5e7c289 100644
--- a/SCons/Tool/ninja/NinjaState.py
+++ b/SCons/Tool/ninja/NinjaState.py
@@ -29,6 +29,8 @@ import signal
import tempfile
import shutil
import sys
+import random
+import filecmp
from os.path import splitext
from tempfile import NamedTemporaryFile
import ninja
@@ -36,13 +38,13 @@ import hashlib
import SCons
from SCons.Script import COMMAND_LINE_TARGETS
-from SCons.Util import is_List
+from SCons.Util import wait_for_process_to_die
from SCons.Errors import InternalError
from .Globals import COMMAND_TYPES, NINJA_RULES, NINJA_POOLS, \
- NINJA_CUSTOM_HANDLERS
+ NINJA_CUSTOM_HANDLERS, NINJA_DEFAULT_TARGETS
from .Rules import _install_action_function, _mkdir_action_function, _lib_symlink_action_function, _copy_action_function
from .Utils import get_path, alias_to_ninja_build, generate_depfile, ninja_noop, get_order_only, \
- get_outputs, get_inputs, get_dependencies, get_rule, get_command_env, to_escaped_list
+ get_outputs, get_inputs, get_dependencies, get_rule, get_command_env, to_escaped_list, ninja_sorted_build
from .Methods import get_command
@@ -68,7 +70,7 @@ class NinjaState:
# couldn't find it, just give the bin name and hope
# its in the path later
self.ninja_bin_path = ninja_bin
-
+ self.ninja_syntax = ninja_syntax
self.writer_class = ninja_syntax.Writer
self.__generated = False
self.translator = SConsToNinjaTranslator(env)
@@ -77,17 +79,31 @@ class NinjaState:
# List of generated builds that will be written at a later stage
self.builds = dict()
- # List of targets for which we have generated a build. This
- # allows us to take multiple Alias nodes as sources and to not
- # fail to build if they have overlapping targets.
- self.built = set()
-
# SCons sets this variable to a function which knows how to do
# shell quoting on whatever platform it's run on. Here we use it
# to make the SCONS_INVOCATION variable properly quoted for things
# like CCFLAGS
scons_escape = env.get("ESCAPE", lambda x: x)
- scons_daemon_port = int(env.get('NINJA_SCONS_DAEMON_PORT',-1))
+
+ # The daemon port should be the same across runs, unless explicitly set
+ # or if the portfile is deleted. This ensures the ninja file is deterministic
+ # across regen's if nothings changed. The construction var should take preference,
+ # then portfile is next, and then otherwise create a new random port to persist in
+ # use.
+ scons_daemon_port = None
+ os.makedirs(get_path(self.env.get("NINJA_DIR")), exist_ok=True)
+ scons_daemon_port_file = str(pathlib.Path(get_path(self.env.get("NINJA_DIR"))) / "scons_daemon_portfile")
+
+ if env.get('NINJA_SCONS_DAEMON_PORT') is not None:
+ scons_daemon_port = int(env.get('NINJA_SCONS_DAEMON_PORT'))
+ elif os.path.exists(scons_daemon_port_file):
+ with open(scons_daemon_port_file) as f:
+ scons_daemon_port = int(f.read())
+ else:
+ scons_daemon_port = random.randint(10000, 60000)
+
+ with open(scons_daemon_port_file, 'w') as f:
+ f.write(str(scons_daemon_port))
# if SCons was invoked from python, we expect the first arg to be the scons.py
# script, otherwise scons was invoked from the scons script
@@ -96,6 +112,11 @@ class NinjaState:
python_bin = ninja_syntax.escape(scons_escape(sys.executable))
self.variables = {
"COPY": "cmd.exe /c 1>NUL copy" if sys.platform == "win32" else "cp",
+ 'PORT': scons_daemon_port,
+ 'NINJA_DIR_PATH': env.get('NINJA_DIR').abspath,
+ 'PYTHON_BIN': sys.executable,
+ 'NINJA_TOOL_DIR': pathlib.Path(__file__).parent,
+ 'NINJA_SCONS_DAEMON_KEEP_ALIVE': str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE')),
"SCONS_INVOCATION": '{} {} --disable-ninja __NINJA_NO=1 $out'.format(
python_bin,
" ".join(
@@ -209,11 +230,17 @@ class NinjaState:
"restat": 1,
},
"TEMPLATE": {
- "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_daemon_build.py'} {scons_daemon_port} {get_path(env.get('NINJA_DIR'))} $out",
+ "command": "$PYTHON_BIN $NINJA_TOOL_DIR/ninja_daemon_build.py $PORT $NINJA_DIR_PATH $out",
"description": "Defer to SCons to build $out",
"pool": "local_pool",
"restat": 1
},
+ "EXIT_SCONS_DAEMON": {
+ "command": "$PYTHON_BIN $NINJA_TOOL_DIR/ninja_daemon_build.py $PORT $NINJA_DIR_PATH --exit",
+ "description": "Shutting down ninja scons daemon server",
+ "pool": "local_pool",
+ "restat": 1
+ },
"SCONS": {
"command": "$SCONS_INVOCATION $out",
"description": "$SCONS_INVOCATION $out",
@@ -238,7 +265,7 @@ class NinjaState:
},
"SCONS_DAEMON": {
- "command": f"{sys.executable} {pathlib.Path(__file__).parent / 'ninja_run_daemon.py'} {scons_daemon_port} {env.get('NINJA_DIR').abspath} {str(env.get('NINJA_SCONS_DAEMON_KEEP_ALIVE'))} $SCONS_INVOCATION",
+ "command": "$PYTHON_BIN $NINJA_TOOL_DIR/ninja_run_daemon.py $PORT $NINJA_DIR_PATH $NINJA_SCONS_DAEMON_KEEP_ALIVE $SCONS_INVOCATION",
"description": "Starting scons daemon...",
"pool": "local_pool",
# restat
@@ -317,7 +344,6 @@ class NinjaState:
else:
raise InternalError("Node {} added to ninja build state more than once".format(node_string))
self.builds[node_string] = build
- self.built.update(build["outputs"])
return True
# TODO: rely on SCons to tell us what is generated source
@@ -361,8 +387,7 @@ class NinjaState:
self.rules[rule]["depfile"] = "$out.d"
else:
raise Exception(f"Unknown 'NINJA_DEPFILE_PARSE_FORMAT'={self.env['NINJA_DEPFILE_PARSE_FORMAT']}, use 'mvsc', 'gcc', or 'clang'.")
-
-
+
for key, rule in self.env.get(NINJA_RULES, {}).items():
# make a non response file rule for users custom response file rules.
if rule.get('rspfile') is not None:
@@ -374,7 +399,6 @@ class NinjaState:
else:
self.rules.update({key: rule})
- self.rules.update(self.env.get(NINJA_RULES, {}))
self.pools.update(self.env.get(NINJA_POOLS, {}))
content = io.StringIO()
@@ -384,13 +408,13 @@ class NinjaState:
ninja.variable("builddir", get_path(self.env.Dir(self.env['NINJA_DIR']).path))
- for pool_name, size in self.pools.items():
+ for pool_name, size in sorted(self.pools.items()):
ninja.pool(pool_name, min(self.env.get('NINJA_MAX_JOBS', size), size))
- for var, val in self.variables.items():
+ for var, val in sorted(self.variables.items()):
ninja.variable(var, val)
- for rule, kwargs in self.rules.items():
+ for rule, kwargs in sorted(self.rules.items()):
if self.env.get('NINJA_MAX_JOBS') is not None and 'pool' not in kwargs:
kwargs['pool'] = 'local_pool'
ninja.rule(rule, **kwargs)
@@ -451,10 +475,20 @@ class NinjaState:
template_builders = []
scons_compiledb = False
+ if SCons.Script._Get_Default_Targets == SCons.Script._Set_Default_Targets_Has_Not_Been_Called:
+ all_targets = set()
+ else:
+ all_targets = None
+
for build in [self.builds[key] for key in sorted(self.builds.keys())]:
if "compile_commands.json" in build["outputs"]:
scons_compiledb = True
+ # this is for the no command line targets, no SCons default case. We want this default
+ # to just be all real files in the build.
+ if all_targets is not None and build['rule'] != 'phony':
+ all_targets = all_targets | set(build["outputs"])
+
if build["rule"] == "TEMPLATE":
template_builders.append(build)
continue
@@ -522,8 +556,9 @@ class NinjaState:
)
if remaining_outputs:
- ninja.build(
- outputs=sorted(remaining_outputs), rule="phony", implicit=first_output,
+ ninja_sorted_build(
+ ninja,
+ outputs=remaining_outputs, rule="phony", implicit=first_output,
)
build["outputs"] = first_output
@@ -541,12 +576,18 @@ class NinjaState:
if "inputs" in build:
build["inputs"].sort()
- ninja.build(**build)
+ ninja_sorted_build(
+ ninja,
+ **build
+ )
scons_daemon_dirty = str(pathlib.Path(get_path(self.env.get("NINJA_DIR"))) / "scons_daemon_dirty")
for template_builder in template_builders:
template_builder["implicit"] += [scons_daemon_dirty]
- ninja.build(**template_builder)
+ ninja_sorted_build(
+ ninja,
+ **template_builder
+ )
# We have to glob the SCons files here to teach the ninja file
# how to regenerate itself. We'll never see ourselves in the
@@ -556,17 +597,19 @@ class NinjaState:
ninja_file_path = self.env.File(self.ninja_file).path
regenerate_deps = to_escaped_list(self.env, self.env['NINJA_REGENERATE_DEPS'])
- ninja.build(
- ninja_file_path,
+ ninja_sorted_build(
+ ninja,
+ outputs=ninja_file_path,
rule="REGENERATE",
implicit=regenerate_deps,
variables={
- "self": ninja_file_path,
+ "self": ninja_file_path
}
)
- ninja.build(
- regenerate_deps,
+ ninja_sorted_build(
+ ninja,
+ outputs=regenerate_deps,
rule="phony",
variables={
"self": ninja_file_path,
@@ -577,8 +620,9 @@ class NinjaState:
# If we ever change the name/s of the rules that include
# compile commands (i.e. something like CC) we will need to
# update this build to reflect that complete list.
- ninja.build(
- "compile_commands.json",
+ ninja_sorted_build(
+ ninja,
+ outputs="compile_commands.json",
rule="CMD",
pool="console",
implicit=[str(self.ninja_file)],
@@ -594,52 +638,71 @@ class NinjaState:
},
)
- ninja.build(
- "compiledb", rule="phony", implicit=["compile_commands.json"],
+ ninja_sorted_build(
+ ninja,
+ outputs="compiledb", rule="phony", implicit=["compile_commands.json"],
)
- ninja.build(
- ["run_ninja_scons_daemon_phony", scons_daemon_dirty],
+ ninja_sorted_build(
+ ninja,
+ outputs=["run_ninja_scons_daemon_phony", scons_daemon_dirty],
rule="SCONS_DAEMON",
)
+ ninja.build(
+ "shutdown_ninja_scons_daemon_phony",
+ rule="EXIT_SCONS_DAEMON",
+ )
- daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(get_path(self.env["NINJA_DIR"])).encode()).hexdigest()))
- pidfile = None
- if os.path.exists(scons_daemon_dirty):
- pidfile = scons_daemon_dirty
- elif os.path.exists(daemon_dir / 'pidfile'):
- pidfile = daemon_dir / 'pidfile'
- if pidfile:
- with open(pidfile) as f:
- pid = int(f.readline())
- try:
- os.kill(pid, signal.SIGINT)
- except OSError:
- pass
+ if all_targets is None:
+ # Look in SCons's list of DEFAULT_TARGETS, find the ones that
+ # we generated a ninja build rule for.
+ all_targets = [str(node) for node in NINJA_DEFAULT_TARGETS]
+ else:
+ all_targets = list(all_targets)
+
+ if len(all_targets) == 0:
+ all_targets = ["phony_default"]
+ ninja_sorted_build(
+ ninja,
+ outputs=all_targets,
+ rule="phony",
+ )
+
+ ninja.default([self.ninja_syntax.escape_path(path) for path in sorted(all_targets)])
- if os.path.exists(scons_daemon_dirty):
- os.unlink(scons_daemon_dirty)
+ with NamedTemporaryFile(delete=False, mode='w') as temp_ninja_file:
+ temp_ninja_file.write(content.getvalue())
+ if self.env.GetOption('skip_ninja_regen') and os.path.exists(ninja_file_path) and filecmp.cmp(temp_ninja_file.name, ninja_file_path):
+ os.unlink(temp_ninja_file.name)
+ else:
- # Look in SCons's list of DEFAULT_TARGETS, find the ones that
- # we generated a ninja build rule for.
- scons_default_targets = [
- get_path(tgt)
- for tgt in SCons.Script.DEFAULT_TARGETS
- if get_path(tgt) in self.built
- ]
+ daemon_dir = pathlib.Path(tempfile.gettempdir()) / ('scons_daemon_' + str(hashlib.md5(str(get_path(self.env["NINJA_DIR"])).encode()).hexdigest()))
+ pidfile = None
+ if os.path.exists(scons_daemon_dirty):
+ pidfile = scons_daemon_dirty
+ elif os.path.exists(daemon_dir / 'pidfile'):
+ pidfile = daemon_dir / 'pidfile'
- # If we found an overlap between SCons's list of default
- # targets and the targets we created ninja builds for then use
- # those as ninja's default as well.
- if scons_default_targets:
- ninja.default(" ".join(scons_default_targets))
+ if pidfile:
+ with open(pidfile) as f:
+ pid = int(f.readline())
+ try:
+ os.kill(pid, signal.SIGINT)
+ except OSError:
+ pass
- with NamedTemporaryFile(delete=False, mode='w') as temp_ninja_file:
- temp_ninja_file.write(content.getvalue())
- shutil.move(temp_ninja_file.name, ninja_file_path)
+ # wait for the server process to fully killed
+ # TODO: update wait_for_process_to_die() to handle timeout and then catch exception
+ # here and do something smart.
+ wait_for_process_to_die(pid)
+
+ if os.path.exists(scons_daemon_dirty):
+ os.unlink(scons_daemon_dirty)
+
+ shutil.move(temp_ninja_file.name, ninja_file_path)
self.__generated = True
@@ -809,6 +872,15 @@ class SConsToNinjaTranslator:
# Remove all preceding and proceeding whitespace
cmdline = cmdline.strip()
+ env = node.env if node.env else self.env
+ executor = node.get_executor()
+ if executor is not None:
+ targets = executor.get_all_targets()
+ else:
+ if hasattr(node, "target_peers"):
+ targets = node.target_peers
+ else:
+ targets = [node]
# Make sure we didn't generate an empty cmdline
if cmdline:
@@ -817,7 +889,7 @@ class SConsToNinjaTranslator:
"rule": get_rule(node, "GENERATED_CMD"),
"variables": {
"cmd": cmdline,
- "env": get_command_env(node.env if node.env else self.env),
+ "env": get_command_env(env, targets, node.sources),
},
"implicit": dependencies,
}
diff --git a/SCons/Tool/ninja/Overrides.py b/SCons/Tool/ninja/Overrides.py
index eb8dbb5..e98ec40 100644
--- a/SCons/Tool/ninja/Overrides.py
+++ b/SCons/Tool/ninja/Overrides.py
@@ -84,7 +84,7 @@ def ninja_always_serial(self, num, taskmaster):
# builds. So here we lie so the Main.py will not give a false
# warning to users.
self.num_jobs = num
- self.job = SCons.Job.Serial(taskmaster)
+ self.job = SCons.Taskmaster.Job.Serial(taskmaster)
# pylint: disable=too-few-public-methods
diff --git a/SCons/Tool/ninja/Utils.py b/SCons/Tool/ninja/Utils.py
index 888218d..7269fb2 100644
--- a/SCons/Tool/ninja/Utils.py
+++ b/SCons/Tool/ninja/Utils.py
@@ -23,12 +23,16 @@
import os
import shutil
from os.path import join as joinpath
+from collections import OrderedDict
import SCons
from SCons.Action import get_default_ENV, _string_from_cmd_list
from SCons.Script import AddOption
from SCons.Util import is_List, flatten_sequence
+class NinjaExperimentalWarning(SCons.Warnings.WarningOnByDefault):
+ pass
+
def ninja_add_command_line_options():
"""
@@ -49,6 +53,15 @@ def ninja_add_command_line_options():
help='Disable ninja generation and build with scons even if tool is loaded. '+
'Also used by ninja to build targets which only scons can build.')
+ AddOption('--skip-ninja-regen',
+ dest='skip_ninja_regen',
+ metavar='BOOL',
+ action="store_true",
+ default=False,
+ help='Allow scons to skip regeneration of the ninja file and restarting of the daemon. ' +
+ 'Care should be taken in cases where Glob is in use or SCons generated files are used in ' +
+ 'command lines.')
+
def is_valid_dependent_node(node):
"""
@@ -123,7 +136,7 @@ def get_dependencies(node, skip_sources=False):
get_path(src_file(child))
for child in filter_ninja_nodes(node.children())
if child not in node.sources
- ]
+ ]
return [get_path(src_file(child)) for child in filter_ninja_nodes(node.children())]
@@ -258,8 +271,22 @@ def ninja_noop(*_args, **_kwargs):
"""
return None
+def ninja_recursive_sorted_dict(build):
+ sorted_dict = OrderedDict()
+ for key, val in sorted(build.items()):
+ if isinstance(val, dict):
+ sorted_dict[key] = ninja_recursive_sorted_dict(val)
+ else:
+ sorted_dict[key] = val
+ return sorted_dict
-def get_command_env(env):
+
+def ninja_sorted_build(ninja, **build):
+ sorted_dict = ninja_recursive_sorted_dict(build)
+ ninja.build(**sorted_dict)
+
+
+def get_command_env(env, target, source):
"""
Return a string that sets the environment for any environment variables that
differ between the OS environment and the SCons command ENV.
@@ -275,7 +302,7 @@ def get_command_env(env):
# os.environ or differ from it. We assume if it's a new or
# differing key from the process environment then it's
# important to pass down to commands in the Ninja file.
- ENV = get_default_ENV(env)
+ ENV = SCons.Action._resolve_shell_env(env, target, source)
scons_specified_env = {
key: value
for key, value in ENV.items()
@@ -285,21 +312,8 @@ def get_command_env(env):
windows = env["PLATFORM"] == "win32"
command_env = ""
+ scons_specified_env = SCons.Util.sanitize_shell_env(scons_specified_env)
for key, value in scons_specified_env.items():
- # Ensure that the ENV values are all strings:
- if is_List(value):
- # If the value is a list, then we assume it is a
- # path list, because that's a pretty common list-like
- # value to stick in an environment variable:
- value = flatten_sequence(value)
- value = joinpath(map(str, value))
- else:
- # If it isn't a string or a list, then we just coerce
- # it to a string, which is the proper way to handle
- # Dir and File instances and will produce something
- # reasonable for just about everything else:
- value = str(value)
-
if windows:
command_env += "set '{}={}' && ".format(key, value)
else:
diff --git a/SCons/Tool/ninja/__init__.py b/SCons/Tool/ninja/__init__.py
index 526db8a..2622641 100644
--- a/SCons/Tool/ninja/__init__.py
+++ b/SCons/Tool/ninja/__init__.py
@@ -26,21 +26,23 @@
import importlib
import os
-import random
+import traceback
import subprocess
import sys
import SCons
+import SCons.Script
import SCons.Tool.ninja.Globals
from SCons.Script import GetOption
+from SCons.Util import sanitize_shell_env
-from .Globals import NINJA_RULES, NINJA_POOLS, NINJA_CUSTOM_HANDLERS
+from .Globals import NINJA_RULES, NINJA_POOLS, NINJA_CUSTOM_HANDLERS, NINJA_DEFAULT_TARGETS, NINJA_CMDLINE_TARGETS
from .Methods import register_custom_handler, register_custom_rule_mapping, register_custom_rule, register_custom_pool, \
set_build_node_callback, get_generic_shell_command, CheckNinjaCompdbExpand, get_command, \
gen_get_response_file_command
from .Overrides import ninja_hack_linkcom, ninja_hack_arcom, NinjaNoResponseFiles, ninja_always_serial, AlwaysExecAction
from .Utils import ninja_add_command_line_options, \
- ninja_noop, ninja_print_conf_log, ninja_csig, ninja_contents, ninja_stat, ninja_whereis
+ ninja_noop, ninja_print_conf_log, ninja_csig, ninja_contents, ninja_stat, ninja_whereis, NinjaExperimentalWarning
try:
import ninja
@@ -65,7 +67,12 @@ def ninja_builder(env, target, source):
print("Generating:", str(target[0]))
generated_build_ninja = target[0].get_abspath()
- NINJA_STATE.generate()
+ try:
+ NINJA_STATE.generate()
+ except Exception:
+ raise SCons.Errors.BuildError(
+ errstr=f"ERROR: an exception occurred while generating the ninja file:\n{traceback.format_exc()}",
+ node=target)
if env["PLATFORM"] == "win32":
# TODO: Is this necessary as you set env variable in the ninja build file per target?
@@ -84,18 +91,26 @@ def ninja_builder(env, target, source):
else:
cmd = [NINJA_STATE.ninja_bin_path, '-f', generated_build_ninja]
- if not env.get("NINJA_DISABLE_AUTO_RUN"):
+ if str(env.get("NINJA_DISABLE_AUTO_RUN")).lower() not in ['1', 'true']:
+ num_jobs = env.get('NINJA_MAX_JOBS', env.GetOption("num_jobs"))
+ cmd += ['-j' + str(num_jobs)] + env.get('NINJA_CMD_ARGS', '').split() + NINJA_CMDLINE_TARGETS
+ print(f"ninja will be run with command line targets: {' '.join(NINJA_CMDLINE_TARGETS)}")
print("Executing:", str(' '.join(cmd)))
# execute the ninja build at the end of SCons, trying to
# reproduce the output like a ninja build would
def execute_ninja():
+ if env['PLATFORM'] == 'win32':
+ spawn_env = os.environ
+ else:
+ spawn_env = sanitize_shell_env(env['ENV'])
+
proc = subprocess.Popen(cmd,
stderr=sys.stderr,
stdout=subprocess.PIPE,
universal_newlines=True,
- env=os.environ if env["PLATFORM"] == "win32" else env['ENV']
+ env=spawn_env
)
for stdout_line in iter(proc.stdout.readline, ""):
yield stdout_line
@@ -118,7 +133,16 @@ def ninja_builder(env, target, source):
# leaving warnings and other output, seems a bit
# prone to failure with such a simple check
erase_previous = output.startswith('[')
+ sys.stdout.write("\n")
+
+def options(opts):
+ """
+ Add command line Variables for Ninja builder.
+ """
+ opts.AddVariables(
+ ("NINJA_CMD_ARGS", "Arguments to pass to ninja"),
+ )
def exists(env):
"""Enable if called."""
@@ -164,7 +188,7 @@ def ninja_emitter(target, source, env):
def generate(env):
"""Generate the NINJA builders."""
- global NINJA_STATE
+ global NINJA_STATE, NINJA_CMDLINE_TARGETS
if 'ninja' not in GetOption('experimental'):
return
@@ -189,10 +213,9 @@ def generate(env):
env["NINJA_ALIAS_NAME"] = env.get("NINJA_ALIAS_NAME", "generate-ninja")
env['NINJA_DIR'] = env.Dir(env.get("NINJA_DIR", '#/.ninja'))
env["NINJA_SCONS_DAEMON_KEEP_ALIVE"] = env.get("NINJA_SCONS_DAEMON_KEEP_ALIVE", 180000)
- env["NINJA_SCONS_DAEMON_PORT"] = env.get('NINJA_SCONS_DAEMON_PORT', random.randint(10000, 60000))
if GetOption("disable_ninja"):
- env.SConsignFile(os.path.join(str(env['NINJA_DIR']),'.ninja.sconsign'))
+ env.SConsignFile(os.path.join(str(env['NINJA_DIR']), '.ninja.sconsign'))
# here we allow multiple environments to construct rules and builds
# into the same ninja file
@@ -200,7 +223,14 @@ def generate(env):
ninja_file = env.Ninja()
env['NINJA_FILE'] = ninja_file[0]
env.AlwaysBuild(ninja_file)
- env.Alias("$NINJA_ALIAS_NAME", ninja_file)
+
+ # We need to force SCons to only build the ninja target when ninja tool is loaded.
+ # The ninja tool is going to 'rip the guts out' of scons and make it basically unable
+ # to do anything in terms of building, so any targets besides the ninja target will
+ # end up doing nothing besides causing confusion. We save the targets however, so that
+ # SCons and invoke ninja to build them in lieu of the user.
+ NINJA_CMDLINE_TARGETS = SCons.Script.BUILD_TARGETS
+ SCons.Script.BUILD_TARGETS = SCons.Script.TargetList(env.Alias("$NINJA_ALIAS_NAME", ninja_file))
else:
if str(NINJA_STATE.ninja_file) != env["NINJA_FILE_NAME"]:
SCons.Warnings.SConsWarning("Generating multiple ninja files not supported, set ninja file name before tool initialization.")
@@ -305,8 +335,8 @@ def generate(env):
if GetOption('disable_ninja'):
return env
- SCons.Warnings.SConsWarning("Initializing ninja tool... this feature is experimental. SCons internals and all environments will be affected.")
-
+ print("Initializing ninja tool... this feature is experimental. SCons internals and all environments will be affected.")
+ print(f"SCons running in ninja mode. {env['NINJA_FILE']} will be generated.")
# This is the point of no return, anything after this comment
# makes changes to SCons that are irreversible and incompatible
# with a normal SCons build. We return early if __NINJA_NO=1 has
@@ -392,7 +422,7 @@ def generate(env):
# The Serial job class is SIGNIFICANTLY (almost twice as) faster
# than the Parallel job class for generating Ninja files. So we
# monkey the Jobs constructor to only use the Serial Job class.
- SCons.Job.Jobs.__init__ = ninja_always_serial
+ SCons.Taskmaster.Job.Jobs.__init__ = ninja_always_serial
ninja_syntax = importlib.import_module(".ninja_syntax", package='ninja')
@@ -445,6 +475,22 @@ def generate(env):
# date-ness.
SCons.Script.Main.BuildTask.needs_execute = lambda x: True
+ def ninja_Set_Default_Targets(env, tlist):
+ """
+ Record the default targets if they were ever set by the user. Ninja
+ will need to write the default targets and make sure not to include
+ the scons daemon shutdown target.
+ """
+ SCons.Script._Get_Default_Targets = SCons.Script._Set_Default_Targets_Has_Been_Called
+ SCons.Script.DEFAULT_TARGETS = ninja_file
+ for t in tlist:
+ if isinstance(t, SCons.Node.Node):
+ NINJA_DEFAULT_TARGETS.append(t)
+ else:
+ nodes = env.arg2nodes(t, env.fs.Entry)
+ NINJA_DEFAULT_TARGETS.extend(nodes)
+ SCons.Script._Set_Default_Targets = ninja_Set_Default_Targets
+
# We will eventually need to overwrite TempFileMunge to make it
# handle persistent tempfiles or get an upstreamed change to add
# some configurability to it's behavior in regards to tempfiles.
@@ -464,5 +510,5 @@ def generate(env):
env['TEMPFILEDIR'] = "$NINJA_DIR/.response_files"
env["TEMPFILE"] = NinjaNoResponseFiles
-
env.Alias('run-ninja-scons-daemon', 'run_ninja_scons_daemon_phony')
+ env.Alias('shutdown-ninja-scons-daemon', 'shutdown_ninja_scons_daemon_phony')
diff --git a/SCons/Tool/ninja/ninja.xml b/SCons/Tool/ninja/ninja.xml
index 6b247d0..664d521 100644
--- a/SCons/Tool/ninja/ninja.xml
+++ b/SCons/Tool/ninja/ninja.xml
@@ -77,7 +77,7 @@ See its __doc__ string for a discussion of the format.
<item>IMPLICIT_COMMAND_DEPENDENCIES</item>
<item>NINJA_SCONS_DAEMON_KEEP_ALIVE</item>
<item>NINJA_SCONS_DAEMON_PORT</item>
-
+ <item>NINJA_CMD_ARGS</item>
<!-- TODO: Document these -->
<!-- <item>NINJA_RULES</item>-->
@@ -395,5 +395,22 @@ python -m pip install ninja
</summary>
</cvar>
+ <cvar name="NINJA_CMD_ARGS">
+ <summary>
+ <para>
+ A string which will pass arguments through SCons to the ninja command when scons executes ninja.
+ Has no effect if &cv-NINJA_DISABLE_AUTO_RUN; is set.
+ </para>
+ <para>
+ This value can also be passed on the command line:
+ </para>
+ <example_commands>
+scons NINJA_CMD_ARGS=-v
+or
+scons NINJA_CMD_ARGS="-v -j 3"
+ </example_commands>
+ </summary>
+ </cvar>
+
</sconsdoc>
diff --git a/SCons/Tool/ninja/ninja_daemon_build.py b/SCons/Tool/ninja/ninja_daemon_build.py
index 80aa741..32c375d 100644
--- a/SCons/Tool/ninja/ninja_daemon_build.py
+++ b/SCons/Tool/ninja/ninja_daemon_build.py
@@ -39,6 +39,7 @@ import pathlib
import tempfile
import hashlib
import traceback
+import socket
ninja_builddir = pathlib.Path(sys.argv[2])
daemon_dir = pathlib.Path(tempfile.gettempdir()) / (
@@ -53,24 +54,37 @@ logging.basicConfig(
level=logging.DEBUG,
)
+
def log_error(msg):
logging.debug(msg)
sys.stderr.write(msg)
+
while True:
try:
+ if not os.path.exists(daemon_dir / "pidfile"):
+ if sys.argv[3] != '--exit':
+ logging.debug(f"ERROR: Server pid not found {daemon_dir / 'pidfile'} for request {sys.argv[3]}")
+ exit(1)
+ else:
+ logging.debug("WARNING: Unnecessary request to shutdown server, it's already shutdown.")
+ exit(0)
+
logging.debug(f"Sending request: {sys.argv[3]}")
conn = http.client.HTTPConnection(
"127.0.0.1", port=int(sys.argv[1]), timeout=60
)
- conn.request("GET", "/?build=" + sys.argv[3])
+ if sys.argv[3] == '--exit':
+ conn.request("GET", "/?exit=1")
+ else:
+ conn.request("GET", "/?build=" + sys.argv[3])
response = None
while not response:
try:
response = conn.getresponse()
- except (http.client.RemoteDisconnected, http.client.ResponseNotReady):
- time.sleep(0.01)
+ except (http.client.RemoteDisconnected, http.client.ResponseNotReady, socket.timeout):
+ time.sleep(0.1)
except http.client.HTTPException:
log_error(f"Error: {traceback.format_exc()}")
exit(1)
@@ -80,9 +94,14 @@ while True:
if status != 200:
log_error(msg.decode("utf-8"))
exit(1)
+
logging.debug(f"Request Done: {sys.argv[3]}")
exit(0)
+ except ConnectionRefusedError:
+ logging.debug(f"Server refused connection to build {sys.argv[3]}, maybe it was too busy, tring again: {traceback.format_exc()}")
+ time.sleep(0.1)
+
except Exception:
log_error(f"Failed to send command: {traceback.format_exc()}")
exit(1)
diff --git a/SCons/Tool/ninja/ninja_run_daemon.py b/SCons/Tool/ninja/ninja_run_daemon.py
index 297bcf4..08029a2 100644
--- a/SCons/Tool/ninja/ninja_run_daemon.py
+++ b/SCons/Tool/ninja/ninja_run_daemon.py
@@ -46,6 +46,7 @@ import logging
import time
import http.client
import traceback
+import socket
ninja_builddir = pathlib.Path(sys.argv[2])
daemon_dir = pathlib.Path(tempfile.gettempdir()) / (
@@ -108,7 +109,7 @@ if not os.path.exists(ninja_builddir / "scons_daemon_dirty"):
try:
response = conn.getresponse()
- except (http.client.RemoteDisconnected, http.client.ResponseNotReady):
+ except (http.client.RemoteDisconnected, http.client.ResponseNotReady, socket.timeout):
time.sleep(0.01)
except http.client.HTTPException:
log_error(f"Error: {traceback.format_exc()}")
diff --git a/SCons/Tool/ninja/ninja_scons_daemon.py b/SCons/Tool/ninja/ninja_scons_daemon.py
index f900343..6802af2 100644
--- a/SCons/Tool/ninja/ninja_scons_daemon.py
+++ b/SCons/Tool/ninja/ninja_scons_daemon.py
@@ -50,6 +50,7 @@ from timeit import default_timer as timer
import traceback
import tempfile
import hashlib
+import signal
port = int(sys.argv[1])
ninja_builddir = pathlib.Path(sys.argv[2])
@@ -127,19 +128,30 @@ input_q = queue.Queue()
output_q = queue.Queue()
error_q = queue.Queue()
-finished_building = []
-error_nodes = []
-
building_cv = Condition()
error_cv = Condition()
-thread_error = False
+class StateInfo:
+ def __init__(self) -> None:
+ self.thread_error = False
+ self.finished_building = []
+ self.error_nodes = []
+ self.startup_failed = False
+ self.startup_output = ''
+ self.daemon_needs_to_shutdown = False
+ self.httpd = None
+
+shared_state = StateInfo()
+
+def sigint_func(signum, frame):
+ global shared_state
+ shared_state.daemon_needs_to_shutdown = True
+
+signal.signal(signal.SIGINT, sigint_func)
def daemon_thread_func():
- global thread_error
- global finished_building
- global error_nodes
+ global shared_state
try:
args_list = args + ["--interactive"]
daemon_log(f"Starting daemon with args: {' '.join(args_list)}")
@@ -156,17 +168,24 @@ def daemon_thread_func():
te.start()
daemon_ready = False
+
building_node = None
+ startup_complete = False
+ # While scons interactive process is stil running...
while p.poll() is None:
+ # while there is scons output to process
while True:
try:
line = output_q.get(block=False, timeout=0.01)
except queue.Empty:
+ # breaks out of the output processing loop
break
else:
daemon_log("output: " + line.strip())
+ if not startup_complete:
+ shared_state.startup_output += line
if "scons: building terminated because of errors." in line:
error_output = ""
@@ -175,13 +194,16 @@ def daemon_thread_func():
error_output += error_q.get(block=False, timeout=0.01)
except queue.Empty:
break
- error_nodes += [{"node": building_node, "error": error_output}]
+ shared_state.error_nodes += [{"node": building_node, "error": error_output}]
daemon_ready = True
building_node = None
with building_cv:
building_cv.notify()
elif line == "scons>>>":
+ shared_state.startup_output = ''
+ startup_complete = True
+
with error_q.mutex:
error_q.queue.clear()
daemon_ready = True
@@ -189,6 +211,7 @@ def daemon_thread_func():
building_cv.notify()
building_node = None
+ # while there is input to process...
while daemon_ready and not input_q.empty():
try:
@@ -196,12 +219,14 @@ def daemon_thread_func():
except queue.Empty:
break
if "exit" in building_node:
+ daemon_log("input: " + "exit")
p.stdin.write("exit\n".encode("utf-8"))
p.stdin.flush()
with building_cv:
- finished_building += [building_node]
+ shared_state.finished_building += [building_node]
daemon_ready = False
- raise
+ shared_state.daemon_needs_to_shutdown = True
+ break
else:
input_command = "build " + building_node + "\n"
@@ -210,12 +235,21 @@ def daemon_thread_func():
p.stdin.write(input_command.encode("utf-8"))
p.stdin.flush()
with building_cv:
- finished_building += [building_node]
+ shared_state.finished_building += [building_node]
daemon_ready = False
+ if shared_state.daemon_needs_to_shutdown:
+ break
time.sleep(0.01)
+
+ # our scons process is done, make sure we are shutting down in this case
+ if not shared_state.daemon_needs_to_shutdown:
+ if not startup_complete:
+ shared_state.startup_failed = True
+ shared_state.daemon_needs_to_shutdown = True
+
except Exception:
- thread_error = True
+ shared_state.thread_error = True
daemon_log("SERVER ERROR: " + traceback.format_exc())
raise
@@ -229,18 +263,20 @@ logging.debug(
)
keep_alive_timer = timer()
-httpd = None
-
def server_thread_func():
+ global shared_state
class S(http.server.BaseHTTPRequestHandler):
def do_GET(self):
- global thread_error
+ global shared_state
global keep_alive_timer
- global error_nodes
-
try:
gets = parse_qs(urlparse(self.path).query)
+
+ # process a request from ninja for a node for scons to build.
+ # Currently this is a serial process because scons interactive is serial
+ # is it was originally meant for a real human user to be providing input
+ # parallel input was never implemented.
build = gets.get("build")
if build:
keep_alive_timer = timer()
@@ -249,12 +285,12 @@ def server_thread_func():
input_q.put(build[0])
def pred():
- return build[0] in finished_building
+ return build[0] in shared_state.finished_building
with building_cv:
building_cv.wait_for(pred)
- for error_node in error_nodes:
+ for error_node in shared_state.error_nodes:
if error_node["node"] == build[0]:
self.send_response(500)
self.send_header("Content-type", "text/html")
@@ -267,6 +303,19 @@ def server_thread_func():
self.end_headers()
return
+ # this message is used in server startup, to make sure the server launched
+ # successfully. If SCons interactive got to a input prompt (scons>>>), then
+ # the server is ready to start processing commands. Otherwise the server will
+ # send an error response back to ninja and shut itself down.
+ ready = gets.get("ready")
+ if ready:
+ if shared_state.startup_failed:
+ self.send_response(500)
+ self.send_header("Content-type", "text/html")
+ self.end_headers()
+ self.wfile.write(shared_state.startup_output.encode())
+ return
+
exitbuild = gets.get("exit")
if exitbuild:
input_q.put("exit")
@@ -276,48 +325,41 @@ def server_thread_func():
self.end_headers()
except Exception:
- thread_error = True
+ shared_state.thread_error = True
daemon_log("SERVER ERROR: " + traceback.format_exc())
raise
def log_message(self, format, *args):
return
- httpd = socketserver.TCPServer(("127.0.0.1", port), S)
- httpd.serve_forever()
+ socketserver.TCPServer.allow_reuse_address = True
+ shared_state.httpd = socketserver.TCPServer(("127.0.0.1", port), S)
+ shared_state.httpd.serve_forever()
server_thread = threading.Thread(target=server_thread_func)
server_thread.daemon = True
server_thread.start()
-while timer() - keep_alive_timer < daemon_keep_alive and not thread_error:
+while (timer() - keep_alive_timer < daemon_keep_alive
+ and not shared_state.thread_error
+ and not shared_state.daemon_needs_to_shutdown):
time.sleep(1)
-if thread_error:
+if shared_state.thread_error:
daemon_log(f"Shutting server on port {port} down because thread error.")
+elif shared_state.daemon_needs_to_shutdown:
+ daemon_log("Server shutting down upon request.")
else:
daemon_log(
f"Shutting server on port {port} down because timed out: {daemon_keep_alive}"
)
-
-# if there are errors, don't immediately shut down the daemon
-# the process which started the server is attempt to connect to
-# the daemon before allowing jobs to start being sent. If the daemon
-# shuts down too fast, the launch script will think it has not
-# started yet and sit and wait. If the launch script is able to connect
-# and then the connection is dropped, it will immediately exit with fail.
-time.sleep(5)
-
+shared_state.httpd.shutdown()
if os.path.exists(ninja_builddir / "scons_daemon_dirty"):
os.unlink(ninja_builddir / "scons_daemon_dirty")
if os.path.exists(daemon_dir / "pidfile"):
os.unlink(daemon_dir / "pidfile")
-httpd.shutdown()
-server_thread.join()
-
-
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/SCons/Tool/packaging/__init__.py b/SCons/Tool/packaging/__init__.py
index 68cedeb..6fe01c1 100644
--- a/SCons/Tool/packaging/__init__.py
+++ b/SCons/Tool/packaging/__init__.py
@@ -217,10 +217,11 @@ def generate(env):
env['BUILDERS']['Package'] = Package
env['BUILDERS']['Tag'] = Tag
+
def exists(env):
return 1
-# XXX
+
def options(opts):
opts.AddVariables(
EnumVariable('PACKAGETYPE',
diff --git a/SCons/Tool/rmic.xml b/SCons/Tool/rmic.xml
index cc5755d..28be1f6 100644
--- a/SCons/Tool/rmic.xml
+++ b/SCons/Tool/rmic.xml
@@ -1,6 +1,8 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -69,15 +71,17 @@ file names.
</para>
<example_commands>
-classes = env.Java(target = 'classdir', source = 'src')
-env.RMIC(target = 'outdir1', source = classes)
-
-env.RMIC(target = 'outdir2',
- source = ['package/foo.class', 'package/bar.class'])
-
-env.RMIC(target = 'outdir3',
- source = ['classes/foo.class', 'classes/bar.class'],
- JAVACLASSDIR = 'classes')
+classes = env.Java(target='classdir', source='src')
+env.RMIC(target='outdir1', source=classes)
+env.RMIC(
+ target='outdir2',
+ source=['package/foo.class', 'package/bar.class'],
+)
+env.RMIC(
+ target='outdir3',
+ source=['classes/foo.class', 'classes/bar.class'],
+ JAVACLASSDIR='classes',
+)
</example_commands>
</summary>
</builder>
@@ -112,7 +116,9 @@ If this is not set, then &cv-link-RMICCOM; (the command line) is displayed.
</para>
<example_commands>
-env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES")
+env = Environment(
+ RMICCOMSTR="Generating stub/skeleton class files $TARGETS from $SOURCES"
+)
</example_commands>
</summary>
</cvar>
diff --git a/SCons/Tool/tex.py b/SCons/Tool/tex.py
index 6d0a5fb..0a688f5 100644
--- a/SCons/Tool/tex.py
+++ b/SCons/Tool/tex.py
@@ -253,10 +253,10 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
# .aux files already processed by BibTex
already_bibtexed = []
- #
- # routine to update MD5 hash and compare
- #
- def check_MD5(filenode, suffix):
+ def check_content_hash(filenode, suffix):
+ """
+ Routine to update content hash and compare
+ """
global must_rerun_latex
# two calls to clear old csig
filenode.clear_memoized_values()
@@ -295,7 +295,6 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
with open(logfilename, "rb") as f:
logContent = f.read().decode(errors='replace')
-
# Read the fls file to find all .aux files
flsfilename = targetbase + '.fls'
flsContent = ''
@@ -345,7 +344,7 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
result = BibTeXAction(bibfile, bibfile, env)
if result != 0:
check_file_error_message(env['BIBTEX'], 'blg')
- must_rerun_latex = True
+ check_content_hash(suffix_nodes[".bbl"], ".bbl")
# Now decide if biber will need to be run.
# When the backend for biblatex is biber (by choice or default) the
@@ -369,10 +368,10 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
result = BiberAction(bibfile, bibfile, env)
if result != 0:
check_file_error_message(env['BIBER'], 'blg')
- must_rerun_latex = True
+ check_content_hash(suffix_nodes[".bbl"], ".bbl")
# Now decide if latex will need to be run again due to index.
- if check_MD5(suffix_nodes['.idx'],'.idx') or (count == 1 and run_makeindex):
+ if check_content_hash(suffix_nodes['.idx'], '.idx') or (count == 1 and run_makeindex):
# We must run makeindex
if Verbose:
print("Need to run makeindex")
@@ -387,10 +386,10 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
# Harder is case is where an action needs to be called -- that should be rare (I hope?)
for index in check_suffixes:
- check_MD5(suffix_nodes[index],index)
+ check_content_hash(suffix_nodes[index], index)
# Now decide if latex will need to be run again due to nomenclature.
- if check_MD5(suffix_nodes['.nlo'],'.nlo') or (count == 1 and run_nomenclature):
+ if check_content_hash(suffix_nodes['.nlo'], '.nlo') or (count == 1 and run_nomenclature):
# We must run makeindex
if Verbose:
print("Need to run makeindex for nomenclature")
@@ -402,7 +401,7 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
#return result
# Now decide if latex will need to be run again due to glossary.
- if check_MD5(suffix_nodes['.glo'],'.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary):
+ if check_content_hash(suffix_nodes['.glo'], '.glo') or (count == 1 and run_glossaries) or (count == 1 and run_glossary):
# We must run makeindex
if Verbose:
print("Need to run makeindex for glossary")
@@ -414,7 +413,7 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
#return result
# Now decide if latex will need to be run again due to acronyms.
- if check_MD5(suffix_nodes['.acn'],'.acn') or (count == 1 and run_acronyms):
+ if check_content_hash(suffix_nodes['.acn'], '.acn') or (count == 1 and run_acronyms):
# We must run makeindex
if Verbose:
print("Need to run makeindex for acronyms")
@@ -427,7 +426,7 @@ def InternalLaTeXAuxAction(XXXLaTeXAction, target = None, source= None, env=None
# Now decide if latex will need to be run again due to newglossary command.
for ng in newglossary_suffix:
- if check_MD5(suffix_nodes[ng[2]], ng[2]) or (count == 1):
+ if check_content_hash(suffix_nodes[ng[2]], ng[2]) or (count == 1):
# We must run makeindex
if Verbose:
print("Need to run makeindex for newglossary")
diff --git a/SCons/Tool/yacc.py b/SCons/Tool/yacc.py
index 61ad61a..42ef1db 100644
--- a/SCons/Tool/yacc.py
+++ b/SCons/Tool/yacc.py
@@ -23,6 +23,9 @@
"""Tool-specific initialization for yacc.
+This tool should support multiple yacc implementations,
+but is in actuality biased towards GNU Bison.
+
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.
@@ -30,14 +33,17 @@ selection method.
import os.path
import sys
+from typing import Optional
import SCons.Defaults
import SCons.Tool
-import SCons.Util
import SCons.Warnings
from SCons.Platform.mingw import MINGW_DEFAULT_PATHS
from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS
from SCons.Platform.win32 import CHOCO_DEFAULT_PATH
+from SCons.Util import CLVar, to_String
+
+DEFAULT_PATHS = CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS
YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR")
@@ -47,10 +53,12 @@ else:
BINS = ["bison", "yacc"]
-def _yaccEmitter(target, source, env, ysuf, hsuf):
- yaccflags = env.subst("$YACCFLAGS", target=target, source=source)
- flags = SCons.Util.CLVar(yaccflags)
- targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0]))
+def _yaccEmitter(target, source, env, ysuf, hsuf) -> tuple:
+ """Adds extra files generated by yacc program to target list."""
+
+ yaccflags = env.subst_list("$YACCFLAGS", target=target, source=source)
+ flags = yaccflags[0]
+ targetBase, targetExt = os.path.splitext(to_String(target[0]))
if '.ym' in ysuf: # If using Objective-C
target = [targetBase + ".m"] # the extension is ".m".
@@ -63,71 +71,88 @@ def _yaccEmitter(target, source, env, ysuf, hsuf):
# If -g is specified on the command line, yacc will emit a .vcg
# file with the same base name as the .y, .yacc, .ym or .yy file.
if "-g" in flags:
- base, ext = os.path.splitext(SCons.Util.to_String(source[0]))
+ base, ext = os.path.splitext(to_String(source[0]))
target.append(base + env.subst("$YACCVCGFILESUFFIX"))
# 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
+ # be noted and also be cleaned (issue #2558)
if "-v" in flags:
env.SideEffect(targetBase + '.output', target[0])
env.Clean(target[0], targetBase + '.output')
- # With --defines and --graph, the name of the file is totally defined
- # in the options.
- fileGenOptions = ["--defines=", "--graph="]
+ # With --defines and --graph, the file to write is defined by the option
+ # argument. Extract this and include in the list of targets.
+ # NOTE: a filename passed to the command this way is not modified by SCons,
+ # and so will be interpreted relative to the project top directory at
+ # execution time, while the name added to the target list will be
+ # interpreted relative to the SConscript directory - a possible mismatch.
+ #
+ # These are GNU bison-only options.
+ # Since bison 3.8, --header is the preferred name over --defines
+ fileGenOptions = ["--defines=", "--header=", "--graph="]
for option in flags:
for fileGenOption in fileGenOptions:
l = len(fileGenOption)
if option[:l] == fileGenOption:
- # A file generating option is present, so add the file
- # name to the list of targets.
fileName = option[l:].strip()
target.append(fileName)
- return (target, source)
+ yaccheaderfile = env.subst("$YACC_HEADER_FILE", target=target, source=source)
+ if yaccheaderfile:
+ target.append(yaccheaderfile)
+ # rewrite user-supplied file string with a node, we need later
+ env.Replace(YACC_HEADER_FILE=env.File(yaccheaderfile))
+
+ yaccgraphfile = env.subst("$YACC_GRAPH_FILE", target=target, source=source)
+ if yaccgraphfile:
+ target.append(yaccgraphfile)
+ # rewrite user-supplied file string with a node, we need later
+ env.Replace(YACC_GRAPH_FILE=env.File(yaccgraphfile))
+ return target, source
-def yEmitter(target, source, env):
+
+def yEmitter(target, source, env) -> tuple:
return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX')
-def ymEmitter(target, source, env):
+def ymEmitter(target, source, env) -> tuple:
return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX')
-def yyEmitter(target, source, env):
+def yyEmitter(target, source, env) -> tuple:
return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX')
-def get_yacc_path(env, append_paths=False):
+def get_yacc_path(env, append_paths=False) -> Optional[str]:
"""
- Find the path to the yacc tool, searching several possible names
+ Returns the path to the yacc tool, searching several possible names.
- Only called in the Windows case, so the default_path
- can be Windows-specific
+ Only called in the Windows case, so the `default_path` argument to
+ :func:`find_program_path` can be Windows-specific.
- :param env: current construction environment
- :param append_paths: if set, add the path to the tool to PATH
- :return: path to yacc tool, if found
+ Args:
+ env: current construction environment
+ append_paths: if true, add the path to the tool to PATH
"""
for prog in BINS:
bin_path = SCons.Tool.find_program_path(
env,
prog,
- default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS)
+ default_paths=DEFAULT_PATHS,
+ add_path=append_paths,
+ )
if bin_path:
- if append_paths:
- env.AppendENVPath('PATH', os.path.dirname(bin_path))
return bin_path
+
SCons.Warnings.warn(
SCons.Warnings.SConsWarning,
'yacc tool requested, but yacc or bison binary not found in ENV PATH'
)
-def generate(env):
+def generate(env) -> None:
"""Add Builders and construction variables for yacc to an Environment."""
c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
@@ -150,17 +175,22 @@ def generate(env):
# ignore the return, all we need is for the path to be added
_ = get_yacc_path(env, append_paths=True)
- if 'YACC' not in env:
- env["YACC"] = env.Detect(BINS)
+ env.SetDefault(
+ YACC=env.Detect(BINS),
+ YACCFLAGS=CLVar(""),
+ YACC_HEADER_FILE="",
+ YACC_GRAPH_FILE="",
+ )
- env['YACCFLAGS'] = SCons.Util.CLVar('')
- env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES'
+ env['YACCCOM'] = '$YACC $YACCFLAGS $_YACC_HEADER $_YACC_GRAPH -o $TARGET $SOURCES'
env['YACCHFILESUFFIX'] = '.h'
env['YACCHXXFILESUFFIX'] = '.hpp'
env['YACCVCGFILESUFFIX'] = '.vcg'
+ env['_YACC_HEADER'] = '${YACC_HEADER_FILE and "--header=" + str(YACC_HEADER_FILE)}'
+ env['_YACC_GRAPH'] = '${YACC_GRAPH_FILE and "--graph=" + str(YACC_GRAPH_FILE)}'
-def exists(env):
+def exists(env) -> Optional[str]:
if 'YACC' in env:
return env.Detect(env['YACC'])
diff --git a/SCons/Tool/yacc.xml b/SCons/Tool/yacc.xml
index c8e1bb4..9ccc4e6 100644
--- a/SCons/Tool/yacc.xml
+++ b/SCons/Tool/yacc.xml
@@ -1,6 +1,28 @@
<?xml version="1.0"?>
<!--
-__COPYRIGHT__
+ MIT License
+
+ Copyright The SCons Foundation
+
+ 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.
+
This file is processed by the bin/SConsDoc.py module.
See its __doc__ string for a discussion of the format.
@@ -39,6 +61,9 @@ Sets construction variables for the &yacc; parse generator.
</sets>
<uses>
<item>YACCCOMSTR</item>
+<item>YACCFLAGS</item>
+<item>YACC_HEADER_FILE</item>
+<item>YACC_GRAPH_FILE</item>
</uses>
</tool>
@@ -68,7 +93,7 @@ If this is not set, then &cv-link-YACCCOM; (the command line) is displayed.
</para>
<example_commands>
-env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES")
+env = Environment(YACCCOMSTR="Yacc'ing $TARGET from $SOURCES")
</example_commands>
</summary>
</cvar>
@@ -77,11 +102,74 @@ env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES")
<summary>
<para>
General options passed to the parser generator.
-If &cv-link-YACCFLAGS; contains a <option>-d</option> option,
-SCons assumes that the call will also create a .h file
-(if the yacc source file ends in a .y suffix)
-or a .hpp file
-(if the yacc source file ends in a .yy suffix)
+In addition to passing the value on during invocation,
+the &t-link-yacc; tool also examines this &consvar; for options
+which cause additional output files to be generated,
+and adds those to the target list.
+</para>
+
+<para>
+If a <option>-d</option> option is present,
+&scons; assumes that the call will also create a header file
+with the suffix defined by &cv-link-YACCHFILESUFFIX;
+if the yacc source file ends in a <filename>.y</filename> suffix,
+or a file with the suffix defined by &cv-link-YACCHXXFILESUFFIX;
+if the yacc source file ends in a <filename>.yy</filename> suffix.
+</para>
+
+<para>
+If a <option>-g</option> option is present,
+&scons; assumes that the call will also create a graph file
+with the suffix defined by &cv-link-YACCVCGFILESUFFIX;.
+</para>
+
+<para>
+If a <option>-v</option> option is present,
+&scons; assumes that the call will also create an output debug file
+with the suffix <filename>.output</filename>.
+</para>
+
+<para>
+Also recognized are GNU &bison; options
+<option>--header=</option> and its deprecated synonym
+<option>--defines=</option>,
+which is similar to
+<option>-d</option>
+but the output filename is named by the option argument;
+and <option>--graph=</option>,
+which is similar to
+<option>-g</option>
+but the output filename is named by the option argument.
+</para>
+
+<para>
+Note that files specified by <option>--header=</option> and
+<option>--graph=</option> may not be properly handled
+by &SCons; in all situations. Consider using
+&cv-link-YACC_HEADER_FILE; and &cv-link-YACC_GRAPH_FILE; instead.
+</para>
+</summary>
+</cvar>
+
+<cvar name="YACC_HEADER_FILE">
+<summary>
+<para>
+If supplied, generate a header file with the name taken from this variable.
+Will be emitted as a <option>--header=</option>
+command-line option. Use this in preference to including
+<option>--header=</option> in &cv-link-YACCFLAGS; directly.
+</para>
+</summary>
+</cvar>
+
+<cvar name="YACC_GRAPH_FILE">
+<summary>
+<para>
+If supplied, write a graph of the automaton to a file with the name
+taken from this variable.
+Will be emitted as a <option>--graph=</option>
+command-line option. Use this in preference to including
+<option>--graph=</option> in &cv-link-YACCFLAGS; directly.
</para>
</summary>
</cvar>
diff --git a/SCons/Util.py b/SCons/Util/__init__.py
index 093ca41..0281e1e 100644
--- a/SCons/Util.py
+++ b/SCons/Util/__init__.py
@@ -21,26 +21,84 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-"""Various SCons utility functions."""
+"""SCons utility functions
+
+This package contains routines for use by other parts of SCons.
+"""
import copy
import hashlib
import os
-import pprint
import re
import sys
-from collections import UserDict, UserList, UserString, OrderedDict
-from collections.abc import MappingView
+import time
+from collections import UserDict, UserList, OrderedDict
from contextlib import suppress
from types import MethodType, FunctionType
from typing import Optional, Union
-
-# Note: Util module cannot import other bits of SCons globally without getting
-# into import loops. Both the below modules import SCons.Util early on.
+from logging import Formatter
+
+from .types import (
+ DictTypes,
+ ListTypes,
+ SequenceTypes,
+ StringTypes,
+ BaseStringTypes,
+ Null,
+ NullSeq,
+ is_Dict,
+ is_List,
+ is_Sequence,
+ is_Tuple,
+ is_String,
+ is_Scalar,
+ to_String,
+ to_String_for_subst,
+ to_String_for_signature,
+ to_bytes,
+ to_str,
+ get_env_bool,
+ get_os_env_bool,
+ get_environment_var,
+)
+from .hashes import (
+ ALLOWED_HASH_FORMATS,
+ DEFAULT_HASH_FORMATS,
+ get_hash_format,
+ set_hash_format,
+ get_current_hash_algorithm_used,
+ hash_signature,
+ hash_file_signature,
+ hash_collect,
+ MD5signature,
+ MD5filesignature,
+ MD5collect,
+)
+from .envs import (
+ MethodWrapper,
+ PrependPath,
+ AppendPath,
+ AddPathIfNotExists,
+ AddMethod,
+)
+
+
+# Note: the Util package cannot import other parts of SCons globally without
+# hitting import loops. Both of these modules import SCons.Util early on,
+# and are imported in many other modules:
# --> SCons.Warnings
# --> SCons.Errors
-# Thus the local imports, which are annotated for pylint to show we mean it.
-
+# If you run into places that have to do local imports for this reason,
+# annotate them for pylint and for human readers to know why:
+# pylint: disable=import-outside-toplevel
+# Be aware that Black can break this if the annotated line is too
+# long and it wants to split:
+# from SCons.Errors import (
+# SConsEnvironmentError,
+# ) # pylint: disable=import-outside-toplevel
+# That's syntactically valid, but pylint won't recorgnize it with the
+# annotation at the end, it would have to be on the first line
+# (issues filed upstream, for now just be aware)
PYPY = hasattr(sys, 'pypy_translation_info')
@@ -120,8 +178,6 @@ class NodeList(UserList):
for example getting the content signature of each node. The term
"attribute" here includes the string representation.
- Example:
-
>>> someList = NodeList([' foo ', ' bar '])
>>> someList.strip()
['foo', 'bar']
@@ -153,27 +209,6 @@ class NodeList(UserList):
return self.data[index]
-_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
-
-def get_environment_var(varstr) -> Optional[str]:
- """Return undecorated construction variable string.
-
- Determine if `varstr` looks like a reference
- to a single environment variable, like `"$FOO"` or `"${FOO}"`.
- If so, return that variable with no decorations, like `"FOO"`.
- If not, return `None`.
- """
-
- mo = _get_env_var.match(to_String(varstr))
- if mo:
- var = mo.group(1)
- if var[0] == '{':
- return var[1:-1]
- return var
-
- return None
-
-
class DisplayEngine:
"""A callable class used to display SCons messages."""
@@ -186,24 +221,24 @@ class DisplayEngine:
if append_newline:
text = text + '\n'
- try:
+ # Stdout might be connected to a pipe that has been closed
+ # by now. The most likely reason for the pipe being closed
+ # is that the user has press ctrl-c. It this is the case,
+ # then SCons is currently shutdown. We therefore ignore
+ # IOError's here so that SCons can continue and shutdown
+ # properly so that the .sconsign is correctly written
+ # before SCons exits.
+ with suppress(IOError):
sys.stdout.write(str(text))
- except IOError:
- # Stdout might be connected to a pipe that has been closed
- # by now. The most likely reason for the pipe being closed
- # is that the user has press ctrl-c. It this is the case,
- # then SCons is currently shutdown. We therefore ignore
- # IOError's here so that SCons can continue and shutdown
- # properly so that the .sconsign is correctly written
- # before SCons exits.
- pass
def set_mode(self, mode):
self.print_it = mode
+display = DisplayEngine()
+
# TODO: W0102: Dangerous default value [] as argument (dangerous-default-value)
-def render_tree(root, child_func, prune=0, margin=[0], visited=None):
+def render_tree(root, child_func, prune=0, margin=[0], visited=None) -> str:
"""Render a tree of nodes into an ASCII tree view.
Args:
@@ -271,9 +306,9 @@ def print_tree(
showtags=False,
margin=[0],
visited=None,
- lastChild=False,
- singleLineDraw=False,
-):
+ lastChild: bool = False,
+ singleLineDraw: bool = False,
+) -> None:
"""Print a tree of nodes.
This is like func:`render_tree`, except it prints lines directly instead
@@ -285,10 +320,11 @@ def print_tree(
child_func: the function called to get the children of a node
prune: don't visit the same node twice
showtags: print status information to the left of each node line
- margin: the format of the left margin to use for children of `root`.
+ margin: the format of the left margin to use for children of *root*.
1 results in a pipe, and 0 results in no pipe.
visited: a dictionary of visited nodes in the current branch if
- prune` is 0, or in the whole tree if `prune` is 1.
+ *prune* is 0, or in the whole tree if *prune* is 1.
+ lastChild: this is the last leaf of a branch
singleLineDraw: use line-drawing characters rather than ASCII.
"""
@@ -347,7 +383,7 @@ def print_tree(
cross = BOX_VERT_RIGHT + BOX_HORIZ # sign used to point to the leaf.
# check if this is the last leaf of the branch
if lastChild:
- #if this if the last leaf, then terminate:
+ # if this if the last leaf, then terminate:
cross = BOX_UP_RIGHT + BOX_HORIZ # sign for the last leaf
# if this branch has children then split it
@@ -359,7 +395,7 @@ def print_tree(
cross += BOX_HORIZ_DOWN
if prune and rname in visited and children:
- sys.stdout.write(''.join(tags + margins + [cross,'[', rname, ']']) + '\n')
+ sys.stdout.write(''.join(tags + margins + [cross, '[', rname, ']']) + '\n')
return
sys.stdout.write(''.join(tags + margins + [cross, rname]) + '\n')
@@ -390,81 +426,6 @@ def print_tree(
margin.pop() # destroy the last margin added
-# Functions for deciding if things are like various types, mainly to
-# handle UserDict, UserList and UserString like their underlying types.
-#
-# Yes, all of this manual testing breaks polymorphism, and the real
-# Pythonic way to do all of this would be to just try it and handle the
-# exception, but handling the exception when it's not the right type is
-# often too slow.
-
-# We are using the following trick to speed up these
-# 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).
-# Since checkers dislike this, it's now annotated for pylint to flag
-# (mostly for other readers of this code) we're doing this intentionally.
-# TODO: PY3 check these are still valid choices for all of these funcs.
-
-DictTypes = (dict, UserDict)
-ListTypes = (list, UserList)
-
-# Handle getting dictionary views.
-SequenceTypes = (list, tuple, UserList, MappingView)
-
-# Note that profiling data shows a speed-up when comparing
-# explicitly with str instead of simply comparing
-# with basestring. (at least on Python 2.5.1)
-# TODO: PY3 check this benchmarking is still correct.
-StringTypes = (str, UserString)
-
-# Empirically, it is faster to check explicitly for str than for basestring.
-BaseStringTypes = str
-
-def is_Dict( # pylint: disable=redefined-outer-name,redefined-builtin
- obj, isinstance=isinstance, DictTypes=DictTypes
-) -> bool:
- return isinstance(obj, DictTypes)
-
-
-def is_List( # pylint: disable=redefined-outer-name,redefined-builtin
- obj, isinstance=isinstance, ListTypes=ListTypes
-) -> bool:
- return isinstance(obj, ListTypes)
-
-
-def is_Sequence( # pylint: disable=redefined-outer-name,redefined-builtin
- obj, isinstance=isinstance, SequenceTypes=SequenceTypes
-) -> bool:
- return isinstance(obj, SequenceTypes)
-
-
-def is_Tuple( # pylint: disable=redefined-builtin
- obj, isinstance=isinstance, tuple=tuple
-) -> bool:
- return isinstance(obj, tuple)
-
-
-def is_String( # pylint: disable=redefined-outer-name,redefined-builtin
- obj, isinstance=isinstance, StringTypes=StringTypes
-) -> bool:
- return isinstance(obj, StringTypes)
-
-
-def is_Scalar( # pylint: disable=redefined-outer-name,redefined-builtin
- obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes
-) -> bool:
-
- # Profiling shows that there is an impressive speed-up of 2x
- # 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 most of the time.
- return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
-
-
def do_flatten(
sequence,
result,
@@ -524,74 +485,6 @@ def flatten_sequence( # pylint: disable=redefined-outer-name,redefined-builtin
do_flatten(item, result)
return result
-# Generic convert-to-string functions. The wrapper
-# to_String_for_signature() will use a for_signature() method if the
-# specified object has one.
-
-def to_String( # pylint: disable=redefined-outer-name,redefined-builtin
- obj,
- isinstance=isinstance,
- str=str,
- UserString=UserString,
- BaseStringTypes=BaseStringTypes,
-) -> str:
- """Return a string version of obj."""
-
- if isinstance(obj, BaseStringTypes):
- # Early out when already a string!
- return obj
-
- if isinstance(obj, UserString):
- # obj.data can only be a regular string. Please see the UserString initializer.
- return obj.data
-
- return str(obj)
-
-def to_String_for_subst( # pylint: disable=redefined-outer-name,redefined-builtin
- obj,
- isinstance=isinstance,
- str=str,
- BaseStringTypes=BaseStringTypes,
- SequenceTypes=SequenceTypes,
- UserString=UserString,
-) -> str:
- """Return a string version of obj for subst usage."""
-
- # Note that the test cases are sorted by order of probability.
- if isinstance(obj, BaseStringTypes):
- return obj
-
- if isinstance(obj, SequenceTypes):
- return ' '.join([to_String_for_subst(e) for e in obj])
-
- if isinstance(obj, UserString):
- # obj.data can only a regular string. Please see the UserString initializer.
- return obj.data
-
- return str(obj)
-
-def to_String_for_signature( # pylint: disable=redefined-outer-name,redefined-builtin
- obj, to_String_for_subst=to_String_for_subst, AttributeError=AttributeError
-) -> str:
- """Return a string version of obj for signature usage.
-
- Like :func:`to_String_for_subst` but has special handling for
- scons objects that have a :meth:`for_signature` method, and for dicts.
- """
-
- try:
- f = obj.for_signature
- except AttributeError:
- if isinstance(obj, dict):
- # pprint will output dictionary in key sorted order
- # with py3.5 the order was randomized. In general depending on dictionary order
- # which was undefined until py3.6 (where it's by insertion order) was not wise.
- # TODO: Change code when floor is raised to PY36
- return pprint.pformat(obj, width=1000000)
- return to_String_for_subst(obj)
- else:
- return f()
-
# The SCons "semi-deep" copy.
#
@@ -709,40 +602,6 @@ class Delegate:
return self
-class MethodWrapper:
- """A generic Wrapper class that associates a method with an object.
-
- As part of creating this MethodWrapper object an attribute with the
- specified name (by default, the name of the supplied method) is added
- to the underlying object. When that new "method" is called, our
- :meth:`__call__` method adds the object as the first argument, simulating
- the Python behavior of supplying "self" on method calls.
-
- We hang on to the name by which the method was added to the underlying
- base class so that we can provide a method to "clone" ourselves onto
- a new underlying object being copied (without which we wouldn't need
- to save that info).
- """
- def __init__(self, obj, method, name=None):
- if name is None:
- name = method.__name__
- self.object = obj
- self.method = method
- self.name = name
- setattr(self.object, name, self)
-
- def __call__(self, *args, **kwargs):
- nargs = (self.object,) + args
- return self.method(*nargs, **kwargs)
-
- def clone(self, new_object):
- """
- Returns an object that re-binds the underlying "method" to
- the specified new object.
- """
- return self.__class__(new_object, self.method, self.name)
-
-
# attempt to load the windows registry module:
can_read_reg = False
try:
@@ -927,206 +786,6 @@ is used. Will not select any path name or names in the optional
`reject` list.
"""
-def PrependPath(
- oldpath, newpath, sep=os.pathsep, delete_existing=True, canonicalize=None
-) -> Union[list, str]:
- """Prepends `newpath` path elements to `oldpath`.
-
- Will only add any particular path once (leaving the first one it
- encounters and ignoring the rest, to preserve path order), and will
- :mod:`os.path.normpath` and :mod:`os.path.normcase` all paths to help
- assure this. This can also handle the case where `oldpath`
- is a list instead of a string, in which case a list will be returned
- instead of a string. For example:
-
- >>> p = PrependPath("/foo/bar:/foo", "/biz/boom:/foo")
- >>> print(p)
- /biz/boom:/foo:/foo/bar
-
- If `delete_existing` is ``False``, then adding a path that exists will
- not move it to the beginning; it will stay where it is in the list.
-
- >>> p = PrependPath("/foo/bar:/foo", "/biz/boom:/foo", delete_existing=False)
- >>> print(p)
- /biz/boom:/foo/bar:/foo
-
- If `canonicalize` is not ``None``, it is applied to each element of
- `newpath` before use.
- """
-
- orig = oldpath
- is_list = True
- paths = orig
- if not is_List(orig) and not is_Tuple(orig):
- paths = paths.split(sep)
- is_list = False
-
- if is_String(newpath):
- newpaths = newpath.split(sep)
- elif not is_List(newpath) and not is_Tuple(newpath):
- newpaths = [ newpath ] # might be a Dir
- else:
- newpaths = newpath
-
- if canonicalize:
- newpaths=list(map(canonicalize, newpaths))
-
- if not delete_existing:
- # First uniquify the old paths, making sure to
- # preserve the first instance (in Unix/Linux,
- # the first one wins), and remembering them in normpaths.
- # Then insert the new paths at the head of the list
- # if they're not already in the normpaths list.
- result = []
- normpaths = []
- for path in paths:
- if not path:
- continue
- normpath = os.path.normpath(os.path.normcase(path))
- if normpath not in normpaths:
- result.append(path)
- normpaths.append(normpath)
- newpaths.reverse() # since we're inserting at the head
- for path in newpaths:
- if not path:
- continue
- normpath = os.path.normpath(os.path.normcase(path))
- if normpath not in normpaths:
- result.insert(0, path)
- normpaths.append(normpath)
- paths = result
-
- else:
- newpaths = newpaths + paths # prepend new paths
-
- normpaths = []
- paths = []
- # now we add them only if they are unique
- for path in newpaths:
- normpath = os.path.normpath(os.path.normcase(path))
- if path and normpath not in normpaths:
- paths.append(path)
- normpaths.append(normpath)
-
- if is_list:
- return paths
-
- return sep.join(paths)
-
-def AppendPath(
- oldpath, newpath, sep=os.pathsep, delete_existing=True, canonicalize=None
-) -> Union[list, str]:
- """Appends `newpath` path elements to `oldpath`.
-
- Will only add any particular path once (leaving the last one it
- encounters and ignoring the rest, to preserve path order), and will
- :mod:`os.path.normpath` and :mod:`os.path.normcase` all paths to help
- assure this. This can also handle the case where `oldpath`
- is a list instead of a string, in which case a list will be returned
- instead of a string. For example:
-
- >>> p = AppendPath("/foo/bar:/foo", "/biz/boom:/foo")
- >>> print(p)
- /foo/bar:/biz/boom:/foo
-
- If `delete_existing` is ``False``, then adding a path that exists
- will not move it to the end; it will stay where it is in the list.
-
- >>> p = AppendPath("/foo/bar:/foo", "/biz/boom:/foo", delete_existing=False)
- >>> print(p)
- /foo/bar:/foo:/biz/boom
-
- If `canonicalize` is not ``None``, it is applied to each element of
- `newpath` before use.
- """
-
- orig = oldpath
- is_list = True
- paths = orig
- if not is_List(orig) and not is_Tuple(orig):
- paths = paths.split(sep)
- is_list = False
-
- if is_String(newpath):
- newpaths = newpath.split(sep)
- elif not is_List(newpath) and not is_Tuple(newpath):
- newpaths = [newpath] # might be a Dir
- else:
- newpaths = newpath
-
- if canonicalize:
- newpaths=list(map(canonicalize, newpaths))
-
- if not delete_existing:
- # add old paths to result, then
- # add new paths if not already present
- # (I thought about using a dict for normpaths for speed,
- # but it's not clear hashing the strings would be faster
- # than linear searching these typically short lists.)
- result = []
- normpaths = []
- for path in paths:
- if not path:
- continue
- result.append(path)
- normpaths.append(os.path.normpath(os.path.normcase(path)))
- for path in newpaths:
- if not path:
- continue
- normpath = os.path.normpath(os.path.normcase(path))
- if normpath not in normpaths:
- result.append(path)
- normpaths.append(normpath)
- paths = result
- else:
- # start w/ new paths, add old ones if not present,
- # then reverse.
- newpaths = paths + newpaths # append new paths
- newpaths.reverse()
-
- normpaths = []
- paths = []
- # now we add them only if they are unique
- for path in newpaths:
- normpath = os.path.normpath(os.path.normcase(path))
- if path and normpath not in normpaths:
- paths.append(path)
- normpaths.append(normpath)
- paths.reverse()
-
- if is_list:
- return paths
-
- return sep.join(paths)
-
-def AddPathIfNotExists(env_dict, key, path, sep=os.pathsep):
- """Add a path element to a construction variable.
-
- `key` is looked up in `env_dict`, and `path` is added to it if it
- is not already present. `env_dict[key]` is assumed to be in the
- format of a PATH variable: a list of paths separated by `sep` tokens.
- Example:
-
- >>> env = {'PATH': '/bin:/usr/bin:/usr/local/bin'}
- >>> AddPathIfNotExists(env, 'PATH', '/opt/bin')
- >>> print(env['PATH'])
- /opt/bin:/bin:/usr/bin:/usr/local/bin
- """
-
- try:
- is_list = True
- paths = env_dict[key]
- if not is_List(env_dict[key]):
- paths = paths.split(sep)
- is_list = False
- 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':
import subprocess # pylint: disable=import-outside-toplevel
@@ -1147,8 +806,6 @@ system object. For other platforms, `path` is unchanged.
"""
-display = DisplayEngine()
-
def Split(arg) -> list:
"""Returns a list of file names or other objects.
@@ -1235,7 +892,7 @@ class Selector(OrderedDict):
# Try to perform Environment substitution on the keys of
# the dictionary before giving up.
s_dict = {}
- for (k,v) in self.items():
+ for (k, v) in self.items():
if k is not None:
s_k = env.subst(k)
if s_k in s_dict:
@@ -1244,7 +901,7 @@ class Selector(OrderedDict):
# and a variable suffix contains this literal,
# the literal wins and we don't raise an error.
raise KeyError(s_dict[s_k][0], k, s_k)
- s_dict[s_k] = (k,v)
+ s_dict[s_k] = (k, v)
try:
return s_dict[ext][1]
except KeyError:
@@ -1290,7 +947,6 @@ def adjustixes(fname, pre, suf, ensure_suffix=False) -> str:
return fname
-
# From Tim Peters,
# https://code.activestate.com/recipes/52560
# ASPN: Python Cookbook: Remove duplicates from a sequence
@@ -1362,44 +1018,11 @@ def unique(seq):
u.append(x)
return u
-
-# From Alex Martelli,
-# https://code.activestate.com/recipes/52560
-# ASPN: Python Cookbook: Remove duplicates from a sequence
-# First comment, dated 2001/10/13.
-# (Also in the printed Python Cookbook.)
-# This not currently used, in favor of the next function...
-
-def uniquer(seq, idfun=None):
- def default_idfun(x):
- return x
- if not idfun:
- idfun = default_idfun
- seen = {}
- result = []
- result_append = result.append # perf: avoid repeated method lookups
- for item in seq:
- marker = idfun(item)
- if marker in seen:
- continue
- seen[marker] = 1
- result_append(item)
- return result
-
-# A more efficient implementation of Alex's uniquer(), this avoids the
-# idfun() argument and function-call overhead by assuming that all
-# items in the sequence are hashable. Order-preserving.
-
+# Best way (assuming Python 3.7, but effectively 3.6) to remove
+# duplicates from a list in while preserving order, according to
+# https://stackoverflow.com/questions/480214/how-do-i-remove-duplicates-from-a-list-while-preserving-order/17016257#17016257
def uniquer_hashables(seq):
- seen = {}
- result = []
- result_append = result.append # perf: avoid repeated method lookups
- for item in seq:
- if item not in seen:
- seen[item] = 1
- result_append(item)
- return result
-
+ return list(dict.fromkeys(seq))
# Recipe 19.11 "Reading Lines with Continuation Characters",
# by Alex Martelli, straight from the Python CookBook (2nd edition).
@@ -1437,7 +1060,7 @@ class UniqueList(UserList):
"""A list which maintains uniqueness.
Uniquing is lazy: rather than being assured on list changes, it is fixed
- up on access by those methods which need to act on a uniqe list to be
+ up on access by those methods which need to act on a unique list to be
correct. That means things like "in" don't have to eat the uniquing time.
"""
def __init__(self, initlist=None):
@@ -1588,418 +1211,14 @@ def make_path_relative(path) -> str:
drive_s, path = os.path.splitdrive(path)
if not drive_s:
- path=re.compile(r"/*(.*)").findall(path)[0]
+ path = re.compile(r"/*(.*)").findall(path)[0]
else:
- path=path[1:]
+ path = path[1:]
assert not os.path.isabs(path), path
return path
-# The original idea for AddMethod() came from the
-# following post to the ActiveState Python Cookbook:
-#
-# ASPN: Python Cookbook : Install bound methods in an instance
-# https://code.activestate.com/recipes/223613
-#
-# Changed as follows:
-# * Switched the installmethod() "object" and "function" arguments,
-# so the order reflects that the left-hand side is the thing being
-# "assigned to" and the right-hand side is the value being assigned.
-# * The instance/class detection is changed a bit, as it's all
-# new-style classes now with Py3.
-# * The by-hand construction of the function object from renamefunction()
-# is not needed, the remaining bit is now used inline in AddMethod.
-
-def AddMethod(obj, function, name=None):
- """Adds a method to an object.
-
- Adds `function` to `obj` if `obj` is a class object.
- Adds `function` as a bound method if `obj` is an instance object.
- If `obj` looks like an environment instance, use `MethodWrapper`
- to add it. If `name` is supplied it is used as the name of `function`.
-
- Although this works for any class object, the intent as a public
- API is to be used on Environment, to be able to add a method to all
- construction environments; it is preferred to use env.AddMethod
- to add to an individual environment.
-
- >>> class A:
- ... ...
-
- >>> a = A()
-
- >>> def f(self, x, y):
- ... self.z = x + y
-
- >>> AddMethod(A, f, "add")
- >>> a.add(2, 4)
- >>> print(a.z)
- 6
- >>> a.data = ['a', 'b', 'c', 'd', 'e', 'f']
- >>> AddMethod(a, lambda self, i: self.data[i], "listIndex")
- >>> print(a.listIndex(3))
- d
-
- """
- if name is None:
- name = function.__name__
- else:
- # "rename"
- function = FunctionType(
- function.__code__, function.__globals__, name, function.__defaults__
- )
-
- if hasattr(obj, '__class__') and obj.__class__ is not type:
- # obj is an instance, so it gets a bound method.
- if hasattr(obj, "added_methods"):
- method = MethodWrapper(obj, function, name)
- obj.added_methods.append(method)
- else:
- method = MethodType(function, obj)
- else:
- # obj is a class
- method = function
-
- setattr(obj, name, method)
-
-
-# Default hash function and format. SCons-internal.
-DEFAULT_HASH_FORMATS = ['md5', 'sha1', 'sha256']
-ALLOWED_HASH_FORMATS = []
-_HASH_FUNCTION = None
-_HASH_FORMAT = None
-
-def _attempt_init_of_python_3_9_hash_object(hash_function_object, sys_used=sys):
- """Python 3.9 and onwards lets us initialize the hash function object with the
- key "usedforsecurity"=false. This lets us continue to use algorithms that have
- been deprecated either by FIPS or by Python itself, as the MD5 algorithm SCons
- prefers is not being used for security purposes as much as a short, 32 char
- hash that is resistant to accidental collisions.
-
- In prior versions of python, hashlib returns a native function wrapper, which
- errors out when it's queried for the optional parameter, so this function
- wraps that call.
-
- It can still throw a ValueError if the initialization fails due to FIPS
- compliance issues, but that is assumed to be the responsibility of the caller.
- """
- if hash_function_object is None:
- return None
-
- # https://stackoverflow.com/a/11887885 details how to check versions with the "packaging" library.
- # however, for our purposes checking the version is greater than or equal to 3.9 is good enough, as
- # the API is guaranteed to have support for the 'usedforsecurity' flag in 3.9. See
- # https://docs.python.org/3/library/hashlib.html#:~:text=usedforsecurity for the version support notes.
- if (sys_used.version_info.major > 3) or (sys_used.version_info.major == 3 and sys_used.version_info.minor >= 9):
- return hash_function_object(usedforsecurity=False)
-
- # note that this can throw a ValueError in FIPS-enabled versions of Linux prior to 3.9
- # the OpenSSL hashlib will throw on first init here, but that is assumed to be responsibility of
- # the caller to diagnose the ValueError & potentially display the error to screen.
- return hash_function_object()
-
-def _set_allowed_viable_default_hashes(hashlib_used, sys_used=sys):
- """Checks if SCons has ability to call the default algorithms normally supported.
-
- This util class is sometimes called prior to setting the user-selected hash algorithm,
- meaning that on FIPS-compliant systems the library would default-initialize MD5
- and throw an exception in set_hash_format. A common case is using the SConf options,
- which can run prior to main, and thus ignore the options.hash_format variable.
-
- This function checks the DEFAULT_HASH_FORMATS and sets the ALLOWED_HASH_FORMATS
- to only the ones that can be called. In Python >= 3.9 this will always default to
- MD5 as in Python 3.9 there is an optional attribute "usedforsecurity" set for the method.
-
- Throws if no allowed hash formats are detected.
- """
- global ALLOWED_HASH_FORMATS
- _last_error = None
- # note: if you call this method repeatedly, example using timeout, this is needed.
- # otherwise it keeps appending valid formats to the string
- ALLOWED_HASH_FORMATS = []
-
- for test_algorithm in DEFAULT_HASH_FORMATS:
- _test_hash = getattr(hashlib_used, test_algorithm, None)
- # we know hashlib claims to support it... check to see if we can call it.
- if _test_hash is not None:
- # the hashing library will throw an exception on initialization in FIPS mode,
- # meaning if we call the default algorithm returned with no parameters, it'll
- # throw if it's a bad algorithm, otherwise it will append it to the known
- # good formats.
- try:
- _attempt_init_of_python_3_9_hash_object(_test_hash, sys_used)
- ALLOWED_HASH_FORMATS.append(test_algorithm)
- except ValueError as e:
- _last_error = e
- continue
-
- if len(ALLOWED_HASH_FORMATS) == 0:
- from SCons.Errors import SConsEnvironmentError # pylint: disable=import-outside-toplevel
- # chain the exception thrown with the most recent error from hashlib.
- raise SConsEnvironmentError(
- 'No usable hash algorithms found.'
- 'Most recent error from hashlib attached in trace.'
- ) from _last_error
- return
-
-_set_allowed_viable_default_hashes(hashlib)
-
-
-def get_hash_format():
- """Retrieves the hash format or ``None`` if not overridden.
-
- A return value of ``None``
- does not guarantee that MD5 is being used; instead, it means that the
- default precedence order documented in :func:`SCons.Util.set_hash_format`
- is respected.
- """
- return _HASH_FORMAT
-
-def _attempt_get_hash_function(hash_name, hashlib_used=hashlib, sys_used=sys):
- """Wrapper used to try to initialize a hash function given.
-
- If successful, returns the name of the hash function back to the user.
-
- Otherwise returns None.
- """
- try:
- _fetch_hash = getattr(hashlib_used, hash_name, None)
- if _fetch_hash is None:
- return None
- _attempt_init_of_python_3_9_hash_object(_fetch_hash, sys_used)
- return hash_name
- except ValueError:
- # if attempt_init_of_python_3_9 throws, this is typically due to FIPS being enabled
- # however, if we get to this point, the viable hash function check has either been
- # bypassed or otherwise failed to properly restrict the user to only the supported
- # functions. As such throw the UserError as an internal assertion-like error.
- return None
-
-def set_hash_format(hash_format, hashlib_used=hashlib, sys_used=sys):
- """Sets the default hash format used by SCons.
-
- If `hash_format` is ``None`` or
- an empty string, the default is determined by this function.
-
- Currently the default behavior is to use the first available format of
- the following options: MD5, SHA1, SHA256.
- """
- global _HASH_FORMAT, _HASH_FUNCTION
-
- _HASH_FORMAT = hash_format
- if hash_format:
- hash_format_lower = hash_format.lower()
- if hash_format_lower not in ALLOWED_HASH_FORMATS:
- from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
-
- # user can select something not supported by their OS but normally supported by
- # SCons, example, selecting MD5 in an OS with FIPS-mode turned on. Therefore we first
- # check if SCons supports it, and then if their local OS supports it.
- if hash_format_lower in DEFAULT_HASH_FORMATS:
- raise UserError('While hash format "%s" is supported by SCons, the '
- 'local system indicates only the following hash '
- 'formats are supported by the hashlib library: %s' %
- (hash_format_lower,
- ', '.join(ALLOWED_HASH_FORMATS))
- )
- else:
- # the hash format isn't supported by SCons in any case. Warn the user, and
- # if we detect that SCons supports more algorithms than their local system
- # supports, warn the user about that too.
- if ALLOWED_HASH_FORMATS == DEFAULT_HASH_FORMATS:
- raise UserError('Hash format "%s" is not supported by SCons. Only '
- 'the following hash formats are supported: %s' %
- (hash_format_lower,
- ', '.join(ALLOWED_HASH_FORMATS))
- )
- else:
- raise UserError('Hash format "%s" is not supported by SCons. '
- 'SCons supports more hash formats than your local system '
- 'is reporting; SCons supports: %s. Your local system only '
- 'supports: %s' %
- (hash_format_lower,
- ', '.join(DEFAULT_HASH_FORMATS),
- ', '.join(ALLOWED_HASH_FORMATS))
- )
-
- # this is not expected to fail. If this fails it means the set_allowed_viable_default_hashes
- # function did not throw, or when it threw, the exception was caught and ignored, or
- # the global ALLOWED_HASH_FORMATS was changed by an external user.
- _HASH_FUNCTION = _attempt_get_hash_function(hash_format_lower, hashlib_used, sys_used)
-
- if _HASH_FUNCTION is None:
- from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
-
- raise UserError(
- 'Hash format "%s" is not available in your Python interpreter. '
- 'Expected to be supported algorithm by set_allowed_viable_default_hashes, '
- 'Assertion error in SCons.'
- % hash_format_lower
- )
- else:
- # Set the default hash format based on what is available, defaulting
- # to the first supported hash algorithm (usually md5) for backwards compatibility.
- # in FIPS-compliant systems this usually defaults to SHA1, unless that too has been
- # disabled.
- for choice in ALLOWED_HASH_FORMATS:
- _HASH_FUNCTION = _attempt_get_hash_function(choice, hashlib_used, sys_used)
-
- if _HASH_FUNCTION is not None:
- break
- else:
- # This is not expected to happen in practice.
- from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
-
- raise UserError(
- 'Your Python interpreter does not have MD5, SHA1, or SHA256. '
- 'SCons requires at least one. Expected to support one or more '
- 'during set_allowed_viable_default_hashes.'
- )
-
-# Ensure that this is initialized in case either:
-# 1. This code is running in a unit test.
-# 2. This code is running in a consumer that does hash operations while
-# SConscript files are being loaded.
-set_hash_format(None)
-
-
-def get_current_hash_algorithm_used():
- """Returns the current hash algorithm name used.
-
- Where the python version >= 3.9, this is expected to return md5.
- If python's version is <= 3.8, this returns md5 on non-FIPS-mode platforms, and
- sha1 or sha256 on FIPS-mode Linux platforms.
-
- This function is primarily useful for testing, where one expects a value to be
- one of N distinct hashes, and therefore the test needs to know which hash to select.
- """
- return _HASH_FUNCTION
-
-def _get_hash_object(hash_format, hashlib_used=hashlib, sys_used=sys):
- """Allocates a hash object using the requested hash format.
-
- Args:
- hash_format: Hash format to use.
-
- Returns:
- hashlib object.
- """
- if hash_format is None:
- if _HASH_FUNCTION is None:
- from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
-
- raise UserError('There is no default hash function. Did you call '
- 'a hashing function before SCons was initialized?')
- return _attempt_init_of_python_3_9_hash_object(getattr(hashlib_used, _HASH_FUNCTION, None), sys_used)
-
- if not hasattr(hashlib, hash_format):
- from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
-
- raise UserError(
- 'Hash format "%s" is not available in your Python interpreter.' %
- hash_format)
-
- return _attempt_init_of_python_3_9_hash_object(getattr(hashlib, hash_format), sys_used)
-
-
-def hash_signature(s, hash_format=None):
- """
- Generate hash signature of a string
-
- Args:
- s: either string or bytes. Normally should be bytes
- hash_format: Specify to override default hash format
-
- Returns:
- String of hex digits representing the signature
- """
- m = _get_hash_object(hash_format)
- try:
- m.update(to_bytes(s))
- except TypeError:
- m.update(to_bytes(str(s)))
-
- return m.hexdigest()
-
-
-def hash_file_signature(fname, chunksize=65536, hash_format=None):
- """
- Generate the md5 signature of a file
-
- Args:
- fname: file to hash
- chunksize: chunk size to read
- hash_format: Specify to override default hash format
-
- Returns:
- String of Hex digits representing the signature
- """
-
- m = _get_hash_object(hash_format)
- with open(fname, "rb") as f:
- while True:
- blck = f.read(chunksize)
- if not blck:
- break
- m.update(to_bytes(blck))
- return m.hexdigest()
-
-
-def hash_collect(signatures, hash_format=None):
- """
- Collects a list of signatures into an aggregate signature.
-
- Args:
- signatures: a list of signatures
- hash_format: Specify to override default hash format
-
- Returns:
- the aggregate signature
- """
-
- if len(signatures) == 1:
- return signatures[0]
-
- return hash_signature(', '.join(signatures), hash_format)
-
-
-_MD5_WARNING_SHOWN = False
-
-def _show_md5_warning(function_name):
- """Shows a deprecation warning for various MD5 functions."""
-
- global _MD5_WARNING_SHOWN
-
- if not _MD5_WARNING_SHOWN:
- import SCons.Warnings # pylint: disable=import-outside-toplevel
-
- SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
- "Function %s is deprecated" % function_name)
- _MD5_WARNING_SHOWN = True
-
-
-def MD5signature(s):
- """Deprecated. Use :func:`hash_signature` instead."""
-
- _show_md5_warning("MD5signature")
- return hash_signature(s)
-
-
-def MD5filesignature(fname, chunksize=65536):
- """Deprecated. Use :func:`hash_file_signature` instead."""
-
- _show_md5_warning("MD5filesignature")
- return hash_file_signature(fname, chunksize)
-
-
-def MD5collect(signatures):
- """Deprecated. Use :func:`hash_collect` instead."""
-
- _show_md5_warning("MD5collect")
- return hash_collect(signatures)
-
-
def silent_intern(x):
"""
Perform :mod:`sys.intern` on the passed argument and return the result.
@@ -2012,116 +1231,91 @@ def silent_intern(x):
return x
-# From Dinu C. Gherman,
-# Python Cookbook, second edition, recipe 6.17, p. 277.
-# Also: https://code.activestate.com/recipes/68205
-# ASPN: Python Cookbook: Null Object Design Pattern
-
-class Null:
- """ Null objects always and reliably "do nothing." """
- def __new__(cls, *args, **kwargs):
- if '_instance' not in vars(cls):
- cls._instance = super(Null, cls).__new__(cls, *args, **kwargs)
- return cls._instance
- def __init__(self, *args, **kwargs):
- pass
- def __call__(self, *args, **kwargs):
- return self
- def __repr__(self):
- return "Null(0x%08X)" % id(self)
- def __bool__(self):
- return False
- def __getattr__(self, name):
- return self
- def __setattr__(self, name, value):
- return self
- def __delattr__(self, name):
- return self
-
-
-class NullSeq(Null):
- """ A Null object that can also be iterated over. """
- def __len__(self):
- return 0
- def __iter__(self):
- return iter(())
- def __getitem__(self, i):
- return self
- def __delitem__(self, i):
- return self
- def __setitem__(self, i, v):
- return self
-
-
-def to_bytes(s) -> bytes:
- if s is None:
- return b'None'
- if isinstance(s, (bytes, bytearray)):
- # if already bytes return.
- return s
- return bytes(s, 'utf-8')
-
-
-def to_str(s) -> str:
- if s is None:
- return 'None'
- if is_String(s):
- return s
- return str(s, 'utf-8')
-
-
def cmp(a, b) -> bool:
"""A cmp function because one is no longer available in python3."""
return (a > b) - (a < b)
-def get_env_bool(env, name, default=False) -> bool:
- """Convert a construction variable to bool.
-
- If the value of `name` in `env` is 'true', 'yes', 'y', 'on' (case
- insensitive) or anything convertible to int that yields non-zero then
- return ``True``; if 'false', 'no', 'n', 'off' (case insensitive)
- or a number that converts to integer zero return ``False``.
- Otherwise, return `default`.
+def print_time():
+ """Hack to return a value from Main if can't import Main."""
+ # pylint: disable=redefined-outer-name,import-outside-toplevel
+ from SCons.Script.Main import print_time
+ return print_time
- Args:
- env: construction environment, or any dict-like object
- name: name of the variable
- default: value to return if `name` not in `env` or cannot
- be converted (default: False)
- Returns:
- the "truthiness" of `name`
+def wait_for_process_to_die(pid):
"""
+ Wait for specified process to die, or alternatively kill it
+ NOTE: This function operates best with psutil pypi package
+ TODO: Add timeout which raises exception
+ """
+ # wait for the process to fully killed
try:
- var = env[name]
- except KeyError:
- return default
- try:
- return bool(int(var))
- except ValueError:
- if str(var).lower() in ('true', 'yes', 'y', 'on'):
- return True
+ import psutil # pylint: disable=import-outside-toplevel
+ while True:
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ break
+ time.sleep(0.1)
+ except ImportError:
+ # if psutil is not installed we can do this the hard way
+ while True:
+ if sys.platform == 'win32':
+ import ctypes # pylint: disable=import-outside-toplevel
+ PROCESS_QUERY_INFORMATION = 0x1000
+ processHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid)
+ if processHandle == 0:
+ break
+ ctypes.windll.kernel32.CloseHandle(processHandle)
+ time.sleep(0.1)
+ else:
+ try:
+ os.kill(pid, 0)
+ except OSError:
+ break
+ time.sleep(0.1)
- if str(var).lower() in ('false', 'no', 'n', 'off'):
- return False
+# From: https://stackoverflow.com/questions/1741972/how-to-use-different-formatters-with-the-same-logging-handler-in-python
+class DispatchingFormatter(Formatter):
- return default
+ def __init__(self, formatters, default_formatter):
+ self._formatters = formatters
+ self._default_formatter = default_formatter
+ def format(self, record):
+ formatter = self._formatters.get(record.name, self._default_formatter)
+ return formatter.format(record)
-def get_os_env_bool(name, default=False) -> bool:
- """Convert an environment variable to bool.
- Conversion is the same as for :func:`get_env_bool`.
- """
- return get_env_bool(os.environ, name, default)
+def sanitize_shell_env(execution_env: dict) -> dict:
+ """Sanitize all values in *execution_env*
+ The execution environment (typically comes from (env['ENV']) is
+ propagated to the shell, and may need to be cleaned first.
-def print_time():
- """Hack to return a value from Main if can't import Main."""
- # pylint: disable=redefined-outer-name,import-outside-toplevel
- from SCons.Script.Main import print_time
- return print_time
+ Args:
+ execution_env: The shell environment variables to be propagated
+ to the spawned shell.
+
+ Returns:
+ sanitized dictionary of env variables (similar to what you'd get
+ from :data:`os.environ`)
+ """
+ # Ensure that the ENV values are all strings:
+ new_env = {}
+ for key, value in execution_env.items():
+ if is_List(value):
+ # If the value is a list, then we assume it is a path list,
+ # because that's a pretty common list-like value to stick
+ # in an environment variable:
+ value = flatten_sequence(value)
+ new_env[key] = os.pathsep.join(map(str, value))
+ else:
+ # It's either a string or something else. If it isn't a
+ # string or a list, then we just coerce it to a string, which
+ # is the proper way to handle Dir and File instances and will
+ # produce something reasonable for just about everything else:
+ new_env[key] = str(value)
+ return new_env
# Local Variables:
# tab-width:4
diff --git a/SCons/Util/envs.py b/SCons/Util/envs.py
new file mode 100644
index 0000000..963c963
--- /dev/null
+++ b/SCons/Util/envs.py
@@ -0,0 +1,325 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright The SCons Foundation
+
+"""Various SCons utility functions
+
+Routines for working with environments and construction variables
+that don't need the specifics of Environment.
+"""
+
+import os
+from types import MethodType, FunctionType
+from typing import Union
+
+from .types import is_List, is_Tuple, is_String
+
+
+def PrependPath(
+ oldpath, newpath, sep=os.pathsep, delete_existing=True, canonicalize=None
+) -> Union[list, str]:
+ """Prepend *newpath* path elements to *oldpath*.
+
+ Will only add any particular path once (leaving the first one it
+ encounters and ignoring the rest, to preserve path order), and will
+ :mod:`os.path.normpath` and :mod:`os.path.normcase` all paths to help
+ assure this. This can also handle the case where *oldpath*
+ is a list instead of a string, in which case a list will be returned
+ instead of a string. For example:
+
+ >>> p = PrependPath("/foo/bar:/foo", "/biz/boom:/foo")
+ >>> print(p)
+ /biz/boom:/foo:/foo/bar
+
+ If *delete_existing* is ``False``, then adding a path that exists will
+ not move it to the beginning; it will stay where it is in the list.
+
+ >>> p = PrependPath("/foo/bar:/foo", "/biz/boom:/foo", delete_existing=False)
+ >>> print(p)
+ /biz/boom:/foo/bar:/foo
+
+ If *canonicalize* is not ``None``, it is applied to each element of
+ *newpath* before use.
+ """
+ orig = oldpath
+ is_list = True
+ paths = orig
+ if not is_List(orig) and not is_Tuple(orig):
+ paths = paths.split(sep)
+ is_list = False
+
+ if is_String(newpath):
+ newpaths = newpath.split(sep)
+ elif not is_List(newpath) and not is_Tuple(newpath):
+ newpaths = [newpath] # might be a Dir
+ else:
+ newpaths = newpath
+
+ if canonicalize:
+ newpaths = list(map(canonicalize, newpaths))
+
+ if not delete_existing:
+ # First uniquify the old paths, making sure to
+ # preserve the first instance (in Unix/Linux,
+ # the first one wins), and remembering them in normpaths.
+ # Then insert the new paths at the head of the list
+ # if they're not already in the normpaths list.
+ result = []
+ normpaths = []
+ for path in paths:
+ if not path:
+ continue
+ normpath = os.path.normpath(os.path.normcase(path))
+ if normpath not in normpaths:
+ result.append(path)
+ normpaths.append(normpath)
+ newpaths.reverse() # since we're inserting at the head
+ for path in newpaths:
+ if not path:
+ continue
+ normpath = os.path.normpath(os.path.normcase(path))
+ if normpath not in normpaths:
+ result.insert(0, path)
+ normpaths.append(normpath)
+ paths = result
+
+ else:
+ newpaths = newpaths + paths # prepend new paths
+
+ normpaths = []
+ paths = []
+ # now we add them only if they are unique
+ for path in newpaths:
+ normpath = os.path.normpath(os.path.normcase(path))
+ if path and normpath not in normpaths:
+ paths.append(path)
+ normpaths.append(normpath)
+
+ if is_list:
+ return paths
+
+ return sep.join(paths)
+
+
+def AppendPath(
+ oldpath, newpath, sep=os.pathsep, delete_existing=True, canonicalize=None
+) -> Union[list, str]:
+ """Append *newpath* path elements to *oldpath*.
+
+ Will only add any particular path once (leaving the last one it
+ encounters and ignoring the rest, to preserve path order), and will
+ :mod:`os.path.normpath` and :mod:`os.path.normcase` all paths to help
+ assure this. This can also handle the case where *oldpath*
+ is a list instead of a string, in which case a list will be returned
+ instead of a string. For example:
+
+ >>> p = AppendPath("/foo/bar:/foo", "/biz/boom:/foo")
+ >>> print(p)
+ /foo/bar:/biz/boom:/foo
+
+ If *delete_existing* is ``False``, then adding a path that exists
+ will not move it to the end; it will stay where it is in the list.
+
+ >>> p = AppendPath("/foo/bar:/foo", "/biz/boom:/foo", delete_existing=False)
+ >>> print(p)
+ /foo/bar:/foo:/biz/boom
+
+ If *canonicalize* is not ``None``, it is applied to each element of
+ *newpath* before use.
+ """
+ orig = oldpath
+ is_list = True
+ paths = orig
+ if not is_List(orig) and not is_Tuple(orig):
+ paths = paths.split(sep)
+ is_list = False
+
+ if is_String(newpath):
+ newpaths = newpath.split(sep)
+ elif not is_List(newpath) and not is_Tuple(newpath):
+ newpaths = [newpath] # might be a Dir
+ else:
+ newpaths = newpath
+
+ if canonicalize:
+ newpaths = list(map(canonicalize, newpaths))
+
+ if not delete_existing:
+ # add old paths to result, then
+ # add new paths if not already present
+ # (I thought about using a dict for normpaths for speed,
+ # but it's not clear hashing the strings would be faster
+ # than linear searching these typically short lists.)
+ result = []
+ normpaths = []
+ for path in paths:
+ if not path:
+ continue
+ result.append(path)
+ normpaths.append(os.path.normpath(os.path.normcase(path)))
+ for path in newpaths:
+ if not path:
+ continue
+ normpath = os.path.normpath(os.path.normcase(path))
+ if normpath not in normpaths:
+ result.append(path)
+ normpaths.append(normpath)
+ paths = result
+ else:
+ # start w/ new paths, add old ones if not present,
+ # then reverse.
+ newpaths = paths + newpaths # append new paths
+ newpaths.reverse()
+
+ normpaths = []
+ paths = []
+ # now we add them only if they are unique
+ for path in newpaths:
+ normpath = os.path.normpath(os.path.normcase(path))
+ if path and normpath not in normpaths:
+ paths.append(path)
+ normpaths.append(normpath)
+ paths.reverse()
+
+ if is_list:
+ return paths
+
+ return sep.join(paths)
+
+
+def AddPathIfNotExists(env_dict, key, path, sep=os.pathsep):
+ """Add a path element to a construction variable.
+
+ `key` is looked up in `env_dict`, and `path` is added to it if it
+ is not already present. `env_dict[key]` is assumed to be in the
+ format of a PATH variable: a list of paths separated by `sep` tokens.
+
+ >>> env = {'PATH': '/bin:/usr/bin:/usr/local/bin'}
+ >>> AddPathIfNotExists(env, 'PATH', '/opt/bin')
+ >>> print(env['PATH'])
+ /opt/bin:/bin:/usr/bin:/usr/local/bin
+ """
+ try:
+ is_list = True
+ paths = env_dict[key]
+ if not is_List(env_dict[key]):
+ paths = paths.split(sep)
+ is_list = False
+ 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
+
+
+class MethodWrapper:
+ """A generic Wrapper class that associates a method with an object.
+
+ As part of creating this MethodWrapper object an attribute with the
+ specified name (by default, the name of the supplied method) is added
+ to the underlying object. When that new "method" is called, our
+ :meth:`__call__` method adds the object as the first argument, simulating
+ the Python behavior of supplying "self" on method calls.
+
+ We hang on to the name by which the method was added to the underlying
+ base class so that we can provide a method to "clone" ourselves onto
+ a new underlying object being copied (without which we wouldn't need
+ to save that info).
+ """
+ def __init__(self, obj, method, name=None):
+ if name is None:
+ name = method.__name__
+ self.object = obj
+ self.method = method
+ self.name = name
+ setattr(self.object, name, self)
+
+ def __call__(self, *args, **kwargs):
+ nargs = (self.object,) + args
+ return self.method(*nargs, **kwargs)
+
+ def clone(self, new_object):
+ """
+ Returns an object that re-binds the underlying "method" to
+ the specified new object.
+ """
+ return self.__class__(new_object, self.method, self.name)
+
+
+# The original idea for AddMethod() came from the
+# following post to the ActiveState Python Cookbook:
+#
+# ASPN: Python Cookbook : Install bound methods in an instance
+# https://code.activestate.com/recipes/223613
+#
+# Changed as follows:
+# * Switched the installmethod() "object" and "function" arguments,
+# so the order reflects that the left-hand side is the thing being
+# "assigned to" and the right-hand side is the value being assigned.
+# * The instance/class detection is changed a bit, as it's all
+# new-style classes now with Py3.
+# * The by-hand construction of the function object from renamefunction()
+# is not needed, the remaining bit is now used inline in AddMethod.
+
+
+def AddMethod(obj, function, name=None):
+ """Add a method to an object.
+
+ Adds *function* to *obj* if *obj* is a class object.
+ Adds *function* as a bound method if *obj* is an instance object.
+ If *obj* looks like an environment instance, use :class:`~SCons.Util.MethodWrapper`
+ to add it. If *name* is supplied it is used as the name of *function*.
+
+ Although this works for any class object, the intent as a public
+ API is to be used on Environment, to be able to add a method to all
+ construction environments; it is preferred to use ``env.AddMethod``
+ to add to an individual environment.
+
+ >>> class A:
+ ... ...
+
+ >>> a = A()
+
+ >>> def f(self, x, y):
+ ... self.z = x + y
+
+ >>> AddMethod(A, f, "add")
+ >>> a.add(2, 4)
+ >>> print(a.z)
+ 6
+ >>> a.data = ['a', 'b', 'c', 'd', 'e', 'f']
+ >>> AddMethod(a, lambda self, i: self.data[i], "listIndex")
+ >>> print(a.listIndex(3))
+ d
+
+ """
+ if name is None:
+ name = function.__name__
+ else:
+ # "rename"
+ function = FunctionType(
+ function.__code__, function.__globals__, name, function.__defaults__
+ )
+
+ if hasattr(obj, '__class__') and obj.__class__ is not type:
+ # obj is an instance, so it gets a bound method.
+ if hasattr(obj, "added_methods"):
+ method = MethodWrapper(obj, function, name)
+ obj.added_methods.append(method)
+ else:
+ method = MethodType(function, obj)
+ else:
+ # obj is a class
+ method = function
+
+ setattr(obj, name, method)
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/SCons/Util/hashes.py b/SCons/Util/hashes.py
new file mode 100644
index 0000000..b97cd4d
--- /dev/null
+++ b/SCons/Util/hashes.py
@@ -0,0 +1,401 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright The SCons Foundation
+
+"""SCons utility functions
+
+Routines for working with hash formats.
+"""
+
+import hashlib
+import sys
+
+from .types import to_bytes
+
+
+# Default hash function and format. SCons-internal.
+DEFAULT_HASH_FORMATS = ['md5', 'sha1', 'sha256']
+ALLOWED_HASH_FORMATS = []
+_HASH_FUNCTION = None
+_HASH_FORMAT = None
+
+
+def _attempt_init_of_python_3_9_hash_object(hash_function_object, sys_used=sys):
+ """Initialize hash function with non-security indicator.
+
+ In Python 3.9 and onwards, :mod:`hashlib` constructors accept a
+ keyword argument *usedforsecurity*, which, if set to ``False``,
+ lets us continue to use algorithms that have been deprecated either
+ by FIPS or by Python itself, as the MD5 algorithm SCons prefers is
+ not being used for security purposes as much as a short, 32 char
+ hash that is resistant to accidental collisions.
+
+ In prior versions of python, :mod:`hashlib` returns a native function
+ wrapper, which errors out when it's queried for the optional
+ parameter, so this function wraps that call.
+
+ It can still throw a ValueError if the initialization fails due to
+ FIPS compliance issues, but that is assumed to be the responsibility
+ of the caller.
+ """
+ if hash_function_object is None:
+ return None
+
+ # https://stackoverflow.com/a/11887885 details how to check versions
+ # with the "packaging" library. However, for our purposes, checking
+ # the version is greater than or equal to 3.9 is good enough, as the API
+ # is guaranteed to have support for the 'usedforsecurity' flag in 3.9. See
+ # https://docs.python.org/3/library/hashlib.html#:~:text=usedforsecurity
+ # for the version support notes.
+ if (sys_used.version_info.major > 3) or (
+ sys_used.version_info.major == 3 and sys_used.version_info.minor >= 9
+ ):
+ return hash_function_object(usedforsecurity=False)
+
+ # Note that this can throw a ValueError in FIPS-enabled versions of
+ # Linux prior to 3.9. The OpenSSL hashlib will throw on first init here,
+ # but it is assumed to be responsibility of the caller to diagnose the
+ # ValueError & potentially display the error to screen.
+ return hash_function_object()
+
+
+def _set_allowed_viable_default_hashes(hashlib_used, sys_used=sys) -> None:
+ """Check if the default hash algorithms can be called.
+
+ This util class is sometimes called prior to setting the
+ user-selected hash algorithm, meaning that on FIPS-compliant systems
+ the library would default-initialize MD5 and throw an exception in
+ set_hash_format. A common case is using the SConf options, which can
+ run prior to main, and thus ignore the options.hash_format variable.
+
+ This function checks the DEFAULT_HASH_FORMATS and sets the
+ ALLOWED_HASH_FORMATS to only the ones that can be called. In Python
+ >= 3.9 this will always default to MD5 as in Python 3.9 there is an
+ optional attribute "usedforsecurity" set for the method.
+
+ Throws if no allowed hash formats are detected.
+ """
+ global ALLOWED_HASH_FORMATS
+ _last_error = None
+ # note: if you call this method repeatedly, example using timeout,
+ # this is needed. Otherwise it keeps appending valid formats to the string.
+ ALLOWED_HASH_FORMATS = []
+
+ for test_algorithm in DEFAULT_HASH_FORMATS:
+ _test_hash = getattr(hashlib_used, test_algorithm, None)
+ # we know hashlib claims to support it... check to see if we can call it.
+ if _test_hash is not None:
+ # The hashing library will throw an exception on initialization
+ # in FIPS mode, meaning if we call the default algorithm returned
+ # with no parameters, it'll throw if it's a bad algorithm,
+ # otherwise it will append it to the known good formats.
+ try:
+ _attempt_init_of_python_3_9_hash_object(_test_hash, sys_used)
+ ALLOWED_HASH_FORMATS.append(test_algorithm)
+ except ValueError as e:
+ _last_error = e
+ continue
+
+ if len(ALLOWED_HASH_FORMATS) == 0:
+ from SCons.Errors import (
+ SConsEnvironmentError,
+ ) # pylint: disable=import-outside-toplevel
+
+ # chain the exception thrown with the most recent error from hashlib.
+ raise SConsEnvironmentError(
+ 'No usable hash algorithms found.'
+ 'Most recent error from hashlib attached in trace.'
+ ) from _last_error
+
+
+_set_allowed_viable_default_hashes(hashlib)
+
+
+def get_hash_format():
+ """Retrieves the hash format or ``None`` if not overridden.
+
+ A return value of ``None``
+ does not guarantee that MD5 is being used; instead, it means that the
+ default precedence order documented in :func:`SCons.Util.set_hash_format`
+ is respected.
+ """
+ return _HASH_FORMAT
+
+
+def _attempt_get_hash_function(hash_name, hashlib_used=hashlib, sys_used=sys):
+ """Wrapper used to try to initialize a hash function given.
+
+ If successful, returns the name of the hash function back to the user.
+
+ Otherwise returns None.
+ """
+ try:
+ _fetch_hash = getattr(hashlib_used, hash_name, None)
+ if _fetch_hash is None:
+ return None
+ _attempt_init_of_python_3_9_hash_object(_fetch_hash, sys_used)
+ return hash_name
+ except ValueError:
+ # If attempt_init_of_python_3_9 throws, this is typically due to FIPS
+ # being enabled. However, if we get to this point, the viable hash
+ # function check has either been bypassed or otherwise failed to
+ # properly restrict the user to only the supported functions.
+ # As such throw the UserError as an internal assertion-like error.
+ return None
+
+
+def set_hash_format(hash_format, hashlib_used=hashlib, sys_used=sys):
+ """Sets the default hash format used by SCons.
+
+ If `hash_format` is ``None`` or
+ an empty string, the default is determined by this function.
+
+ Currently the default behavior is to use the first available format of
+ the following options: MD5, SHA1, SHA256.
+ """
+ global _HASH_FORMAT, _HASH_FUNCTION
+
+ _HASH_FORMAT = hash_format
+ if hash_format:
+ hash_format_lower = hash_format.lower()
+ if hash_format_lower not in ALLOWED_HASH_FORMATS:
+ from SCons.Errors import (
+ UserError,
+ ) # pylint: disable=import-outside-toplevel
+
+ # User can select something not supported by their OS but
+ # normally supported by SCons, example, selecting MD5 in an
+ # OS with FIPS-mode turned on. Therefore we first check if
+ # SCons supports it, and then if their local OS supports it.
+ if hash_format_lower in DEFAULT_HASH_FORMATS:
+ raise UserError(
+ 'While hash format "%s" is supported by SCons, the '
+ 'local system indicates only the following hash '
+ 'formats are supported by the hashlib library: %s'
+ % (hash_format_lower, ', '.join(ALLOWED_HASH_FORMATS))
+ )
+
+ # The hash format isn't supported by SCons in any case.
+ # Warn the user, and if we detect that SCons supports more
+ # algorithms than their local system supports,
+ # warn the user about that too.
+ if ALLOWED_HASH_FORMATS == DEFAULT_HASH_FORMATS:
+ raise UserError(
+ 'Hash format "%s" is not supported by SCons. Only '
+ 'the following hash formats are supported: %s'
+ % (hash_format_lower, ', '.join(ALLOWED_HASH_FORMATS))
+ )
+
+ raise UserError(
+ 'Hash format "%s" is not supported by SCons. '
+ 'SCons supports more hash formats than your local system '
+ 'is reporting; SCons supports: %s. Your local system only '
+ 'supports: %s'
+ % (
+ hash_format_lower,
+ ', '.join(DEFAULT_HASH_FORMATS),
+ ', '.join(ALLOWED_HASH_FORMATS),
+ )
+ )
+
+ # This is not expected to fail. If this fails it means the
+ # set_allowed_viable_default_hashes function did not throw,
+ # or when it threw, the exception was caught and ignored, or
+ # the global ALLOWED_HASH_FORMATS was changed by an external user.
+ _HASH_FUNCTION = _attempt_get_hash_function(
+ hash_format_lower, hashlib_used, sys_used
+ )
+
+ if _HASH_FUNCTION is None:
+ from SCons.Errors import (
+ UserError,
+ ) # pylint: disable=import-outside-toplevel
+
+ raise UserError(
+ f'Hash format "{hash_format_lower}" is not available in your '
+ 'Python interpreter. Expected to be supported algorithm by '
+ 'set_allowed_viable_default_hashes. Assertion error in SCons.'
+ )
+ else:
+ # Set the default hash format based on what is available, defaulting
+ # to the first supported hash algorithm (usually md5) for backwards
+ # compatibility. In FIPS-compliant systems this usually defaults to
+ # SHA1, unless that too has been disabled.
+ for choice in ALLOWED_HASH_FORMATS:
+ _HASH_FUNCTION = _attempt_get_hash_function(choice, hashlib_used, sys_used)
+
+ if _HASH_FUNCTION is not None:
+ break
+ else:
+ # This is not expected to happen in practice.
+ from SCons.Errors import (
+ UserError,
+ ) # pylint: disable=import-outside-toplevel
+
+ raise UserError(
+ 'Your Python interpreter does not have MD5, SHA1, or SHA256. '
+ 'SCons requires at least one. Expected to support one or more '
+ 'during set_allowed_viable_default_hashes.'
+ )
+
+
+# Ensure that this is initialized in case either:
+# 1. This code is running in a unit test.
+# 2. This code is running in a consumer that does hash operations while
+# SConscript files are being loaded.
+set_hash_format(None)
+
+
+def get_current_hash_algorithm_used():
+ """Returns the current hash algorithm name used.
+
+ Where the python version >= 3.9, this is expected to return md5.
+ If python's version is <= 3.8, this returns md5 on non-FIPS-mode platforms, and
+ sha1 or sha256 on FIPS-mode Linux platforms.
+
+ This function is primarily useful for testing, where one expects a value to be
+ one of N distinct hashes, and therefore the test needs to know which hash to select.
+ """
+ return _HASH_FUNCTION
+
+
+def _get_hash_object(hash_format, hashlib_used=hashlib, sys_used=sys):
+ """Allocates a hash object using the requested hash format.
+
+ Args:
+ hash_format: Hash format to use.
+
+ Returns:
+ hashlib object.
+ """
+ if hash_format is None:
+ if _HASH_FUNCTION is None:
+ from SCons.Errors import (
+ UserError,
+ ) # pylint: disable=import-outside-toplevel
+
+ raise UserError(
+ 'There is no default hash function. Did you call '
+ 'a hashing function before SCons was initialized?'
+ )
+ return _attempt_init_of_python_3_9_hash_object(
+ getattr(hashlib_used, _HASH_FUNCTION, None), sys_used
+ )
+
+ if not hasattr(hashlib, hash_format):
+ from SCons.Errors import UserError # pylint: disable=import-outside-toplevel
+
+ raise UserError(
+ f'Hash format "{hash_format}" is not available in your Python interpreter.'
+ )
+
+ return _attempt_init_of_python_3_9_hash_object(
+ getattr(hashlib, hash_format), sys_used
+ )
+
+
+def hash_signature(s, hash_format=None):
+ """
+ Generate hash signature of a string
+
+ Args:
+ s: either string or bytes. Normally should be bytes
+ hash_format: Specify to override default hash format
+
+ Returns:
+ String of hex digits representing the signature
+ """
+ m = _get_hash_object(hash_format)
+ try:
+ m.update(to_bytes(s))
+ except TypeError:
+ m.update(to_bytes(str(s)))
+
+ return m.hexdigest()
+
+
+def hash_file_signature(fname, chunksize=65536, hash_format=None):
+ """
+ Generate the md5 signature of a file
+
+ Args:
+ fname: file to hash
+ chunksize: chunk size to read
+ hash_format: Specify to override default hash format
+
+ Returns:
+ String of Hex digits representing the signature
+ """
+
+ m = _get_hash_object(hash_format)
+ with open(fname, "rb") as f:
+ while True:
+ blck = f.read(chunksize)
+ if not blck:
+ break
+ m.update(to_bytes(blck))
+ return m.hexdigest()
+
+
+def hash_collect(signatures, hash_format=None):
+ """
+ Collects a list of signatures into an aggregate signature.
+
+ Args:
+ signatures: a list of signatures
+ hash_format: Specify to override default hash format
+
+ Returns:
+ the aggregate signature
+ """
+
+ if len(signatures) == 1:
+ return signatures[0]
+
+ return hash_signature(', '.join(signatures), hash_format)
+
+
+_MD5_WARNING_SHOWN = False
+
+
+def _show_md5_warning(function_name):
+ """Shows a deprecation warning for various MD5 functions."""
+
+ global _MD5_WARNING_SHOWN
+
+ if not _MD5_WARNING_SHOWN:
+ import SCons.Warnings # pylint: disable=import-outside-toplevel
+
+ SCons.Warnings.warn(
+ SCons.Warnings.DeprecatedWarning,
+ f"Function {function_name} is deprecated",
+ )
+ _MD5_WARNING_SHOWN = True
+
+
+def MD5signature(s):
+ """Deprecated. Use :func:`hash_signature` instead."""
+
+ _show_md5_warning("MD5signature")
+ return hash_signature(s)
+
+
+def MD5filesignature(fname, chunksize=65536):
+ """Deprecated. Use :func:`hash_file_signature` instead."""
+
+ _show_md5_warning("MD5filesignature")
+ return hash_file_signature(fname, chunksize)
+
+
+def MD5collect(signatures):
+ """Deprecated. Use :func:`hash_collect` instead."""
+
+ _show_md5_warning("MD5collect")
+ return hash_collect(signatures)
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/SCons/Util/types.py b/SCons/Util/types.py
new file mode 100644
index 0000000..9aef13e
--- /dev/null
+++ b/SCons/Util/types.py
@@ -0,0 +1,310 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright The SCons Foundation
+
+"""Various SCons utility functions
+
+Routines which check types and do type conversions.
+"""
+
+import os
+import pprint
+import re
+from typing import Optional
+
+from collections import UserDict, UserList, UserString
+from collections.abc import MappingView
+
+# Functions for deciding if things are like various types, mainly to
+# handle UserDict, UserList and UserString like their underlying types.
+#
+# Yes, all of this manual testing breaks polymorphism, and the real
+# Pythonic way to do all of this would be to just try it and handle the
+# exception, but handling the exception when it's not the right type is
+# often too slow.
+
+# We are using the following trick to speed up these
+# 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).
+# Since checkers dislike this, it's now annotated for pylint to flag
+# (mostly for other readers of this code) we're doing this intentionally.
+# TODO: PY3 check these are still valid choices for all of these funcs.
+
+DictTypes = (dict, UserDict)
+ListTypes = (list, UserList)
+
+# Handle getting dictionary views.
+SequenceTypes = (list, tuple, UserList, MappingView)
+
+# Note that profiling data shows a speed-up when comparing
+# explicitly with str instead of simply comparing
+# with basestring. (at least on Python 2.5.1)
+# TODO: PY3 check this benchmarking is still correct.
+StringTypes = (str, UserString)
+
+# Empirically, it is faster to check explicitly for str than for basestring.
+BaseStringTypes = str
+
+
+def is_Dict( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj, isinstance=isinstance, DictTypes=DictTypes
+) -> bool:
+ """Check if object is a dict."""
+ return isinstance(obj, DictTypes)
+
+
+def is_List( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj, isinstance=isinstance, ListTypes=ListTypes
+) -> bool:
+ """Check if object is a list."""
+ return isinstance(obj, ListTypes)
+
+
+def is_Sequence( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj, isinstance=isinstance, SequenceTypes=SequenceTypes
+) -> bool:
+ """Check if object is a sequence."""
+ return isinstance(obj, SequenceTypes)
+
+
+def is_Tuple( # pylint: disable=redefined-builtin
+ obj, isinstance=isinstance, tuple=tuple
+) -> bool:
+ """Check if object is a tuple."""
+ return isinstance(obj, tuple)
+
+
+def is_String( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj, isinstance=isinstance, StringTypes=StringTypes
+) -> bool:
+ """Check if object is a string."""
+ return isinstance(obj, StringTypes)
+
+
+def is_Scalar( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes
+) -> bool:
+ """Check if object is a scalar."""
+ # Profiling shows that there is an impressive speed-up of 2x
+ # 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 most of the time.
+ return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
+
+
+# From Dinu C. Gherman,
+# Python Cookbook, second edition, recipe 6.17, p. 277.
+# Also: https://code.activestate.com/recipes/68205
+# ASPN: Python Cookbook: Null Object Design Pattern
+
+
+class Null:
+ """Null objects always and reliably 'do nothing'."""
+
+ def __new__(cls, *args, **kwargs):
+ if '_instance' not in vars(cls):
+ cls._instance = super(Null, cls).__new__(cls, *args, **kwargs)
+ return cls._instance
+
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def __call__(self, *args, **kwargs):
+ return self
+
+ def __repr__(self):
+ return f"Null(0x{id(self):08X})"
+
+ def __bool__(self):
+ return False
+
+ def __getattr__(self, name):
+ return self
+
+ def __setattr__(self, name, value):
+ return self
+
+ def __delattr__(self, name):
+ return self
+
+
+class NullSeq(Null):
+ """A Null object that can also be iterated over."""
+
+ def __len__(self):
+ return 0
+
+ def __iter__(self):
+ return iter(())
+
+ def __getitem__(self, i):
+ return self
+
+ def __delitem__(self, i):
+ return self
+
+ def __setitem__(self, i, v):
+ return self
+
+
+def to_bytes(s) -> bytes:
+ """Convert object to bytes."""
+ if s is None:
+ return b'None'
+ if isinstance(s, (bytes, bytearray)):
+ # if already bytes return.
+ return s
+ return bytes(s, 'utf-8')
+
+
+def to_str(s) -> str:
+ """Convert object to string."""
+ if s is None:
+ return 'None'
+ if is_String(s):
+ return s
+ return str(s, 'utf-8')
+
+
+# Generic convert-to-string functions. The wrapper
+# to_String_for_signature() will use a for_signature() method if the
+# specified object has one.
+
+
+def to_String( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj,
+ isinstance=isinstance,
+ str=str,
+ UserString=UserString,
+ BaseStringTypes=BaseStringTypes,
+) -> str:
+ """Return a string version of obj."""
+ if isinstance(obj, BaseStringTypes):
+ # Early out when already a string!
+ return obj
+
+ if isinstance(obj, UserString):
+ # obj.data can only be a regular string. Please see the UserString initializer.
+ return obj.data
+
+ return str(obj)
+
+
+def to_String_for_subst( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj,
+ isinstance=isinstance,
+ str=str,
+ BaseStringTypes=BaseStringTypes,
+ SequenceTypes=SequenceTypes,
+ UserString=UserString,
+) -> str:
+ """Return a string version of obj for subst usage."""
+ # Note that the test cases are sorted by order of probability.
+ if isinstance(obj, BaseStringTypes):
+ return obj
+
+ if isinstance(obj, SequenceTypes):
+ return ' '.join([to_String_for_subst(e) for e in obj])
+
+ if isinstance(obj, UserString):
+ # obj.data can only a regular string. Please see the UserString initializer.
+ return obj.data
+
+ return str(obj)
+
+
+def to_String_for_signature( # pylint: disable=redefined-outer-name,redefined-builtin
+ obj, to_String_for_subst=to_String_for_subst, AttributeError=AttributeError,
+) -> str:
+ """Return a string version of obj for signature usage.
+
+ Like :func:`to_String_for_subst` but has special handling for
+ scons objects that have a :meth:`for_signature` method, and for dicts.
+ """
+ try:
+ f = obj.for_signature
+ except AttributeError:
+ if isinstance(obj, dict):
+ # pprint will output dictionary in key sorted order
+ # with py3.5 the order was randomized. Depending on dict order
+ # which was undefined until py3.6 (where it's by insertion order)
+ # was not wise.
+ # TODO: Change code when floor is raised to PY36
+ return pprint.pformat(obj, width=1000000)
+ return to_String_for_subst(obj)
+ else:
+ return f()
+
+
+def get_env_bool(env, name, default=False) -> bool:
+ """Convert a construction variable to bool.
+
+ If the value of *name* in *env* is 'true', 'yes', 'y', 'on' (case
+ insensitive) or anything convertible to int that yields non-zero then
+ return ``True``; if 'false', 'no', 'n', 'off' (case insensitive)
+ or a number that converts to integer zero return ``False``.
+ Otherwise, return `default`.
+
+ Args:
+ env: construction environment, or any dict-like object
+ name: name of the variable
+ default: value to return if *name* not in *env* or cannot
+ be converted (default: False)
+
+ Returns:
+ the "truthiness" of `name`
+ """
+ try:
+ var = env[name]
+ except KeyError:
+ return default
+ try:
+ return bool(int(var))
+ except ValueError:
+ if str(var).lower() in ('true', 'yes', 'y', 'on'):
+ return True
+
+ if str(var).lower() in ('false', 'no', 'n', 'off'):
+ return False
+
+ return default
+
+
+def get_os_env_bool(name, default=False) -> bool:
+ """Convert an environment variable to bool.
+
+ Conversion is the same as for :func:`get_env_bool`.
+ """
+ return get_env_bool(os.environ, name, default)
+
+
+_get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
+
+
+def get_environment_var(varstr) -> Optional[str]:
+ """Return undecorated construction variable string.
+
+ Determine if `varstr` looks like a reference
+ to a single environment variable, like `"$FOO"` or `"${FOO}"`.
+ If so, return that variable with no decorations, like `"FOO"`.
+ If not, return `None`.
+ """
+ mo = _get_env_var.match(to_String(varstr))
+ if mo:
+ var = mo.group(1)
+ if var[0] == '{':
+ return var[1:-1]
+ return var
+
+ return None
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/SCons/UtilTests.py b/SCons/UtilTests.py
index 0cfb70f..785caf7 100644
--- a/SCons/UtilTests.py
+++ b/SCons/UtilTests.py
@@ -46,10 +46,6 @@ from SCons.Util import (
Proxy,
Selector,
WhereIs,
- _attempt_init_of_python_3_9_hash_object,
- _attempt_get_hash_function,
- _get_hash_object,
- _set_allowed_viable_default_hashes,
adjustixes,
containsAll,
containsAny,
@@ -76,6 +72,12 @@ from SCons.Util import (
to_bytes,
to_str,
)
+from SCons.Util.hashes import (
+ _attempt_init_of_python_3_9_hash_object,
+ _attempt_get_hash_function,
+ _get_hash_object,
+ _set_allowed_viable_default_hashes,
+)
# These Util classes have no unit tests. Some don't make sense to test?
# DisplayEngine, Delegate, MethodWrapper, UniqueList, Unbuffered, Null, NullSeq
diff --git a/SCons/Utilities/sconsign.py b/SCons/Utilities/sconsign.py
index e595b2d..ae69196 100644
--- a/SCons/Utilities/sconsign.py
+++ b/SCons/Utilities/sconsign.py
@@ -28,6 +28,7 @@
"""Utility script to dump information from SCons signature database."""
import getopt
+import importlib
import os
import sys
from dbm import whichdb
@@ -51,13 +52,18 @@ def my_whichdb(filename):
def my_import(mname):
+ """Import database module.
+
+ This was used if the module was *not* SCons.dblite, to allow
+ for programmatic importing. It is no longer used, in favor of
+ importlib.import_module, and will be removed eventually.
+ """
import imp
if '.' in mname:
i = mname.rfind('.')
parent = my_import(mname[:i])
- fp, pathname, description = imp.find_module(mname[i+1:],
- parent.__path__)
+ fp, pathname, description = imp.find_module(mname[i+1:], parent.__path__)
else:
fp, pathname, description = imp.find_module(mname)
return imp.load_module(mname, fp, pathname, description)
@@ -371,36 +377,50 @@ def Do_SConsignDir(name):
##############################################################################
def main():
- global Do_Call
+ global Do_Call
global nodeinfo_string
global args
global Verbose
global Readable
helpstr = """\
- Usage: sconsign [OPTIONS] [FILE ...]
- Options:
- -a, --act, --action Print build action information.
- -c, --csig Print content signature information.
- -d DIR, --dir=DIR Print only info about DIR.
- -e ENTRY, --entry=ENTRY Print only info about ENTRY.
- -f FORMAT, --format=FORMAT FILE is in the specified FORMAT.
- -h, --help Print this message and exit.
- -i, --implicit Print implicit dependency information.
- -r, --readable Print timestamps in human-readable form.
- --raw Print raw Python object representations.
- -s, --size Print file sizes.
- -t, --timestamp Print timestamp information.
- -v, --verbose Verbose, describe each field.
- """
+Usage: sconsign [OPTIONS] [FILE ...]
+
+Options:
+ -a, --act, --action Print build action information.
+ -c, --csig Print content signature information.
+ -d DIR, --dir=DIR Print only info about DIR.
+ -e ENTRY, --entry=ENTRY Print only info about ENTRY.
+ -f FORMAT, --format=FORMAT FILE is in the specified FORMAT.
+ -h, --help Print this message and exit.
+ -i, --implicit Print implicit dependency information.
+ -r, --readable Print timestamps in human-readable form.
+ --raw Print raw Python object representations.
+ -s, --size Print file sizes.
+ -t, --timestamp Print timestamp information.
+ -v, --verbose Verbose, describe each field.
+"""
try:
- opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv",
- ['act', 'action',
- 'csig', 'dir=', 'entry=',
- 'format=', 'help', 'implicit',
- 'raw', 'readable',
- 'size', 'timestamp', 'verbose'])
+ opts, args = getopt.getopt(
+ sys.argv[1:],
+ 'acd:e:f:hirstv',
+ [
+ 'act',
+ 'action',
+ 'csig',
+ 'dir=',
+ 'entry=',
+ 'format=',
+ 'help',
+ 'implicit',
+ 'raw',
+ 'readable',
+ 'size',
+ 'timestamp',
+ 'verbose',
+ ],
+ )
except getopt.GetoptError as err:
sys.stderr.write(str(err) + '\n')
print(helpstr)
@@ -423,7 +443,7 @@ def main():
if dbm_name:
try:
if dbm_name != "SCons.dblite":
- dbm = my_import(dbm_name)
+ dbm = importlib.import_module(dbm_name)
else:
import SCons.dblite
@@ -466,7 +486,7 @@ def main():
if dbm_name:
Map_Module = {'SCons.dblite': 'dblite'}
if dbm_name != "SCons.dblite":
- dbm = my_import(dbm_name)
+ dbm = importlib.import_module(dbm_name)
else:
import SCons.dblite
diff --git a/SCons/Variables/BoolVariable.py b/SCons/Variables/BoolVariable.py
index d4a7de7..71e44c9 100644
--- a/SCons/Variables/BoolVariable.py
+++ b/SCons/Variables/BoolVariable.py
@@ -26,10 +26,10 @@
Usage example::
opts = Variables()
- opts.Add(BoolVariable('embedded', 'build for an embedded system', 0))
- ...
- if env['embedded'] == 1:
+ opts.Add(BoolVariable('embedded', 'build for an embedded system', False))
...
+ if env['embedded']:
+ ...
"""
from typing import Tuple, Callable
@@ -42,17 +42,17 @@ TRUE_STRINGS = ('y', 'yes', 'true', 't', '1', 'on' , 'all')
FALSE_STRINGS = ('n', 'no', 'false', 'f', '0', 'off', 'none')
-def _text2bool(val) -> bool:
- """Converts strings to True/False.
+def _text2bool(val: str) -> bool:
+ """Convert boolean-like string to boolean.
If *val* looks like it expresses a bool-like value, based on
- the :data:`TRUE_STRINGS` and :data:`FALSE_STRINGS` tuples,
+ the :const:`TRUE_STRINGS` and :const:`FALSE_STRINGS` tuples,
return the appropriate value.
This is usable as a converter function for SCons Variables.
Raises:
- ValueError: if the string cannot be converted.
+ ValueError: if *val* cannot be converted to boolean.
"""
lval = val.lower()
@@ -64,13 +64,15 @@ def _text2bool(val) -> bool:
def _validator(key, val, env) -> None:
- """Validates the given value to be either true or false.
+ """Validate that the value of *key* in *env* is a boolean.
+
+ Parmaeter *val* is not used in the check.
- This is usable as a validator function for SCons Variables.
+ Usable as a validator function for SCons Variables.
Raises:
- KeyError: if key is not set in env
- UserError: if key does not validate.
+ KeyError: if *key* is not set in *env*
+ UserError: if the value of *key* is not ``True`` or ``False``.
"""
if not env[key] in (True, False):
raise SCons.Errors.UserError(
diff --git a/SCons/Variables/BoolVariableTests.py b/SCons/Variables/BoolVariableTests.py
index e486e4b..868e7e0 100644
--- a/SCons/Variables/BoolVariableTests.py
+++ b/SCons/Variables/BoolVariableTests.py
@@ -30,7 +30,7 @@ class BoolVariableTestCase(unittest.TestCase):
def test_BoolVariable(self):
"""Test BoolVariable creation"""
opts = SCons.Variables.Variables()
- opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
+ opts.Add(SCons.Variables.BoolVariable('test', 'test option help', False))
o = opts.options[0]
assert o.key == 'test', o.key
@@ -42,7 +42,7 @@ class BoolVariableTestCase(unittest.TestCase):
def test_converter(self):
"""Test the BoolVariable converter"""
opts = SCons.Variables.Variables()
- opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
+ opts.Add(SCons.Variables.BoolVariable('test', 'test option help', False))
o = opts.options[0]
@@ -73,17 +73,17 @@ class BoolVariableTestCase(unittest.TestCase):
x = o.converter(f)
assert not x, "converter returned true for '%s'" % f
- caught = None
+ caught = False
try:
o.converter('x')
except ValueError:
- caught = 1
- assert caught, "did not catch expected ValueError"
+ caught = True
+ assert caught, "did not catch expected ValueError for 'x'"
def test_validator(self):
"""Test the BoolVariable validator"""
opts = SCons.Variables.Variables()
- opts.Add(SCons.Variables.BoolVariable('test', 'test option help', 0))
+ opts.Add(SCons.Variables.BoolVariable('test', 'test option help', False))
o = opts.options[0]
@@ -93,23 +93,24 @@ class BoolVariableTestCase(unittest.TestCase):
'N' : 'xyzzy',
}
+ # positive checks
o.validator('T', 0, env)
-
o.validator('F', 0, env)
- caught = None
+ # negative checks
+ caught = False
try:
o.validator('N', 0, env)
except SCons.Errors.UserError:
- caught = 1
- assert caught, "did not catch expected UserError for N"
+ caught = True
+ assert caught, "did not catch expected UserError for value %s" % env['N']
- caught = None
+ caught = False
try:
o.validator('NOSUCHKEY', 0, env)
except KeyError:
- caught = 1
- assert caught, "did not catch expected KeyError for NOSUCHKEY"
+ caught = True
+ assert caught, "did not catch expected KeyError for 'NOSUCHKEY'"
if __name__ == "__main__":
diff --git a/SCons/Warnings.py b/SCons/Warnings.py
index c460897..754f05d 100644
--- a/SCons/Warnings.py
+++ b/SCons/Warnings.py
@@ -46,6 +46,9 @@ class CacheVersionWarning(WarningOnByDefault):
class CacheWriteErrorWarning(SConsWarning):
pass
+class CacheCleanupErrorWarning(SConsWarning):
+ pass
+
class CorruptSConsignWarning(WarningOnByDefault):
pass
diff --git a/SCons/__init__.py b/SCons/__init__.py
index c95386a..8f397bc 100644
--- a/SCons/__init__.py
+++ b/SCons/__init__.py
@@ -1,9 +1,9 @@
-__version__="4.3.0"
-__copyright__="Copyright (c) 2001 - 2021 The SCons Foundation"
-__developer__="bdeegan"
-__date__="Tue, 16 Nov 2021 19:09:21 +0000"
-__buildsys__="octodog"
-__revision__="559790274f66fa55251f5754de34820a29c7327a"
-__build__="559790274f66fa55251f5754de34820a29c7327a"
+__version__="4.4.1"
+__copyright__="Copyright (c) 2001 - 2022 The SCons Foundation"
+__developer__="bdbaddog"
+__date__="Sun, 13 Nov 2022 11:31:29 -0400"
+__buildsys__="M1DOG2021"
+__revision__="15379ff3b369560c4bf13459b630387bf7349bb7"
+__build__="15379ff3b369560c4bf13459b630387bf7349bb7"
# make sure compatibility is always in place
import SCons.compat # noqa \ No newline at end of file
diff --git a/SCons/cpp.py b/SCons/cpp.py
index 353aa70..144f498 100644
--- a/SCons/cpp.py
+++ b/SCons/cpp.py
@@ -591,6 +591,9 @@ class PreProcessor:
while not s[0] in '<"':
try:
s = self.cpp_namespace[s]
+ # strip backslashes from the computed include (-DFOO_H=\"foo.h\")
+ for c in '<">':
+ s = s.replace(f"\\{c}", c)
except KeyError:
m = function_name.search(s)
diff --git a/SCons/cppTests.py b/SCons/cppTests.py
index a9aef9d..f781e81 100644
--- a/SCons/cppTests.py
+++ b/SCons/cppTests.py
@@ -55,6 +55,8 @@ substitution_input = """
#include XXX_FILE5
#include XXX_FILE6
+
+#include SHELL_ESCAPED_H
"""
@@ -441,7 +443,8 @@ if_no_space_input = """
class cppTestCase(unittest.TestCase):
def setUp(self):
self.cpp = self.cpp_class(current = ".",
- cpppath = ['/usr/include'])
+ cpppath = ['/usr/include'],
+ dict={"SHELL_ESCAPED_H": '\\"file-shell-computed-yes\\"'})
def test_basic(self):
"""Test basic #include scanning"""
@@ -531,6 +534,7 @@ class cppAllTestCase(cppTestCase):
def setUp(self):
self.cpp = self.cpp_class(current = ".",
cpppath = ['/usr/include'],
+ dict={"SHELL_ESCAPED_H": '\\"file-shell-computed-yes\\"'},
all=1)
class PreProcessorTestCase(cppAllTestCase):
@@ -546,6 +550,7 @@ class PreProcessorTestCase(cppAllTestCase):
('include', '<', 'file4-yes'),
('include', '"', 'file5-yes'),
('include', '<', 'file6-yes'),
+ ('include', '"', 'file-shell-computed-yes'),
]
ifdef_expect = [
@@ -647,6 +652,7 @@ class DumbPreProcessorTestCase(cppAllTestCase):
('include', '<', 'file4-yes'),
('include', '"', 'file5-yes'),
('include', '<', 'file6-yes'),
+ ('include', '"', 'file-shell-computed-yes'),
]
ifdef_expect = [
@@ -876,7 +882,9 @@ if __name__ == '__main__':
fileTestCase,
]
for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
+ loader = unittest.TestLoader()
+ loader.testMethodPrefix = 'test_'
+ names = loader.getTestCaseNames(tclass)
try:
names = sorted(set(names))
except NameError:
diff --git a/SConstruct b/SConstruct
index 1728271..11ae13e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -38,7 +38,7 @@ month_year = strftime('%B %Y')
project = 'scons'
-default_version = '4.3.1'
+default_version = '4.4.1'
copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
#
diff --git a/bin/update_doc_files.sh b/bin/update_doc_files.sh
new file mode 100755
index 0000000..5e5cbe6
--- /dev/null
+++ b/bin/update_doc_files.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+python bin/docs-update-generated.py
+python bin/docs-validate.py
+python bin/docs-create-example-outputs.py
diff --git a/doc/design/chtml.xsl b/doc/design/chtml.xsl
index 1051247..6d3dfde 100644
--- a/doc/design/chtml.xsl
+++ b/doc/design/chtml.xsl
@@ -1,57 +1,57 @@
-<?xml version='1.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.
-
--->
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
-<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/chunk.xsl"/>
-
-<xsl:param name="base.dir" select="'scons-design/'"/>
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"/>
-<xsl:param name="section.label.includes.component.label" select="1"/>
-<xsl:param name="html.stylesheet" select="'scons.css'"/>
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-</xsl:stylesheet>
-
+<?xml version='1.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.
+
+-->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/chunk.xsl"/>
+
+<xsl:param name="base.dir" select="'scons-design/'"/>
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="section.label.includes.component.label" select="1"/>
+<xsl:param name="html.stylesheet" select="'scons.css'"/>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+</xsl:stylesheet>
+
diff --git a/doc/design/html.xsl b/doc/design/html.xsl
index c50e17e..0cd9f9b 100644
--- a/doc/design/html.xsl
+++ b/doc/design/html.xsl
@@ -1,151 +1,151 @@
-<?xml version='1.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.
-
--->
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/docbook.xsl"/>
-
-<xsl:output method="html"
- encoding="UTF-8"
- indent="no"/>
-
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"/>
-<xsl:param name="section.label.includes.component.label" select="1"/>
-<xsl:param name="html.stylesheet" select="'scons.css'"/>
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-<xsl:template match="function">
- <xsl:call-template name="inline.boldmonoseq"/>
-</xsl:template>
-<xsl:template match="methodname">
- <xsl:call-template name="inline.boldmonoseq"/>
-</xsl:template>
-<!-- Prevent our EPUB cover image from getting included -->
-<xsl:template match="mediaobject[@role = 'cover']">
-</xsl:template>
-
-
-
-<xsl:template name="head.content">
- <xsl:param name="node" select="."/>
- <xsl:param name="title">
- <xsl:apply-templates select="$node" mode="object.title.markup.textonly"/>
- </xsl:param>
-
- <title>
- <xsl:copy-of select="$title"/>
- </title>
-
- <xsl:if test="$html.base != ''">
- <base href="{$html.base}"/>
- </xsl:if>
-
- <!-- Insert links to CSS files or insert literal style elements -->
- <xsl:call-template name="generate.css"/>
-
- <xsl:if test="$html.stylesheet != ''">
- <xsl:call-template name="output.html.stylesheets">
- <xsl:with-param name="stylesheets" select="normalize-space($html.stylesheet)"/>
- </xsl:call-template>
- </xsl:if>
-
- <xsl:if test="$link.mailto.url != ''">
- <link rev="made"
- href="{$link.mailto.url}"/>
- </xsl:if>
-
- <meta name="generator" content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-
- <xsl:if test="$generate.meta.abstract != 0">
- <xsl:variable name="info" select="(articleinfo
- |bookinfo
- |prefaceinfo
- |chapterinfo
- |appendixinfo
- |sectioninfo
- |sect1info
- |sect2info
- |sect3info
- |sect4info
- |sect5info
- |referenceinfo
- |refentryinfo
- |partinfo
- |info
- |docinfo)[1]"/>
- <xsl:if test="$info and $info/abstract">
- <meta name="description">
- <xsl:attribute name="content">
- <xsl:for-each select="$info/abstract[1]/*">
- <xsl:value-of select="normalize-space(.)"/>
- <xsl:if test="position() &lt; last()">
- <xsl:text> </xsl:text>
- </xsl:if>
- </xsl:for-each>
- </xsl:attribute>
- </meta>
- </xsl:if>
- </xsl:if>
-
- <xsl:if test="($draft.mode = 'yes' or
- ($draft.mode = 'maybe' and
- ancestor-or-self::*[@status][1]/@status = 'draft'))
- and $draft.watermark.image != ''">
- <style type="text/css"><xsl:text>
-body { background-image: url('</xsl:text>
-<xsl:value-of select="$draft.watermark.image"/><xsl:text>');
- background-repeat: no-repeat;
- background-position: top left;
- /* The following properties make the watermark "fixed" on the page. */
- /* I think that's just a bit too distracting for the reader... */
- /* background-attachment: fixed; */
- /* background-position: center center; */
- }</xsl:text>
- </style>
- </xsl:if>
- <xsl:apply-templates select="." mode="head.keywords.content"/>
-</xsl:template>
-
-</xsl:stylesheet>
-
+<?xml version='1.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.
+
+-->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/docbook.xsl"/>
+
+<xsl:output method="html"
+ encoding="UTF-8"
+ indent="no"/>
+
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="section.label.includes.component.label" select="1"/>
+<xsl:param name="html.stylesheet" select="'scons.css'"/>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+<xsl:template match="function">
+ <xsl:call-template name="inline.boldmonoseq"/>
+</xsl:template>
+<xsl:template match="methodname">
+ <xsl:call-template name="inline.boldmonoseq"/>
+</xsl:template>
+<!-- Prevent our EPUB cover image from getting included -->
+<xsl:template match="mediaobject[@role = 'cover']">
+</xsl:template>
+
+
+
+<xsl:template name="head.content">
+ <xsl:param name="node" select="."/>
+ <xsl:param name="title">
+ <xsl:apply-templates select="$node" mode="object.title.markup.textonly"/>
+ </xsl:param>
+
+ <title>
+ <xsl:copy-of select="$title"/>
+ </title>
+
+ <xsl:if test="$html.base != ''">
+ <base href="{$html.base}"/>
+ </xsl:if>
+
+ <!-- Insert links to CSS files or insert literal style elements -->
+ <xsl:call-template name="generate.css"/>
+
+ <xsl:if test="$html.stylesheet != ''">
+ <xsl:call-template name="output.html.stylesheets">
+ <xsl:with-param name="stylesheets" select="normalize-space($html.stylesheet)"/>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:if test="$link.mailto.url != ''">
+ <link rev="made"
+ href="{$link.mailto.url}"/>
+ </xsl:if>
+
+ <meta name="generator" content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+
+ <xsl:if test="$generate.meta.abstract != 0">
+ <xsl:variable name="info" select="(articleinfo
+ |bookinfo
+ |prefaceinfo
+ |chapterinfo
+ |appendixinfo
+ |sectioninfo
+ |sect1info
+ |sect2info
+ |sect3info
+ |sect4info
+ |sect5info
+ |referenceinfo
+ |refentryinfo
+ |partinfo
+ |info
+ |docinfo)[1]"/>
+ <xsl:if test="$info and $info/abstract">
+ <meta name="description">
+ <xsl:attribute name="content">
+ <xsl:for-each select="$info/abstract[1]/*">
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:if test="position() &lt; last()">
+ <xsl:text> </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:attribute>
+ </meta>
+ </xsl:if>
+ </xsl:if>
+
+ <xsl:if test="($draft.mode = 'yes' or
+ ($draft.mode = 'maybe' and
+ ancestor-or-self::*[@status][1]/@status = 'draft'))
+ and $draft.watermark.image != ''">
+ <style type="text/css"><xsl:text>
+body { background-image: url('</xsl:text>
+<xsl:value-of select="$draft.watermark.image"/><xsl:text>');
+ background-repeat: no-repeat;
+ background-position: top left;
+ /* The following properties make the watermark "fixed" on the page. */
+ /* I think that's just a bit too distracting for the reader... */
+ /* background-attachment: fixed; */
+ /* background-position: center center; */
+ }</xsl:text>
+ </style>
+ </xsl:if>
+ <xsl:apply-templates select="." mode="head.keywords.content"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/doc/design/pdf.xsl b/doc/design/pdf.xsl
index faea7fc..a398e5c 100644
--- a/doc/design/pdf.xsl
+++ b/doc/design/pdf.xsl
@@ -1,73 +1,73 @@
-<?xml version='1.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.
-
--->
-
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
-<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/fo/docbook.xsl"/>
-
-<xsl:include href="scons_title.xsl"/>
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"></xsl:param>
-<xsl:param name="section.label.includes.component.label" select="1"/>
-<xsl:param name="paper.type" select="'letter'"></xsl:param>
-<xsl:param name="body.start.indent">0pt</xsl:param>
-<xsl:param name="shade.verbatim" select="1"></xsl:param>
-<xsl:param name="variablelist.term.break.after" select="1"></xsl:param>
-
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-<xsl:attribute-set name="variablelist.term.properties">
- <xsl:attribute name="font-weight">bold</xsl:attribute>
-</xsl:attribute-set>
-
-<xsl:template match="variablelist">
- <xsl:variable name="presentation">
- <xsl:call-template name="pi.dbfo_list-presentation"/>
- </xsl:variable>
- <xsl:apply-templates select="." mode="vl.as.blocks"/>
-</xsl:template>
-
-</xsl:stylesheet>
-
+<?xml version='1.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.
+
+-->
+
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/fo/docbook.xsl"/>
+
+<xsl:include href="scons_title.xsl"/>
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"></xsl:param>
+<xsl:param name="section.label.includes.component.label" select="1"/>
+<xsl:param name="paper.type" select="'letter'"></xsl:param>
+<xsl:param name="body.start.indent">0pt</xsl:param>
+<xsl:param name="shade.verbatim" select="1"></xsl:param>
+<xsl:param name="variablelist.term.break.after" select="1"></xsl:param>
+
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+<xsl:attribute-set name="variablelist.term.properties">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+</xsl:attribute-set>
+
+<xsl:template match="variablelist">
+ <xsl:variable name="presentation">
+ <xsl:call-template name="pi.dbfo_list-presentation"/>
+ </xsl:variable>
+ <xsl:apply-templates select="." mode="vl.as.blocks"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen
index e21c782..ff3bb39 100644
--- a/doc/generated/builders.gen
+++ b/doc/generated/builders.gen
@@ -1247,31 +1247,34 @@ env.MSVSSolution(
<term><function>Ninja</function>()</term>
<term><replaceable>env</replaceable>.<methodname>Ninja</methodname>()</term>
<listitem><para>
- &b-Ninja; is a special builder which
- adds a target to create a ninja build file.
+ A special builder which
+ adds a target to create a Ninja build file.
The builder does not require any source files to be specified.
</para>
<note>
<para>This is an experimental feature. To enable it you must use one of the following methods
</para>
-<!-- NOTE DO NOT INDENT THE FOLLOWING AS IT WILL ALTER THE FORMATTING-->
+<!-- NOTE DO NOT INDENT example_commands CONTENTS AS IT WILL ALTER THE FORMATTING-->
<example_commands>
- # On the command line
- --experimental=ninja
+# On the command line
+--experimental=ninja
- # Or in your SConstruct
- SetOption('experimental', 'ninja')
+# Or in your SConstruct
+SetOption('experimental', 'ninja')
</example_commands>
<para>This functionality is subject to change and/or removal without deprecation cycle.</para>
-
<para>
- To use this tool you must install pypi's <ulink url="https://pypi.org/project/ninja/">ninja
- package</ulink>.
- This can be done via
- <command>pip install ninja</command>
+ To use this tool you need to install the &Python; &ninja; package,
+ as the tool by default depends on being able to do an
+ <systemitem>import</systemitem> of the package
+<!-- (although see &cv-link-__NINJA_NO;).-->
+ This can be done via:
+ <example_commands>
+python -m pip install ninja
+ </example_commands>
</para>
</note>
diff --git a/doc/generated/examples/caching_ex-random_1.xml b/doc/generated/examples/caching_ex-random_1.xml
index b751534..2e3b879 100644
--- a/doc/generated/examples/caching_ex-random_1.xml
+++ b/doc/generated/examples/caching_ex-random_1.xml
@@ -1,8 +1,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 f5.o -c f5.c
-cc -o f2.o -c f2.c
+cc -o f3.o -c f3.c
cc -o f4.o -c f4.c
cc -o f1.o -c f1.c
-cc -o f3.o -c f3.c
+cc -o f2.o -c f2.c
+cc -o f5.o -c f5.c
cc -o prog f1.o f2.o f3.o f4.o f5.o
</screen>
diff --git a/doc/generated/examples/depends_include_SConstruct b/doc/generated/examples/depends_include_SConstruct
index 2da2ecf..ec81661 100644
--- a/doc/generated/examples/depends_include_SConstruct
+++ b/doc/generated/examples/depends_include_SConstruct
@@ -1,3 +1,3 @@
-Program('hello.c', CPPPATH = '.')
+Program('hello.c', CPPPATH='.')
diff --git a/doc/generated/examples/depends_mixing_1.xml b/doc/generated/examples/depends_mixing_1.xml
index 64511e0..9e5ed3f 100644
--- a/doc/generated/examples/depends_mixing_1.xml
+++ b/doc/generated/examples/depends_mixing_1.xml
@@ -1,6 +1,6 @@
<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 program1.o -c -I. program1.c
-cc -o prog-MD5 program1.o
+cc -o prog-content program1.o
cc -o program2.o -c -I. program2.c
cc -o prog-timestamp program2.o
% <userinput>touch inc.h</userinput>
diff --git a/doc/generated/examples/mergeflags_MergeFlags1_1.xml b/doc/generated/examples/mergeflags_MergeFlags1_1.xml
index 5985ec2..89cd499 100644
--- a/doc/generated/examples/mergeflags_MergeFlags1_1.xml
+++ b/doc/generated/examples/mergeflags_MergeFlags1_1.xml
@@ -1,4 +1,4 @@
<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>
-['-option', '-O1', '-whatever', '-O3']
+CCFLAGS: ['-option', '-O1', '-whatever', '-O3']
scons: `.' is up to date.
</screen>
diff --git a/doc/generated/examples/mergeflags_MergeFlags2_1.xml b/doc/generated/examples/mergeflags_MergeFlags2_1.xml
index 3ff1e5d..dcc5f56 100644
--- a/doc/generated/examples/mergeflags_MergeFlags2_1.xml
+++ b/doc/generated/examples/mergeflags_MergeFlags2_1.xml
@@ -1,4 +1,4 @@
<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>
-['/include', '/usr/local/include', '/usr/include', '/usr/opt/include']
+CPPPATH: ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include']
scons: `.' is up to date.
</screen>
diff --git a/doc/generated/examples/mergeflags_MergeFlags3_1.xml b/doc/generated/examples/mergeflags_MergeFlags3_1.xml
index d0af8e2..d34efb9 100644
--- a/doc/generated/examples/mergeflags_MergeFlags3_1.xml
+++ b/doc/generated/examples/mergeflags_MergeFlags3_1.xml
@@ -1,5 +1,5 @@
<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>
-['-option', '-O1', '-whatever', '-O3']
-['/include', '/usr/local/include', '/usr/include', '/usr/opt/include']
+CCFLAGS: ['-option', '-O1', '-whatever', '-O3']
+CPPPATH: ['/include', '/usr/local/include', '/usr/include', '/usr/opt/include']
scons: `.' is up to date.
</screen>
diff --git a/doc/generated/examples/troubleshoot_Dump_1.xml b/doc/generated/examples/troubleshoot_Dump_1.xml
index 64ef1ab..6a0fa82 100644
--- a/doc/generated/examples/troubleshoot_Dump_1.xml
+++ b/doc/generated/examples/troubleshoot_Dump_1.xml
@@ -31,7 +31,7 @@ scons: Reading SConscript files ...
'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin:/snap/bin'},
'ESCAPE': &lt;function escape at 0x700000&gt;,
'File': &lt;SCons.Defaults.Variable_Method_Caller object at 0x700000&gt;,
- 'HOST_ARCH': 'x86_64',
+ 'HOST_ARCH': 'arm64',
'HOST_OS': 'posix',
'IDLSUFFIXES': ['.idl', '.IDL'],
'INSTALL': &lt;function copyFunc at 0x700000&gt;,
diff --git a/doc/generated/examples/troubleshoot_Dump_2.xml b/doc/generated/examples/troubleshoot_Dump_2.xml
index b7f0da6..c952602 100644
--- a/doc/generated/examples/troubleshoot_Dump_2.xml
+++ b/doc/generated/examples/troubleshoot_Dump_2.xml
@@ -10,6 +10,7 @@ scons: Reading SConscript files ...
'_InternalInstallVersionedLib': &lt;function InstallVersionedBuilderWrapper at 0x700000&gt;},
'CC': 'cl',
'CCCOM': &lt;SCons.Action.FunctionAction object at 0x700000&gt;,
+ 'CCDEPFLAGS': '/showIncludes',
'CCFLAGS': ['/nologo'],
'CCPCHFLAGS': &lt;function gen_ccpchflags at 0x700000&gt;,
'CCPDBFLAGS': ['${(PDB and "/Z7") or ""}'],
@@ -52,7 +53,7 @@ scons: Reading SConscript files ...
'SystemRoot': 'C:\\WINDOWS'},
'ESCAPE': &lt;function escape at 0x700000&gt;,
'File': &lt;SCons.Defaults.Variable_Method_Caller object at 0x700000&gt;,
- 'HOST_ARCH': 'x86_64',
+ 'HOST_ARCH': 'arm64',
'HOST_OS': 'win32',
'IDLSUFFIXES': ['.idl', '.IDL'],
'INCPREFIX': '/I',
@@ -66,6 +67,7 @@ scons: Reading SConscript files ...
'LIBSUFFIXES': ['$LIBSUFFIX'],
'MAXLINELENGTH': 2048,
'MSVC_SETUP_RUN': True,
+ 'NINJA_DEPFILE_PARSE_FORMAT': 'msvc',
'OBJPREFIX': '',
'OBJSUFFIX': '.obj',
'PCHCOM': '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS '
@@ -90,7 +92,7 @@ scons: Reading SConscript files ...
'SHCXXCOM': '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES '
'$SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCXXCOMSTR")}',
'SHCXXFLAGS': ['$CXXFLAGS'],
- 'SHELL': None,
+ 'SHELL': 'command',
'SHLIBPREFIX': '',
'SHLIBSUFFIX': '.dll',
'SHOBJPREFIX': '$OBJPREFIX',
diff --git a/doc/generated/examples/troubleshoot_explain1_3.xml b/doc/generated/examples/troubleshoot_explain1_3.xml
index bb5b21e..7a02fd5 100644
--- a/doc/generated/examples/troubleshoot_explain1_3.xml
+++ b/doc/generated/examples/troubleshoot_explain1_3.xml
@@ -2,5 +2,5 @@
cp file.in file.oout
scons: warning: Cannot find target file.out after building
-File "/home/bdeegan/devel/scons/git/as_scons/scripts/scons.py", line 98, in &lt;module&gt;
+File "/Users/bdbaddog/devel/scons/git/scons-bugfixes-4/scripts/scons.py", line 97, in &lt;module&gt;
</screen>
diff --git a/doc/generated/examples/troubleshoot_stacktrace_2.xml b/doc/generated/examples/troubleshoot_stacktrace_2.xml
index 7c3cc36..7c471eb 100644
--- a/doc/generated/examples/troubleshoot_stacktrace_2.xml
+++ b/doc/generated/examples/troubleshoot_stacktrace_2.xml
@@ -3,7 +3,7 @@ scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'.
scons: internal stack trace:
File "SCons/Job.py", line 203, in start
task.prepare()
- File "SCons/Script/Main.py", line 178, in prepare
+ File "SCons/Script/Main.py", line 180, in prepare
return SCons.Taskmaster.OutOfDateTask.prepare(self)
File "SCons/Taskmaster.py", line 186, in prepare
executor.prepare()
diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen
index c982b96..1c580f5 100644
--- a/doc/generated/functions.gen
+++ b/doc/generated/functions.gen
@@ -63,7 +63,7 @@ Added methods propagate through &f-env-Clone; calls.
</para>
<para>
-Examples:
+More examples:
</para>
<example_commands>
@@ -472,7 +472,7 @@ and/or suffix,
so the contents are treated as a list of strings, that is,
adding a string will result in a separate string entry,
not a combined string. For &cv-CPPDEFINES; as well as
-for &cv-link-LIBS;, and the various <varname>*PATH</varname>
+for &cv-link-LIBS;, and the various <literal>*PATH</literal>;
variables, &SCons; will supply the compiler-specific
syntax (e.g. adding a <literal>-D</literal> or <literal>/D</literal>
prefix for &cv-CPPDEFINES;), so this syntax should be omitted when
@@ -550,7 +550,7 @@ do not make sense and a &Python; exception will be raised.
<para>
When using &f-env-Append; to modify &consvars;
which are path specifications (conventionally,
-the names of such end in <varname>PATH</varname>),
+the names of such end in <literal>PATH</literal>),
it is recommended to add the values as a list of strings,
even if there is only a single string to add.
The same goes for adding library names to &cv-LIBS;.
@@ -570,26 +570,26 @@ See also &f-link-env-AppendUnique;,
<varlistentry id="f-AppendENVPath">
<term><replaceable>env</replaceable>.<methodname>AppendENVPath</methodname>(<parameter>name, newpath, [envname, sep, delete_existing=False]</parameter>)</term>
<listitem><para>
-Append new path elements to the given path in the
-specified external environment (&cv-link-ENV; by default).
-This will only add
-any particular path once (leaving the last one it encounters and
-ignoring the rest, to preserve path order),
-and to help assure this,
-will normalize all paths (using
-<function>os.path.normpath</function>
+Append path elements specified by <parameter>newpath</parameter>
+to the given search path string or list <parameter>name</parameter>
+in mapping <parameter>envname</parameter> in the &consenv;.
+Supplying <parameter>envname</parameter> is optional:
+the default is the execution environment &cv-link-ENV;.
+Optional <parameter>sep</parameter> is used as the search path separator,
+the default is the platform's separator (<systemitem>os.pathsep</systemitem>).
+A path element will only appear once.
+Any duplicates in <parameter>newpath</parameter> are dropped,
+keeping the last appearing (to preserve path order).
+If <parameter>delete_existing</parameter>
+is <constant>False</constant> (the default)
+any addition duplicating an existing path element is ignored;
+if <parameter>delete_existing</parameter>
+is <constant>True</constant> the existing value will
+be dropped and the path element will be added at the end.
+To help maintain uniqueness all paths are normalized (using
+<systemitem>os.path.normpath</systemitem>
and
-<function>os.path.normcase</function>).
-This can also handle the
-case where the given old path variable is a list instead of a
-string, in which case a list will be returned instead of a string.
-</para>
-
-<para>
-If
-<parameter>delete_existing</parameter>
-is <constant>False</constant>, then adding a path that already exists
-will not move it to the end; it will stay where it is in the list.
+<systemitem>os.path.normcase</systemitem>).
</para>
<para>
@@ -608,6 +608,11 @@ print('after:', env['ENV']['INCLUDE'])
before: /foo:/biz
after: /biz:/foo/bar:/foo
</screen>
+
+<para>
+See also &f-link-env-PrependENVPath;.
+</para>
+
</listitem>
</varlistentry>
<varlistentry id="f-AppendUnique">
@@ -718,7 +723,7 @@ is being used and
&scons;
finds a derived file that needs to be rebuilt,
it will first look in the cache to see if a
-file with matching build signature exists
+file with matching &buildsig; exists
(indicating the input file(s) and build action(s)
were identical to those for the current target),
and if so, will retrieve the file from the cache.
@@ -730,7 +735,7 @@ If the derived file is not present in the cache,
&scons;
will build it and
then place a copy of the built file in the cache,
-identified by its build signature, for future use.
+identified by its &buildsig;, for future use.
</para>
<para>
@@ -787,6 +792,13 @@ method can be used to disable caching of specific files. This can be
useful if inputs and/or outputs of some tool are impossible to
predict or prohibitively large.
</para>
+
+<para>
+Note that (at this time) &SCons; provides no facilities
+for managing the derived-file cache. It is up to the developer
+to arrange for cache pruning, expiry, etc. if needed.
+</para>
+
</listitem>
</varlistentry>
<varlistentry id="f-Clean">
@@ -1194,7 +1206,7 @@ was built.
This can be consulted to match various
file characteristics
such as the timestamp,
-size, or content signature.
+size, or &contentsig;.
</para>
</listitem>
</varlistentry>
@@ -1390,10 +1402,11 @@ Find an executable from one or more choices:
<parameter>progs</parameter> may be a string or a list of strings.
Returns the first value from <parameter>progs</parameter>
that was found, or <constant>None</constant>.
-Executable is searched by checking the paths specified
-by <varname>env</varname><literal>['ENV']['PATH']</literal>.
+Executable is searched by checking the paths in the execution environment
+(<varname>env</varname><literal>['ENV']['PATH']</literal>).
On Windows systems, additionally applies the filename suffixes found in
-<varname>env</varname><literal>['ENV']['PATHEXT']</literal>
+the execution environment
+(<varname>env</varname><literal>['ENV']['PATHEXT']</literal>)
but will not include any such extension in the return value.
&f-env-Detect; is a wrapper around &f-link-env-WhereIs;.
</para>
@@ -1598,45 +1611,49 @@ See the manpage section "Construction Environments" for more details.
</listitem>
</varlistentry>
<varlistentry id="f-Execute">
- <term><function>Execute</function>(<parameter>action, [strfunction, varlist]</parameter>)</term>
- <term><replaceable>env</replaceable>.<methodname>Execute</methodname>(<parameter>action, [strfunction, varlist]</parameter>)</term>
+ <term><function>Execute</function>(<parameter>action, [actionargs ...]</parameter>)</term>
+ <term><replaceable>env</replaceable>.<methodname>Execute</methodname>(<parameter>action, [actionargs ...]</parameter>)</term>
<listitem><para>
-Executes an Action object.
-The specified
+Executes an Action.
<parameter>action</parameter>
may be an Action object
-(see manpage section "Action Objects"
-for an explanation of behavior),
or it may be a command-line string,
list of commands,
or executable &Python; function,
-each of which will be converted
+each of which will first be converted
into an Action object
and then executed.
Any additional arguments to &f-Execute;
-(<parameter>strfunction</parameter>, <parameter>varlist</parameter>)
are passed on to the &f-link-Action; factory function
-which actually creates the Action object.
-The exit value of the command
-or return value of the &Python; function
-will be returned.
+which actually creates the Action object
+(see the manpage section <link linkend="action_objects">Action Objects</link>
+for a description). Example:
+</para>
+
+<example_commands>
+Execute(Copy('file.out', 'file.in'))
+</example_commands>
+
+<para>&f-Execute; performs its action immediately,
+as part of the SConscript-reading phase.
+There are no sources or targets declared in an
+&f-Execute; call, so any objects it manipulates
+will not be tracked as part of the &SCons; dependency graph.
+In the example above, neither
+<filename>file.out</filename> nor
+<filename>file.in</filename> will be tracked objects.
</para>
<para>
-Note that
+&f-Execute; returns the exit value of the command
+or return value of the &Python; function.
&scons;
-will print an error message if the executed
+prints an error message if the executed
<parameter>action</parameter>
-fails--that is,
-exits with or returns a non-zero value.
-&scons;
-will
+fails (exits with or returns a non-zero value),
+however it does
<emphasis>not</emphasis>,
-however,
-automatically terminate the build
-if the specified
-<parameter>action</parameter>
-fails.
+automatically terminate the build for such a failure.
If you want the build to stop in response to a failed
&f-Execute;
call,
@@ -1644,8 +1661,6 @@ you must explicitly check for a non-zero return value:
</para>
<example_commands>
-Execute(Copy('file.out', 'file.in'))
-
if Execute("mkdir sub/dir/ectory"):
# The mkdir failed, don't try to build.
Exit(1)
@@ -2640,12 +2655,8 @@ not as separate arguments to
</para>
<para>
-By default,
-duplicate values are eliminated;
-you can, however, specify
-<literal>unique=False</literal>
-to allow duplicate
-values to be added.
+If <literal>unique</literal> is true (the default),
+duplicate values are not stored.
When eliminating duplicate values,
any &consvars; that end with
the string
@@ -2653,6 +2664,8 @@ the string
keep the left-most unique value.
All other &consvars; keep
the right-most unique value.
+If <literal>unique</literal> is false,
+values are added even if they are duplicates.
</para>
<para>
@@ -2669,9 +2682,13 @@ env.MergeFlags(['!pkg-config gtk+-2.0 --cflags', '-O3'])
# Combine an optimization flag with the flags returned from running pkg-config
# twice and merge the result into the construction variables.
-env.MergeFlags(['-O3',
- '!pkg-config gtk+-2.0 --cflags --libs',
- '!pkg-config libpng12 --cflags --libs'])
+env.MergeFlags(
+ [
+ '-O3',
+ '!pkg-config gtk+-2.0 --cflags --libs',
+ '!pkg-config libpng12 --cflags --libs',
+ ]
+)
</example_commands>
</listitem>
</varlistentry>
@@ -2773,15 +2790,13 @@ NoClean(env.Program('hello', 'hello.c'))
<term><replaceable>env</replaceable>.<methodname>ParseConfig</methodname>(<parameter>command, [function, unique]</parameter>)</term>
<listitem><para>
Updates the current &consenv; with the values extracted
-from the output from running external <parameter>command</parameter>,
-by calling a helper function <parameter>function</parameter>
-which understands
-the output of <parameter>command</parameter>.
+from the output of running external <parameter>command</parameter>,
+by passing it to a helper <parameter>function</parameter>.
<parameter>command</parameter> may be a string
or a list of strings representing the command and
its arguments.
If <parameter>function</parameter>
-is not given,
+is omitted or <constant>None</constant>,
&f-link-env-MergeFlags; is used.
By default,
duplicate values are not
@@ -2792,33 +2807,32 @@ to allow duplicate values to be added.
</para>
<para>
-If &f-env-MergeFlags; is used,
-it expects a response in the style of a
-<command>*-config</command>
-command typical of the POSIX programming environment
-(for example,
-<application>gtk-config</application>)
-and adds the options
-to the appropriate construction variables.
-Interpreted options
-and the construction variables they affect
-are as specified for the
-&f-link-env-ParseFlags;
-method (which
-&f-env-MergeFlags; calls).
-See that method's description
-for a table of options and corresponding construction variables.
+<parameter>command</parameter> is executed using the
+SCons execution environment (that is, the &consvar;
+&cv-link-ENV; in the current &consenv;).
+If <parameter>command</parameter> needs additional information
+to operate properly, that needs to be set in the execution environment.
+For example, <command>pkg-config</command>
+may need a custom value set in the <envar>PKG_CONFIG_PATH</envar>
+environment variable.
</para>
<para>
-If &f-env-MergeFlags; cannot interpret the results of
+&f-env-MergeFlags; needs to understand
+the output produced by <parameter>command</parameter>
+in order to distribute it to appropriate &consvars;.
+&f-env-MergeFlags; uses a separate function to
+do that processing -
+see &f-link-env-ParseFlags; for the details, including a
+a table of options and corresponding construction variables.
+To provide alternative processing of the output of
<parameter>command</parameter>,
you can suppply a custom
-<parameter>function</parameter> to do so.
-<parameter>function</parameter>
-must accept three arguments:
-the &consenv; to modify, the string returned
-by running <parameter>command</parameter>,
+<parameter>function</parameter>,
+which must accept three arguments:
+the &consenv; to modify,
+a string argument containing the output from running
+<parameter>command</parameter>,
and the optional
<parameter>unique</parameter> flag.
</para>
@@ -2828,8 +2842,7 @@ and the optional
<term><function>ParseDepends</function>(<parameter>filename, [must_exist, only_one]</parameter>)</term>
<term><replaceable>env</replaceable>.<methodname>ParseDepends</methodname>(<parameter>filename, [must_exist, only_one]</parameter>)</term>
<listitem><para>
-Parses the contents of the specified
-<parameter>filename</parameter>
+Parses the contents of <parameter>filename</parameter>
as a list of dependencies in the style of
&Make;
or
@@ -2840,27 +2853,21 @@ and explicitly establishes all of the listed dependencies.
<para>
By default,
it is not an error
-if the specified
-<parameter>filename</parameter>
+if <parameter>filename</parameter>
does not exist.
The optional
<parameter>must_exist</parameter>
-argument may be set to a non-zero
-value to have
-scons
-throw an exception and
-generate an error if the file does not exist,
+argument may be set to <constant>True</constant>
+to have &SCons;
+raise an exception if the file does not exist,
or is otherwise inaccessible.
</para>
<para>
The optional
<parameter>only_one</parameter>
-argument may be set to a non-zero
-value to have
-scons
-thrown an exception and
-generate an error
+argument may be set to <constant>True</constant>
+to have &SCons; raise an exception
if the file contains dependency
information for more than one target.
This can provide a small sanity check
@@ -2876,7 +2883,6 @@ file.
</para>
<para>
-The
<parameter>filename</parameter>
and all of the files listed therein
will be interpreted relative to
@@ -2892,10 +2898,10 @@ function.
<term><replaceable>env</replaceable>.<methodname>ParseFlags</methodname>(<parameter>flags, ...</parameter>)</term>
<listitem><para>
Parses one or more strings containing
-typical command-line flags for GCC tool chains
+typical command-line flags for GCC-style tool chains
and returns a dictionary with the flag values
separated into the appropriate SCons construction variables.
-This is intended as a companion to the
+Intended as a companion to the
&f-link-env-MergeFlags;
method, but allows for the values in the returned dictionary
to be modified, if necessary,
@@ -2910,15 +2916,24 @@ directly unless you want to manipulate the values.)
<para>
If the first character in any string is
-an exclamation mark (!),
+an exclamation mark (<literal>!</literal>),
the rest of the string is executed as a command,
and the output from the command is
parsed as GCC tool chain command-line flags
and added to the resulting dictionary.
+This can be used to call a <filename>*-config</filename>
+command typical of the POSIX programming environment
+(for example,
+<command>pkg-config</command>).
+Note that such a command is executed using the
+SCons execution environment;
+if the command needs additional information,
+that information needs to be explicitly provided.
+See &f-link-ParseConfig; for more details.
</para>
<para>
-Flag values are translated accordig to the prefix found,
+Flag values are translated according to the prefix found,
and added to the following construction variables:
</para>
@@ -2929,6 +2944,7 @@ and added to the following construction variables:
-frameworkdir= FRAMEWORKPATH
-fmerge-all-constants CCFLAGS, LINKFLAGS
-fopenmp CCFLAGS, LINKFLAGS
+-fsanitize CCFLAGS, LINKFLAGS
-include CCFLAGS
-imacros CCFLAGS
-isysroot CCFLAGS, LINKFLAGS
@@ -3051,30 +3067,28 @@ and &f-link-env-PrependUnique;.
</listitem>
</varlistentry>
<varlistentry id="f-PrependENVPath">
- <term><replaceable>env</replaceable>.<methodname>PrependENVPath</methodname>(<parameter>name, newpath, [envname, sep, delete_existing]</parameter>)</term>
+ <term><replaceable>env</replaceable>.<methodname>PrependENVPath</methodname>(<parameter>name, newpath, [envname, sep, delete_existing=True]</parameter>)</term>
<listitem><para>
-Prepend new path elements to the given path in the
-specified external environment (&cv-link-ENV; by default).
-This will only add
-any particular path once (leaving the first one it encounters and
-ignoring the rest, to preserve path order),
-and to help assure this,
-will normalize all paths (using
+Prepend path elements specified by <parameter>newpath</parameter>
+to the given search path string or list <parameter>name</parameter>
+in mapping <parameter>envname</parameter> in the &consenv;.
+Supplying <parameter>envname</parameter> is optional:
+the default is the execution environment &cv-link-ENV;.
+Optional <parameter>sep</parameter> is used as the search path separator,
+the default is the platform's separator (<systemitem>os.pathsep</systemitem>).
+A path element will only appear once.
+Any duplicates in <parameter>newpath</parameter> are dropped,
+keeping the first appearing (to preserve path order).
+If <parameter>delete_existing</parameter>
+is <constant>False</constant>
+any addition duplicating an existing path element is ignored;
+if <parameter>delete_existing</parameter>
+is <constant>True</constant> (the default) the existing value will
+be dropped and the path element will be inserted at the beginning.
+To help maintain uniqueness all paths are normalized (using
<systemitem>os.path.normpath</systemitem>
and
<systemitem>os.path.normcase</systemitem>).
-This can also handle the
-case where the given old path variable is a list instead of a
-string, in which case a list will be returned instead of a string.
-</para>
-
-<para>
-If
-<parameter>delete_existing</parameter>
-is <constant>False</constant>,
-then adding a path that already exists
-will not move it to the beginning;
-it will stay where it is in the list.
</para>
<para>
@@ -3094,6 +3108,11 @@ print('after:', env['ENV']['INCLUDE'])
before: /biz:/foo
after: /foo/bar:/foo:/biz
</screen>
+
+<para>
+See also &f-link-env-AppendENVPath;.
+</para>
+
</listitem>
</varlistentry>
<varlistentry id="f-PrependUnique">
@@ -3103,7 +3122,7 @@ Prepend values to &consvars; in the current &consenv;,
maintaining uniqueness.
Works like &f-link-env-Append; (see for details),
except that values are added to the front,
-rather than the end, of any existing value of the the &consvar;,
+rather than the end, of any existing value of the &consvar;,
and values already present in the &consvar;
will not be added again.
If <parameter>delete_existing</parameter>
@@ -3461,40 +3480,36 @@ for a complete explanation of the arguments and behavior.
<varlistentry id="f-SConscript">
<term><function>SConscript</function>(<parameter>scripts, [exports, variant_dir, duplicate, must_exist]</parameter>)</term>
<term><replaceable>env</replaceable>.<methodname>SConscript</methodname>(<parameter>scripts, [exports, variant_dir, duplicate, must_exist]</parameter>)</term>
- <term><function>SConscript</function>(<parameter>dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist]</parameter>)</term>
- <term><replaceable>env</replaceable>.<methodname>SConscript</methodname>(<parameter>dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist]</parameter>)</term>
+ <term><function>SConscript</function>(<parameter>dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist]</parameter>)</term>
+ <term><replaceable>env</replaceable>.<methodname>SConscript</methodname>(<parameter>dirs=subdirs, [name=scriptname, exports, variant_dir, duplicate, must_exist]</parameter>)</term>
<listitem><para>
-Execute one or more subsidiary SConscript (configuration) files.
+Executes one or more subsidiary SConscript (configuration) files.
There are two ways to call the
&f-SConscript; function.
</para>
<para>
-The first calling style
-is to explicitly specify one or more
-<varname>scripts</varname>
-as the first argument.
+The first calling style is to supply
+one or more SConscript file names
+as the first (positional) argument.
A single script may be specified as a string;
-multiple scripts must be specified as a list
+multiple scripts must be specified as a list of strings
(either explicitly or as created by
a function like
&f-link-Split;).
Examples:
</para>
<example_commands>
-SConscript('SConscript') # run SConscript in the current directory
+SConscript('SConscript') # run SConscript in the current directory
SConscript('src/SConscript') # run SConscript in the src directory
SConscript(['src/SConscript', 'doc/SConscript'])
config = SConscript('MyConfig.py')
</example_commands>
<para>
-The second way to call
-&f-SConscript;
-is to specify a list of (sub)directory names
-as a
-<varname>dirs</varname>=<replaceable>subdirs</replaceable>
-keyword argument.
+The other calling style is to omit the positional argument naming
+scripts and instead specify a list of directory names using the
+<varname>dirs</varname> keyword argument.
In this case,
&scons;
will
@@ -3504,14 +3519,14 @@ in each of the specified directories.
You may specify a name other than
&SConscript;
by supplying an optional
-<varname>name</varname>=<replaceable>script</replaceable>
+<varname>name</varname>=<replaceable>scriptname</replaceable>
keyword argument.
The first three examples below have the same effect
as the first three examples above:
</para>
<example_commands>
-SConscript(dirs='.') # run SConscript in the current directory
-SConscript(dirs='src') # run SConscript in the src directory
+SConscript(dirs='.') # run SConscript in the current directory
+SConscript(dirs='src') # run SConscript in the src directory
SConscript(dirs=['src', 'doc'])
SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
</example_commands>
@@ -3519,8 +3534,12 @@ SConscript(dirs=['sub1', 'sub2'], name='MySConscript')
<para>
The optional
<varname>exports</varname>
-argument provides a string or list of strings representing
+keyword argument provides a string or list of strings representing
variable names, or a dictionary of named values, to export.
+For the first calling style only, a second positional argument
+will be interpreted as <varname>exports</varname>; the
+second calling style must use the keyword argument form
+for <varname>exports</varname>.
These variables are locally exported only to the called
SConscript file(s)
and do not affect the global pool of variables managed by the
@@ -3544,38 +3563,24 @@ SConscript(dirs=['one', 'two', 'three'], exports='shared_info')
If the optional
<varname>variant_dir</varname>
argument is present, it causes an effect equivalent to the
-&f-link-VariantDir; function.
+&f-link-VariantDir; function,
+but in effect only within the scope of the &f-SConscript; call.
The <varname>variant_dir</varname>
-argument is interpreted relative to the directory of the calling
-SConscript file.
-The optional
-<varname>duplicate</varname> argument is
-interpreted as for &f-link-VariantDir;.
-If <varname>variant_dir</varname>
-is omitted, the <varname>duplicate</varname> argument is ignored.
-See the description of
-&f-link-VariantDir;
-below for additional details and restrictions.
-</para>
-
-<para>
-If
-<varname>variant_dir</varname>
-is present,
-the source directory is the directory in which the
-SConscript
-file resides and the
-SConscript
+argument is interpreted relative to the directory of the
+<emphasis>calling</emphasis> SConscript file.
+The source directory is the directory in which the
+<emphasis>called</emphasis> SConscript
+file resides and the SConscript
file is evaluated as if it were in the
<varname>variant_dir</varname>
-directory:
+directory. Thus:
</para>
<example_commands>
SConscript('src/SConscript', variant_dir='build')
</example_commands>
<para>
-is equivalent to
+is equivalent to:
</para>
<example_commands>
@@ -3584,9 +3589,8 @@ SConscript('build/SConscript')
</example_commands>
<para>
-This later paradigm is often used when the sources are
-in the same directory as the
-&SConstruct;:
+If the sources are in the same directory as the
+&SConstruct;,
</para>
<example_commands>
@@ -3594,7 +3598,7 @@ SConscript('SConscript', variant_dir='build')
</example_commands>
<para>
-is equivalent to
+is equivalent to:
</para>
<example_commands>
@@ -3603,6 +3607,17 @@ SConscript('build/SConscript')
</example_commands>
<para>
+The optional
+<varname>duplicate</varname> argument is
+interpreted as for &f-link-VariantDir;.
+If the <varname>variant_dir</varname> argument
+is omitted, the <varname>duplicate</varname> argument is ignored.
+See the description of
+&f-link-VariantDir;
+for additional details and restrictions.
+</para>
+
+<para>
<!--
If
<varname>variant_dir</varname>
@@ -3685,11 +3700,11 @@ SConscript('src/SConscript', variant_dir='build/ppc', duplicate=0)
<para>
&f-SConscript; returns the values of any variables
-named by the executed SConscript(s) in arguments
-to the &f-link-Return; function (see above for details).
+named by the executed SConscript file(s) in arguments
+to the &f-link-Return; function.
If a single &f-SConscript; call causes multiple scripts to
be executed, the return value is a tuple containing
-the returns of all of the scripts. If an executed
+the returns of each of the scripts. If an executed
script does not explicitly call &Return;, it returns
<constant>None</constant>.
</para>
@@ -3724,7 +3739,7 @@ when all the directories in which SConscript files may be found
don't necessarily exist locally.)
You may enable and disable
this ability by calling
-SConscriptChdir()
+&f-SConscriptChdir;
multiple times.
</para>
@@ -4159,9 +4174,16 @@ files = Split("""
<varlistentry id="f-subst">
<term><replaceable>env</replaceable>.<methodname>subst</methodname>(<parameter>input, [raw, target, source, conv]</parameter>)</term>
<listitem><para>
-Performs construction variable interpolation
+Performs &consvar; interpolation
+(<firstterm>substitution</firstterm>)
on <parameter>input</parameter>,
which can be a string or a sequence.
+Substitutable elements take the form
+<literal>${<replaceable>expression</replaceable>}</literal>,
+although if there is no ambiguity in recognizing the element,
+the braces can be omitted.
+A literal <emphasis role="bold">$</emphasis> can be entered by
+using <emphasis role="bold">$$</emphasis>.
</para>
<para>
@@ -4196,7 +4218,7 @@ pairs
</para>
<para>
-If the input is a sequence
+If <parameter>input</parameter> is a sequence
(list or tuple),
the individual elements of
the sequence will be expanded,
@@ -4362,6 +4384,61 @@ the tool object, previously it did not return
</para>
</listitem>
</varlistentry>
+ <varlistentry id="f-ValidateOptions">
+ <term><function>ValidateOptions</function>(<parameter>[throw_exception=False]</parameter>)</term>
+ <listitem><para>
+ Check that all the options specified on the command line are either defined by SCons itself
+ or defined by calls to &f-link-AddOption;.
+ </para>
+ <para>
+ This function should only be called after the last &f-link-AddOption; call in your &SConscript;
+ logic.
+ </para>
+ <para>
+ Be aware that some tools call &f-link-AddOption;, if you are getting error messages for arguments
+ that they add, you will need to ensure that you load those tools before you call &f-ValidateOptions;.
+ </para>
+ <para>
+ If there are any command line options not defined, calling this function will cause SCons to issue an
+ error message and then exit with an error exit
+ status.</para>
+ <para>If the optional <parameter>throw_exception</parameter> is <literal>True</literal>, &f-ValidateOptions; will raise a
+ <exceptionname>SConsBadOptionError</exceptionname>
+ exception. This would allow the calling
+ &SConscript; logic can catch that exception and handle invalid options itself.
+ </para>
+
+ <para>
+ Example:
+ </para>
+
+ <example_commands>
+try:
+ ValidateOptions(throw_exception=True)
+except SConsBadOptionError as e:
+ print("Parser is SConsOptionParser:%s" % (isinstance(e.parser, SConsOptionParser)))
+ print("Message is :%s" % e.opt_str)
+ Exit(3)
+ </example_commands>
+
+ <para>
+ This function is useful to force SCons to fail fast before you execute any expensive logic later in your
+ build logic.
+ For example if you specify build options via any flags, a simple typo could yield the incorrect build
+ option throughout your entire build.
+ </para>
+ <example_commands>
+scons --compilers=mingw (the correct flag is --compiler)
+ </example_commands>
+ <para>
+ Could cause SCons to run configure steps with the incorrect compiler. Costing developer time trying to
+ track down why the configure logic failed with a compiler which should work.
+ </para>
+
+
+
+ </listitem>
+ </varlistentry>
<varlistentry id="f-Value">
<term><function>Value</function>(<parameter>value, [built_value], [name]</parameter>)</term>
<term><replaceable>env</replaceable>.<methodname>Value</methodname>(<parameter>value, [built_value], [name]</parameter>)</term>
@@ -4424,7 +4501,7 @@ env.Config(target = 'package-config', source = Value(prefix))
def build_value(target, source, env):
# A function that "builds" a Python Value by updating
- # the the Python value with the contents of the file
+ # the Python value with the contents of the file
# specified as the source of the Builder call ($SOURCE).
target[0].write(source[0].get_contents())
@@ -4442,42 +4519,41 @@ env.UpdateValue(target = Value(output), source = Value(input))
<term><function>VariantDir</function>(<parameter>variant_dir, src_dir, [duplicate]</parameter>)</term>
<term><replaceable>env</replaceable>.<methodname>VariantDir</methodname>(<parameter>variant_dir, src_dir, [duplicate]</parameter>)</term>
<listitem><para>
-Sets up an alternate build location.
-When building in the <parameter>variant_dir</parameter>,
-&SCons; backfills as needed with files from <parameter>src_dir</parameter>
-to create a complete build directory.
+Sets up a mapping to define a variant build directory in
+<parameter>variant_dir</parameter>.
+<parameter>src_dir</parameter> may not be underneath
+<parameter>variant_dir</parameter>.
+A &f-VariantDir; mapping is global, even if called using the
+&f-env-VariantDir; form.
&f-VariantDir;
can be called multiple times with the same
<parameter>src_dir</parameter>
-to set up multiple builds with different options
-(<emphasis>variants</emphasis>).
+to set up multiple variant builds with different options.
</para>
<para>
-The
-<parameter>variant</parameter>
-location must be in or underneath the project top directory,
-and <parameter>src_dir</parameter>
-may not be underneath
-<parameter>variant_dir</parameter>.
+Note if <parameter>variant_dir</parameter>
+is not under the project top directory,
+target selection rules will not pick targets in the
+variant directory unless they are explicitly specified.
</para>
<para>
+When files in <parameter>variant_dir</parameter> are referenced,
+&SCons; backfills as needed with files from <parameter>src_dir</parameter>
+to create a complete build directory.
By default, &SCons;
-physically duplicates the source files and SConscript files
-as needed into the variant tree.
-Thus, a build performed in the variant tree is guaranteed to be identical
-to a build performed in the source tree even if
+physically duplicates the source files, SConscript files,
+and directory structure as needed into the variant directory.
+Thus, a build performed in the variant directory is guaranteed to be identical
+to a build performed in the source directory even if
intermediate source files are generated during the build,
or if preprocessors or other scanners search for included files
-relative to the source file,
+using paths relative to the source file,
or if individual compilers or other invoked tools are hard-coded
to put derived files in the same directory as source files.
Only the files &SCons; calculates are needed for the build are
duplicated into <parameter>variant_dir</parameter>.
-</para>
-
-<para>
If possible on the platform,
the duplication is performed by linking rather than copying.
This behavior is affected by the
@@ -4496,44 +4572,46 @@ to invoke Builders using the path names of source files in
<parameter>src_dir</parameter>
and the path names of derived files within
<parameter>variant_dir</parameter>.
-This is more efficient than
-<literal>duplicate=True</literal>,
+This is more efficient than duplicating,
and is safe for most builds;
-revert to <constant>True</constant>
+revert to <literal>duplicate=True</literal>
if it causes problems.
</para>
<para>
&f-VariantDir;
-works most naturally with used with a subsidiary SConscript file.
-The subsidiary SConscript file is called as if it
-were in
+works most naturally when used with a subsidiary SConscript file.
+The subsidiary SConscript file must be called as if it were in
<parameter>variant_dir</parameter>,
regardless of the value of
<parameter>duplicate</parameter>.
-This is how you tell
-&scons;
-which variant of a source tree to build:
+When calling an SConscript file, you can use the
+<parameter>exports</parameter> keyword argument
+to pass parameters (individually or as an appropriately set up environment)
+so the SConscript can pick up the right settings for that variant build.
+The SConscript must &f-link-Import; these to use them. Example:
</para>
<example_commands>
+env1 = Environment(...settings for variant1...)
+env2 = Environment(...settings for variant2...)
+
# run src/SConscript in two variant directories
VariantDir('build/variant1', 'src')
-SConscript('build/variant1/SConscript')
+SConscript('build/variant1/SConscript', exports={"env": env1})
VariantDir('build/variant2', 'src')
-SConscript('build/variant2/SConscript')
+SConscript('build/variant2/SConscript', exports={"env": env2})
</example_commands>
<para>
See also the
-&f-link-SConscript;
-function, described above,
+&f-link-SConscript; function
for another way to specify a variant directory
in conjunction with calling a subsidiary SConscript file.
</para>
<para>
-Examples:
+More examples:
</para>
<example_commands>
diff --git a/doc/generated/functions.mod b/doc/generated/functions.mod
index 9701bf3..34424a5 100644
--- a/doc/generated/functions.mod
+++ b/doc/generated/functions.mod
@@ -84,6 +84,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY f-subst "<function xmlns='http://www.scons.org/dbxsd/v1.0'>subst</function>">
<!ENTITY f-Tag "<function xmlns='http://www.scons.org/dbxsd/v1.0'>Tag</function>">
<!ENTITY f-Tool "<function xmlns='http://www.scons.org/dbxsd/v1.0'>Tool</function>">
+<!ENTITY f-ValidateOptions "<function xmlns='http://www.scons.org/dbxsd/v1.0'>ValidateOptions</function>">
<!ENTITY f-Value "<function xmlns='http://www.scons.org/dbxsd/v1.0'>Value</function>">
<!ENTITY f-VariantDir "<function xmlns='http://www.scons.org/dbxsd/v1.0'>VariantDir</function>">
<!ENTITY f-WhereIs "<function xmlns='http://www.scons.org/dbxsd/v1.0'>WhereIs</function>">
@@ -164,6 +165,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY f-env-subst "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.subst</function>">
<!ENTITY f-env-Tag "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.Tag</function>">
<!ENTITY f-env-Tool "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.Tool</function>">
+<!ENTITY f-env-ValidateOptions "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.ValidateOptions</function>">
<!ENTITY f-env-Value "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.Value</function>">
<!ENTITY f-env-VariantDir "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.VariantDir</function>">
<!ENTITY f-env-WhereIs "<function xmlns='http://www.scons.org/dbxsd/v1.0'>env.WhereIs</function>">
@@ -250,6 +252,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY f-link-subst "<link linkend='f-subst' xmlns='http://www.scons.org/dbxsd/v1.0'><function>subst</function></link>">
<!ENTITY f-link-Tag "<link linkend='f-Tag' xmlns='http://www.scons.org/dbxsd/v1.0'><function>Tag</function></link>">
<!ENTITY f-link-Tool "<link linkend='f-Tool' xmlns='http://www.scons.org/dbxsd/v1.0'><function>Tool</function></link>">
+<!ENTITY f-link-ValidateOptions "<link linkend='f-ValidateOptions' xmlns='http://www.scons.org/dbxsd/v1.0'><function>ValidateOptions</function></link>">
<!ENTITY f-link-Value "<link linkend='f-Value' xmlns='http://www.scons.org/dbxsd/v1.0'><function>Value</function></link>">
<!ENTITY f-link-VariantDir "<link linkend='f-VariantDir' xmlns='http://www.scons.org/dbxsd/v1.0'><function>VariantDir</function></link>">
<!ENTITY f-link-WhereIs "<link linkend='f-WhereIs' xmlns='http://www.scons.org/dbxsd/v1.0'><function>WhereIs</function></link>">
@@ -330,6 +333,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY f-link-env-subst "<link linkend='f-subst' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.subst</function></link>">
<!ENTITY f-link-env-Tag "<link linkend='f-Tag' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.Tag</function></link>">
<!ENTITY f-link-env-Tool "<link linkend='f-Tool' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.Tool</function></link>">
+<!ENTITY f-link-env-ValidateOptions "<link linkend='f-ValidateOptions' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.ValidateOptions</function></link>">
<!ENTITY f-link-env-Value "<link linkend='f-Value' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.Value</function></link>">
<!ENTITY f-link-env-VariantDir "<link linkend='f-VariantDir' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.VariantDir</function></link>">
<!ENTITY f-link-env-WhereIs "<link linkend='f-WhereIs' xmlns='http://www.scons.org/dbxsd/v1.0'><function>env.WhereIs</function></link>">
diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen
index 43c6531..f05b470 100644
--- a/doc/generated/tools.gen
+++ b/doc/generated/tools.gen
@@ -82,14 +82,14 @@ Sets construction variables for the bcc32 compiler.
<listitem><para>
Sets construction variables for generic POSIX C compilers.
</para>
-<para>Sets: &cv-link-CC;, &cv-link-CCCOM;, &cv-link-CCFLAGS;, &cv-link-CFILESUFFIX;, &cv-link-CFLAGS;, &cv-link-CPPDEFPREFIX;, &cv-link-CPPDEFSUFFIX;, &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKS;, &cv-link-INCPREFIX;, &cv-link-INCSUFFIX;, &cv-link-SHCC;, &cv-link-SHCCCOM;, &cv-link-SHCCFLAGS;, &cv-link-SHCFLAGS;, &cv-link-SHOBJSUFFIX;.</para><para>Uses: &cv-link-CCCOMSTR;, &cv-link-PLATFORM;, &cv-link-SHCCCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-CC;, &cv-link-CCCOM;, &cv-link-CCDEPFLAGS;, &cv-link-CCFLAGS;, &cv-link-CFILESUFFIX;, &cv-link-CFLAGS;, &cv-link-CPPDEFPREFIX;, &cv-link-CPPDEFSUFFIX;, &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKS;, &cv-link-INCPREFIX;, &cv-link-INCSUFFIX;, &cv-link-SHCC;, &cv-link-SHCCCOM;, &cv-link-SHCCFLAGS;, &cv-link-SHCFLAGS;, &cv-link-SHOBJSUFFIX;.</para><para>Uses: &cv-link-CCCOMSTR;, &cv-link-PLATFORM;, &cv-link-SHCCCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-clang">
<term>clang</term>
<listitem><para>
Set construction variables for the Clang C compiler.
</para>
-<para>Sets: &cv-link-CC;, &cv-link-CCVERSION;, &cv-link-SHCCFLAGS;.</para></listitem>
+<para>Sets: &cv-link-CC;, &cv-link-CCDEPFLAGS;, &cv-link-CCVERSION;, &cv-link-SHCCFLAGS;.</para></listitem>
</varlistentry>
<varlistentry id="t-clangxx">
<term>clangxx</term>
@@ -389,42 +389,42 @@ Sets construction variables for the dvips utility.
<listitem><para>
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>
+<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-FORTRANCOMMONFLAGS;, &cv-link-SHF03COMSTR;, &cv-link-SHF03PPCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-f08">
<term>f08</term>
<listitem><para>
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>
+<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-FORTRANCOMMONFLAGS;, &cv-link-SHF08COMSTR;, &cv-link-SHF08PPCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-f77">
<term>f77</term>
<listitem><para>
Set construction variables for generic POSIX Fortran 77 compilers.
</para>
-<para>Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77FLAGS;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;, &cv-link-_F77INCFLAGS;.</para><para>Uses: &cv-link-F77COMSTR;, &cv-link-F77PPCOMSTR;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHF77COMSTR;, &cv-link-SHF77PPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANPPCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77FLAGS;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;, &cv-link-_F77INCFLAGS;.</para><para>Uses: &cv-link-F77COMSTR;, &cv-link-F77PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANFLAGS;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHF77COMSTR;, &cv-link-SHF77PPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-f90">
<term>f90</term>
<listitem><para>
Set construction variables for generic POSIX Fortran 90 compilers.
</para>
-<para>Sets: &cv-link-F90;, &cv-link-F90COM;, &cv-link-F90FLAGS;, &cv-link-F90PPCOM;, &cv-link-SHF90;, &cv-link-SHF90COM;, &cv-link-SHF90FLAGS;, &cv-link-SHF90PPCOM;, &cv-link-_F90INCFLAGS;.</para><para>Uses: &cv-link-F90COMSTR;, &cv-link-F90PPCOMSTR;, &cv-link-SHF90COMSTR;, &cv-link-SHF90PPCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-F90;, &cv-link-F90COM;, &cv-link-F90FLAGS;, &cv-link-F90PPCOM;, &cv-link-SHF90;, &cv-link-SHF90COM;, &cv-link-SHF90FLAGS;, &cv-link-SHF90PPCOM;, &cv-link-_F90INCFLAGS;.</para><para>Uses: &cv-link-F90COMSTR;, &cv-link-F90PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF90COMSTR;, &cv-link-SHF90PPCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-f95">
<term>f95</term>
<listitem><para>
Set construction variables for generic POSIX Fortran 95 compilers.
</para>
-<para>Sets: &cv-link-F95;, &cv-link-F95COM;, &cv-link-F95FLAGS;, &cv-link-F95PPCOM;, &cv-link-SHF95;, &cv-link-SHF95COM;, &cv-link-SHF95FLAGS;, &cv-link-SHF95PPCOM;, &cv-link-_F95INCFLAGS;.</para><para>Uses: &cv-link-F95COMSTR;, &cv-link-F95PPCOMSTR;, &cv-link-SHF95COMSTR;, &cv-link-SHF95PPCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-F95;, &cv-link-F95COM;, &cv-link-F95FLAGS;, &cv-link-F95PPCOM;, &cv-link-SHF95;, &cv-link-SHF95COM;, &cv-link-SHF95FLAGS;, &cv-link-SHF95PPCOM;, &cv-link-_F95INCFLAGS;.</para><para>Uses: &cv-link-F95COMSTR;, &cv-link-F95PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF95COMSTR;, &cv-link-SHF95PPCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-fortran">
<term>fortran</term>
<listitem><para>
-Set construction variables for generic POSIX Fortran compilers.
+Set &consvars; for generic POSIX Fortran compilers.
</para>
-<para>Sets: &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;.</para><para>Uses: &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANPPCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;.</para><para>Uses: &cv-link-CPPFLAGS;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANPPCOMSTR;, &cv-link-_CPPDEFFLAGS;.</para></listitem>
</varlistentry>
<varlistentry id="t-gXX">
<term>g++</term>
@@ -437,10 +437,8 @@ Set construction variables for the &gXX; C++ compiler.
<term>g77</term>
<listitem><para>
Set construction variables for the &g77; Fortran compiler.
-Calls the &t-f77; Tool module
-to set variables.
</para>
-</listitem>
+<para>Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANPPCOM;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;.</para><para>Uses: &cv-link-F77FLAGS;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-FORTRANFLAGS;.</para></listitem>
</varlistentry>
<varlistentry id="t-gas">
<term>gas</term>
@@ -455,7 +453,7 @@ Calls the &t-link-as; tool.
<listitem><para>
Set construction variables for the &gcc; C compiler.
</para>
-<para>Sets: &cv-link-CC;, &cv-link-CCVERSION;, &cv-link-SHCCFLAGS;.</para></listitem>
+<para>Sets: &cv-link-CC;, &cv-link-CCDEPFLAGS;, &cv-link-CCVERSION;, &cv-link-SHCCFLAGS;.</para></listitem>
</varlistentry>
<varlistentry id="t-gdc">
<term>gdc</term>
@@ -516,7 +514,8 @@ environment:
<varlistentry id="t-gfortran">
<term>gfortran</term>
<listitem><para>
-Sets construction variables for the GNU F95/F2003 GNU compiler.
+Sets construction variables for the GNU Fortran compiler.
+Calls the &t-link-fortran; Tool module to set variables.
</para>
<para>Sets: &cv-link-F77;, &cv-link-F90;, &cv-link-F95;, &cv-link-FORTRAN;, &cv-link-SHF77;, &cv-link-SHF77FLAGS;, &cv-link-SHF90;, &cv-link-SHF90FLAGS;, &cv-link-SHF95;, &cv-link-SHF95FLAGS;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANFLAGS;.</para></listitem>
</varlistentry>
@@ -668,7 +667,7 @@ Sets construction variables for the D language compiler LDC2.
<listitem><para>
Sets construction variables for the &lex; lexical analyser.
</para>
-<para>Sets: &cv-link-LEX;, &cv-link-LEXCOM;, &cv-link-LEXFLAGS;, &cv-link-LEXUNISTD;.</para><para>Uses: &cv-link-LEXCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-LEX;, &cv-link-LEXCOM;, &cv-link-LEXFLAGS;, &cv-link-LEXUNISTD;.</para><para>Uses: &cv-link-LEXCOMSTR;, &cv-link-LEXFLAGS;, &cv-link-LEX_HEADER_FILE;, &cv-link-LEX_TABLES_FILE;.</para></listitem>
</varlistentry>
<varlistentry id="t-link">
<term>link</term>
@@ -782,7 +781,7 @@ the Microsoft toolchain:
<listitem><para>
Sets construction variables for the Microsoft Visual C/C++ compiler.
</para>
-<para>Sets: &cv-link-BUILDERS;, &cv-link-CC;, &cv-link-CCCOM;, &cv-link-CCFLAGS;, &cv-link-CCPCHFLAGS;, &cv-link-CCPDBFLAGS;, &cv-link-CFILESUFFIX;, &cv-link-CFLAGS;, &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-OBJPREFIX;, &cv-link-OBJSUFFIX;, &cv-link-PCHCOM;, &cv-link-PCHPDBFLAGS;, &cv-link-RC;, &cv-link-RCCOM;, &cv-link-RCFLAGS;, &cv-link-SHCC;, &cv-link-SHCCCOM;, &cv-link-SHCCFLAGS;, &cv-link-SHCFLAGS;, &cv-link-SHCXX;, &cv-link-SHCXXCOM;, &cv-link-SHCXXFLAGS;, &cv-link-SHOBJPREFIX;, &cv-link-SHOBJSUFFIX;.</para><para>Uses: &cv-link-CCCOMSTR;, &cv-link-CXXCOMSTR;, &cv-link-PCH;, &cv-link-PCHSTOP;, &cv-link-PDB;, &cv-link-SHCCCOMSTR;, &cv-link-SHCXXCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-BUILDERS;, &cv-link-CC;, &cv-link-CCCOM;, &cv-link-CCDEPFLAGS;, &cv-link-CCFLAGS;, &cv-link-CCPCHFLAGS;, &cv-link-CCPDBFLAGS;, &cv-link-CFILESUFFIX;, &cv-link-CFLAGS;, &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-OBJPREFIX;, &cv-link-OBJSUFFIX;, &cv-link-PCHCOM;, &cv-link-PCHPDBFLAGS;, &cv-link-RC;, &cv-link-RCCOM;, &cv-link-RCFLAGS;, &cv-link-SHCC;, &cv-link-SHCCCOM;, &cv-link-SHCCFLAGS;, &cv-link-SHCFLAGS;, &cv-link-SHCXX;, &cv-link-SHCXXCOM;, &cv-link-SHCXXFLAGS;, &cv-link-SHOBJPREFIX;, &cv-link-SHOBJSUFFIX;.</para><para>Uses: &cv-link-CCCOMSTR;, &cv-link-CXXCOMSTR;, &cv-link-MSVC_NOTFOUND_POLICY;, &cv-link-PCH;, &cv-link-PCHSTOP;, &cv-link-PDB;, &cv-link-SHCCCOMSTR;, &cv-link-SHCXXCOMSTR;.</para></listitem>
</varlistentry>
<varlistentry id="t-msvs">
<term>msvs</term>
@@ -814,15 +813,15 @@ Sets construction variables for the
<varlistentry id="t-ninja">
<term>ninja</term>
<listitem><para>
- Sets up &b-link-Ninja; builder which generates a ninja build file, and then optionally runs &ninja;.
+ Sets up the &b-link-Ninja; builder, which generates a &ninja; build file, and then optionally runs &ninja;.
</para>
<note>
<para>This is an experimental feature.
+ This functionality is subject to change and/or removal without a deprecation cycle.
</para>
- <para>This functionality is subject to change and/or removal without deprecation cycle.</para>
</note>
- <para>Sets: &cv-link-IMPLICIT_COMMAND_DEPENDENCIES;, &cv-link-NINJA_ALIAS_NAME;, &cv-link-NINJA_COMPDB_EXPAND;, &cv-link-NINJA_DIR;, &cv-link-NINJA_DISABLE_AUTO_RUN;, &cv-link-NINJA_ENV_VAR_CACHE;, &cv-link-NINJA_FILE_NAME;, &cv-link-NINJA_FORCE_SCONS_BUILD;, &cv-link-NINJA_GENERATED_SOURCE_SUFFIXES;, &cv-link-NINJA_MSVC_DEPS_PREFIX;, &cv-link-NINJA_POOL;, &cv-link-NINJA_REGENERATE_DEPS;, &cv-link-NINJA_SYNTAX;, &cv-link-_NINJA_REGENERATE_DEPS_FUNC;, &cv-link-__NINJA_NO;.</para><para>Uses: &cv-link-AR;, &cv-link-ARCOM;, &cv-link-ARFLAGS;, &cv-link-CC;, &cv-link-CCCOM;, &cv-link-CCFLAGS;, &cv-link-CXX;, &cv-link-CXXCOM;, &cv-link-ESCAPE;, &cv-link-LINK;, &cv-link-LINKCOM;, &cv-link-PLATFORM;, &cv-link-PRINT_CMD_LINE_FUNC;, &cv-link-PROGSUFFIX;, &cv-link-RANLIB;, &cv-link-RANLIBCOM;, &cv-link-SHCCCOM;, &cv-link-SHCXXCOM;, &cv-link-SHLINK;, &cv-link-SHLINKCOM;.</para></listitem>
+ <para>Sets: &cv-link-IMPLICIT_COMMAND_DEPENDENCIES;, &cv-link-NINJA_ALIAS_NAME;, &cv-link-NINJA_CMD_ARGS;, &cv-link-NINJA_COMPDB_EXPAND;, &cv-link-NINJA_DEPFILE_PARSE_FORMAT;, &cv-link-NINJA_DIR;, &cv-link-NINJA_DISABLE_AUTO_RUN;, &cv-link-NINJA_ENV_VAR_CACHE;, &cv-link-NINJA_FILE_NAME;, &cv-link-NINJA_FORCE_SCONS_BUILD;, &cv-link-NINJA_GENERATED_SOURCE_ALIAS_NAME;, &cv-link-NINJA_GENERATED_SOURCE_SUFFIXES;, &cv-link-NINJA_MSVC_DEPS_PREFIX;, &cv-link-NINJA_POOL;, &cv-link-NINJA_REGENERATE_DEPS;, &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE;, &cv-link-NINJA_SCONS_DAEMON_PORT;, &cv-link-NINJA_SYNTAX;, &cv-link-_NINJA_REGENERATE_DEPS_FUNC;.</para><para>Uses: &cv-link-AR;, &cv-link-ARCOM;, &cv-link-ARFLAGS;, &cv-link-CC;, &cv-link-CCCOM;, &cv-link-CCDEPFLAGS;, &cv-link-CCFLAGS;, &cv-link-CXX;, &cv-link-CXXCOM;, &cv-link-ESCAPE;, &cv-link-LINK;, &cv-link-LINKCOM;, &cv-link-PLATFORM;, &cv-link-PRINT_CMD_LINE_FUNC;, &cv-link-PROGSUFFIX;, &cv-link-RANLIB;, &cv-link-RANLIBCOM;, &cv-link-SHCCCOM;, &cv-link-SHCXXCOM;, &cv-link-SHLINK;, &cv-link-SHLINKCOM;.</para></listitem>
</varlistentry>
<varlistentry id="t-packaging">
<term>packaging</term>
@@ -1049,7 +1048,7 @@ Sets construction variables for the Sun linker.
<varlistentry id="t-swig">
<term>swig</term>
<listitem><para>
-Sets construction variables for the SWIG interface generator.
+Sets construction variables for the &swig; interface compiler.
</para>
<para>Sets: &cv-link-SWIG;, &cv-link-SWIGCFILESUFFIX;, &cv-link-SWIGCOM;, &cv-link-SWIGCXXFILESUFFIX;, &cv-link-SWIGDIRECTORSUFFIX;, &cv-link-SWIGFLAGS;, &cv-link-SWIGINCPREFIX;, &cv-link-SWIGINCSUFFIX;, &cv-link-SWIGPATH;, &cv-link-SWIGVERSION;, &cv-link-_SWIGINCFLAGS;.</para><para>Uses: &cv-link-SWIGCOMSTR;.</para></listitem>
</varlistentry>
@@ -1098,7 +1097,7 @@ provides &b-POTUpdate; builder to make <literal>PO</literal>
<listitem><para>
Sets construction variables for the &yacc; parse generator.
</para>
-<para>Sets: &cv-link-YACC;, &cv-link-YACCCOM;, &cv-link-YACCFLAGS;, &cv-link-YACCHFILESUFFIX;, &cv-link-YACCHXXFILESUFFIX;, &cv-link-YACCVCGFILESUFFIX;.</para><para>Uses: &cv-link-YACCCOMSTR;.</para></listitem>
+<para>Sets: &cv-link-YACC;, &cv-link-YACCCOM;, &cv-link-YACCFLAGS;, &cv-link-YACCHFILESUFFIX;, &cv-link-YACCHXXFILESUFFIX;, &cv-link-YACCVCGFILESUFFIX;.</para><para>Uses: &cv-link-YACCCOMSTR;, &cv-link-YACCFLAGS;, &cv-link-YACC_GRAPH_FILE;, &cv-link-YACC_HEADER_FILE;.</para></listitem>
</varlistentry>
<varlistentry id="t-zip">
<term>zip</term>
diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen
index cf7751f..fdfa9bc 100644
--- a/doc/generated/variables.gen
+++ b/doc/generated/variables.gen
@@ -22,16 +22,6 @@ if &cv-link-LDMODULEVERSION; is set. Othervise it evaluates to an empty string.
</para>
</listitem>
</varlistentry>
- <varlistentry id="cv-__NINJA_NO">
- <term>
- <envar>__NINJA_NO</envar>
- </term>
- <listitem><para>
- Internal flag. Used to tell SCons whether or not to try to import pypi's ninja python package.
- This is set to True when being called by Ninja?
- </para>
- </listitem>
- </varlistentry>
<varlistentry id="cv-__SHLIBVERSIONFLAGS">
<term>
<envar>__SHLIBVERSIONFLAGS</envar>
@@ -427,6 +417,18 @@ env = Environment(CCCOMSTR = "Compiling static object $TARGET")
</example_commands>
</listitem>
</varlistentry>
+ <varlistentry id="cv-CCDEPFLAGS">
+ <term>
+ <envar>CCDEPFLAGS</envar>
+ </term>
+ <listitem><para>
+Options to pass to C or C++ compiler to generate list of dependency files.
+</para>
+ <para>
+ This is set only by compilers which support this functionality. (&t-link-gcc;, &t-link-clang;, and &t-link-msvc; currently)
+ </para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-CCFLAGS">
<term>
<envar>CCFLAGS</envar>
@@ -1641,56 +1643,59 @@ General options passed to the TeX DVI file to PostScript converter.
<envar>ENV</envar>
</term>
<listitem><para>
-A dictionary of environment variables
-to use when invoking commands. When
-&cv-ENV; is used in a command all list
-values will be joined using the path separator and any other non-string
-values will simply be coerced to a string.
-Note that, by default,
-&scons;
+The <firstterm>execution environment</firstterm> -
+a dictionary of environment variables
+used when &SCons; invokes external commands
+to build targets defined in this &consenv;.
+When &cv-ENV; is passed to a command,
+all list values are assumed to be path lists and
+are joined using the search path separator.
+Any other non-string values are coerced to a string.
+</para>
+
+<para>
+Note that by default
+&SCons;
does
<emphasis>not</emphasis>
-propagate the environment in effect when you
-execute
-&scons;
-to the commands used to build target files.
+propagate the environment in effect when you execute
+&scons; (the "shell environment")
+to the execution environment.
This is so that builds will be guaranteed
repeatable regardless of the environment
variables set at the time
&scons;
is invoked.
-</para>
-
-<para>
-If you want to propagate your
-environment variables
+If you want to propagate a
+shell environment variable
to the commands executed
to build target files,
-you must do so explicitly:
+you must do so explicitly.
+A common example is
+the system &PATH;
+environment variable,
+so that
+&scons;
+will find utilities the same way
+as the invoking shell (or other process):
</para>
<example_commands>
import os
-env = Environment(ENV=os.environ.copy())
+env = Environment(ENV={'PATH': os.environ['PATH']})
</example_commands>
<para>
-Note that you can choose only to propagate
-certain environment variables.
-A common example is
-the system
-<envar>PATH</envar>
-environment variable,
-so that
-&scons;
-uses the same utilities
-as the invoking shell (or other process):
+Although it is usually not recommended,
+you can propagate the entire shell environment
+in one go:
</para>
<example_commands>
import os
-env = Environment(ENV={'PATH': os.environ['PATH']})
+env = Environment(ENV=os.environ.copy())
</example_commands>
+
</listitem>
</varlistentry>
<varlistentry id="cv-ESCAPE">
@@ -2671,11 +2676,20 @@ for all versions of Fortran.
The command line used to compile a Fortran source file to an object file.
By default, any options specified
in the &cv-link-FORTRANFLAGS;,
-&cv-link-CPPFLAGS;,
-&cv-link-_CPPDEFFLAGS;,
&cv-link-_FORTRANMODFLAG;, and
-&cv-link-_FORTRANINCFLAGS; construction variables
-are included on this command line.
+&cv-link-_FORTRANINCFLAGS;
+&consvars; are included on this command line.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-FORTRANCOMMONFLAGS">
+ <term>
+ <envar>FORTRANCOMMONFLAGS</envar>
+ </term>
+ <listitem><para>
+General user-specified options that are passed to the Fortran compiler.
+Similar to &cv-link-FORTRANFLAGS;,
+but this variable is applied to all dialects.
</para>
</listitem>
</varlistentry>
@@ -2706,7 +2720,8 @@ default, this is <literal>['.f', '.for', '.ftn']</literal>
<envar>FORTRANFLAGS</envar>
</term>
<listitem><para>
-General user-specified options that are passed to the Fortran compiler.
+General user-specified options for the FORTRAN dialect
+that are passed to the Fortran compiler.
Note that this variable does
<emphasis>not</emphasis>
contain
@@ -2725,7 +2740,7 @@ for the variables that expand those options.
<envar>_FORTRANINCFLAGS</envar>
</term>
<listitem><para>
-An automatically-generated construction variable
+An automatically-generated &consvar;
containing the Fortran compiler command-line options
for specifying directories to be searched for include
files and module files.
@@ -2757,7 +2772,7 @@ for module files, as well.
The prefix used to specify a module directory on the Fortran compiler command
line.
This will be prepended to the beginning of the directory
-in the &cv-link-FORTRANMODDIR; construction variables
+in the &cv-link-FORTRANMODDIR; &consvars;
when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
</para>
</listitem>
@@ -2770,7 +2785,7 @@ when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
The suffix used to specify a module directory on the Fortran compiler command
line.
This will be appended to the end of the directory
-in the &cv-link-FORTRANMODDIR; construction variables
+in the &cv-link-FORTRANMODDIR; &consvars;
when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
</para>
</listitem>
@@ -2780,7 +2795,7 @@ when the &cv-link-_FORTRANMODFLAG; variables is automatically generated.
<envar>_FORTRANMODFLAG</envar>
</term>
<listitem><para>
-An automatically-generated construction variable
+An automatically-generated &consvar;
containing the Fortran compiler command-line option
for specifying the directory location where the Fortran
compiler should place any module files that happen to get
@@ -2861,11 +2876,11 @@ env = Environment(FORTRANPATH=include)
The directory list will be added to command lines
through the automatically-generated
&cv-link-_FORTRANINCFLAGS;
-construction variable,
+&consvar;,
which is constructed by
respectively prepending and appending the values of the
&cv-link-INCPREFIX; and &cv-link-INCSUFFIX;
-construction variables
+&consvars;
to the beginning and end
of each directory in &cv-link-FORTRANPATH;.
Any command lines you define that need
@@ -2890,7 +2905,7 @@ By default, any options specified in the &cv-link-FORTRANFLAGS;,
&cv-link-_CPPDEFFLAGS;,
&cv-link-_FORTRANMODFLAG;, and
&cv-link-_FORTRANINCFLAGS;
-construction variables are included on this command line.
+&consvars; are included on this command line.
</para>
</listitem>
</varlistentry>
@@ -3551,15 +3566,6 @@ env = Environment(JAVACCOMSTR="Compiling class files $TARGETS from $SOURCES")
<filename>;</filename>
on Windows).
</para>
-
- <para>
- Note that this currently just adds the specified
- directory via the <option>-classpath</option> option.
- &SCons; does not currently search the
- &cv-JAVACLASSPATH; directories for dependency
- <filename>.class</filename>
- files.
- </para>
</listitem>
</varlistentry>
<varlistentry id="cv-JAVACLASSSUFFIX">
@@ -3920,6 +3926,31 @@ The lexical analyzer generator.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-LEX_HEADER_FILE">
+ <term>
+ <envar>LEX_HEADER_FILE</envar>
+ </term>
+ <listitem><para>
+If supplied, generate a C header file with the name taken from this variable.
+Will be emitted as a <option>--header-file=</option>
+command-line option. Use this in preference to including
+<option>--header-file=</option> in &cv-link-LEXFLAGS; directly.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-LEX_TABLES_FILE">
+ <term>
+ <envar>LEX_TABLES_FILE</envar>
+ </term>
+ <listitem><para>
+If supplied, write the lex tables to a file with the name
+taken from this variable.
+Will be emitted as a <option>--tables-file=</option>
+command-line option. Use this in preference to including
+<option>--tables-file=</option> in &cv-link-LEXFLAGS; directly.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-LEXCOM">
<term>
<envar>LEXCOM</envar>
@@ -3941,7 +3972,7 @@ If this is not set, then &cv-link-LEXCOM; (the command line) is displayed.
</para>
<example_commands>
-env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES")
+env = Environment(LEXCOMSTR="Lex'ing $TARGET from $SOURCES")
</example_commands>
</listitem>
</varlistentry>
@@ -3951,6 +3982,20 @@ env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES")
</term>
<listitem><para>
General options passed to the lexical analyzer generator.
+In addition to passing the value on during invocation,
+the &t-link-lex; tool also examines this &consvar; for options
+which cause additional output files to be generated,
+and adds those to the target list.
+Recognized for this purpose are GNU &flex; options
+<option>--header-file=</option> and
+<option>--tables-file=</option>;
+the output file is named by the option argument.
+</para>
+<para>
+Note that files specified by <option>--header-file=</option> and
+<option>--tables-file=</option> may not be properly handled
+by &SCons; in all situations. Consider using
+&cv-link-LEX_HEADER_FILE; and &cv-link-LEX_TABLES_FILE; instead.
</para>
</listitem>
</varlistentry>
@@ -4687,6 +4732,749 @@ will be compiled separately.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-MSVC_NOTFOUND_POLICY">
+ <term>
+ <envar>MSVC_NOTFOUND_POLICY</envar>
+ </term>
+ <listitem><para>
+Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not detected.
+</para>
+
+<para>
+ The &cv-MSVC_NOTFOUND_POLICY; specifies the &scons; behavior when no msvc versions are detected or
+ when the requested msvc version is not detected.
+</para>
+
+<para>
+The valid values for &cv-MSVC_NOTFOUND_POLICY; and the corresponding &scons; behavior are:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><parameter>'Error' or 'Exception'</parameter></term>
+<listitem>
+<para>
+Raise an exception when no msvc versions are detected or when the requested msvc version is not detected.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Warning' or 'Warn'</parameter></term>
+<listitem>
+<para>
+Issue a warning and continue when no msvc versions are detected or when the requested msvc version is not detected.
+Depending on usage, this could result in build failure(s).
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Ignore' or 'Suppress'</parameter></term>
+<listitem>
+<para>
+Take no action and continue when no msvc versions are detected or when the requested msvc version is not detected.
+Depending on usage, this could result in build failure(s).
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>
+Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well.
+</para>
+
+<para>
+The &cv-MSVC_NOTFOUND_POLICY; is applied when any of the following conditions are satisfied:
+<itemizedlist>
+<listitem><para>
+&cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified),
+and the default tools list contains one or more of the msvc tools.
+</para></listitem>
+<listitem><para>
+&cv-MSVC_VERSION; is specified, the default tools list is explicitly specified (e.g., <literal>tools=['default']</literal>),
+and the default tools list contains one or more of the msvc tools.
+</para></listitem>
+<listitem><para>
+A non-default tools list is specified that contains one or more of the msvc tools (e.g., <literal>tools=['msvc', 'mslink']</literal>).
+</para></listitem>
+</itemizedlist>
+</para>
+
+<para>
+The &cv-MSVC_NOTFOUND_POLICY; is ignored when any of the following conditions are satisfied:
+<itemizedlist>
+<listitem><para>
+&cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified).
+</para></listitem>
+<listitem><para>
+&cv-MSVC_VERSION; is not specified and the default tools list is explicitly specified (e.g., <literal>tools=['default']</literal>).
+</para></listitem>
+<listitem><para>
+A non-default tool list is specified that does not contain any of the msvc tools (e.g., <literal>tools=['mingw']</literal>).
+</para></listitem>
+</itemizedlist>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_NOTFOUND_POLICY; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_NOTFOUND_POLICY; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+When &cv-MSVC_NOTFOUND_POLICY; is not specified, the default &scons; behavior is to issue a warning and continue
+subject to the conditions listed above. The default &scons; behavior may change in the future.
+</para>
+
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_SCRIPT_ARGS">
+ <term>
+ <envar>MSVC_SCRIPT_ARGS</envar>
+ </term>
+ <listitem><para>
+Pass user-defined arguments to the Visual C++ batch file determined via autodetection.
+</para>
+
+<para>
+&cv-MSVC_SCRIPT_ARGS; is available for msvc batch file arguments that do not have first-class support
+via construction variables or when there is an issue with the appropriate construction variable validation.
+When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;)
+rather than &cv-MSVC_SCRIPT_ARGS; arguments.
+</para>
+
+<para>
+The valid values for &cv-MSVC_SCRIPT_ARGS; are: <literal>None</literal>, a string,
+or a list of strings.
+</para>
+
+<para>
+The &cv-MSVC_SCRIPT_ARGS; value is converted to a scalar string (i.e., "flattened").
+The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined
+via autodetection subject to the validation conditions listed below.
+</para>
+
+<para>
+&cv-MSVC_SCRIPT_ARGS; is ignored when the value is <literal>None</literal> and when the
+result from argument conversion is an empty string. The validation conditions below do not apply.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SCRIPT_ARGS; is specified for Visual Studio 2013 and earlier.
+</para></listitem>
+
+<listitem><para>
+Multiple SDK version arguments (e.g., <literal>'10.0.20348.0'</literal>) are specified
+in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument
+(e.g., <literal>'10.0.20348.0'</literal>) is specified in &cv-MSVC_SCRIPT_ARGS;.
+Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and &cv-MSVC_SCRIPT_ARGS;
+are not allowed.
+</para></listitem>
+
+<listitem><para>
+Multiple toolset version arguments (e.g., <literal>'-vcvars_ver=14.29'</literal>)
+are specified in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument
+(e.g., <literal>'-vcvars_ver=14.29'</literal>) is specified in &cv-MSVC_SCRIPT_ARGS;.
+Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and
+&cv-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+Multiple spectre library arguments (e.g., <literal>'-vcvars_spectre_libs=spectre'</literal>)
+are specified in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument
+(e.g., <literal>'-vcvars_spectre_libs=spectre'</literal>) is specified in
+&cv-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS;
+and &cv-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+Multiple UWP arguments (e.g., <literal>uwp</literal> or <literal>store</literal>) are specified
+in &cv-MSVC_SCRIPT_ARGS;.
+</para></listitem>
+
+<listitem><para>
+&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., <literal>uwp</literal> or
+<literal>store</literal>) is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple UWP declarations
+via &cv-link-MSVC_UWP_APP; and &cv-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example 1 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a string argument:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS='10.0.20348.0 -vcvars_ver=14.29.30133')
+</example_commands>
+</para>
+
+<para>
+Example 2 - A Visual Studio 2022 build with an SDK version and a toolset version
+specified with a list argument:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['10.0.20348.0', '-vcvars_ver=14.29.30133'])
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SCRIPT_ARGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SCRIPT_ARGS; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+Other than checking for multiple declarations as described above, &cv-MSVC_SCRIPT_ARGS; arguments
+are not validated.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+Erroneous, inconsistent, and/or version incompatible &cv-MSVC_SCRIPT_ARGS; arguments are likely
+to result in build failures for reasons that are not readily apparent and may be difficult to diagnose.
+</emphasis>
+The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent
+and compatible with the version of msvc selected.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_SCRIPTERROR_POLICY">
+ <term>
+ <envar>MSVC_SCRIPTERROR_POLICY</envar>
+ </term>
+ <listitem><para>
+Specify the &scons; behavior when Microsoft Visual C/C++ batch file errors are detected.
+</para>
+
+<para>
+The &cv-MSVC_SCRIPTERROR_POLICY; specifies the &scons; behavior when msvc batch file errors are
+detected.
+When &cv-MSVC_SCRIPTERROR_POLICY; is not specified, the default &scons; behavior is to suppress
+msvc batch file error messages.
+</para>
+<para>
+The root cause of msvc build failures may be difficult to diagnose. In these situations, setting
+the &scons; behavior to issue a warning when msvc batch file errors are detected <emphasis>may</emphasis>
+produce additional diagnostic information.
+</para>
+
+<para>
+The valid values for &cv-MSVC_SCRIPTERROR_POLICY; and the corresponding &scons; behavior are:
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><parameter>'Error' or 'Exception'</parameter></term>
+<listitem>
+<para>
+Raise an exception when msvc batch file errors are detected.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Warning' or 'Warn'</parameter></term>
+<listitem>
+<para>
+Issue a warning when msvc batch file errors are detected.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>'Ignore' or 'Suppress'</parameter></term>
+<listitem>
+<para>
+Suppress msvc batch file error messages.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>
+Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well.
+</para>
+
+<para>
+Example 1 - A Visual Studio 2022 build with user-defined script arguments:
+<example_commands>
+env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1'])
+env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject'])
+</example_commands>
+</para>
+
+<para>
+Example 1 - Output fragment:
+<example_commands>
+...
+link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj
+LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib'
+...
+</example_commands>
+</para>
+
+<para>
+Example 2 - A Visual Studio 2022 build with user-defined script arguments and the script error policy set
+to issue a warning when msvc batch file errors are detected:
+<example_commands>
+env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1'], MSVC_SCRIPTERROR_POLICY='warn')
+env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject'])
+</example_commands>
+</para>
+
+<para>
+Example 2 - Output fragment:
+<example_commands>
+...
+scons: warning: vc script errors detected:
+[ERROR:vcvars.bat] The UWP Application Platform requires a Windows 10 SDK.
+[ERROR:vcvars.bat] WindowsSdkDir = "C:\Program Files (x86)\Windows Kits\8.1\"
+[ERROR:vcvars.bat] host/target architecture is not supported : { x64 , x64 }
+...
+link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj
+LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib'
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SCRIPTERROR_POLICY; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SCRIPTERROR_POLICY; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+Due to &scons; implementation details, not all Windows system environment variables are propagated
+to the environment in which the msvc batch file is executed. Depending on Visual Studio version
+and installation options, non-fatal msvc batch file error messages may be generated for ancillary
+tools which may not affect builds with the msvc compiler. For this reason, caution is recommended
+when setting the script error policy to raise an exception (e.g., <literal>'Error'</literal>).
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_SDK_VERSION">
+ <term>
+ <envar>MSVC_SDK_VERSION</envar>
+ </term>
+ <listitem><para>
+Build with a specific version of the Microsoft Software Development Kit (SDK).
+</para>
+
+<para>
+The valid values for &cv-MSVC_SDK_VERSION; are: <literal>None</literal>
+or a string containing the requested SDK version (e.g., <literal>'10.0.20348.0'</literal>).
+</para>
+
+<para>
+&cv-MSVC_SDK_VERSION; is ignored when the value is <literal>None</literal> and when
+the value is an empty string. The validation conditions below do not apply.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SDK_VERSION; is specified for Visual Studio 2013 and earlier.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_SDK_VERSION; is specified and an SDK version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via &cv-MSVC_SDK_VERSION;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+The &cv-MSVC_SDK_VERSION; specified does not match any of the supported formats:
+<itemizedlist>
+<listitem><para>
+<literal>'10.0.XXXXX.Y'</literal> [SDK 10.0]
+</para></listitem>
+<listitem><para>
+<literal>'8.1'</literal> [SDK 8.1]
+</para></listitem>
+</itemizedlist>
+</para></listitem>
+
+<listitem><para>
+The system folder for the corresponding &cv-MSVC_SDK_VERSION; version is not found.
+The requested SDK version does not appear to be installed.
+</para></listitem>
+
+<listitem><para>
+The &cv-MSVC_SDK_VERSION; version does not appear to support the requested platform
+type (i.e., <literal>UWP</literal> or <literal>Desktop</literal>). The requested SDK version
+platform type components do not appear to be installed.
+</para></listitem>
+
+<listitem><para>
+The &cv-MSVC_SDK_VERSION; version is <literal>8.1</literal>, the platform type is
+<literal>UWP</literal>, and the build tools selected are from Visual Studio 2017
+and later (i.e., &cv-link-MSVC_VERSION; must be '14.0' or &cv-link-MSVC_TOOLSET_VERSION;
+must be '14.0').
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example 1 - A Visual Studio 2022 build with a specific Windows SDK version:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0')
+</example_commands>
+</para>
+
+<para>
+Example 2 - A Visual Studio 2022 build with a specific SDK version for the Universal Windows Platform:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0', MSVC_UWP_APP=True)
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SDK_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SDK_VERSION; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem>
+<para><emphasis>
+Should a SDK 10.0 version be installed that does not follow the naming scheme above, the
+SDK version will need to be specified via &cv-link-MSVC_SCRIPT_ARGS; until the version number
+validation format can be extended.
+</emphasis></para>
+</listitem>
+
+<listitem><para>
+Should an exception be raised indicating that the SDK version is not found, verify that
+the requested SDK version is installed with the necessary platform type components.
+</para></listitem>
+
+<listitem><para>
+There is a known issue with the Microsoft libraries when the target architecture is
+<literal>ARM64</literal> and a Windows 11 SDK (version <literal>'10.0.22000.0'</literal> and later) is used
+with the <literal>v141</literal> build tools and older <literal>v142</literal> toolsets
+(versions <literal>'14.28.29333'</literal> and earlier). Should build failures arise with these combinations
+of settings due to unresolved symbols in the Microsoft libraries, &cv-MSVC_SDK_VERSION; may be employed to
+specify a Windows 10 SDK (e.g., <literal>'10.0.20348.0'</literal>) for the build.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_SPECTRE_LIBS">
+ <term>
+ <envar>MSVC_SPECTRE_LIBS</envar>
+ </term>
+ <listitem><para>
+Build with the spectre-mitigated Visual C++ libraries.
+</para>
+
+<para>
+The valid values for &cv-MSVC_SPECTRE_LIBS; are: <literal>True</literal>,
+<literal>False</literal>, or <literal>None</literal>.
+</para>
+
+<para>
+When &cv-MSVC_SPECTRE_LIBS; is enabled (i.e., <literal>True</literal>),
+the Visual C++ environment will include the paths to the spectre-mitigated implementations
+of the Microsoft Visual C++ libraries.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; is enabled for Visual Studio 2015 and earlier.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-MSVC_SPECTRE_LIBS;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; is enabled and the platform type is <literal>UWP</literal>. There
+are no spectre-mitigated libraries for Universal Windows Platform (UWP) applications or
+components.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example - A Visual Studio 2022 build with spectre mitigated Visual C++ libraries:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True)
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_SPECTRE_LIBS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_SPECTRE_LIBS; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+Additional compiler switches (e.g., <literal>/Qspectre</literal>) are necessary for including
+spectre mitigations when building user artifacts. Refer to the Visual Studio documentation for
+details.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The existence of the spectre libraries host architecture and target architecture folders are not
+verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures.
+</emphasis>
+The burden is on the user to ensure the requisite libraries with spectre mitigations are installed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_TOOLSET_VERSION">
+ <term>
+ <envar>MSVC_TOOLSET_VERSION</envar>
+ </term>
+ <listitem><para>
+Build with a specific Visual C++ toolset version.
+</para>
+
+<para><emphasis>
+Specifying &cv-MSVC_TOOLSET_VERSION; does not affect the autodetection and selection
+of msvc instances. The &cv-MSVC_TOOLSET_VERSION; is applied <emphasis>after</emphasis>
+an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION;
+is not specified.
+</emphasis></para>
+
+<para>
+The valid values for &cv-MSVC_TOOLSET_VERSION; are: <literal>None</literal>
+or a string containing the requested toolset version (e.g., <literal>'14.29'</literal>).
+</para>
+
+<para>
+&cv-MSVC_TOOLSET_VERSION; is ignored when the value is <literal>None</literal> and when
+the value is an empty string. The validation conditions below do not apply.
+</para>
+
+<para>
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_TOOLSET_VERSION; is specified for Visual Studio 2015 and earlier.
+</para></listitem>
+
+<listitem><para>
+&cv-MSVC_TOOLSET_VERSION; is specified and a toolset version argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-MSVC_TOOLSET_VERSION;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+
+<listitem>
+<para>
+The &cv-MSVC_TOOLSET_VERSION; specified does not match any of the supported formats:
+</para>
+
+<itemizedlist>
+
+<listitem><para>
+<literal>'XX.Y'</literal>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY'</literal>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.ZZZZZ'</literal>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.Z'</literal> to <literal>'XX.YY.ZZZZ'</literal>
+<emphasis>
+[&scons; extension not directly supported by the msvc batch files and may be removed in the future]
+</emphasis>
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.ZZ.N'</literal> [SxS format]
+</para></listitem>
+
+<listitem><para>
+<literal>'XX.YY.ZZ.NN'</literal> [SxS format]
+</para></listitem>
+
+</itemizedlist>
+
+</listitem>
+
+<listitem><para>
+The major msvc version prefix (i.e., <literal>'XX.Y'</literal>) of the &cv-MSVC_TOOLSET_VERSION; specified
+is for Visual Studio 2013 and earlier (e.g., <literal>'12.0'</literal>).
+</para></listitem>
+
+<listitem><para>
+The major msvc version prefix (i.e., <literal>'XX.Y'</literal>) of the &cv-MSVC_TOOLSET_VERSION; specified
+is greater than the msvc version selected (e.g., <literal>'99.0'</literal>).
+</para></listitem>
+
+<listitem><para>
+A system folder for the corresponding &cv-MSVC_TOOLSET_VERSION; version is not found.
+The requested toolset version does not appear to be installed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Toolset selection details:
+<itemizedlist>
+
+<listitem><para>
+When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number:
+the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION;
+prefix is selected.
+</para></listitem>
+
+<listitem><para>
+When &cv-MSVC_TOOLSET_VERSION; is specified using the major msvc version prefix
+(i.e., <literal>'XX.Y'</literal>) and the major msvc version is that of the latest release of
+Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version.
+</para><para>
+In the latest release of Visual Studio, the default Visual C++ toolset version is not necessarily the
+toolset with the largest version number.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+<para>
+Example 1 - A default Visual Studio build with a partial toolset version specified:
+<example_commands>
+env = Environment(MSVC_TOOLSET_VERSION='14.2')
+</example_commands>
+</para>
+
+<para>
+Example 2 - A default Visual Studio build with a partial toolset version specified:
+<example_commands>
+env = Environment(MSVC_TOOLSET_VERSION='14.29')
+</example_commands>
+</para>
+
+<para>
+Example 3 - A Visual Studio 2022 build with a full toolset version specified:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133')
+</example_commands>
+</para>
+
+<para>
+Example 4 - A Visual Studio 2022 build with an SxS toolset version specified:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11')
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_TOOLSET_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_TOOLSET_VERSION; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The existence of the toolset host architecture and target architecture folders are not verified
+when &cv-MSVC_TOOLSET_VERSION; is specified which could result in build failures.
+</emphasis>
+The burden is on the user to ensure the requisite toolset target architecture build tools are installed.
+</para></listitem>
+
+</itemizedlist>
+</para>
+
+</listitem>
+ </varlistentry>
<varlistentry id="cv-MSVC_USE_SCRIPT">
<term>
<envar>MSVC_USE_SCRIPT</envar>
@@ -4705,6 +5493,7 @@ and extract the relevant variables from the result (typically
<envar>%PATH%</envar>) for supplying to the build.
This can be useful to force the use of a compiler version that
&SCons; does not detect.
+&cv-link-MSVC_USE_SCRIPT_ARGS; provides arguments passed to this script.
</para>
<para>
@@ -4717,8 +5506,103 @@ is, if you are sure everything is set correctly already and
you don't want &SCons; to change anything.
</para>
<para>
-&cv-MSVC_USE_SCRIPT; overrides &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
+&cv-MSVC_USE_SCRIPT; ignores &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_USE_SCRIPT_ARGS">
+ <term>
+ <envar>MSVC_USE_SCRIPT_ARGS</envar>
+ </term>
+ <listitem><para>
+Provides arguments passed to the script &cv-link-MSVC_USE_SCRIPT;.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-MSVC_USE_SETTINGS">
+ <term>
+ <envar>MSVC_USE_SETTINGS</envar>
+ </term>
+ <listitem><para>
+Use a dictionary to set up the Microsoft Visual C++ compiler.
+</para>
+
+<para>
+&cv-MSVC_USE_SETTINGS; is ignored when &cv-link-MSVC_USE_SCRIPT; is defined
+and/or when &cv-MSVC_USE_SETTINGS; is set to <constant>None</constant>.
+</para>
+
+<para>
+The dictionary is used to populate the environment with the relevant variables
+(typically <envar>%INCLUDE%</envar>, <envar>%LIB%</envar>, and <envar>%PATH%</envar>)
+for supplying to the build. This can be useful to force the use of a compiler environment
+that &SCons; does not configure correctly. This is an alternative to manually configuring
+the environment when bypassing Visual Studio autodetection entirely by setting
+&cv-link-MSVC_USE_SCRIPT; to <constant>None</constant>.
+</para>
+
+<para>
+Here is an example of configuring a build environment using the Microsoft Visual C/C++ compiler
+included in the Microsoft SDK on a 64-bit host and building for a 64-bit architecture:
+<programlisting>
+# Microsoft SDK 6.0 (MSVC 8.0): 64-bit host and 64-bit target
+msvc_use_settings = {
+ "PATH": [
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\VC\\Bin\\x64",
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\Bin\\x64",
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\Bin",
+ "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727",
+ "C:\\Windows\\system32",
+ "C:\\Windows",
+ "C:\\Windows\\System32\\Wbem",
+ "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\"
+ ],
+ "INCLUDE": [
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\VC\\Include",
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\VC\\Include\\Sys",
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\Include",
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\Include\\gl",
+ ],
+ "LIB": [
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\VC\\Lib\\x64",
+ "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\Lib\\x64",
+ ],
+ "LIBPATH": [],
+ "VSCMD_ARG_app_plat": [],
+ "VCINSTALLDIR": [],
+ "VCToolsInstallDir": []
+}
+
+# Specifying MSVC_VERSION is recommended
+env = Environment(MSVC_VERSION='8.0', MSVC_USE_SETTINGS=msvc_use_settings)
+</programlisting>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_USE_SETTINGS; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_USE_SETTINGS; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The dictionary content requirements are based on the internal msvc implementation and
+therefore may change at any time.
+</emphasis>
+The burden is on the user to ensure the dictionary contents are minimally sufficient to
+ensure successful builds.
+</para></listitem>
+
+</itemizedlist>
</para>
+
</listitem>
</varlistentry>
<varlistentry id="cv-MSVC_UWP_APP">
@@ -4726,21 +5610,69 @@ you don't want &SCons; to change anything.
<envar>MSVC_UWP_APP</envar>
</term>
<listitem><para>
-Build libraries for a Universal Windows Platform (UWP) Application.
+Build with the Universal Windows Platform (UWP) application Visual C++ libraries.
</para>
<para>
-If &cv-MSVC_UWP_APP; is set, the Visual C++ environment will be set up to point
+The valid values for &cv-MSVC_UWP_APP; are: <literal>True</literal>,
+<literal>'1'</literal>, <literal>False</literal>, <literal>'0'</literal>,
+or <literal>None</literal>.
+</para>
+
+<para>
+When &cv-MSVC_UWP_APP; is enabled (i.e., <literal>True</literal> or
+<literal>'1'</literal>), the Visual C++ environment will be set up to point
to the Windows Store compatible libraries and Visual C++ runtimes. In doing so,
any libraries that are built will be able to be used in a UWP App and published
to the Windows Store.
-This flag will only have an effect with Visual Studio 2015 or later.
-This variable must be passed as an argument to the Environment()
-constructor; setting it later has no effect.
+<!-- This flag will only have an effect with Visual Studio 2015 or later. -->
+<!-- This variable must be passed as an argument to the Environment()
+constructor; setting it later has no effect. -->
</para>
<para>
-Valid values are '1' or '0'
+An exception is raised when any of the following conditions are satisfied:
+<itemizedlist>
+<listitem><para>
+&cv-MSVC_UWP_APP; is enabled for Visual Studio 2013 and earlier.
+</para></listitem>
+<listitem><para>
+&cv-MSVC_UWP_APP; is enabled and a UWP argument is specified in
+&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via &cv-MSVC_UWP_APP;
+and &cv-link-MSVC_SCRIPT_ARGS; are not allowed.
+</para></listitem>
+</itemizedlist>
+</para>
+
+<para>
+Example - A Visual Studio 2022 build for the Universal Windows Platform:
+<example_commands>
+env = Environment(MSVC_VERSION='14.3', MSVC_UWP_APP=True)
+</example_commands>
+</para>
+
+<para>
+Important usage details:
+<itemizedlist>
+
+<listitem><para>
+&cv-MSVC_UWP_APP; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_UWP_APP; must be set before the first msvc tool is
+loaded into the environment.
+</para></listitem>
+
+<listitem><para>
+<emphasis>
+The existence of the UWP libraries is not verified when &cv-MSVC_UWP_APP; is enabled
+which could result in build failures.
+</emphasis>
+The burden is on the user to ensure the requisite UWP libraries are installed.
+</para></listitem>
+
+</itemizedlist>
</para>
</listitem>
@@ -4757,8 +5689,15 @@ Sets the preferred version of Microsoft Visual C/C++ to use.
If &cv-MSVC_VERSION; is not set, SCons will (by default) select the
latest version of Visual C/C++ installed on your system. If the
specified version isn't installed, tool initialization will fail.
-This variable must be passed as an argument to the &f-link-Environment;
-constructor; setting it later has no effect.
+</para>
+
+<para>
+&cv-MSVC_VERSION; must be passed as an argument to the &f-link-Environment;
+constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is
+loaded via the default tools list or via a tools list passed to the
+&f-link-Environment; constructor.
+Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is
+loaded into the environment.
</para>
<para>
@@ -5190,23 +6129,55 @@ Specfies the name of the project to package.
<envar>NINJA_ALIAS_NAME</envar>
</term>
<listitem><para>
- Name of the Alias() which is will cause SCons to create the <filename>ninja.build</filename> file, and
- then (optionally) run ninja.
+ The name of the alias target which will cause &SCons; to create the &ninja; build file,
+ and then (optionally) run &ninja;.
+ The default value is <literal>generate-ninja</literal>.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-NINJA_CMD_ARGS">
+ <term>
+ <envar>NINJA_CMD_ARGS</envar>
+ </term>
+ <listitem><para>
+ A string which will pass arguments through SCons to the ninja command when scons executes ninja.
+ Has no effect if &cv-NINJA_DISABLE_AUTO_RUN; is set.
+ </para>
+ <para>
+ This value can also be passed on the command line:
+ </para>
+ <example_commands>
+scons NINJA_CMD_ARGS=-v
+or
+scons NINJA_CMD_ARGS="-v -j 3"
+ </example_commands>
+ </listitem>
+ </varlistentry>
<varlistentry id="cv-NINJA_COMPDB_EXPAND">
<term>
<envar>NINJA_COMPDB_EXPAND</envar>
</term>
<listitem><para>
- Boolean value (True|False) to instruct ninja to expand the command line arguments normally put into
+ Boolean value to instruct &ninja; to expand the command line arguments normally put into
response files.
- This prevents lines in the compilation database like <quote>gcc @rsp_file</quote> and instead yields
- <quote>gcc -c -o myfile.o myfile.c -Ia -DXYZ</quote>
+ If true, prevents unexpanded lines in the compilation database like
+ <quote><literal>gcc @rsp_file</literal></quote> and instead yields expanded lines like
+ <quote><literal>gcc -c -o myfile.o myfile.c -Ia -DXYZ</literal></quote>.
</para>
<para>
- Ninja's compdb tool added the <quote>-x</quote> flag in Ninja V1.9.0
+ Ninja's compdb tool added the <option>-x</option> flag in Ninja V1.9.0
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry id="cv-NINJA_DEPFILE_PARSE_FORMAT">
+ <term>
+ <envar>NINJA_DEPFILE_PARSE_FORMAT</envar>
+ </term>
+ <listitem><para>
+ Determines the type of format ninja should expect when parsing header
+ include depfiles. Can be <option>msvc</option>, <option>gcc</option>, or <option>clang</option>.
+ The <option>msvc</option> option corresponds to <option>/showIncludes</option> format, and
+ <option>gcc</option> or <option>clang</option> correspond to <option>-MMD -MF</option>.
</para>
</listitem>
</varlistentry>
@@ -5215,15 +6186,14 @@ Specfies the name of the project to package.
<envar>NINJA_DIR</envar>
</term>
<listitem><para>
- This propagates directly into the generated <filename>ninja.build</filename> file.
+ The <parameter>builddir</parameter> value.
+ Propagates directly into the generated &ninja; build file.
From Ninja's docs:
- <blockquote>
- <para>
- builddir
- A directory for some Ninja output files. ... (You can also store other build output in this
- directory.)
- </para>
- </blockquote>
+ <quote>
+ A directory for some Ninja output files. ... (You can also store other build output in this
+ directory.)
+ </quote>
+ The default value is <filename>.ninja</filename>.
</para>
</listitem>
</varlistentry>
@@ -5232,11 +6202,14 @@ Specfies the name of the project to package.
<envar>NINJA_DISABLE_AUTO_RUN</envar>
</term>
<listitem><para>
- Boolean (True|False). Default: False
- When True, SCons will not run ninja automatically after creating the <filename>ninja.build</filename> file.
+ Boolean. Default: <constant>False</constant>.
+ If true, &SCons; will not run &ninja; automatically after creating the &ninja; build file.
+ </para>
- If not set, this will be set to True if <quote>--disable_execute_ninja</quote> or
- <code>SetOption('disable_execute_ninja', True)</code>
+ <para>
+ If not explicitly set, this will be set to <constant>True</constant>
+ if <option>--disable_execute_ninja</option> or
+ <code>SetOption('disable_execute_ninja', True)</code> is seen.
</para>
</listitem>
</varlistentry>
@@ -5246,12 +6219,17 @@ Specfies the name of the project to package.
</term>
<listitem><para>
A string that sets the environment for any environment variables that
- differ between the OS environment and the SCons command ENV.
+ differ between the OS environment and the &SCons; execution environment.
+ </para>
+ <para>
It will be compatible with the default shell of the operating system.
+ </para>
- If not explicitly specified, SCons will generate this dynamically from the Environment()'s 'ENV'
- <quote>env['ENV']</quote>
+ <para>
+ If not explicitly set, &SCons; will generate this dynamically from the
+ execution environment stored in the current &consenv;
+ (e.g. <literal>env['ENV']</literal>)
where those values differ from the existing shell..
</para>
</listitem>
@@ -5261,8 +6239,8 @@ Specfies the name of the project to package.
<envar>NINJA_FILE_NAME</envar>
</term>
<listitem><para>
- The filename for the generated Ninja build file defaults to
- <filename>ninja.build</filename>
+ The filename for the generated Ninja build file.
+ The default is <filename>ninja.build</filename>.
</para>
</listitem>
</varlistentry>
@@ -5271,9 +6249,21 @@ Specfies the name of the project to package.
<envar>NINJA_FORCE_SCONS_BUILD</envar>
</term>
<listitem><para>
- When NINJA_FORCE_SCONS_BUILD is True, this will cause the build nodes to callback to scons instead of using
- ninja to build them. This is intended to be passed to the environment on the builder invocation.
- It is useful if you have a build node which does something which is not easily translated into ninja.
+ If true, causes the build nodes to callback to scons instead of using
+ &ninja; to build them. This is intended to be passed to the environment on the builder invocation.
+ It is useful if you have a build node which does something which is not easily translated into &ninja;.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry id="cv-NINJA_GENERATED_SOURCE_ALIAS_NAME">
+ <term>
+ <envar>NINJA_GENERATED_SOURCE_ALIAS_NAME</envar>
+ </term>
+ <listitem><para>
+ A string matching the name of a user defined alias which represents a list of all generated sources.
+ This will prevent the auto-detection of generated sources from &cv-NINJA_GENERATED_SOURCE_SUFFIXES;.
+ Then all other source files will be made to depend on this in the &ninja; build file, forcing the
+ generated sources to be built first.
</para>
</listitem>
</varlistentry>
@@ -5282,10 +6272,10 @@ Specfies the name of the project to package.
<envar>NINJA_GENERATED_SOURCE_SUFFIXES</envar>
</term>
<listitem><para>
- The list of source file suffixes which are generated by SCons build steps.
+ The list of source file suffixes which are generated by &SCons; build steps.
All source files which match these suffixes will be added to the _generated_sources alias in the output
- <filename>ninja.build</filename> file.
- Then all other source files will be made to depend on this in the <filename>ninja.build</filename> file, forcing the
+ &ninja; build file.
+ Then all other source files will be made to depend on this in the &ninja; build file, forcing the
generated sources to be built first.
</para>
</listitem>
@@ -5295,9 +6285,10 @@ Specfies the name of the project to package.
<envar>NINJA_MSVC_DEPS_PREFIX</envar>
</term>
<listitem><para>
- This propagates directly into the generated <filename>ninja.build</filename> file.
- From Ninja's docs
- <quote>defines the string which should be stripped from msvc’s /showIncludes output</quote>
+ The <parameter>msvc_deps_prefix</parameter> string.
+ Propagates directly into the generated &ninja; build file.
+ From Ninja's docs:
+ <quote>defines the string which should be stripped from msvc's <option>/showIncludes</option> output</quote>
</para>
</listitem>
</varlistentry>
@@ -5306,7 +6297,7 @@ Specfies the name of the project to package.
<envar>NINJA_POOL</envar>
</term>
<listitem><para>
- Set the <quote>ninja_pool</quote> for this or all targets in scope for this env var.
+ Set the <parameter>ninja_pool</parameter> for this or all targets in scope for this env var.
</para>
</listitem>
</varlistentry>
@@ -5315,8 +6306,9 @@ Specfies the name of the project to package.
<envar>NINJA_REGENERATE_DEPS</envar>
</term>
<listitem><para>
- A generator function used to create a ninja depsfile which includes all the files which would require
- SCons to be invoked if they change.
+ A generator function used to create a &ninja; depfile which
+ includes all the files which would require
+ &SCons; to be invoked if they change.
Or a list of said files.
</para>
</listitem>
@@ -5327,7 +6319,28 @@ Specfies the name of the project to package.
</term>
<listitem><para>
Internal value used to specify the function to call with argument env to generate the list of files
- which if changed would require the ninja file to be regenerated.
+ which if changed would require the &ninja; build file to be regenerated.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry id="cv-NINJA_SCONS_DAEMON_KEEP_ALIVE">
+ <term>
+ <envar>NINJA_SCONS_DAEMON_KEEP_ALIVE</envar>
+ </term>
+ <listitem><para>
+ The number of seconds for the SCons deamon launched by ninja to stay alive.
+ (Default: 180000)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry id="cv-NINJA_SCONS_DAEMON_PORT">
+ <term>
+ <envar>NINJA_SCONS_DAEMON_PORT</envar>
+ </term>
+ <listitem><para>
+ The TCP/IP port for the SCons daemon to listen on.
+ <emphasis>NOTE: You cannot use a port already being listened to on your build machine.</emphasis>
+ (Default: random number between 10000,60000)
</para>
</listitem>
</varlistentry>
@@ -5336,9 +6349,9 @@ Specfies the name of the project to package.
<envar>NINJA_SYNTAX</envar>
</term>
<listitem><para>
- Theres also NINJA_SYNTAX which is the path to a custom ninja_syntax.py file which is used in generation.
- The tool currently assumes you have ninja installed through pip, and grabs the syntax file from that
- installation if none specified.
+ The path to a custom <filename>ninja_syntax.py</filename> file which is used in generation.
+ The tool currently assumes you have &ninja; installed as a &Python; module and grabs the syntax file from that
+ installation if &cv-NINJA_SYNTAX; is not explicitly set.
</para>
</listitem>
</varlistentry>
@@ -5766,44 +6779,65 @@ A Python function used to print the command lines as they are executed
or
<option>-s</option>
options or their equivalents).
-The function should take four arguments:
+The function must accept four arguments:
<varname>s</varname>,
-the command being executed (a string),
<varname>target</varname>,
-the target being built (file node, list, or string name(s)),
+<varname>source</varname> and
+<varname>env</varname>.
+<varname>s</varname>
+is a string showing the command being executed,
+<varname>target</varname>,
+is the target being built (file node, list, or string name(s)),
<varname>source</varname>,
-the source(s) used (file node, list, or string name(s)), and
-<varname>env</varname>,
-the environment being used.
+is the source(s) used (file node, list, or string name(s)),
+and <varname>env</varname>
+is the environment being used.
</para>
<para>
-The function must do the printing itself. The default implementation,
-used if this variable is not set or is None, is:
+The function must do the printing itself.
+The default implementation,
+used if this variable is not set or is <constant>None</constant>,
+is to just print the string, as in:
</para>
<example_commands>
def print_cmd_line(s, target, source, env):
- sys.stdout.write(s + "\n")
+ sys.stdout.write(s + "\n")
</example_commands>
<para>
-Here's an example of a more interesting function:
+Here is an example of a more interesting function:
</para>
<example_commands>
def print_cmd_line(s, target, source, env):
- sys.stdout.write("Building %s -&gt; %s...\n" %
- (' and '.join([str(x) for x in source]),
- ' and '.join([str(x) for x in target])))
-env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
-env.Program('foo', 'foo.c')
+ sys.stdout.write(
+ "Building %s -&gt; %s...\n"
+ % (
+ ' and '.join([str(x) for x in source]),
+ ' and '.join([str(x) for x in target]),
+ )
+ )
+
+env = Environment(PRINT_CMD_LINE_FUNC=print_cmd_line)
+env.Program('foo', ['foo.c', 'bar.c'])
</example_commands>
<para>
-This just prints "Building <varname>targetname</varname> from <varname>sourcename</varname>..." instead
-of the actual commands.
-Such a function could also log the actual commands to a log file,
-for example.
+This prints:
+</para>
+
+<screen>
+...
+scons: Building targets ...
+Building bar.c -&gt; bar.o...
+Building foo.c -&gt; foo.o...
+Building foo.o and bar.o -&gt; foo...
+scons: done building targets.
+</screen>
+
+<para>
+Another example could be a function that logs the actual commands to a file.
</para>
</listitem>
</varlistentry>
@@ -6751,6 +7785,56 @@ construction variable for more information.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-SHELL_ENV_GENERATORS">
+ <term>
+ <envar>SHELL_ENV_GENERATORS</envar>
+ </term>
+ <listitem><para>
+Must be a list (or an iterable) containing functions where each function generates or
+alters the environment dictionary which will be used
+when executing the &cv-link-SPAWN; function. The functions will initially
+be passed a reference of the current execution environment (e.g. env['ENV']),
+and each called while iterating the list. Each function must return a dictionary
+which will then be passed to the next function iterated. The return dictionary
+should contain keys which represent the environment variables and their respective
+values.
+
+This primary purpose of this construction variable is to give the user the ability
+to substitute execution environment variables based on env, targets, and sources.
+If desired, the user can completely customize the execution environment for particular
+targets.
+ </para>
+
+ <example_commands>
+def custom_shell_env(env, target, source, shell_env):
+ """customize shell_env if desired"""
+ if str(target[0]) == 'special_target':
+ shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source)
+ return shell_env
+
+env["SHELL_ENV_GENERATORS"] = [custom_shell_env]
+ </example_commands>
+
+ <para>
+ <varname>env</varname>
+The SCons construction environment from which the
+execution environment can be derived from.
+ </para>
+ <para>
+ <varname>target</varname>
+The list of targets associated with this action.
+ </para>
+ <para>
+ <varname>source</varname>
+The list of sources associated with this action.
+ </para>
+ <para>
+ <varname>shell_env</varname>
+The current shell_env after iterating other SHELL_ENV_GENERATORS functions. This can be compared
+to the passed env['ENV'] to detect any changes.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry id="cv-SHF03">
<term>
<envar>SHF03</envar>
@@ -6801,7 +7885,7 @@ Options that are passed to the Fortran 03 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF03FLAGS; if you need to define specific
user options for Fortran 03 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -6889,7 +7973,7 @@ 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,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -6977,7 +8061,7 @@ Options that are passed to the Fortran 77 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF77FLAGS; if you need to define specific
user options for Fortran 77 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -7065,7 +8149,7 @@ Options that are passed to the Fortran 90 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF90FLAGS; if you need to define specific
user options for Fortran 90 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -7153,7 +8237,7 @@ Options that are passed to the Fortran 95 compiler
to generated shared-library objects.
You only need to set &cv-link-SHF95FLAGS; if you need to define specific
user options for Fortran 95 files.
-You should normally set the &cv-link-SHFORTRANFLAGS; variable,
+You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable,
which specifies the user-specified options
passed to the default Fortran compiler
for all Fortran versions.
@@ -7207,6 +8291,12 @@ The default Fortran compiler used for generating shared-library objects.
<listitem><para>
The command line used to compile a Fortran source file
to a shared-library object file.
+By default, any options specified
+in the &cv-link-SHFORTRANFLAGS;,
+&cv-link-_FORTRANMODFLAG;, and
+&cv-link-_FORTRANINCFLAGS;
+&consvars; are included on this command line.
+See also &cv-link-FORTRANCOM;.
</para>
</listitem>
</varlistentry>
@@ -7240,10 +8330,13 @@ to generate shared-library objects.
The command line used to compile a Fortran source file to a
shared-library object file
after first running the file through the C preprocessor.
-Any options specified
-in the &cv-link-SHFORTRANFLAGS; and
-&cv-link-CPPFLAGS; construction variables
-are included on this command line.
+By default, any options specified in the &cv-link-SHFORTRANFLAGS;,
+&cv-link-CPPFLAGS;,
+&cv-link-_CPPDEFFLAGS;,
+&cv-link-_FORTRANMODFLAG;, and
+&cv-link-_FORTRANINCFLAGS;
+&consvars; are included on this command line.
+See also &cv-link-SHFORTRANCOM;.
</para>
</listitem>
</varlistentry>
@@ -7518,7 +8611,7 @@ which would be a symlink and point to <filename>libtest.so.0.1.2</filename>
</term>
<listitem><para>
A command interpreter function that will be called to execute command line
-strings. The function must expect the following arguments:
+strings. The function must accept five arguments:
</para>
<example_commands>
@@ -7526,18 +8619,18 @@ def spawn(shell, escape, cmd, args, env):
</example_commands>
<para>
-<varname>sh</varname>
-is a string naming the shell program to use.
+<varname>shell</varname>
+is a string naming the shell program to use,
<varname>escape</varname>
is a function that can be called to escape shell special characters in
-the command line.
+the command line,
<varname>cmd</varname>
-is the path to the command to be executed.
+is the path to the command to be executed,
<varname>args</varname>
-is the arguments to the command.
+holds the arguments to the command and
<varname>env</varname>
-is a dictionary of the environment variables
-in which the command should be executed.
+is a dictionary of environment variables
+defining the execution environment in which the command should be executed.
</para>
</listitem>
</varlistentry>
@@ -7604,7 +8697,7 @@ field in MSI packages.
<envar>SWIG</envar>
</term>
<listitem><para>
-The scripting language wrapper and interface generator.
+The name of the &swig; compiler to use.
</para>
</listitem>
</varlistentry>
@@ -7614,10 +8707,11 @@ The scripting language wrapper and interface generator.
</term>
<listitem><para>
The suffix that will be used for intermediate C
-source files generated by
-the scripting language wrapper and interface generator.
-The default value is
-<filename>_wrap</filename>&cv-link-CFILESUFFIX;.
+source files generated by &swig;.
+The default value is <literal>'_wrap$CFILESUFFIX'</literal> -
+that is, the concatenation of the string
+<literal>_wrap</literal>
+and the current C suffix &cv-link-CFILESUFFIX;.
By default, this value is used whenever the
<option>-c++</option>
option is
@@ -7633,8 +8727,7 @@ construction variable.
<envar>SWIGCOM</envar>
</term>
<listitem><para>
-The command line used to call
-the scripting language wrapper and interface generator.
+The command line used to call &swig;.
</para>
</listitem>
</varlistentry>
@@ -7643,8 +8736,7 @@ the scripting language wrapper and interface generator.
<envar>SWIGCOMSTR</envar>
</term>
<listitem><para>
-The string displayed when calling
-the scripting language wrapper and interface generator.
+The string displayed when calling &swig;.
If this is not set, then &cv-link-SWIGCOM; (the command line) is displayed.
</para>
</listitem>
@@ -7655,12 +8747,13 @@ If this is not set, then &cv-link-SWIGCOM; (the command line) is displayed.
</term>
<listitem><para>
The suffix that will be used for intermediate C++
-source files generated by
-the scripting language wrapper and interface generator.
-The default value is
-<filename>_wrap</filename>&cv-link-CFILESUFFIX;.
+source files generated by &swig;.
+The default value is <literal>'_wrap$CXXFILESUFFIX'</literal> -
+that is, the concatenation of the string
+<literal>_wrap</literal>
+and the current C++ suffix &cv-link-CXXFILESUFFIX;.
By default, this value is used whenever the
-<filename>-c++</filename>
+<option>-c++</option>
option is specified as part of the
&cv-link-SWIGFLAGS;
construction variable.
@@ -7673,8 +8766,8 @@ construction variable.
</term>
<listitem><para>
The suffix that will be used for intermediate C++ header
-files generated by the scripting language wrapper and interface generator.
-These are only generated for C++ code when the SWIG 'directors' feature is
+files generated by &swig;.
+These are only generated for C++ code when the &swig; 'directors' feature is
turned on.
The default value is
<filename>_wrap.h</filename>.
@@ -7686,22 +8779,14 @@ The default value is
<envar>SWIGFLAGS</envar>
</term>
<listitem><para>
-General options passed to
-the scripting language wrapper and interface generator.
-This is where you should set
-<option>-python</option>,
+General options passed to &swig;.
+This is where you should set the target language
+(<option>-python</option>,
<option>-perl5</option>,
-<option>-tcl</option>,
-or whatever other options you want to specify to SWIG.
-If you set the
-<option>-c++</option>
-option in this variable,
-&scons;
-will, by default,
-generate a C++ intermediate source file
-with the extension that is specified as the
-&cv-link-CXXFILESUFFIX;
-variable.
+<option>-tcl</option>, etc.)
+and whatever other options you want to specify to &swig;,
+such as the <option>-c++</option> to generate C++ code
+instead of C Code.
</para>
</listitem>
</varlistentry>
@@ -7711,7 +8796,7 @@ variable.
</term>
<listitem><para>
An automatically-generated construction variable
-containing the SWIG command-line options
+containing the &swig; command-line options
for specifying directories to be searched for included files.
The value of &cv-_SWIGINCFLAGS; is created
by respectively prepending and appending
@@ -7726,7 +8811,7 @@ of each directory in &cv-SWIGPATH;.
<envar>SWIGINCPREFIX</envar>
</term>
<listitem><para>
-The prefix used to specify an include directory on the SWIG command line.
+The prefix used to specify an include directory on the &swig; command line.
This will be prepended to the beginning of each directory
in the &cv-SWIGPATH; construction variable
when the &cv-_SWIGINCFLAGS; variable is automatically generated.
@@ -7738,7 +8823,7 @@ when the &cv-_SWIGINCFLAGS; variable is automatically generated.
<envar>SWIGINCSUFFIX</envar>
</term>
<listitem><para>
-The suffix used to specify an include directory on the SWIG command line.
+The suffix used to specify an include directory on the &swig; command line.
This will be appended to the end of each directory
in the &cv-SWIGPATH; construction variable
when the &cv-_SWIGINCFLAGS; variable is automatically generated.
@@ -7750,8 +8835,7 @@ when the &cv-_SWIGINCFLAGS; variable is automatically generated.
<envar>SWIGOUTDIR</envar>
</term>
<listitem><para>
-Specifies the output directory in which
-the scripting language wrapper and interface generator
+Specifies the output directory in which &swig;
should place generated language-specific files.
This will be used by SCons to identify
the files that will be generated by the &swig; call,
@@ -7765,22 +8849,24 @@ and translated into the
<envar>SWIGPATH</envar>
</term>
<listitem><para>
-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
+The list of directories that &swig;
+will search for included files.
+&SCons;' SWIG implicit dependency scanner will search these
directories for include files. The default value is an empty list.
</para>
<para>
Don't explicitly put include directory
-arguments in SWIGFLAGS;
+arguments in &cv-link-SWIGFLAGS;
the result will be non-portable
and the directories will not be searched by the dependency scanner.
-Note: directory names in SWIGPATH will be looked-up relative to the SConscript
+Note: directory names in &cv-link-SWIGPATH;
+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 #:
+to look-up a directory relative to the root of the source tree use
+a top-relative path (<literal>#</literal>):
</para>
<example_commands>
@@ -7824,7 +8910,7 @@ env = Environment(SWIGCOM="my_swig -o $TARGET $_SWIGINCFLAGS $SOURCES")
<envar>SWIGVERSION</envar>
</term>
<listitem><para>
-The version number of the SWIG tool.
+The detected version string of the &swig; tool.
</para>
</listitem>
</varlistentry>
@@ -8985,6 +10071,31 @@ The parser generator.
</para>
</listitem>
</varlistentry>
+ <varlistentry id="cv-YACC_GRAPH_FILE">
+ <term>
+ <envar>YACC_GRAPH_FILE</envar>
+ </term>
+ <listitem><para>
+If supplied, write a graph of the automaton to a file with the name
+taken from this variable.
+Will be emitted as a <option>--graph=</option>
+command-line option. Use this in preference to including
+<option>--graph=</option> in &cv-link-YACCFLAGS; directly.
+</para>
+</listitem>
+ </varlistentry>
+ <varlistentry id="cv-YACC_HEADER_FILE">
+ <term>
+ <envar>YACC_HEADER_FILE</envar>
+ </term>
+ <listitem><para>
+If supplied, generate a header file with the name taken from this variable.
+Will be emitted as a <option>--header=</option>
+command-line option. Use this in preference to including
+<option>--header=</option> in &cv-link-YACCFLAGS; directly.
+</para>
+</listitem>
+ </varlistentry>
<varlistentry id="cv-YACCCOM">
<term>
<envar>YACCCOM</envar>
@@ -9006,7 +10117,7 @@ If this is not set, then &cv-link-YACCCOM; (the command line) is displayed.
</para>
<example_commands>
-env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES")
+env = Environment(YACCCOMSTR="Yacc'ing $TARGET from $SOURCES")
</example_commands>
</listitem>
</varlistentry>
@@ -9016,11 +10127,51 @@ env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES")
</term>
<listitem><para>
General options passed to the parser generator.
-If &cv-link-YACCFLAGS; contains a <option>-d</option> option,
-SCons assumes that the call will also create a .h file
-(if the yacc source file ends in a .y suffix)
-or a .hpp file
-(if the yacc source file ends in a .yy suffix)
+In addition to passing the value on during invocation,
+the &t-link-yacc; tool also examines this &consvar; for options
+which cause additional output files to be generated,
+and adds those to the target list.
+</para>
+
+<para>
+If a <option>-d</option> option is present,
+&scons; assumes that the call will also create a header file
+with the suffix defined by &cv-link-YACCHFILESUFFIX;
+if the yacc source file ends in a <filename>.y</filename> suffix,
+or a file with the suffix defined by &cv-link-YACCHXXFILESUFFIX;
+if the yacc source file ends in a <filename>.yy</filename> suffix.
+</para>
+
+<para>
+If a <option>-g</option> option is present,
+&scons; assumes that the call will also create a graph file
+with the suffix defined by &cv-link-YACCVCGFILESUFFIX;.
+</para>
+
+<para>
+If a <option>-v</option> option is present,
+&scons; assumes that the call will also create an output debug file
+with the suffix <filename>.output</filename>.
+</para>
+
+<para>
+Also recognized are GNU &bison; options
+<option>--header=</option> and its deprecated synonym
+<option>--defines=</option>,
+which is similar to
+<option>-d</option>
+but the output filename is named by the option argument;
+and <option>--graph=</option>,
+which is similar to
+<option>-g</option>
+but the output filename is named by the option argument.
+</para>
+
+<para>
+Note that files specified by <option>--header=</option> and
+<option>--graph=</option> may not be properly handled
+by &SCons; in all situations. Consider using
+&cv-link-YACC_HEADER_FILE; and &cv-link-YACC_GRAPH_FILE; instead.
</para>
</listitem>
</varlistentry>
diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod
index cd4e2a4..ceb58ac 100644
--- a/doc/generated/variables.mod
+++ b/doc/generated/variables.mod
@@ -9,7 +9,6 @@ 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-__NINJA_NO "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$__NINJA_NO</envar>">
<!ENTITY cv-__SHLIBVERSIONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$__SHLIBVERSIONFLAGS</envar>">
<!ENTITY cv-APPLELINK_COMPATIBILITY_VERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$APPLELINK_COMPATIBILITY_VERSION</envar>">
<!ENTITY cv-_APPLELINK_COMPATIBILITY_VERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_APPLELINK_COMPATIBILITY_VERSION</envar>">
@@ -38,6 +37,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-CC "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CC</envar>">
<!ENTITY cv-CCCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CCCOM</envar>">
<!ENTITY cv-CCCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CCCOMSTR</envar>">
+<!ENTITY cv-CCDEPFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CCDEPFLAGS</envar>">
<!ENTITY cv-CCFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CCFLAGS</envar>">
<!ENTITY cv-CCPCHFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CCPCHFLAGS</envar>">
<!ENTITY cv-CCPDBFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$CCPDBFLAGS</envar>">
@@ -186,6 +186,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-File "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$File</envar>">
<!ENTITY cv-FORTRAN "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$FORTRAN</envar>">
<!ENTITY cv-FORTRANCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$FORTRANCOM</envar>">
+<!ENTITY cv-FORTRANCOMMONFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$FORTRANCOMMONFLAGS</envar>">
<!ENTITY cv-FORTRANCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$FORTRANCOMSTR</envar>">
<!ENTITY cv-FORTRANFILESUFFIXES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$FORTRANFILESUFFIXES</envar>">
<!ENTITY cv-FORTRANFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$FORTRANFLAGS</envar>">
@@ -266,6 +267,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-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-LEX_HEADER_FILE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LEX_HEADER_FILE</envar>">
+<!ENTITY cv-LEX_TABLES_FILE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LEX_TABLES_FILE</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>">
<!ENTITY cv-LEXFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$LEXFLAGS</envar>">
@@ -320,7 +323,15 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-MSSDK_DIR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSSDK_DIR</envar>">
<!ENTITY cv-MSSDK_VERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSSDK_VERSION</envar>">
<!ENTITY cv-MSVC_BATCH "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_BATCH</envar>">
+<!ENTITY cv-MSVC_NOTFOUND_POLICY "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_NOTFOUND_POLICY</envar>">
+<!ENTITY cv-MSVC_SCRIPT_ARGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_SCRIPT_ARGS</envar>">
+<!ENTITY cv-MSVC_SCRIPTERROR_POLICY "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_SCRIPTERROR_POLICY</envar>">
+<!ENTITY cv-MSVC_SDK_VERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_SDK_VERSION</envar>">
+<!ENTITY cv-MSVC_SPECTRE_LIBS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_SPECTRE_LIBS</envar>">
+<!ENTITY cv-MSVC_TOOLSET_VERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_TOOLSET_VERSION</envar>">
<!ENTITY cv-MSVC_USE_SCRIPT "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_USE_SCRIPT</envar>">
+<!ENTITY cv-MSVC_USE_SCRIPT_ARGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_USE_SCRIPT_ARGS</envar>">
+<!ENTITY cv-MSVC_USE_SETTINGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_USE_SETTINGS</envar>">
<!ENTITY cv-MSVC_UWP_APP "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_UWP_APP</envar>">
<!ENTITY cv-MSVC_VERSION "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVC_VERSION</envar>">
<!ENTITY cv-MSVS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MSVS</envar>">
@@ -351,17 +362,22 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-MWCW_VERSIONS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$MWCW_VERSIONS</envar>">
<!ENTITY cv-NAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NAME</envar>">
<!ENTITY cv-NINJA_ALIAS_NAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_ALIAS_NAME</envar>">
+<!ENTITY cv-NINJA_CMD_ARGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_CMD_ARGS</envar>">
<!ENTITY cv-NINJA_COMPDB_EXPAND "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_COMPDB_EXPAND</envar>">
+<!ENTITY cv-NINJA_DEPFILE_PARSE_FORMAT "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_DEPFILE_PARSE_FORMAT</envar>">
<!ENTITY cv-NINJA_DIR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_DIR</envar>">
<!ENTITY cv-NINJA_DISABLE_AUTO_RUN "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_DISABLE_AUTO_RUN</envar>">
<!ENTITY cv-NINJA_ENV_VAR_CACHE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_ENV_VAR_CACHE</envar>">
<!ENTITY cv-NINJA_FILE_NAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_FILE_NAME</envar>">
<!ENTITY cv-NINJA_FORCE_SCONS_BUILD "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_FORCE_SCONS_BUILD</envar>">
+<!ENTITY cv-NINJA_GENERATED_SOURCE_ALIAS_NAME "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_GENERATED_SOURCE_ALIAS_NAME</envar>">
<!ENTITY cv-NINJA_GENERATED_SOURCE_SUFFIXES "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_GENERATED_SOURCE_SUFFIXES</envar>">
<!ENTITY cv-NINJA_MSVC_DEPS_PREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_MSVC_DEPS_PREFIX</envar>">
<!ENTITY cv-NINJA_POOL "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_POOL</envar>">
<!ENTITY cv-NINJA_REGENERATE_DEPS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_REGENERATE_DEPS</envar>">
<!ENTITY cv-_NINJA_REGENERATE_DEPS_FUNC "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$_NINJA_REGENERATE_DEPS_FUNC</envar>">
+<!ENTITY cv-NINJA_SCONS_DAEMON_KEEP_ALIVE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_SCONS_DAEMON_KEEP_ALIVE</envar>">
+<!ENTITY cv-NINJA_SCONS_DAEMON_PORT "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_SCONS_DAEMON_PORT</envar>">
<!ENTITY cv-NINJA_SYNTAX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$NINJA_SYNTAX</envar>">
<!ENTITY cv-no_import_lib "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$no_import_lib</envar>">
<!ENTITY cv-OBJPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$OBJPREFIX</envar>">
@@ -480,6 +496,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-SHDLINKCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDLINKCOM</envar>">
<!ENTITY cv-SHDLINKFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHDLINKFLAGS</envar>">
<!ENTITY cv-SHELL "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHELL</envar>">
+<!ENTITY cv-SHELL_ENV_GENERATORS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHELL_ENV_GENERATORS</envar>">
<!ENTITY cv-SHF03 "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF03</envar>">
<!ENTITY cv-SHF03COM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF03COM</envar>">
<!ENTITY cv-SHF03COMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$SHF03COMSTR</envar>">
@@ -642,6 +659,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-XGETTEXTPATHPREFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$XGETTEXTPATHPREFIX</envar>">
<!ENTITY cv-XGETTEXTPATHSUFFIX "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$XGETTEXTPATHSUFFIX</envar>">
<!ENTITY cv-YACC "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$YACC</envar>">
+<!ENTITY cv-YACC_GRAPH_FILE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$YACC_GRAPH_FILE</envar>">
+<!ENTITY cv-YACC_HEADER_FILE "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$YACC_HEADER_FILE</envar>">
<!ENTITY cv-YACCCOM "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$YACCCOM</envar>">
<!ENTITY cv-YACCCOMSTR "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$YACCCOMSTR</envar>">
<!ENTITY cv-YACCFLAGS "<envar xmlns='http://www.scons.org/dbxsd/v1.0'>$YACCFLAGS</envar>">
@@ -664,7 +683,6 @@ 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-__NINJA_NO "<link linkend='cv-__NINJA_NO' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$__NINJA_NO</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-APPLELINK_COMPATIBILITY_VERSION "<link linkend='cv-APPLELINK_COMPATIBILITY_VERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$APPLELINK_COMPATIBILITY_VERSION</envar></link>">
<!ENTITY cv-link-_APPLELINK_COMPATIBILITY_VERSION "<link linkend='cv-_APPLELINK_COMPATIBILITY_VERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_APPLELINK_COMPATIBILITY_VERSION</envar></link>">
@@ -693,6 +711,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-CC "<link linkend='cv-CC' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CC</envar></link>">
<!ENTITY cv-link-CCCOM "<link linkend='cv-CCCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CCCOM</envar></link>">
<!ENTITY cv-link-CCCOMSTR "<link linkend='cv-CCCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CCCOMSTR</envar></link>">
+<!ENTITY cv-link-CCDEPFLAGS "<link linkend='cv-CCDEPFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CCDEPFLAGS</envar></link>">
<!ENTITY cv-link-CCFLAGS "<link linkend='cv-CCFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CCFLAGS</envar></link>">
<!ENTITY cv-link-CCPCHFLAGS "<link linkend='cv-CCPCHFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CCPCHFLAGS</envar></link>">
<!ENTITY cv-link-CCPDBFLAGS "<link linkend='cv-CCPDBFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$CCPDBFLAGS</envar></link>">
@@ -841,6 +860,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-File "<link linkend='cv-File' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$File</envar></link>">
<!ENTITY cv-link-FORTRAN "<link linkend='cv-FORTRAN' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$FORTRAN</envar></link>">
<!ENTITY cv-link-FORTRANCOM "<link linkend='cv-FORTRANCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$FORTRANCOM</envar></link>">
+<!ENTITY cv-link-FORTRANCOMMONFLAGS "<link linkend='cv-FORTRANCOMMONFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$FORTRANCOMMONFLAGS</envar></link>">
<!ENTITY cv-link-FORTRANCOMSTR "<link linkend='cv-FORTRANCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$FORTRANCOMSTR</envar></link>">
<!ENTITY cv-link-FORTRANFILESUFFIXES "<link linkend='cv-FORTRANFILESUFFIXES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$FORTRANFILESUFFIXES</envar></link>">
<!ENTITY cv-link-FORTRANFLAGS "<link linkend='cv-FORTRANFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$FORTRANFLAGS</envar></link>">
@@ -921,6 +941,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-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-LEX_HEADER_FILE "<link linkend='cv-LEX_HEADER_FILE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LEX_HEADER_FILE</envar></link>">
+<!ENTITY cv-link-LEX_TABLES_FILE "<link linkend='cv-LEX_TABLES_FILE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LEX_TABLES_FILE</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>">
<!ENTITY cv-link-LEXFLAGS "<link linkend='cv-LEXFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$LEXFLAGS</envar></link>">
@@ -975,7 +997,15 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-MSSDK_DIR "<link linkend='cv-MSSDK_DIR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSSDK_DIR</envar></link>">
<!ENTITY cv-link-MSSDK_VERSION "<link linkend='cv-MSSDK_VERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSSDK_VERSION</envar></link>">
<!ENTITY cv-link-MSVC_BATCH "<link linkend='cv-MSVC_BATCH' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_BATCH</envar></link>">
+<!ENTITY cv-link-MSVC_NOTFOUND_POLICY "<link linkend='cv-MSVC_NOTFOUND_POLICY' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_NOTFOUND_POLICY</envar></link>">
+<!ENTITY cv-link-MSVC_SCRIPT_ARGS "<link linkend='cv-MSVC_SCRIPT_ARGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_SCRIPT_ARGS</envar></link>">
+<!ENTITY cv-link-MSVC_SCRIPTERROR_POLICY "<link linkend='cv-MSVC_SCRIPTERROR_POLICY' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_SCRIPTERROR_POLICY</envar></link>">
+<!ENTITY cv-link-MSVC_SDK_VERSION "<link linkend='cv-MSVC_SDK_VERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_SDK_VERSION</envar></link>">
+<!ENTITY cv-link-MSVC_SPECTRE_LIBS "<link linkend='cv-MSVC_SPECTRE_LIBS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_SPECTRE_LIBS</envar></link>">
+<!ENTITY cv-link-MSVC_TOOLSET_VERSION "<link linkend='cv-MSVC_TOOLSET_VERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_TOOLSET_VERSION</envar></link>">
<!ENTITY cv-link-MSVC_USE_SCRIPT "<link linkend='cv-MSVC_USE_SCRIPT' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_USE_SCRIPT</envar></link>">
+<!ENTITY cv-link-MSVC_USE_SCRIPT_ARGS "<link linkend='cv-MSVC_USE_SCRIPT_ARGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_USE_SCRIPT_ARGS</envar></link>">
+<!ENTITY cv-link-MSVC_USE_SETTINGS "<link linkend='cv-MSVC_USE_SETTINGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_USE_SETTINGS</envar></link>">
<!ENTITY cv-link-MSVC_UWP_APP "<link linkend='cv-MSVC_UWP_APP' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_UWP_APP</envar></link>">
<!ENTITY cv-link-MSVC_VERSION "<link linkend='cv-MSVC_VERSION' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVC_VERSION</envar></link>">
<!ENTITY cv-link-MSVS "<link linkend='cv-MSVS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MSVS</envar></link>">
@@ -1006,17 +1036,22 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-MWCW_VERSIONS "<link linkend='cv-MWCW_VERSIONS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$MWCW_VERSIONS</envar></link>">
<!ENTITY cv-link-NAME "<link linkend='cv-NAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NAME</envar></link>">
<!ENTITY cv-link-NINJA_ALIAS_NAME "<link linkend='cv-NINJA_ALIAS_NAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_ALIAS_NAME</envar></link>">
+<!ENTITY cv-link-NINJA_CMD_ARGS "<link linkend='cv-NINJA_CMD_ARGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_CMD_ARGS</envar></link>">
<!ENTITY cv-link-NINJA_COMPDB_EXPAND "<link linkend='cv-NINJA_COMPDB_EXPAND' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_COMPDB_EXPAND</envar></link>">
+<!ENTITY cv-link-NINJA_DEPFILE_PARSE_FORMAT "<link linkend='cv-NINJA_DEPFILE_PARSE_FORMAT' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_DEPFILE_PARSE_FORMAT</envar></link>">
<!ENTITY cv-link-NINJA_DIR "<link linkend='cv-NINJA_DIR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_DIR</envar></link>">
<!ENTITY cv-link-NINJA_DISABLE_AUTO_RUN "<link linkend='cv-NINJA_DISABLE_AUTO_RUN' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_DISABLE_AUTO_RUN</envar></link>">
<!ENTITY cv-link-NINJA_ENV_VAR_CACHE "<link linkend='cv-NINJA_ENV_VAR_CACHE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_ENV_VAR_CACHE</envar></link>">
<!ENTITY cv-link-NINJA_FILE_NAME "<link linkend='cv-NINJA_FILE_NAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_FILE_NAME</envar></link>">
<!ENTITY cv-link-NINJA_FORCE_SCONS_BUILD "<link linkend='cv-NINJA_FORCE_SCONS_BUILD' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_FORCE_SCONS_BUILD</envar></link>">
+<!ENTITY cv-link-NINJA_GENERATED_SOURCE_ALIAS_NAME "<link linkend='cv-NINJA_GENERATED_SOURCE_ALIAS_NAME' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_GENERATED_SOURCE_ALIAS_NAME</envar></link>">
<!ENTITY cv-link-NINJA_GENERATED_SOURCE_SUFFIXES "<link linkend='cv-NINJA_GENERATED_SOURCE_SUFFIXES' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_GENERATED_SOURCE_SUFFIXES</envar></link>">
<!ENTITY cv-link-NINJA_MSVC_DEPS_PREFIX "<link linkend='cv-NINJA_MSVC_DEPS_PREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_MSVC_DEPS_PREFIX</envar></link>">
<!ENTITY cv-link-NINJA_POOL "<link linkend='cv-NINJA_POOL' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_POOL</envar></link>">
<!ENTITY cv-link-NINJA_REGENERATE_DEPS "<link linkend='cv-NINJA_REGENERATE_DEPS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_REGENERATE_DEPS</envar></link>">
<!ENTITY cv-link-_NINJA_REGENERATE_DEPS_FUNC "<link linkend='cv-_NINJA_REGENERATE_DEPS_FUNC' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$_NINJA_REGENERATE_DEPS_FUNC</envar></link>">
+<!ENTITY cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE "<link linkend='cv-NINJA_SCONS_DAEMON_KEEP_ALIVE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_SCONS_DAEMON_KEEP_ALIVE</envar></link>">
+<!ENTITY cv-link-NINJA_SCONS_DAEMON_PORT "<link linkend='cv-NINJA_SCONS_DAEMON_PORT' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_SCONS_DAEMON_PORT</envar></link>">
<!ENTITY cv-link-NINJA_SYNTAX "<link linkend='cv-NINJA_SYNTAX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$NINJA_SYNTAX</envar></link>">
<!ENTITY cv-link-no_import_lib "<link linkend='cv-no_import_lib' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$no_import_lib</envar></link>">
<!ENTITY cv-link-OBJPREFIX "<link linkend='cv-OBJPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$OBJPREFIX</envar></link>">
@@ -1135,6 +1170,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-SHDLINKCOM "<link linkend='cv-SHDLINKCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDLINKCOM</envar></link>">
<!ENTITY cv-link-SHDLINKFLAGS "<link linkend='cv-SHDLINKFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHDLINKFLAGS</envar></link>">
<!ENTITY cv-link-SHELL "<link linkend='cv-SHELL' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHELL</envar></link>">
+<!ENTITY cv-link-SHELL_ENV_GENERATORS "<link linkend='cv-SHELL_ENV_GENERATORS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHELL_ENV_GENERATORS</envar></link>">
<!ENTITY cv-link-SHF03 "<link linkend='cv-SHF03' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF03</envar></link>">
<!ENTITY cv-link-SHF03COM "<link linkend='cv-SHF03COM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF03COM</envar></link>">
<!ENTITY cv-link-SHF03COMSTR "<link linkend='cv-SHF03COMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$SHF03COMSTR</envar></link>">
@@ -1297,6 +1333,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT.
<!ENTITY cv-link-XGETTEXTPATHPREFIX "<link linkend='cv-XGETTEXTPATHPREFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$XGETTEXTPATHPREFIX</envar></link>">
<!ENTITY cv-link-XGETTEXTPATHSUFFIX "<link linkend='cv-XGETTEXTPATHSUFFIX' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$XGETTEXTPATHSUFFIX</envar></link>">
<!ENTITY cv-link-YACC "<link linkend='cv-YACC' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$YACC</envar></link>">
+<!ENTITY cv-link-YACC_GRAPH_FILE "<link linkend='cv-YACC_GRAPH_FILE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$YACC_GRAPH_FILE</envar></link>">
+<!ENTITY cv-link-YACC_HEADER_FILE "<link linkend='cv-YACC_HEADER_FILE' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$YACC_HEADER_FILE</envar></link>">
<!ENTITY cv-link-YACCCOM "<link linkend='cv-YACCCOM' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$YACCCOM</envar></link>">
<!ENTITY cv-link-YACCCOMSTR "<link linkend='cv-YACCCOMSTR' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$YACCCOMSTR</envar></link>">
<!ENTITY cv-link-YACCFLAGS "<link linkend='cv-YACCFLAGS' xmlns='http://www.scons.org/dbxsd/v1.0'><envar>$YACCFLAGS</envar></link>">
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index a98f4e8..15270e2 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -106,15 +106,16 @@ and a database of information about previous builds so
details do not have to be recalculated each run.
</para>
-<para>&scons; requires Python 3.6 or later to run;
-there should be no other dependencies or requirements.
+<para>&scons; requires &Python; 3.6 or later to run;
+there should be no other dependencies or requirements,
unless the experimental Ninja tool is used.
-<emphasis>
-Support for Python 3.5 is removed since
-&SCons; 4.3.0.
-The CPython project has retired 3.5:
+</para>
+<para>
+<emphasis>Changed in version 4.3.0:</emphasis>
+support for &Python; 3.5 is removed;
+the CPython project has retired 3.5:
<ulink url="https://www.python.org/dev/peps/pep-0478"/>.
-</emphasis></para>
+</para>
<para>You set up an &SCons;
build system by writing a script
@@ -144,46 +145,64 @@ to support additional input file types.
</para>
<para>Information about files involved in the build,
-including a cryptographic hash of the contents,
+including a cryptographic hash of the contents of source files,
is cached for later reuse.
By default this hash (the <firstterm>&contentsig;</firstterm>)
is used to determine if a file has changed since the last build,
-but this can be controlled by selecting an appropriate
+although this can be controlled by selecting an appropriate
<firstterm>&f-link-Decider;</firstterm> function.
Implicit dependency files are also part of out-of-date computation.
The scanned implicit dependency information can optionally be
cached and used to speed up future builds.
-A hash of each executed build action (the <firstterm>&buildsig;</firstterm>
+A hash of each executed build action (the <firstterm>&buildsig;</firstterm>)
is cached, so that changes to build instructions (changing flags, etc.)
or to the build tools themselves (new version)
can also trigger a rebuild.
</para>
<para>
+&SCons; supports the concept of separated source and build
+directories through the definition of
+<firstterm>variant directories</firstterm>
+(see the &f-link-VariantDir; function).
+</para>
+
+<para>
When invoked, &scons;
looks for a file named
&SConstruct;
in the current directory and reads the
build configuration from that file
(other names are allowed,
-see <xref linkend="sconscript_files"/> for more information).
-The &SConstruct;
+see <xref linkend="sconscript_files"/>
+and the <link linkend="opt-f"><option>-f</option></link> option
+for more information).
+The build may be structured in a hierarchical manner:
+the &SConstruct;
file may specify subsidiary
configuration files by calling the
-&f-link-SConscript; function.
+&f-link-SConscript; function,
+and these may, in turn, do the same.
By convention,
these subsidiary files are named
&SConscript;,
although any name may be used.
As a result of this naming convention,
-the term <firstterm>SConscript files</firstterm>
+the term <firstterm>&SConscript; files</firstterm>
is used to refer
generically to the complete set of
configuration files for a project
(including the &SConstruct; file),
-regardless of the actual file names or number of such files.</para>
+regardless of the actual file names or number of such files.
+A hierarchical build is not recursive - all of
+the SConscript files are processed in a single pass,
+although each is processed in a separate context so
+as not to interfere with one another. &SCons; provides
+mechanisms for information to be shared between
+SConscript files when needed.
+</para>
-<para>Before reading the SConscript files,
+<para>Before reading the &SConscript; files,
&scons;
looks for a directory named
<filename>site_scons</filename>
@@ -193,9 +212,9 @@ or, if specified, the
directory from the
<link linkend="opt-site-dir"><option>--site-dir</option></link>
option instead, and prepends the ones it
-finds to the Python module search path (<varname>sys.path</varname>),
+finds to the &Python; module search path (<varname>sys.path</varname>),
thus allowing modules in such directories to be imported in
-the normal Python way in SConscript files.
+the normal &Python; way in &SConscript; files.
For each found site directory,
(1) if it contains a file <filename>site_init.py</filename>
that file is evaluated,
@@ -211,24 +230,24 @@ controlling the site directories.
</para>
<para>
-SConscript files are written in the
-<firstterm>Python</firstterm> programming language,
-although it is normally not necessary to be a Python
+&SConscript; files are written in the
+<firstterm>&Python;</firstterm> programming language,
+although it is normally not necessary to be a &Python;
programmer to use &scons; effectively.
-SConscript files are invoked in a context that makes
+&SConscript; files are invoked in a context that makes
the facilities described in this manual page available
in their local namespace without any special steps.
-Standard Python scripting capabilities
-such as flow control, data manipulation, and imported Python libraries
+Standard &Python; scripting capabilities
+such as flow control, data manipulation, and imported &Python; libraries
are available to use to handle complicated build situations.
-Other Python files can be made a part of the build system,
+Other &Python; files can be made a part of the build system,
but they do not automatically have the &SCons; context and
need to import it if they need access (described later).
</para>
<para>
&scons;
-reads and executes all of the included SConscript files
+reads and executes all of the included &SConscript; files
<emphasis>before</emphasis>
it begins building any targets.
To make this clear,
@@ -251,25 +270,42 @@ may be suppressed using the
<link linkend="opt-Q"><option>-Q</option></link>
option.</para>
-<para>&scons;
-does not automatically propagate
-the external environment used to execute
-&scons;
-to the commands used to build target files.
-This is so that builds will be guaranteed
-repeatable regardless of the environment
-variables set at the time
-&scons;
-is invoked.
-This also means that if the compiler or other commands
+<para>
+To assure reproducible builds,
+&SCons;
+uses a restricted <firstterm>execution environment</firstterm>
+for running external commands used to build targets,
+rather then propagating the full environment
+in effect at the time &scons; was called.
+This helps avoid problems like picking up accidental settings,
+temporary debug values that are no longer needed,
+or one developer having different settings than another
+(or than the CI/CD pipeline).
+Environment variables that are needed for proper
+operation of such commands need to be set explicitly,
+which can be done either by assigning the desired values,
+or by picking values individually out of environment variables
+using the &Python; <systemitem>os.environ</systemitem> dictionary.
+The execution environment for a given &consenv; is
+contained in its &cv-link-ENV; &consvar;.
+A few environment variables are picked up automatically -
+see <xref linkend="environment"/>).
+</para>
+
+<para>
+In particular, if the compiler or other commands
that you want to use to build your target files
are not in standard system locations,
&scons;
will not find them unless
you explicitly include the locations into the
-execution environment by setting the path in the
-<varname>ENV</varname> &consvar; in the
-internal &consenv;:</para>
+<varname>PATH</varname> element of the
+execution environment.
+One example approach is to
+extract the entire <envar>PATH</envar>
+environment variable and set that into the
+execution environment:
+</para>
<programlisting language="python">
import os
@@ -321,7 +357,7 @@ Command-line options (see <xref linkend="options"/>) are consumed.
Any variable argument assignments are collected, and
remaining arguments are taken as targets to build.</para>
-<para>Values of variables to be passed to the SConscript files
+<para>Values of variables to be passed to the &SConscript; files
may be specified on the command line:</para>
<screen>
@@ -330,7 +366,7 @@ may be specified on the command line:</para>
<para>These variables are available through the
<link linkend="v-ARGUMENTS">&ARGUMENTS;</link> dictionary,
-and can be used in the SConscript files to modify
+and can be used in the &SConscript; files to modify
the build in any way:</para>
<programlisting language="python">
@@ -356,7 +392,7 @@ for more information.
<para>&scons;
can maintain a cache of target (derived) files that can
be shared between multiple builds. When derived-file caching is enabled in an
-SConscript file, any target files built by
+&SConscript; file, any target files built by
&scons;
will be copied
to the cache. If an up-to-date target file is found in the cache, it
@@ -432,7 +468,7 @@ The targets listed on the command line are made available in the
<listitem>
<para>If no targets are specified on the command line,
&scons; will select those targets
-specified in the SConscript files via calls
+specified in the &SConscript; files via calls
to the &f-link-Default; function. These are
known as the <firstterm>default targets</firstterm>,
and are made available in the
@@ -443,11 +479,11 @@ and are made available in the
<para>
If no targets are selected by the previous steps,
&scons; selects the current directory for scanning,
-unless command-line options which affect the target
-scan are detected
-(<link linkend="opt-C"><option>-C</option></link>,
+unless command-line options which affect the directory
+for target scanning are present
+(<link linkend="opt-directory"><option>-C</option></link>,
<link linkend="opt-D"><option>-D</option></link>,
-<link linkend="opt-u"><option>-u</option></link>,
+<link linkend="opt-up"><option>-u</option></link>,
<link linkend="opt-U"><option>-U</option></link>).
Since targets thus selected were not the result of
user instructions, this target list is not made available
@@ -504,7 +540,7 @@ build:</para>
</screen>
<para>or by changing directory and invoking scons with the
-<link linkend="opt-u"><option>-u</option></link>
+<link linkend="opt-up"><option>-u</option></link>
option, which traverses up the directory
hierarchy until it finds the
&SConstruct;
@@ -525,7 +561,7 @@ options):</para>
requested, as &scons; needs to make
sure any dependent files are built.</para>
-<para>Specifying "cleanup" targets in SConscript files is
+<para>Specifying "cleanup" targets in &SConscript; files is
usually not necessary.
The
<link linkend="opt-clean"><option>-c</option></link>
@@ -547,7 +583,7 @@ and <filename>export</filename>.</para>
<para>
Additional files or directories to remove can be specified using the
-&f-link-Clean; function in the SConscript files.
+&f-link-Clean; function in the &SConscript; files.
Conversely, targets that would normally be removed by the
<option>-c</option>
invocation can be retained by calling the
@@ -555,7 +591,7 @@ invocation can be retained by calling the
<para>&scons;
supports building multiple targets in parallel via a
-<link linkend="opt-j"><option>-j</option></link>
+<link linkend="opt-jobs"><option>-j</option></link>
option that takes, as its argument, the number
of simultaneous tasks that may be spawned:</para>
@@ -610,10 +646,10 @@ preservation through calls to the &f-link-NoClean; function.
</para>
<para>
While clean mode removes targets rather than building them,
-work which is done directly in Python code in SConscript files
+work which is done directly in &Python; code in &SConscript; files
will still be carried out. If it is important to avoid some
such work from taking place in clean mode, it should be protected.
-An SConscript file can determine which mode
+An &SConscript; file can determine which mode
is active by querying &f-link-GetOption;, as in the call
<code>if GetOption("clean"):</code>
</para>
@@ -621,7 +657,7 @@ is active by querying &f-link-GetOption;, as in the call
</varlistentry>
<varlistentry id="opt-cache-debug">
- <term>-<option>-cache-debug=<replaceable>file</replaceable></option></term>
+ <term><option>--cache-debug=<replaceable>file</replaceable></option></term>
<listitem>
<para>Write debug information about
derived-file caching to the specified
@@ -682,7 +718,7 @@ built during this invocation.
</listitem>
</varlistentry>
- <varlistentry id="opt-show">
+ <varlistentry id="opt-cache-show">
<term><option>--cache-show</option></term>
<listitem>
<para>When using a derived-file cache show the command
@@ -753,7 +789,7 @@ have any results in the cache.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-C">
+ <varlistentry id="opt-directory">
<term>
<option>-C <replaceable>directory</replaceable></option>,
<option>--directory=<replaceable>directory</replaceable></option>
@@ -777,12 +813,12 @@ options are given, each subsequent non-absolute
<option>-C</option> <replaceable>directory</replaceable>
is interpreted relative to the preceding one.
This option is similar to using
-<link linkend="opt-f"><option>-f <replaceable>directory</replaceable>/SConstruct</option></link>,
+<link linkend="opt-sconstruct"><option>-f <replaceable>directory</replaceable>/SConstruct</option></link>,
but <option>-f</option> does not search for any of the
predefined &SConstruct; names
in the specified directory.
See also options
-<link linkend="opt-u"><option>-u</option></link>,
+<link linkend="opt-up"><option>-u</option></link>,
<link linkend="opt-U"><option>-U</option></link>
and
<link linkend="opt-D"><option>-D</option></link>
@@ -801,7 +837,7 @@ to change the &SConstruct; search behavior when this option is used.
<term><option>-D</option></term>
<listitem>
<para>Works exactly the same way as the
-<link linkend="opt-u"><option>-u</option></link>
+<link linkend="opt-up"><option>-u</option></link>
option except for the way default targets are handled.
When this option is used and no targets are specified on the command line,
all default targets are built, whether or not they are below the current
@@ -827,7 +863,7 @@ each command, shows the absolute start and end times.
This may be useful in debugging parallel builds.
Implies the <option>--debug=time</option> option.
</para>
-<para><emphasis>Available since &scons; 3.1.</emphasis></para>
+<para><emphasis>New in version 3.1.</emphasis></para>
</listitem>
</varlistentry>
@@ -836,9 +872,9 @@ Implies the <option>--debug=time</option> option.
<listitem>
<para>Print how many objects are created
of the various classes used internally by SCons
-before and after reading the SConscript files
+before and after reading the &SConscript; files
and before and after building targets.
-This is not supported when SCons is executed with the Python
+This is not supported when SCons is executed with the &Python;
<option>-O</option>
(optimized) option
or when the SCons modules
@@ -852,9 +888,10 @@ files).</para>
<varlistentry>
<term><emphasis role="bold">duplicate</emphasis></term>
<listitem>
-<para>Print a line for each unlink/relink (or copy) of a variant file from
-its source file. Includes debugging info for unlinking stale variant
-files, as well as unlinking old targets before building them.</para>
+<para>Print a line for each unlink/relink (or copy) of a file in
+a variant directory from its source file.
+Includes debugging info for unlinking stale variant directory files,
+as well as unlinking old targets before building them.</para>
</listitem>
</varlistentry>
@@ -906,7 +943,7 @@ instead of recomputing them each time they're needed.</para>
<term><emphasis role="bold">memory</emphasis></term>
<listitem>
<para>Prints how much memory SCons uses
-before and after reading the SConscript files
+before and after reading the &SConscript; files
and before and after building targets.</para>
</listitem>
</varlistentry>
@@ -923,8 +960,8 @@ of the various classes used internally by SCons.</para>
<term><emphasis role="bold">pdb</emphasis></term>
<listitem>
<para>Re-run &scons; under the control of the
-<command>pdb</command>
-Python debugger.</para>
+<systemitem>pdb</systemitem>
+&Python; debugger.</para>
</listitem>
</varlistentry>
@@ -963,7 +1000,7 @@ Building myprog.o with action(s):
<varlistentry>
<term><emphasis role="bold">stacktrace</emphasis></term>
<listitem>
-<para>Prints an internal Python stack trace
+<para>Prints an internal &Python; stack trace
when encountering an otherwise unexplained error.</para>
</listitem>
</varlistentry>
@@ -980,11 +1017,11 @@ when encountering an otherwise unexplained error.</para>
<para>The total build time (time SCons ran from beginning to end)</para>
</listitem>
<listitem>
-<para>The total time spent reading and executing SConscript files</para>
+<para>The total time spent reading and executing &SConscript; files</para>
</listitem>
<listitem>
<para>The total time SCons itself spent running
-(that is, not counting reading and executing SConscript files)</para>
+(that is, not counting reading and executing &SConscript; files)</para>
</listitem>
<listitem>
<para>The total time spent executing all build commands</para></listitem>
@@ -992,7 +1029,7 @@ when encountering an otherwise unexplained error.</para>
<para>The elapsed wall-clock time spent executing those build commands</para>
</listitem>
<listitem>
-<para>The time spent processing each file passed to the &SConscriptFunc; function</para>
+<para>The time spent processing each file passed to the &f-link-SConscript; function</para>
</listitem>
</itemizedlist>
<para>
@@ -1108,15 +1145,18 @@ the mechanisms in the specified order.</para>
the special tokens <literal>all</literal> or <literal>none</literal>.
A comma-separated string can be used to select multiple features.
The default setting is <literal>none</literal>.</para>
- <para>Current available features are: <literal>ninja</literal>.</para>
+ <para>Current available features are:
+ <literal>ninja</literal> (<emphasis>added in version 4.2</emphasis>),
+ <literal>tm_v2</literal> (<emphasis>added in version 4.4.1</emphasis>).
+ </para>
<caution><para>
No Support offered for any features or tools enabled by this flag.
</para></caution>
- <para><emphasis>Available since &scons; 4.2.</emphasis></para>
+ <para><emphasis>New in version 4.2 (experimental).</emphasis></para>
</listitem>
</varlistentry>
- <varlistentry id="opt-f">
+ <varlistentry id="opt-sconstruct">
<term>
<option>-f <replaceable>file</replaceable></option>,
<option>--file=<replaceable>file</replaceable></option>,
@@ -1126,7 +1166,7 @@ the mechanisms in the specified order.</para>
<listitem>
<para>Use
<replaceable>file</replaceable>
- as the initial SConscript file.
+ as the initial &SConscript; file.
Multiple
<option>-f</option>
options may be specified,
@@ -1137,14 +1177,14 @@ the mechanisms in the specified order.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-h">
+ <varlistentry id="opt-help">
<term>
<option>-h</option>,
<option>--help</option>
</term>
<listitem>
<para>Print a local help message for this project,
-if one is defined in the SConscript files
+if one is defined in the &SConscript; files
(see the &f-link-Help; function),
plus a line that refers to the standard &SCons; help message.
If no local help message is defined,
@@ -1154,7 +1194,7 @@ plus help for any local options defined through &f-link-AddOption;.
Exits after displaying the appropriate message.</para>
<para>
Note that use of this option requires &SCons; to process
-the SConscript files, so syntax errors may cause
+the &SConscript; files, so syntax errors may cause
the help message not to be displayed.
</para>
</listitem>
@@ -1176,7 +1216,7 @@ small block-size slows down the build considerably.</para>
<para>The default value is to use a chunk size of 64 kilobytes, which should
be appropriate for most uses.</para>
-<para><emphasis>Available since &scons; 4.2.</emphasis></para>
+<para><emphasis>New in version 4.2.</emphasis></para>
</listitem>
</varlistentry>
@@ -1188,28 +1228,35 @@ be appropriate for most uses.</para>
<para>Set the hashing algorithm used by SCons to
<replaceable>ALGORITHM</replaceable>.
This value determines the hashing algorithm used in generating
-&contentsigs; or &CacheDir; keys.</para>
+&contentsigs;, &buildsigs; and &CacheDir; keys.</para>
-<para>The supported list of values are: md5, sha1, and sha256.
-However, the Python interpreter used to run SCons must have the corresponding
+<para>The supported list of values are:
+<parameter>md5</parameter>,
+<parameter>sha1</parameter>
+and <parameter>sha256</parameter>.
+However, the &Python; interpreter used to run &scons; must have the corresponding
support available in the <systemitem>hashlib</systemitem> module
to use the specified algorithm.</para>
-<para>Specifying this value changes the name of the SConsign database.
-For example, <option>--hash-format=sha256</option> will create a SConsign
-database with name <filename>.sconsign_sha256.dblite</filename>.</para>
-
-<para>If this option is not specified, a the first supported hash format found
-is selected. Typically this is MD5, however, if you are on a FIPS-compliant system
-and using a version of Python less than 3.9, SHA1 or SHA256 will be chosen as the default.
-Python 3.9 and onwards clients will always default to MD5, even in FIPS mode, unless
-otherwise specified with the <option>--hash-format</option> option.</para>
+<para>If this option is omitted,
+the first supported hash format found is selected.
+Typically this is MD5, however, on a FIPS-compliant system
+using a version of &Python; older than 3.9,
+SHA1 or SHA256 is chosen as the default.
+&Python; 3.9 and onwards clients always default to MD5, even in FIPS mode.
+</para>
-<para>For MD5 databases (either explicitly specified with <option>--hash-format=md5</option> or
-defaulted), the SConsign database is<filename>.sconsign.dblite</filename>. The newer SHA1 and
-SHA256 selections meanwhile store their databases to <filename>.sconsign_algorithmname.dblite</filename></para>
+<para>Specifying this option changes the name of the SConsign database.
+The default database is <filename>.sconsign.dblite</filename>.
+In the presence of this option,
+<replaceable>ALGORITHM</replaceable> is
+included in the name to indicate the difference,
+even if the argument is <parameter>md5</parameter>.
+For example, <option>--hash-format=sha256</option> uses a SConsign
+database named <filename>.sconsign_sha256.dblite</filename>.
+</para>
-<para><emphasis>Available since &scons; 4.2.</emphasis></para>
+<para><emphasis>New in version 4.2.</emphasis></para>
</listitem>
</varlistentry>
@@ -1224,7 +1271,7 @@ command-line options and exit.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-i">
+ <varlistentry id="opt-ignore-errors">
<term>
<option>-i</option>,
<option>--ignore-errors</option>
@@ -1235,7 +1282,7 @@ command-line options and exit.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-I">
+ <varlistentry id="opt-include-dir">
<term>
<option>-I <replaceable>directory</replaceable></option>,
<option>--include-dir=<replaceable>directory</replaceable></option>
@@ -1244,7 +1291,7 @@ command-line options and exit.</para>
<para>Specifies a
<replaceable>directory</replaceable>
to search for
-imported Python modules. If several
+imported &Python; modules. If several
<option>-I</option>
options
are used, the directories are searched in the order specified.</para>
@@ -1304,7 +1351,7 @@ This implies
</listitem>
</varlistentry>
- <varlistentry id="opt-install-sandbos">
+ <varlistentry id="opt-install-sandbox">
<term><option>--install-sandbox=<replaceable>sandbox_path</replaceable></option></term>
<listitem>
<para>
@@ -1313,7 +1360,7 @@ When using the &Install; builders, prepend
to the installation paths such that all installed files will be placed
under that directory. This option is unavailable if
one of &b-link-Install;, &b-link-InstallAs; or
-&b-link-InstallVersionedLib; is not used in the SConscript files.
+&b-link-InstallVersionedLib; is not used in the &SConscript; files.
</para>
</listitem>
</varlistentry>
@@ -1322,11 +1369,11 @@ one of &b-link-Install;, &b-link-InstallAs; or
<term><option>--interactive</option></term>
<listitem>
<para>Starts SCons in interactive mode.
-The SConscript files are read once and a
+The &SConscript; files are read once and a
<computeroutput>scons&gt;&gt;&gt;</computeroutput>
prompt is printed.
Targets may now be rebuilt by typing commands at interactive prompt
-without having to re-read the SConscript files
+without having to re-read the &SConscript; files
and re-initialize the dependency graph from scratch.</para>
<para>SCons interactive mode supports the following commands:</para>
@@ -1374,7 +1421,7 @@ do not cause errors
but have no effect on the
<emphasis role="bold">build</emphasis>
command
-(mainly because they affect how the SConscript files are read,
+(mainly because they affect how the &SConscript; files are read,
which only happens once at the beginning of interactive mode).</para>
</listitem>
</varlistentry>
@@ -1475,7 +1522,7 @@ scons&gt;&gt;&gt; exit
</listitem>
</varlistentry>
- <varlistentry id="opt-j">
+ <varlistentry id="opt-jobs">
<term>
<option>-j <replaceable>N</replaceable></option>,
<option>--jobs=<replaceable>N</replaceable></option>
@@ -1495,7 +1542,7 @@ option, the last one is effective.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-k">
+ <varlistentry id="opt-keep-going">
<term>
<option>-k</option>,
<option>--keep-going</option>
@@ -1570,11 +1617,11 @@ no matter how old the file is.</para>
<link linkend="opt-hash-chunksize"><option>--hash-chunksize</option></link>.
</para>
-<para><emphasis>Deprecated since &scons; 4.2.</emphasis></para>
+<para><emphasis>Changed in version 4.2:</emphasis> deprecated.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-n">
+ <varlistentry id="opt-no-exec">
<term>
<option>-n</option>,
<option>--no-exec</option>,
@@ -1583,24 +1630,29 @@ no matter how old the file is.</para>
<option>--recon</option>
</term>
<listitem>
-<para>Set <firstterm>no execute</firstterm> mode.
+<para>Set <firstterm>no-exec</firstterm> mode.
Print the commands that would be executed to build
-any out-of-date target files, but do not execute the commands.</para>
-
-<para>The output is a best effort, as &SCons; cannot always precisely
-determine what would be built. For example, if a file is generated
-by a builder action that is later used in the build,
-that file is not available to scan for dependencies on an unbuilt tree,
-or may contain out of date information in a built tree.
+any out-of-date targets, but do not execute those commands.
</para>
+
<para>
-Work which is done directly in Python code in SConscript files,
-as opposed to work done by builder actions during the build phase,
-will still be carried out. If it is important to avoid some
-such work from taking place in no execute mode, it should be protected.
-An SConscript file can determine which mode
-is active by querying &f-link-GetOption;, as in the call
-<code>if GetOption("no_exec"):</code>
+Only target building is suppressed - any work in the build
+system that is done directly (in regular &Python; code)
+will still be carried out. You can add guards around
+code which should not be executed in no-exec mode by
+checking the value of the option at run time with &f-link-GetOption;:
+</para>
+<programlisting language="python">
+if not GetOption("no_exec"):
+ # run regular instructions
+</programlisting>
+
+<para>The output is a best effort, as &SCons; cannot always precisely
+determine what would be built. For example, if a file generated
+by a builder action is also used as a source in the build,
+that file is not available to scan for dependencies at all
+in an unbuilt tree, and may contain out of date information in a
+previously built tree.
</para>
</listitem>
</varlistentry>
@@ -1610,13 +1662,13 @@ is active by querying &f-link-GetOption;, as in the call
<listitem>
<para>Prevents the automatic addition of the standard
<filename>site_scons</filename>
-dirs to
+directories to
<varname>sys.path</varname>.
Also prevents loading the
<filename>site_scons/site_init.py</filename>
modules if they exist, and prevents adding their
<filename>site_scons/site_tools</filename>
-dirs to the toolpath.</para>
+directories to the toolpath.</para>
</listitem>
</varlistentry>
@@ -1654,11 +1706,10 @@ dirs to the toolpath.</para>
<varlistentry id="opt-package-type">
<term><option>--package-type=<replaceable>type</replaceable></option></term>
<listitem>
-<para>The type or types
+<para>The type
of package to create when using the &b-link-Package; builder.
-In the case of multiple types, <replaceable>type</replaceable>
-should be a comma-separated string; &SCons; will try to build
-for all of those packages.
+Multiple types can be specified by using a comma-separated string,
+in which case &SCons; will try to build for all of those package types.
Note this option is only available if the &t-link-packaging; tool
has been enabled.
</para>
@@ -1668,15 +1719,14 @@ has been enabled.
<varlistentry id="opt-profile">
<term><option>--profile=<replaceable>file</replaceable></option></term>
<listitem>
-<para>Run SCons under the Python profiler
-and save the results in the specified
-<replaceable>file</replaceable>.
-The results may be analyzed using the Python
-pstats module.</para>
+<para>Run &SCons; under the &Python; profiler
+and save the results to <replaceable>file</replaceable>.
+The results may be analyzed using the &Python;
+<systemitem>pstats</systemitem> module.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-q">
+ <varlistentry id="opt-question">
<term>
<option>-q</option>,
<option>--question</option></term>
@@ -1690,8 +1740,8 @@ date, non-zero otherwise.</para>
<varlistentry id="opt-Q">
<term><option>-Q</option></term>
<listitem>
-<para>Quiets SCons status messages about
-reading SConscript files,
+<para>Suppress status messages about
+reading &SConscript; files,
building targets
and entering directories.
Commands that are executed
@@ -1714,7 +1764,7 @@ or retrieve the same target files.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-s">
+ <varlistentry id="opt-silent">
<term>
<option>-s</option>,
<option>--silent</option>,
@@ -1727,7 +1777,7 @@ Also suppresses SCons status messages.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-S">
+ <varlistentry id="opt-no-keep-going">
<term>
<option>-S</option>,
<option>--no-keep-going</option>,
@@ -1851,7 +1901,7 @@ unless you encounter stack overflow errors.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-t">
+ <varlistentry id="opt-touch">
<term>
<option>-t</option>,
<option>--touch</option>
@@ -1916,7 +1966,7 @@ specified alone, without any <replaceable>type</replaceable>,
it behaves as if <emphasis role="bold">all</emphasis>
had been specified.
</para>
-<para><emphasis>Available since &scons; 4.0.</emphasis></para>
+<para><emphasis>New in version 4.0.</emphasis></para>
</listitem>
</varlistentry>
@@ -1956,7 +2006,7 @@ choices may be specified, separated by commas:</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-u">
+ <varlistentry id="opt-up">
<term>
<option>-u</option>,
<option>--up</option>,
@@ -1981,13 +2031,13 @@ current directory will be built.</para>
<option>-u</option>
option except for the way default targets are handled.
When this option is used and no targets are specified on the command line,
-all default targets that are defined in the SConscript(s) in the current
+all default targets that are defined in the &SConscript; file(s) in the current
directory are built, regardless of what directory the resultant targets end
up in.</para>
</listitem>
</varlistentry>
- <varlistentry id="opt-v">
+ <varlistentry id="opt-version">
<term>
<option>-v</option>,
<option>--version</option>
@@ -2158,7 +2208,7 @@ can themselves refer to lists of names or nodes.</para>
These warnings are disabled by default for the first
phase of deprecation. Enable to be reminded about use
of this tool module.
-<emphasis>Available since &SCons; 4.3.</emphasis>
+<emphasis>New in version 4.3.</emphasis>
</para>
</listitem>
</varlistentry>
@@ -2166,7 +2216,7 @@ of this tool module.
<varlistentry>
<term><emphasis role="bold">missing-sconscript</emphasis></term>
<listitem>
-<para>Warnings about missing SConscript files.
+<para>Warnings about missing &SConscript; files.
These warnings are enabled by default.</para>
</listitem>
</varlistentry>
@@ -2178,16 +2228,16 @@ These warnings are enabled by default.</para>
<option>--debug=object</option>
feature not working when
&scons;
-is run with the Python
+is run with the &Python;
<option>-O</option>
-option or from optimized Python (.pyo) modules.</para>
+option or from optimized &Python; (<filename>.pyo</filename>) modules.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><emphasis role="bold">no-parallel-support</emphasis></term>
<listitem>
-<para>Warnings about the version of Python
+<para>Warnings about the version of &Python;
not being able to support parallel builds when the
<option>-j</option>
option is used.
@@ -2199,7 +2249,7 @@ These warnings are enabled by default.</para>
<term><emphasis role="bold">python-version</emphasis></term>
<listitem>
<para>Warnings about running
-SCons with a deprecated version of Python.
+&SCons; using a version of &Python; that has been deprecated.
These warnings are enabled by default.</para>
</listitem>
</varlistentry>
@@ -2264,7 +2314,7 @@ These warnings are enabled by default.</para>
<!-- .B \-n -->
<!-- ... what? XXX -->
- <varlistentry id="opt-Y">
+ <varlistentry id="opt-repository">
<term>
<option>-Y <replaceable>repository</replaceable></option>,
<option>--repository=<replaceable>repository</replaceable></option>,
@@ -2289,7 +2339,7 @@ repositories are searched in the order specified.</para>
<title>SConscript Files</title>
<para>
The build configuration is described by one or more files,
-known as SConscript files.
+known as &SConscript; files.
There must be at least one file for a valid build
(&scons; will quit if it does not find one).
&scons; by default looks for this file by the name
@@ -2309,20 +2359,20 @@ included or excluded at run-time depending on how &scons; is invoked.
</para>
<para>
-Each SConscript file in a build configuration is invoked
+Each &SConscript; file in a build configuration is invoked
independently in a separate context.
This provides necessary isolation so that different parts of
the build don't accidentally step on each other.
You have to be explicit about sharing information,
by using the &f-link-Export; function or the &exports; argument
-to the &SConscript; function, as well as the &f-link-Return; function
-in a called SConscript file, and comsume shared information by using the
+to the &f-link-SConscript; function, as well as the &f-link-Return; function
+in a called &SConscript; file, and comsume shared information by using the
&f-link-Import; function.
</para>
<para>
The following sections describe the various &SCons; facilities
-that can be used in SConscript files. Quick links:
+that can be used in &SConscript; files. Quick links:
</para>
<simplelist type="vert">
@@ -2521,7 +2571,7 @@ See <xref linkend="commandline_construction_variables"/> for details.
(more properly, <firstterm>tool specification modules</firstterm>)
which are used to help initialize the &consenv;.
An &SCons; tool is only responsible for setup.
-For example, if an SConscript file declares
+For example, if an &SConscript; file declares
the need to construct an object file from
a C-language source file by calling the
&b-link-Object; builder, then a tool representing
@@ -2612,28 +2662,35 @@ and specifically <link linkend='tool_modules'>Tool Modules</link>.
<refsect2 id='builder_methods'>
<title>Builder Methods</title>
-<para>You tell &scons; what to build
+<para>You tell &SCons; what to build
by calling <firstterm>Builders</firstterm>,
-functions which take
-particular action(s) to produce a particular result type
-(conventionally described by the builder name such as &b-Program;)
-when given source files of a particular type.
-Calling a builder defines one or more targets to the build system;
-whether the targets are actually built on a given
-invocation is determined by command-line options,
-target selection rules, and whether &SCons;
-determines the target(s) are out of date.
+functions which take particular action(s)
+to produce target(s) of a particular type
+(conventionally hinted at by the builder name, e.g. &Program;)
+from the specified source files.
+A builder call is a declaration: &SCons; enters the
+specified relationship into its internal dependency node graph,
+and only later makes the decision on whether anything is actually built,
+since this depends on command-line options,
+target selection rules, and whether the target(s) are
+out of date with respect to the sources.
</para>
<para>
&SCons;
-defines a number of builders, and you can also write your own.
-Builders are attached to a &consenv; as methods,
-and the available builder methods are listed as
+provides a number of builders, and you can also write your own
+(see <link linkend='builder_objects'>Builder Objects</link>).
+Builders are created dynamically at run-time,
+often (though not always) by tools which determine
+whether the external dependencies for the builder are satisfied,
+and which perform the necessary setup
+(see <link linkend='tools'>Tools</link>).
+Builders are attached to a &consenv; as methods.
+The available builder methods are registered as
key-value pairs in the
-<varname>BUILDERS</varname> attribute of the &consenv;.
-The available builders can be displayed like this
-for debugging purposes:
+&cv-link-BUILDERS; attribute of the &consenv;,
+so the available builders can be examined.
+This example displays them for debugging purposes:
</para>
<programlisting language="python">
@@ -2646,7 +2703,6 @@ Builder methods take two required arguments:
<parameter>target</parameter>
and
<parameter>source</parameter>.
-Either can be passed as a scalar or as a list.
The <parameter>target</parameter> and
<parameter>source</parameter> arguments
can be specified either as positional arguments,
@@ -2660,24 +2716,17 @@ Builder methods also take a variety of
keyword arguments, described below.
</para>
-<para>
-The builder <emphasis>may</emphasis> add other targets
-beyond those requested if indicated by an <firstterm>Emitter</firstterm>
-(see <xref linkend="builder_objects"/> and, for example,
-&cv-link-PROGEMITTER; for more information).
-</para>
-
<para>Because long lists of file names
-can lead to a lot of quoting,
-&scons;
+can lead to a lot of quoting in a builder call,
+&SCons;
supplies a &f-link-Split;
global function
and a same-named environment method
that splits a single string
into a list, using
-strings of white-space characters as the delimiter.
-(similar to the Python string <function>split</function>
-method, but succeeds even if the input isn't a string.)</para>
+strings of white-space characters as the delimiter
+(similar to the &Python; string <function>split</function>
+method, but succeeds even if the input isn't a string).</para>
<para>
The following are equivalent examples of calling the
@@ -2695,9 +2744,12 @@ env.Program('bar', source='bar.c foo.c'.split())
</programlisting>
<para>
-Python follows the POSIX pathname convention for path
-strings: if a string begins with the operating system pathname separator
-(on Windows both the slash and backslash separator work,
+Sources and targets can be specified as a scalar or as a list,
+composed of either strings or nodes (more on nodes below).
+When specifying path strings,
+&Python; follows the POSIX pathname convention:
+if a string begins with the operating system pathname separator
+(on Windows both the slash and backslash separator are accepted,
and any leading drive specifier is ignored for
the determination) it is considered an absolute path,
otherwise it is a relative path.
@@ -2706,32 +2758,26 @@ it is searched for as a file in the current directory. If it
contains separator characters, the search follows down
from the starting point, which is the top of the directory tree for
an absolute path and the current directory for a relative path.
+The "current directory" in this context is the directory
+of the &SConscript; file currently being processed.
</para>
<para>
-&scons; recognizes a third way to specify
+&SCons; also recognizes a third way to specify
path strings: if the string begins with
the <emphasis role="bold">#</emphasis> character it is
<firstterm>top-relative</firstterm> - it works like a relative path but the
search follows down from the directory containing the top-level
&SConstruct; rather than
-from the current directory. The <emphasis role="bold">#</emphasis> is allowed
-to be followed by a pathname separator, which is ignored if
-found in that position.
+from the current directory. The <emphasis role="bold">#</emphasis>
+can optionally be followed by a pathname separator,
+which is ignored if found in that position.
Top-relative paths only work in places where &scons; will
interpret the path (see some examples below). To be
used in other contexts the string will need to be converted
to a relative or absolute path first.
</para>
-<para>
-<parameter>target</parameter> and <parameter>source</parameter>
-can be absolute, relative, or
-top-relative. Relative pathnames are searched considering the
-directory of the SConscript
-file currently being processed as the "current directory".
-</para>
-
<para>Examples:</para>
<programlisting language="python">
@@ -2786,14 +2832,12 @@ env.Program(source='bar.c')
env.Program('bar.c')
</programlisting>
-<para>As a convenience, a
+<para>The optional
<parameter>srcdir</parameter>
-keyword argument may be specified
-when calling a Builder.
-When specified,
+keyword argument specifies that
all source file strings that are not absolute paths
or top-relative paths
-will be interpreted relative to the specified
+shall be interpreted relative to the specified
<parameter>srcdir</parameter>.
The following example will build the
<filename>build/prog</filename>
@@ -2810,6 +2854,99 @@ and
env.Program('build/prog', ['f1.c', 'f2.c'], srcdir='src')
</programlisting>
+<para>The optional
+<parameter>parse_flags</parameter>
+keyword argument causes behavior similar to the
+&f-link-env-MergeFlags; method, where the argument value is
+broken into individual settings and merged into the appropriate &consvars;.
+</para>
+
+<programlisting language="python">
+env.Program('hello', 'hello.c', parse_flags='-Iinclude -DEBUG -lm')
+</programlisting>
+
+<para>This example adds 'include' to
+the &cv-link-CPPPATH; &consvar;,
+'EBUG' to
+&cv-link-CPPDEFINES;,
+and 'm' to
+&cv-link-LIBS;.
+</para>
+
+<para>The optional
+<parameter>chdir</parameter>
+keyword argument
+specifies that the Builder's action(s)
+should be executed
+after changing directory.
+If the
+<parameter>chdir</parameter>
+argument is
+a path string or a directory Node,
+scons will change to the specified directory.
+If the
+<parameter>chdir</parameter>
+is not a string or Node
+and evaluates true,
+then &scons; will change to the
+target file's directory.</para>
+
+<warning>
+<para>
+&Python; only keeps one current directory
+location even if there are multiple threads.
+This means that use of the
+<parameter>chdir</parameter>
+argument
+will
+<emphasis>not</emphasis>
+work with the SCons
+<option>-j</option>
+option,
+because individual worker threads spawned
+by &SCons; interfere with each other
+when they start changing directory.</para>
+</warning>
+
+<programlisting language="python">
+# scons will change to the "sub" subdirectory
+# before executing the "cp" command.
+env.Command(
+ target='sub/dir/foo.out',
+ source='sub/dir/foo.in',
+ action="cp dir/foo.in dir/foo.out",
+ chdir='sub',
+)
+
+# Because chdir is not a string, scons will change to the
+# target's directory ("sub/dir") before executing the
+# "cp" command.
+env.Command('sub/dir/foo.out', 'sub/dir/foo.in', "cp foo.in foo.out", chdir=True)
+</programlisting>
+
+<para>Note that &SCons; will
+<emphasis>not</emphasis>
+automatically modify
+its expansion of
+&consvars; like &cv-link-TARGET;
+and &cv-link-SOURCE;
+when using the <parameter>chdir</parameter>
+keyword argument--that is,
+the expanded file names
+will still be relative to
+the top-level directory where the &SConstruct; was found,
+and consequently incorrect
+relative to the chdir directory.
+If you use the <parameter>chdir</parameter> keyword argument,
+you will typically need to supply a different
+command line using
+expansions like
+<literal>${TARGET.file}</literal>
+and
+<literal>${SOURCE.file}</literal>
+to use just the filename portion of the
+target and source.</para>
+
<para>Keyword arguments that are not specifically
recognized are treated as &consvar;
<firstterm>overrides</firstterm>,
@@ -2837,29 +2974,9 @@ env.SharedLibrary(
<para>Note that both the &cv-link-SHLIBSUFFIX;
and &cv-link-LIBSUFFIXES;
-variables must be set if you want &scons; to search automatically
+&consvars; must be set if you want &scons; to search automatically
for dependencies on the non-standard library names;
-see the descriptions below of these variables for more information.</para>
-
-<para>The optional
-<parameter>parse_flags</parameter>
-keyword argument is recognized by builders.
-This works similarly to the
-&f-link-env-MergeFlags; method, where the argument value is
-broken into individual settings and merged into the appropriate &consvars;.
-</para>
-
-<programlisting language="python">
-env.Program('hello', 'hello.c', parse_flags='-Iinclude -DEBUG -lm')
-</programlisting>
-
-<para>This example adds 'include' to
-the &cv-link-CPPPATH; &consvar;,
-'EBUG' to
-&cv-link-CPPDEFINES;,
-and 'm' to
-&cv-link-LIBS;.
-</para>
+see the descriptions of these variables for more information.</para>
<para>Although the builder methods defined by
&scons;
@@ -2872,30 +2989,47 @@ Program('hello', 'hello.c')
SharedLibrary('word', 'word.cpp')
</programlisting>
-<para>If called this way, methods will internally use the
-&defenv; that consists of the tools and values that
+<para>If called this way, the builder will internally use the
+&DefEnv; that consists of the tools and values that
&scons;
has determined are appropriate for the local system.</para>
<para>Builder methods that can be called without an explicit
-environment (indicated in the listing of builders without
-a leading <varname>env.</varname>)
-may be called from custom Python modules that you
-import into an SConscript file by adding the following
-to the Python module:</para>
+environment (indicated in the listing of builders below
+without a leading <varname>env.</varname>)
+may be called from custom &Python; modules that you
+import into an &SConscript; file by adding the following
+to the &Python; module:</para>
<programlisting language="python">
from SCons.Script import *
</programlisting>
-<para>Builder methods return a <classname>NodeList</classname>,
-a list-like object whose elements are Nodes,
-&SCons;' internal representation of build targets or sources.
-See <xref linkend="node_objects"/> for more information.
+<para>
+A builder <emphasis>may</emphasis> add additional targets
+beyond those requested
+if an attached <firstterm>Emitter</firstterm> chooses to do so
+(see <xref linkend="builder_objects"/> for more information.
+&cv-link-PROGEMITTER; is an example).
+For example, the GNU linker takes a command-line argument
+<option>-Map=<replaceable>mapfile</replaceable></option>,
+which causes it to produce a linker map file in addition
+to the executable file actually being linked.
+If the &b-link-Program; builder's emitter is configured
+to add this mapfile if the option is set,
+then two targets will be returned when you only provided for one.
+</para>
+
+<para>
+For this reason,
+builder methods always return a <classname>NodeList</classname>,
+a list-like object whose elements are Nodes.
+Nodes are the internal representation of build targets or sources
+(see <xref linkend="node_objects"/> for more information).
The returned <classname>NodeList</classname> object
can be passed to other builder methods as source(s)
-or passed to any &SCons; function or method
-where a filename would normally be accepted.
+or to other &SCons; functions or methods
+where a path string would normally be accepted.
</para>
<para> For example,
@@ -2912,7 +3046,7 @@ env.Program("prog", ['foo.c', bar_obj_list, 'main.c'])
makes for a more portable build
by avoiding having to specify
a platform-specific object suffix
-when calling the &Program; builder method.
+when calling the &b-link-Program; builder method.
</para>
<para>The <classname>NodeList</classname> object
@@ -2929,9 +3063,9 @@ Default(tgt)
lists passed as source and target, so they are free to
contain elements which are themselves lists, such as
<varname>bar_obj_list</varname>
-returned by the &StaticObject; call above.
+returned by the &b-link-StaticObject; call.
If you need to manipulate a list of lists returned by builders
-directly in Python code,
+directly in &Python; code,
you can either build a new list by hand:</para>
<programlisting language="python">
@@ -2943,7 +3077,7 @@ for obj in objects:
</programlisting>
<para>Or you can use the &f-link-Flatten;
-function supplied by &scons;
+function supplied by &SCons;
to create a list containing just the Nodes,
which may be more convenient:</para>
@@ -2955,20 +3089,20 @@ for obj in objects:
print(str(obj))
</programlisting>
-<para>&SCons; builder calls return
-a list-like object, not an actual Python list,
-so it is not appropriate to use the Python add
+<para>Since builder calls return
+a list-like object, not an actual &Python; list,
+it is not appropriate to use the &Python; add
operator (<literal>+</literal> or <literal>+=</literal>)
-to append builder results to a Python list.
+to append builder results to a &Python; list.
Because the list and the object are different types,
-Python will not update the original list in place,
+&Python; will not update the original list in place,
but will instead create a new <classname>NodeList</classname> object
containing the concatenation of the list
elements and the builder results.
-This will cause problems for any other Python variables
+This will cause problems for any other &Python; variables
in your SCons configuration
that still hold on to a reference to the original list.
-Instead, use the Python list
+Instead, use the &Python; list
<function>extend</function>
method to make sure the list is updated in-place.
Example:</para>
@@ -2986,7 +3120,7 @@ object_files.extend(Object('bar.c'))
</programlisting>
<para>The path name for a Node's file may be used
-by passing the Node to Python's builtin
+by passing the Node to &Python;'s builtin
<function>str</function>
function:</para>
@@ -2997,89 +3131,40 @@ print("The path to bar_obj is:", str(bar_obj_list[0]))
<para>Note that because the Builder call returns a
<classname>NodeList</classname>,
-you have to access the first element in the list,
+you have to access the first element in the list
(<literal>bar_obj_list[0]</literal> in the example)
to get at the Node that actually represents
the object file.</para>
-<para>Builder calls support a
-<parameter>chdir</parameter>
-keyword argument that
-specifies that the Builder's action(s)
-should be executed
-after changing directory.
-If the
-<parameter>chdir</parameter>
-argument is
-a string or a directory Node,
-scons will change to the specified directory.
-If the
-<parameter>chdir</parameter>
-is not a string or Node
-and is non-zero,
-then scons will change to the
-target file's directory.</para>
-
-<programlisting language="python">
-# scons will change to the "sub" subdirectory
-# before executing the "cp" command.
-env.Command('sub/dir/foo.out', 'sub/dir/foo.in',
- "cp dir/foo.in dir/foo.out",
- chdir='sub')
-
-# Because chdir is not a string, scons will change to the
-# target's directory ("sub/dir") before executing the
-# "cp" command.
-env.Command('sub/dir/foo.out', 'sub/dir/foo.in',
- "cp foo.in foo.out",
- chdir=1)
-</programlisting>
-
-<para>Note that &SCons; will
-<emphasis>not</emphasis>
-automatically modify
-its expansion of
-&consvars; like
-<envar>$TARGET</envar>
-and
-<envar>$SOURCE</envar>
-when using the <parameter>chdir</parameter>
-keyword argument--that is,
-the expanded file names
-will still be relative to
-the top-level directory where &SConstruct; was found,
-and consequently incorrect
-relative to the chdir directory.
-If you use the <parameter>chdir</parameter> keyword argument,
-you will typically need to supply a different
-command line using
-expansions like
-<literal>${TARGET.file}</literal>
-and
-<literal>${SOURCE.file}</literal>
-to use just the filename portion of the
-targets and source.</para>
-
<para>
When trying to handle errors that may occur in a builder method,
consider that the corresponding Action is executed at a different
-time than the SConscript file statement calling the builder.
+time than the &SConscript; file statement calling the builder.
It is not useful to wrap a builder call in a
<systemitem>try</systemitem> block,
since success in the builder call is not the same as
the builder itself succeeding.
If necessary, a Builder's Action should be coded to exit with
-a useful exception message indicating the problem in the SConscript files -
+a useful exception message indicating the problem in the &SConscript; files -
programmatically recovering from build errors is rarely useful.
</para>
-<para>&scons;
-predefines the following builder methods.
+<para>
+The following builder methods are predefined in the
+&SCons; core software distribution.
Depending on the setup of a particular
&consenv; and on the type and software
installation status of the underlying system,
-not all builders may be available to that
-&consenv;.</para>
+not all builders may be available in that
+&consenv;.
+Since the function calling signature is the same for all builders:
+</para>
+<programlisting language="python">
+<function>Buildername</function>(<parameter>target, source, [key=val, ...]</parameter>)
+</programlisting>
+<para>
+it is omitted in this listing for brevity.
+</para>
<!-- '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -->
<!-- '\" BEGIN GENERATED BUILDER DESCRIPTIONS -->
@@ -3216,7 +3301,7 @@ see the next section on
&consvars;.</para>
<para>Global functions may be called from custom Python modules that you
-import into an SConscript file by adding the following import
+import into an &SConscript; file by adding the following import
to the Python module:</para>
<programlisting language="python">
@@ -3256,7 +3341,7 @@ include:</para>
<para>In addition to the global functions and methods,
&scons;
supports a number of variables
-that can be used in SConscript files
+that can be used in &SConscript; files
to affect how you want the build to be performed.</para>
<variablelist>
@@ -3429,7 +3514,7 @@ print([str(t) for t in DEFAULT_TARGETS]) # back to []
only after you've made all of your
&Default;() calls,
or else simply be careful of the order
-of these statements in your SConscript files
+of these statements in your &SConscript; files
so that you don't look for a specific
default target before it's actually been added to the list.</para>
</listitem>
@@ -3438,7 +3523,7 @@ default target before it's actually been added to the list.</para>
<para>
These variables may be accessed from custom Python modules that you
-import into an SConscript file by adding the following
+import into an &SConscript; file by adding the following
to the Python module:</para>
<programlisting language="python">
@@ -3580,7 +3665,7 @@ does not maintain an explicit cache of the tested values
(this is different than &Autoconf;),
but uses its normal dependency tracking to keep the checked values
up to date. However, users may override this behaviour with the
-<option>--config</option>
+<link linkend="opt-config"><option>--config</option></link>
command line option.</para>
<variablelist>
@@ -3594,14 +3679,16 @@ discovered while running tests. The context includes a local &consenv;
which is used when running the tests and
which can be updated with the check results.
Only one context may be active
-at a time (<emphasis>since 4.0, &scons; will raise an exception
-on an attempt to create a new context when there is
-an active context</emphasis>), but a new context can be created
+at a time, but a new context can be created
after the active one is completed.
For the global function form, the required <parameter>env</parameter>
describes the initial values for the context's local &consenv;;
for the &consenv; method form the instance provides the values.
</para>
+<para>
+<emphasis>Changed in version 4.0</emphasis>: raises an exception
+on an attempt to create a new context when there is an active context.
+</para>
<para><parameter>custom_tests</parameter>
specifies a dictionary containing custom tests
(see the section on custom tests below).
@@ -3618,9 +3705,9 @@ The default value is
specifies a file which collects the output from commands
that are executed to check for the existence of header files, libraries, etc.
The default is <quote><filename>#/config.log</filename></quote>.
-If you are using the
-&VariantDir; function,
-you may want to specify a subdirectory under your variant directory.</para>
+If you are using variant directories,
+you may want to place the log file for a given build
+under that build's variant directory.</para>
<para>
<parameter>config_h</parameter>
specifies a C header file where the results of tests
@@ -3708,13 +3795,16 @@ env = conf.Finish()
<para>A &configure_context;
has the following predefined methods which
can be used to perform checks. Where
-<parameter>language</parameter> is a required or
-optional parameter, the choice can currently
-be C or C++. The spellings accepted for
+<parameter>language</parameter> is an optional parameter,
+it specifies the compiler to use for the check,
+currently a choice of C or C++.
+The spellings accepted for
C are <quote>C</quote> or <quote>c</quote>;
for C++ the value can be
<quote>CXX</quote>, <quote>cxx</quote>, <quote>C++</quote>
or <quote>c++</quote>.
+If <parameter>language</parameter> is omitted,
+<quote>C</quote> is assumed.
</para>
<variablelist>
@@ -3723,7 +3813,7 @@ or <quote>c++</quote>.
<listitem>
<para>Checks if
<parameter>header</parameter>
-is usable in the specified language.
+is usable in the specified <parameter>language</parameter>.
<parameter>header</parameter>
may be a list,
in which case the last item in the list
@@ -3739,14 +3829,8 @@ must be
a two character string, where the first character denotes the opening
quote and the second character denotes the closing quote.
By default, both characters are <markup>"</markup> (double quote).
-The optional argument
-<parameter>language</parameter>
-should be either
-<emphasis role="bold">C</emphasis>
-or
-<emphasis role="bold">C++</emphasis>
-and selects the compiler to be used for the check.
-Returns a boolean indicating success or failure.</para>
+</para>
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -3807,25 +3891,23 @@ Returns a boolean indicating success or failure.</para>
<varlistentry>
<term><replaceable>context</replaceable>.<methodname>CheckFunc</methodname>(<parameter>function_name, [header, language]</parameter>)</term>
<listitem>
-<para>Checks if the specified
-C or C++ library function is available based on the
-context's local environment settings (that is, using
-the values of &cv-link-CFLAGS;, &cv-link-CPPFLAGS;, &cv-link-LIBS;
-or other relevant &consvars;).
+<para>Checks if <parameter>function_name</parameter> is usable
+in the context's local environment using the compiler
+specified by <parameter>language</parameter> - that is,
+can a check referencing it be compiled using the current values
+of &cv-link-CFLAGS;, &cv-link-CPPFLAGS;,
+&cv-link-LIBS; or other relevant &consvars;.
</para>
<para>
-<parameter>function_name</parameter>
-is the name of the function to check for.
The optional
<parameter>header</parameter>
-argument is a string
-that will be
-placed at the top
-of the test file
-that will be compiled
-to check if the function exists;
-the default is:</para>
+argument is a string representing a code fragment
+to place at the top of the test program
+that will be compiled to check if the function exists.
+If omitted, the default stanza will be
+(with <parameter>function_name</parameter> appropriately substituted):
+</para>
<programlisting language="C">
#ifdef __cplusplus
@@ -3835,61 +3917,79 @@ char function_name();
</programlisting>
<para>
-Returns an empty string on success, a string containing
-an error message on failure.
+Note: do not use <parameter>header</parameter>
+to include the standard header file that declares
+<parameter>function_name</parameter> - successful
+compilation of the test program depends on using
+a dummy prototype for it,
+to avoid probems with compilers which object to
+function signature mismatches.
</para>
+
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><replaceable>context</replaceable>.<methodname>CheckLib</methodname>(<parameter>[library, symbol, header, language, autoadd=True]</parameter>) </term>
+ <term><replaceable>context</replaceable>.<methodname>CheckLib</methodname>(<parameter>[library, symbol, header, language, autoadd=True, append=True]</parameter>) </term>
<listitem>
<para>Checks if
<parameter>library</parameter>
provides
-<parameter>symbol</parameter>.
-If
-<parameter>autoadd</parameter>
-is true (the default) and the library provides the specified
-<parameter>symbol</parameter>,
-appends the library to the <varname>LIBS</varname> &consvar;
-<parameter>library</parameter>
-may also be <constant>None</constant> (the default),
-in which case
-<parameter>symbol</parameter>
-is checked with the current <varname>LIBS</varname> variable,
-or a list of library names,
-in which case each library in the list
-will be checked for
-<parameter>symbol</parameter>.
-If
-<parameter>symbol</parameter>
-is not set or is
-<constant>None</constant>,
-then
-<function>CheckLib</function>
+<parameter>symbol</parameter> by compiling a simple stub program
+with the compiler selected by <parameter>language</parameter>,
+and optionally adds that library to the context.
+If supplied, the text of <parameter>header</parameter> is included at the
+top of the stub.
+If <parameter>autoadd</parameter> is true (the default),
+and the library provides the specified
+<parameter>symbol</parameter> (as defined by successfully
+linking the stub program),
+it is added to the &cv-link-LIBS; &consvar; in the context.
+if <parameter>append</parameter> is true (the default),
+the library is appended, otherwise it is prepended.
+</para>
+<para>
+<parameter>library</parameter> can be a list of library names,
+or <constant>None</constant> (the default if the argument is omitted).
+If the former, <parameter>symbol</parameter> is checked against
+each library name in order, returning on the first
+successful test; if the latter,
+it is checked with the current value of &cv-LIBS;
+(in this case no library name would be added).
+If <parameter>symbol</parameter>
+is omitted or <constant>None</constant>,
+then <function>CheckLib</function>
just checks if
you can link against the specified
-<parameter>library</parameter>.
+<parameter>library</parameter>,
Note though it is legal syntax, it would
not be very useful to call this method
with <parameter>library</parameter>
and <parameter>symbol</parameter> both
-omitted or <constant>None</constant>.
-Returns a boolean indicating success or failure.</para>
+omitted or <constant>None</constant> -
+at least one should be supplied.
+</para>
+<para>Returns a boolean indicating success or failure.</para>
+<para>
+<emphasis>Changed in version 4.5.0: added the
+<parameter>append</parameter> parameter.</emphasis>
+</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><replaceable>context</replaceable>.<methodname>CheckLibWithHeader</methodname>(<parameter>library, header, language, [call, autoadd=True]</parameter>)</term>
+ <term><replaceable>context</replaceable>.<methodname>CheckLibWithHeader</methodname>(<parameter>library, header, [language, call, autoadd=True, append=True]</parameter>)</term>
<listitem>
-<para>Provides a more sophisticated way to check against libraries then the
-<function>CheckLib</function> call.
+<para>Provides an alternative to the
+<methodname>CheckLib</methodname> method
+for checking for libraries usable in a build.
<parameter>library</parameter>
-specifies the library or a list of libraries to check.
+specifies a library or list of libraries to check.
<parameter>header</parameter>
-specifies a header to check for.
+specifies a header to include in the test program,
+and <parameter>language</parameter> indicates the compiler to use.
<parameter>header</parameter>
may be a list,
in which case the last item in the list
@@ -3899,18 +3999,25 @@ header files whose
<literal>#include</literal>
lines should precede the
header line being checked for.
-<parameter>call</parameter>
-can be any valid expression (with a trailing ';').
-If
-<parameter>call</parameter>
-is not set,
-the default simply checks that you
-can link against the specified
+A code fragment
+(must be a a valid expression, including a trailing semicolon)
+to serve as the test can be supplied in
+<parameter>call</parameter>;
+if not supplied,
+the default checks the ability to link against the specified
<parameter>library</parameter>.
-<parameter>autoadd</parameter> (default true)
-specifies whether to add the library to the environment if the check
-succeeds.
-Returns a boolean indicating success or failure.</para>
+If <parameter>autoadd</parameter> is true (the default),
+the first library that passes the check
+is added to the &cv-link-LIBS; &consvar; in the context
+and the method returns.
+If <parameter>append</parameter> is true (the default),
+the library is appended, otherwise prepended.
+</para>
+<para>Returns a boolean indicating success or failure.</para>
+<para>
+<emphasis>Changed in version 4.5.0: added the
+<parameter>append</parameter> parameter.</emphasis>
+</para>
</listitem>
</varlistentry>
@@ -3932,11 +4039,7 @@ Example:</para>
sconf.CheckType('foo_type', '#include "my_types.h"', 'C++')
</programlisting>
-<para>
-Returns an empty string on success, a string containing
-an error message on failure.
-</para>
-
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -3963,7 +4066,7 @@ is supplied, it should be an integer size;
<parameter>type_name</parameter> is actually
that size.
Returns the size in bytes, or zero if the type was not found
-(or if the size did not match <parameter>expect</parameter>).</para>
+(or if the size did not match optional <parameter>expect</parameter>).</para>
<para>
For example,</para>
@@ -3994,6 +4097,7 @@ for C source files, so by setting relevant &consvars;
it can be used to detect if particular compiler flags will
be accepted or rejected by the compiler.
</para>
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -4014,6 +4118,7 @@ for C++ source files, so by setting relevant &consvars;
it can be used to detect if particular compiler flags will
be accepted or rejected by the compiler.
</para>
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -4036,6 +4141,7 @@ be accepted or rejected by the compiler.
Note this does not check whether a shared library/dll can
be created.
</para>
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -4058,6 +4164,7 @@ be accepted or rejected by the compiler.
Note this does not check whether a shared library/dll can
be created.
</para>
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -4084,7 +4191,8 @@ is a string containing one or more
<literal>#include</literal>
lines that will be inserted into the program
that will be run to test for the existence of the symbol.
-Returns a boolean indicating success or failure.</para>
+</para>
+<para>Returns a boolean indicating success or failure.</para>
</listitem>
</varlistentry>
@@ -4366,7 +4474,8 @@ from various sources, often from the command line:</para>
<userinput>scons VARIABLE=foo</userinput>
</screen>
-<para>The variable values can also be specified in a configuration file or an SConscript file.</para>
+<para>The variable values can also be specified in a configuration file
+or an &SConscript; file.</para>
<para>To obtain the object for manipulating values,
call the &Variables; function:</para>
@@ -4678,7 +4787,7 @@ the &Add; or &AddVariables; method:</para>
<varlistentry id="v-BoolVariable">
<term><function>BoolVariable</function>(<parameter>key, help, default</parameter>)</term>
<listitem>
-<para>Returns a tuple of arguments
+<para>Return a tuple of arguments
to set up a Boolean option.
The option will use
the specified name
@@ -4687,7 +4796,7 @@ have a default value of
<parameter>default</parameter>,
and <parameter>help</parameter>
will form the descriptive part of the help text.
-The option will interpret the values
+The option will interpret the command-line values
<userinput>y</userinput>,
<userinput>yes</userinput>,
<userinput>t</userinput>,
@@ -4697,7 +4806,7 @@ The option will interpret the values
and
<userinput>all</userinput>
as true,
-and the values
+and the command-line values
<userinput>n</userinput>,
<userinput>no</userinput>,
<userinput>f</userinput>,
@@ -4931,7 +5040,7 @@ vars.AddVariables(
BoolVariable(
"warnings",
help="compilation with -Wall and similar",
- default=1,
+ default=True,
),
EnumVariable(
"debug",
@@ -5002,7 +5111,7 @@ Builders return the target Node(s) in the form of a list,
which you can then make use of.
However, since filesystem Nodes have some useful
public attributes and methods
-that you can use in SConscript files,
+that you can use in &SConscript; files,
it is sometimes appropriate to create them manually,
outside the regular context of a Builder call.
</para>
@@ -5207,7 +5316,7 @@ the dependency graph related to their sources.
An alias is checked for up to date by checking if
its sources are up to date.
An alias is built by making sure its sources have been built,
-and if any building took place,
+and if any building took place,
applying any Actions that are defined as part of the alias.
</para>
@@ -5585,38 +5694,37 @@ and the list of sources for this builder.</para>
<programlisting language="python">
def e(target, source, env):
- return (target + ['foo.foo'], source + ['foo.src'])
+ return target + ['foo.foo'], source + ['foo.src']
# Simple association of an emitter function with a Builder.
-b = Builder("my_build &lt; $TARGET &gt; $SOURCE",
- emitter = e)
+b = Builder("my_build &lt; $TARGET &gt; $SOURCE", emitter=e)
def e2(target, source, env):
- return (target + ['bar.foo'], source + ['bar.src'])
+ return target + ['bar.foo'], source + ['bar.src']
# Simple association of a list of emitter functions with a Builder.
-b = Builder("my_build &lt; $TARGET &gt; $SOURCE",
- emitter = [e, e2])
+b = Builder("my_build &lt; $TARGET &gt; $SOURCE", emitter=[e, e2])
-# Calling an emitter function through a &consvar;.
+# Calling an emitter function through a construction variable.
env = Environment(MY_EMITTER=e)
-b = Builder("my_build &lt; $TARGET &gt; $SOURCE",
- emitter='$MY_EMITTER')
+b = Builder("my_build &lt; $TARGET &gt; $SOURCE", emitter='$MY_EMITTER')
-# Calling a list of emitter functions through a &consvar;.
+# Calling a list of emitter functions through a construction variable.
env = Environment(EMITTER_LIST=[e, e2])
-b = Builder("my_build &lt; $TARGET &gt; $SOURCE",
- emitter='$EMITTER_LIST')
+b = Builder("my_build &lt; $TARGET &gt; $SOURCE", emitter='$EMITTER_LIST')
# Associating multiple emitters with different file
# suffixes using a dictionary.
def e_suf1(target, source, env):
- return (target + ['another_target_file'], source)
+ return target + ['another_target_file'], source
+
def e_suf2(target, source, env):
- return (target, source + ['another_source_file'])
-b = Builder("my_build &lt; $TARGET &gt; $SOURCE",
- emitter={'.suf1' : e_suf1,
- '.suf2' : e_suf2})
+ return target, source + ['another_source_file']
+
+b = Builder(
+ action="my_build &lt; $TARGET &gt; $SOURCE",
+ emitter={'.suf1': e_suf1, '.suf2': e_suf2}
+)
</programlisting>
</listitem>
</varlistentry>
@@ -5904,7 +6012,7 @@ you need to create the action object using &f-Action;.
returns an appropriate object for the action
represented by the type of the
<parameter>action</parameter> argument
-(the first positional parmeter):</para>
+(the first positional parameter):</para>
<itemizedlist>
<listitem>
@@ -6034,19 +6142,22 @@ The following argument types are accepted:
<itemizedlist>
<listitem>
-<para>If <parameter>output</parameter> is a string,
-substitution is performed on the string before it is printed.
-The string typically contains variables, notably
+<para>If the second argument is a string,
+or if the <parameter>cmdstr</parameter> keyword argument is supplied,
+the string defines what is printed.
+Substitution is performed on the string before it is printed.
+The string typically contains substitutable variables, notably
<literal>$TARGET(S)</literal> and <literal>$SOURCE(S)</literal>,
-or consists of just a single
-variable, which is optionally defined somewhere else.
+or consists of just a single variable
+which is optionally defined somewhere else.
&SCons; itself heavily uses the latter variant.</para>
</listitem>
<listitem>
-<para>If <parameter>output</parameter> is a function,
-the function will be called to obtain a string
-describing the action being executed.
+<para>If the second argument is a function,
+or if the <parameter>strfunction</parameter> keyword argument is supplied,
+the function will be called to obtain the string
+to be printed when the action is performed.
The function
must accept three keyword arguments:
<parameter>target</parameter>,
@@ -6054,22 +6165,26 @@ must accept three keyword arguments:
<parameter>env</parameter>,
with the same interpretation as for a callable
<parameter>action</parameter> argument above.
+The function is responsible for handling any required substitutions.
</para>
</listitem>
<listitem>
-<para>If <parameter>output</parameter>is <constant>None</constant>,
+<para>If the second argument is <constant>None</constant>,
+or if <literal>cmdstr=None</literal> is supplied,
output is suppressed entirely.</para>
</listitem>
</itemizedlist>
<para>
-Instead of using a positional argument,
-the <parameter>cmdstr</parameter>
-keyword argument may be used to specify the output string,
-or the <parameter>strfunction</parameter> keyword argument
-may be used to specify a function to return the output string.
-<literal>cmdstr=None</literal> suppresses output entirely.
+The <parameter>cmdstr</parameter> and
+<parameter>strfunction</parameter>
+keyword arguments may not both be supplied in a single call to &f-Action;
+</para>
+
+<para>
+Printing of action strings is affected by the setting of
+&cv-link-PRINT_CMD_LINE_FUNC;.
</para>
<para>Examples:</para>
@@ -6328,7 +6443,7 @@ are intended to be used.</para>
<para>First,
if you need
to perform the action
-at the time the SConscript
+at the time the &SConscript;
file is being read,
you can use the
&f-link-Execute;
@@ -6373,21 +6488,26 @@ changes the permissions on the specified
<parameter>dest</parameter>
file or directory to the specified
<parameter>mode</parameter>
-which can be octal or string, similar to the bash command.
+which can be octal or string, similar to the POSIX
+<command>chmod</command> command.
Examples:</para>
<programlisting language="python">
Execute(Chmod('file', 0o755))
-env.Command('foo.out', 'foo.in',
- [Copy('$TARGET', '$SOURCE'),
- Chmod('$TARGET', 0o755)])
+env.Command(
+ 'foo.out',
+ 'foo.in',
+ [Copy('$TARGET', '$SOURCE'), Chmod('$TARGET', 0o755)],
+)
Execute(Chmod('file', "ugo+w"))
-env.Command('foo.out', 'foo.in',
- [Copy('$TARGET', '$SOURCE'),
- Chmod('$TARGET', "ugo+w")])
+env.Command(
+ 'foo.out',
+ 'foo.in',
+ [Copy('$TARGET', '$SOURCE'), Chmod('$TARGET', "ugo+w")],
+)
</programlisting>
<para>
@@ -6408,6 +6528,9 @@ that will copy the
source file or directory to the
<parameter>dest</parameter>
destination file or directory.
+If <parameter>src</parameter> is a list,
+<parameter>dest</parameter> must be a directory
+if it already exists.
Examples:</para>
<programlisting language="python">
@@ -6637,7 +6760,7 @@ For example, the command line string:</para>
echo Last build occurred $TODAY. &gt; $TARGET
</screen>
-<para>but the build signature added to any target files would be computed from:</para>
+<para>but the &buildsig; added to any target files would be computed from:</para>
<screen>
echo Last build occurred . &gt; $TARGET
@@ -6658,8 +6781,8 @@ The callable must accept four arguments:
<parameter>env</parameter> is the &consenv; to use for context,
and <parameter>for_signature</parameter> is
a boolean value that tells the callable
-if it is being called for the purpose of generating a build signature.
-Since the build signature is used for rebuild determination,
+if it is being called for the purpose of generating a &buildsig;.
+Since the &buildsig; is used for rebuild determination,
variable elements that do not affect whether
a rebuild should be triggered
should be omitted from the returned string
@@ -6689,8 +6812,8 @@ builder action. The value of <literal>env['BAR']</literal>
will be exactly as it was set: <literal>"$FOO baz"</literal>.
This can make debugging tricky,
as the substituted result is not available at the time
-the SConscript files are being interpreted and
-thus not available to <systemitem>print()</systemitem>.
+the &SConscript; files are being interpreted and
+thus not available to the <systemitem>print</systemitem> function.
However, you can perform the substitution on demand
by calling the &f-link-env-subst; method for this purpose.
</para>
@@ -6967,9 +7090,9 @@ echo BAR &gt; foo.out
<para>according to the current value of <literal>env['COND']</literal>
when the command is executed.
The evaluation takes place when the target is being
-built, not when the SConscript is being read. So if
+built, not when the &SConscript; is being read. So if
<literal>env['COND']</literal> is changed
-later in the SConscript, the final value will be used.</para>
+later in the &SConscript;, the final value will be used.</para>
<para>Here's a more complete example. Note that all of
<envar>COND</envar>,
@@ -7004,7 +7127,7 @@ which &SCons; passes to <function>eval</function> which
returns the value.
</para>
-<note><para>
+<caution><para>
Use of the Python <function>eval</function> function
is considered to have security implications, since,
depending on input sources,
@@ -7012,7 +7135,7 @@ arbitrary unchecked strings of code can be executed by the Python interpreter.
Although &SCons; makes use of it in a somewhat restricted context,
you should be aware of this issue when using the
<literal>${python-expression-for-subst}</literal> form.
-</para></note>
+</para></caution>
</refsect3>
</refsect2>
@@ -7155,7 +7278,7 @@ it will be expanded into a list by the current environment.
<para>A Python function that takes four or five arguments:
a &consenv;,
a Node for the directory containing
-the SConscript file in which
+the &SConscript; file in which
the first target was defined,
a list of target nodes,
a list of source nodes,
@@ -7470,7 +7593,7 @@ There are, however, a few portability
issues waiting to trap the unwary.</para>
<refsect2 id='c_file_suffix'>
-<title>.C file suffix</title>
+<title>.C File Suffix</title>
<para>&scons; handles the upper-case
<filename>.C</filename>
@@ -7490,50 +7613,125 @@ suffix as a C source file.</para>
</refsect2>
<refsect2 id='f_file_suffix'>
-<title>Fortran file suffixes</title>
+<title>Fortran File Suffixes</title>
-<para>&scons; handles upper-case
-Fortran file suffixes differently
-depending on the capabilities of
-the underlying system.
-On a case-sensitive system
-such as Linux or UNIX,
-&scons; treats a file with a
-<filename>.F</filename>
-as a Fortran source file
-that is to be first run through
-the standard C preprocessor,
-while the lower-case version is not.
-This matches the convention of <command>gfortran</command>,
-which may also be followed by other Fortran compilers.
-This also applies to other naming variants,
+<para>
+There are several ways source file suffixes impact the
+behavior of &SCons; when working with Fortran language code
+(not all are system-specific, but they are included here
+for completeness).
+</para>
+
+<para>
+As the Fortran language has evolved through multiple
+standards editions, projects might have a need to handle
+files from different language generations differently.
+To this end, &SCons; dispatches to a different compiler
+dialect setup (expressed as a set of &consvars;)
+depending on the file suffix.
+By default, all of these setups start out the same,
+but individual &consvars; can be modified as needed to tune a given dialect.
+Each of these dialacts has a tool specification module
+whose documentation describes the &consvars; associated
+with that dialect: <filename>.f</filename>
+(as well as <filename>.for</filename> and <filename>.ftn</filename>)
+in &t-link-fortran;; (&consvars; start with <literal>FORTRAN</literal>)
+<filename>.f77</filename> in &t-link-f77;;
+(&consvars; start with <literal>F77</literal>)
+<filename>.f90</filename> in &t-link-f90;;
+(&consvars; start with <literal>F90</literal>)
+<filename>.f95</filename> in &t-link-f95;;
+(&consvars; start with <literal>F95</literal>)
+<filename>.f03</filename> in &t-link-f03;;
+(&consvars; start with <literal>F03</literal>)
+<filename>.f08</filename> in &t-link-f08;
+(&consvars; start with <literal>F08</literal>).
+</para>
+
+<para>
+While &SCons; recognizes multiple internal dialects
+based on filename suffixes,
+the convention of various available Fortran compilers is
+to assign an actual meaning to only two of these suffixes:
+<filename>.f</filename>
+(as well as <filename>.for</filename> and <filename>.ftn</filename>)
+refers to the fixed-format source
+code that was the only available option in FORTRAN 77 and earlier,
+and <filename>.f90</filename> refers to free-format source code
+which became available as of the Fortran 90 standard.
+Some compilers recognize suffixes which correspond to Fortran
+specifications later then F90 as equivalent to
+<filename>.f90</filename> for this purpose,
+while some do not - check the documentation for your compiler.
+An occasionally suggested policy suggestion is to use only
+<filename>.f</filename> and <filename>.f90</filename>
+as Fortran filename suffixes.
+The fixed/free form determination can usually be controlled
+explicitly with compiler flags
+(e.g. <option>-ffixed-form</option> for gfortran),
+overriding any assumption that may be made based on the source file suffix.
+</para>
+
+<para>
+The source file suffix does not imply conformance
+with the similarly-named Fortran standard - a suffix of
+<filename>.f08</filename> does not mean you are compiling
+specifically for Fortran 2008. Normally, compilers
+provide command-line options for making this selection
+(e.g. <option>-std=f2008</option> for gfortran).
+</para>
+
+<para>
+For dialects from F90 on (including the generic FORTRAN dialect),
+a suffix of <filename>.mod</filename> is recognized for Fortran modules.
+These files are a side effect of compiling a Fortran
+source file containing module declarations,
+and must be available when other code which declares
+that it uses the module is processed.
+&SCons; does not currently have integrated support for submodules,
+introduced in the Fortran 2008 standard -
+the invoked compiler will produce results,
+but &SCons; will not recognize
+<filename>.smod</filename> files as tracked objects.
+</para>
+
+<para>
+On a case-sensitive system such as Linux or UNIX,
+a file with a an upper-cased suffix from the set
+<filename>.F</filename>,
<filename>.FOR</filename>,
<filename>.FTN</filename>,
<filename>.F90</filename>,
<filename>.F95</filename>,
<filename>.F03</filename> and
-<filename>.F08</filename>;
-files suffixed with
+<filename>.F08</filename>
+is treated as a Fortran source file
+which shall first be run through
+the standard C preprocessor.
+The lower-cased versions of these suffixes do not
+trigger this behavior.
+On systems which do not distinguish between uppper
+and lower case in filenames,
+this behavior is not available,
+but files suffixed with either
<filename>.FPP</filename>
-and <filename>.fpp</filename>
-are both run through the preprocessor,
-as indicated by the <literal>pp</literal>
-part of the name.
-On a case-insensitive system
-such as Windows,
-&scons; treats a file with a
-<filename>.F</filename>
-suffix as a Fortran source file that should
-<emphasis>not</emphasis>
-be run through the C preprocessor.</para>
+or <filename>.fpp</filename>
+are always passed to the preprocessor first.
+This matches the convention of <command>gfortran</command>
+from the GNU Compiler Collection,
+and also followed by certain other Fortran compilers.
+For these two suffixes,
+the generic <emphasis>FORTRAN</emphasis> dialect will be selected.
+</para>
+
<para>
-<emphasis>Run through the C preprocessor</emphasis>
-here means that a different set of &consvars; will
-be applied in constructed commands, for example
+&SCons; itself does not invoke the preprocessor,
+that is handled by the compiler,
+but it adds &consvars; which are applicable to the preprocessor run.
+You can see this difference by examining
&cv-link-FORTRANPPCOM; and &cv-link-FORTRANPPCOMSTR;
-instead of
-&cv-link-FORTRANCOM; and &cv-link-FORTRANCOMSTR;.
-See the Fortran-related &consvars; for more details.
+which are used instead of
+&cv-link-FORTRANCOM; and &cv-link-FORTRANCOMSTR; for that dialect.
</para>
</refsect2>
@@ -7542,10 +7740,10 @@ See the Fortran-related &consvars; for more details.
<para>Cygwin supplies a set of tools and utilities
that let users work on a
-Windows system using a more POSIX-like environment.
-The Cygwin tools, including Cygwin Python,
+Windows system using a POSIX-like environment.
+The Cygwin tools, including Cygwin &Python;,
do this, in part,
-by sharing an ability to interpret UNIX-like path names.
+by sharing an ability to interpret POSIX-style path names.
For example, the Cygwin tools
will internally translate a Cygwin path name
like <filename>/cygdrive/c/mydir</filename>
@@ -7553,28 +7751,36 @@ to an equivalent Windows pathname
of <filename>C:/mydir</filename> (equivalent to <filename>C:\mydir</filename>).
</para>
-<para>Versions of Python
+<para>Versions of &Python;
that are built for native Windows execution,
such as the python.org and ActiveState versions,
-do not have the Cygwin path name semantics.
-This means that using a native Windows version of Python
+do not understand the Cygwin path name semantics.
+This means that using a native Windows version of &Python;
to build compiled programs using Cygwin tools
(such as &gcc;, &bison; and &flex;)
may yield unpredictable results.
"Mixing and matching" in this way
can be made to work,
but it requires careful attention to the use of path names
-in your SConscript files.</para>
+in your &SConscript; files.</para>
<para>In practice, users can sidestep
-the issue by adopting the following rules:
+the issue by adopting the following guidelines:
When using Cygwin's &gcc; for compiling,
-use the Cygwin-supplied Python interpreter
+use the Cygwin-supplied &Python; interpreter
to run &scons;;
when using Microsoft Visual C/C++
-(or some other Windows compiler)
-use the python.org or Microsoft Store or ActiveState version of Python
-to run &scons;.</para>
+(or some other "native" Windows compiler)
+use the python.org, Microsoft Store, ActiveState or other
+native version of &Python; to run &scons;.
+</para>
+
+<para>
+This discussion largely applies to the msys2 environment
+as well (with the use of the mingw compiler toolchain),
+in particular the recommendation to use the msys2 version of
+&Python; if running &scons; from inside an msys2 shell.
+</para>
</refsect2>
<refsect2 id='windows_sconsbat_file'>
@@ -7584,7 +7790,7 @@ to run &scons;.</para>
<filename>scons.bat</filename> batch file,
there are (at least) two ramifications.
Note this is no longer the default - &scons; installed
-via Python's <command>pip</command> installer
+via &Python;''s <command>pip</command> installer
will have an <command>scons.exe</command> which does
not have these limitations:
</para>
@@ -7624,7 +7830,7 @@ directory must be in your <envar>PATH</envar>
environment variable or the
<varname>['ENV']['PATH']</varname> &consvar; for &scons;
to detect and use the MinGW tools. When running under the native Windows
-Python interpreter, &scons; will prefer the MinGW tools over the Cygwin
+Python; interpreter, &scons; will prefer the MinGW tools over the Cygwin
tools, if they are both installed, regardless of the order of the bin
directories in the <envar>PATH</envar> variable.
If you have both MSVC and MinGW
@@ -7935,7 +8141,7 @@ scanner = Scanner(
<title>Creating a Hierarchical Build</title>
<para>Notice that the file names specified in a subdirectory's
-SConscript file are relative to that subdirectory.</para>
+&SConscript; file are relative to that subdirectory.</para>
<para><filename>SConstruct</filename>:</para>
@@ -7971,7 +8177,7 @@ env.Program(target='foo', source='foo.c')
<para>You must explicitly call &f-link-Export; and &f-link-Import;
for variables that
-you want to share between SConscript files.</para>
+you want to share between &SConscript; files.</para>
<para><filename>SConstruct</filename>:</para>
@@ -8022,7 +8228,7 @@ env.Program(target='src', source='src.c')
<para>Note the use of the &f-link-Export; method
to set the <varname>cppdefines</varname> variable to a different
-value each time we call the &SConscriptFunc; function.</para>
+value each time we call the &f-link-SConscript; function.</para>
</refsect2>
@@ -8205,7 +8411,7 @@ env.Program('MyApp', ['Foo.cpp', 'Bar.cpp'])
<para>In general, &scons; is not controlled by environment
variables set in the shell used to invoke it, leaving it
-up to the SConscript file author to import those if desired.
+up to the &SConscript; file author to import those if desired.
However the following variables are imported by
&scons; itself if set:
</para>
@@ -8242,29 +8448,29 @@ so the command line can be used to override
<listitem>
<para>(Windows only). If set, save the shell environment variables
generated when setting up the Microsoft Visual C++ compiler
-(and/or Build Tools) to a cache file, to give these settings,
-which are relatively expensive to generate, persistence
-across &scons; invocations.
-Use of this option is primarily intended to aid performance
-in tightly controlled Continuous Integration setups.</para>
+(and/or Build Tools) to a cache file, to give these settings
+persistence across &scons; invocations.
+Generating this information is relatively expensive,
+so using this option may aid performance where &scons; is run often,
+such as Continuous Integration setups.</para>
<para>If set to a True-like value (<literal>"1"</literal>,
<literal>"true"</literal> or
<literal>"True"</literal>) will cache to a file named
-<filename>.scons_msvc_cache.json</filename> in the user's home directory.
+<filename>scons_msvc_cache.json</filename> in the user's home directory.
If set to a pathname, will use that pathname for the cache.</para>
-<para>Note: use this cache with caution as it
-might be somewhat fragile: while each major toolset version
-(e.g. Visual Studio 2017 vs 2019) and architecture pair will get separate
-cache entries, if toolset updates cause a change
-to settings within a given release series, &scons; will not
-detect the change and will reuse old settings.
-Remove the cache file in case of problems with this.
-&scons; will ignore failures reading or writing the file
-and will silently revert to non-cached behavior in such cases.</para>
-
-<para><emphasis>Available since &scons; 3.1 (experimental)</emphasis>.</para>
+<para>Note: this implementation may still be somewhat fragile.
+In case of problems, remove the cache file - recreating with
+fresh info normally resolves any issues.
+&SCons; ignores failures reading or writing the cache file
+and will silently revert to non-cached behavior in such cases.
+</para>
+
+<para><emphasis>New in 3.1 (experimental).
+The default cache file name was changed to
+its present value in 4.4, and contents were expanded.</emphasis>
+</para>
</listitem>
</varlistentry>
diff --git a/doc/reference/chtml.xsl b/doc/reference/chtml.xsl
index f0c9474..c2e7404 100644
--- a/doc/reference/chtml.xsl
+++ b/doc/reference/chtml.xsl
@@ -1,57 +1,57 @@
-<?xml version='1.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.
-
--->
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
-<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/chunk.xsl"/>
-
-<xsl:param name="base.dir" select="'scons-reference/'"/>
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"/>
-<xsl:param name="section.label.includes.component.label" select="1"/>
-<xsl:param name="html.stylesheet" select="'scons.css'"/>
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-</xsl:stylesheet>
-
+<?xml version='1.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.
+
+-->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/chunk.xsl"/>
+
+<xsl:param name="base.dir" select="'scons-reference/'"/>
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="section.label.includes.component.label" select="1"/>
+<xsl:param name="html.stylesheet" select="'scons.css'"/>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+</xsl:stylesheet>
+
diff --git a/doc/reference/html.xsl b/doc/reference/html.xsl
index c50e17e..0cd9f9b 100644
--- a/doc/reference/html.xsl
+++ b/doc/reference/html.xsl
@@ -1,151 +1,151 @@
-<?xml version='1.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.
-
--->
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- version="1.0">
-
-<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/docbook.xsl"/>
-
-<xsl:output method="html"
- encoding="UTF-8"
- indent="no"/>
-
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"/>
-<xsl:param name="section.label.includes.component.label" select="1"/>
-<xsl:param name="html.stylesheet" select="'scons.css'"/>
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-<xsl:template match="function">
- <xsl:call-template name="inline.boldmonoseq"/>
-</xsl:template>
-<xsl:template match="methodname">
- <xsl:call-template name="inline.boldmonoseq"/>
-</xsl:template>
-<!-- Prevent our EPUB cover image from getting included -->
-<xsl:template match="mediaobject[@role = 'cover']">
-</xsl:template>
-
-
-
-<xsl:template name="head.content">
- <xsl:param name="node" select="."/>
- <xsl:param name="title">
- <xsl:apply-templates select="$node" mode="object.title.markup.textonly"/>
- </xsl:param>
-
- <title>
- <xsl:copy-of select="$title"/>
- </title>
-
- <xsl:if test="$html.base != ''">
- <base href="{$html.base}"/>
- </xsl:if>
-
- <!-- Insert links to CSS files or insert literal style elements -->
- <xsl:call-template name="generate.css"/>
-
- <xsl:if test="$html.stylesheet != ''">
- <xsl:call-template name="output.html.stylesheets">
- <xsl:with-param name="stylesheets" select="normalize-space($html.stylesheet)"/>
- </xsl:call-template>
- </xsl:if>
-
- <xsl:if test="$link.mailto.url != ''">
- <link rev="made"
- href="{$link.mailto.url}"/>
- </xsl:if>
-
- <meta name="generator" content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
-
- <xsl:if test="$generate.meta.abstract != 0">
- <xsl:variable name="info" select="(articleinfo
- |bookinfo
- |prefaceinfo
- |chapterinfo
- |appendixinfo
- |sectioninfo
- |sect1info
- |sect2info
- |sect3info
- |sect4info
- |sect5info
- |referenceinfo
- |refentryinfo
- |partinfo
- |info
- |docinfo)[1]"/>
- <xsl:if test="$info and $info/abstract">
- <meta name="description">
- <xsl:attribute name="content">
- <xsl:for-each select="$info/abstract[1]/*">
- <xsl:value-of select="normalize-space(.)"/>
- <xsl:if test="position() &lt; last()">
- <xsl:text> </xsl:text>
- </xsl:if>
- </xsl:for-each>
- </xsl:attribute>
- </meta>
- </xsl:if>
- </xsl:if>
-
- <xsl:if test="($draft.mode = 'yes' or
- ($draft.mode = 'maybe' and
- ancestor-or-self::*[@status][1]/@status = 'draft'))
- and $draft.watermark.image != ''">
- <style type="text/css"><xsl:text>
-body { background-image: url('</xsl:text>
-<xsl:value-of select="$draft.watermark.image"/><xsl:text>');
- background-repeat: no-repeat;
- background-position: top left;
- /* The following properties make the watermark "fixed" on the page. */
- /* I think that's just a bit too distracting for the reader... */
- /* background-attachment: fixed; */
- /* background-position: center center; */
- }</xsl:text>
- </style>
- </xsl:if>
- <xsl:apply-templates select="." mode="head.keywords.content"/>
-</xsl:template>
-
-</xsl:stylesheet>
-
+<?xml version='1.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.
+
+-->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ version="1.0">
+
+<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/html/docbook.xsl"/>
+
+<xsl:output method="html"
+ encoding="UTF-8"
+ indent="no"/>
+
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="section.label.includes.component.label" select="1"/>
+<xsl:param name="html.stylesheet" select="'scons.css'"/>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+<xsl:template match="function">
+ <xsl:call-template name="inline.boldmonoseq"/>
+</xsl:template>
+<xsl:template match="methodname">
+ <xsl:call-template name="inline.boldmonoseq"/>
+</xsl:template>
+<!-- Prevent our EPUB cover image from getting included -->
+<xsl:template match="mediaobject[@role = 'cover']">
+</xsl:template>
+
+
+
+<xsl:template name="head.content">
+ <xsl:param name="node" select="."/>
+ <xsl:param name="title">
+ <xsl:apply-templates select="$node" mode="object.title.markup.textonly"/>
+ </xsl:param>
+
+ <title>
+ <xsl:copy-of select="$title"/>
+ </title>
+
+ <xsl:if test="$html.base != ''">
+ <base href="{$html.base}"/>
+ </xsl:if>
+
+ <!-- Insert links to CSS files or insert literal style elements -->
+ <xsl:call-template name="generate.css"/>
+
+ <xsl:if test="$html.stylesheet != ''">
+ <xsl:call-template name="output.html.stylesheets">
+ <xsl:with-param name="stylesheets" select="normalize-space($html.stylesheet)"/>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:if test="$link.mailto.url != ''">
+ <link rev="made"
+ href="{$link.mailto.url}"/>
+ </xsl:if>
+
+ <meta name="generator" content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+
+ <xsl:if test="$generate.meta.abstract != 0">
+ <xsl:variable name="info" select="(articleinfo
+ |bookinfo
+ |prefaceinfo
+ |chapterinfo
+ |appendixinfo
+ |sectioninfo
+ |sect1info
+ |sect2info
+ |sect3info
+ |sect4info
+ |sect5info
+ |referenceinfo
+ |refentryinfo
+ |partinfo
+ |info
+ |docinfo)[1]"/>
+ <xsl:if test="$info and $info/abstract">
+ <meta name="description">
+ <xsl:attribute name="content">
+ <xsl:for-each select="$info/abstract[1]/*">
+ <xsl:value-of select="normalize-space(.)"/>
+ <xsl:if test="position() &lt; last()">
+ <xsl:text> </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:attribute>
+ </meta>
+ </xsl:if>
+ </xsl:if>
+
+ <xsl:if test="($draft.mode = 'yes' or
+ ($draft.mode = 'maybe' and
+ ancestor-or-self::*[@status][1]/@status = 'draft'))
+ and $draft.watermark.image != ''">
+ <style type="text/css"><xsl:text>
+body { background-image: url('</xsl:text>
+<xsl:value-of select="$draft.watermark.image"/><xsl:text>');
+ background-repeat: no-repeat;
+ background-position: top left;
+ /* The following properties make the watermark "fixed" on the page. */
+ /* I think that's just a bit too distracting for the reader... */
+ /* background-attachment: fixed; */
+ /* background-position: center center; */
+ }</xsl:text>
+ </style>
+ </xsl:if>
+ <xsl:apply-templates select="." mode="head.keywords.content"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/doc/reference/pdf.xsl b/doc/reference/pdf.xsl
index faea7fc..a398e5c 100644
--- a/doc/reference/pdf.xsl
+++ b/doc/reference/pdf.xsl
@@ -1,73 +1,73 @@
-<?xml version='1.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.
-
--->
-
-<xsl:stylesheet
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
-<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/fo/docbook.xsl"/>
-
-<xsl:include href="scons_title.xsl"/>
-<xsl:param name="l10n.gentext.default.language" select="'en'"/>
-<xsl:param name="section.autolabel" select="1"></xsl:param>
-<xsl:param name="section.label.includes.component.label" select="1"/>
-<xsl:param name="paper.type" select="'letter'"></xsl:param>
-<xsl:param name="body.start.indent">0pt</xsl:param>
-<xsl:param name="shade.verbatim" select="1"></xsl:param>
-<xsl:param name="variablelist.term.break.after" select="1"></xsl:param>
-
-<xsl:param name="generate.toc">
-/appendix toc,title
-article/appendix nop
-/article toc,title
-book toc,title,figure,table,example,equation
-/chapter toc,title
-part toc,title
-/preface toc,title
-reference toc,title
-/sect1 toc
-/sect2 toc
-/sect3 toc
-/sect4 toc
-/sect5 toc
-/section toc
-set toc,title
-</xsl:param>
-
-<xsl:attribute-set name="variablelist.term.properties">
- <xsl:attribute name="font-weight">bold</xsl:attribute>
-</xsl:attribute-set>
-
-<xsl:template match="variablelist">
- <xsl:variable name="presentation">
- <xsl:call-template name="pi.dbfo_list-presentation"/>
- </xsl:variable>
- <xsl:apply-templates select="." mode="vl.as.blocks"/>
-</xsl:template>
-
-</xsl:stylesheet>
-
+<?xml version='1.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.
+
+-->
+
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+<xsl:import href="../../SCons/Tool/docbook/docbook-xsl-1.76.1/fo/docbook.xsl"/>
+
+<xsl:include href="scons_title.xsl"/>
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"></xsl:param>
+<xsl:param name="section.label.includes.component.label" select="1"/>
+<xsl:param name="paper.type" select="'letter'"></xsl:param>
+<xsl:param name="body.start.indent">0pt</xsl:param>
+<xsl:param name="shade.verbatim" select="1"></xsl:param>
+<xsl:param name="variablelist.term.break.after" select="1"></xsl:param>
+
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix nop
+/article toc,title
+book toc,title,figure,table,example,equation
+/chapter toc,title
+part toc,title
+/preface toc,title
+reference toc,title
+/sect1 toc
+/sect2 toc
+/sect3 toc
+/sect4 toc
+/sect5 toc
+/section toc
+set toc,title
+</xsl:param>
+
+<xsl:attribute-set name="variablelist.term.properties">
+ <xsl:attribute name="font-weight">bold</xsl:attribute>
+</xsl:attribute-set>
+
+<xsl:template match="variablelist">
+ <xsl:variable name="presentation">
+ <xsl:call-template name="pi.dbfo_list-presentation"/>
+ </xsl:variable>
+ <xsl:apply-templates select="." mode="vl.as.blocks"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/doc/scons.mod b/doc/scons.mod
index 77a7d24..ea1decc 100644
--- a/doc/scons.mod
+++ b/doc/scons.mod
@@ -59,7 +59,6 @@
<!ENTITY Cons "<application xmlns='http://www.scons.org/dbxsd/v1.0'>Cons</application>">
<!ENTITY cp "<application xmlns='http://www.scons.org/dbxsd/v1.0'>cp</application>">
<!ENTITY csh "<application xmlns='http://www.scons.org/dbxsd/v1.0'>csh</application>">
-<!ENTITY flex "<application xmlns='http://www.scons.org/dbxsd/v1.0'>flex</application>">
<!ENTITY f77 "<application xmlns='http://www.scons.org/dbxsd/v1.0'>f77</application>">
<!ENTITY f90 "<application xmlns='http://www.scons.org/dbxsd/v1.0'>f90</application>">
<!ENTITY f95 "<application xmlns='http://www.scons.org/dbxsd/v1.0'>f95</application>">
@@ -454,6 +453,7 @@
<!ENTITY contentsig "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>content signature</phrase>">
<!ENTITY contentsigs "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>content signatures</phrase>">
<!ENTITY buildsig "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>build signature</phrase>">
+<!ENTITY buildsigs "<phrase xmlns='http://www.scons.org/dbxsd/v1.0'>build signatures</phrase>">
<!ENTITY true "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>true</literal>">
<!ENTITY false "<literal xmlns='http://www.scons.org/dbxsd/v1.0'>false</literal>">
diff --git a/doc/sphinx/SCons.Scanner.rst b/doc/sphinx/SCons.Scanner.rst
index 181dbde..484bd43 100644
--- a/doc/sphinx/SCons.Scanner.rst
+++ b/doc/sphinx/SCons.Scanner.rst
@@ -44,6 +44,14 @@ SCons.Scanner.IDL module
:undoc-members:
:show-inheritance:
+SCons.Scanner.Java module
+-------------------------
+
+.. automodule:: SCons.Scanner.Java
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
SCons.Scanner.LaTeX module
--------------------------
diff --git a/doc/sphinx/SCons.Taskmaster.rst b/doc/sphinx/SCons.Taskmaster.rst
new file mode 100644
index 0000000..adf9013
--- /dev/null
+++ b/doc/sphinx/SCons.Taskmaster.rst
@@ -0,0 +1,21 @@
+SCons.Taskmaster package
+========================
+
+Submodules
+----------
+
+SCons.Taskmaster.Job module
+---------------------------
+
+.. automodule:: SCons.Taskmaster.Job
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: SCons.Taskmaster
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/sphinx/SCons.rst b/doc/sphinx/SCons.rst
index b9959e3..45e20ee 100644
--- a/doc/sphinx/SCons.rst
+++ b/doc/sphinx/SCons.rst
@@ -18,6 +18,7 @@ Subpackages
SCons.Platform
SCons.Scanner
SCons.Script
+ SCons.Taskmaster
SCons.Tool
SCons.Variables
SCons.compat
@@ -83,11 +84,13 @@ SCons.Environment module
SCons.Errors module
-------------------
+.. Turn off inherited members to quiet fluff from the Python base Exception
.. automodule:: SCons.Errors
:members:
:undoc-members:
:show-inheritance:
+ :no-inherited-members:
SCons.Executor module
---------------------
@@ -97,14 +100,6 @@ SCons.Executor module
:undoc-members:
:show-inheritance:
-SCons.Job module
-----------------
-
-.. automodule:: SCons.Job
- :members:
- :undoc-members:
- :show-inheritance:
-
SCons.Memoize module
--------------------
@@ -145,14 +140,6 @@ SCons.Subst module
:undoc-members:
:show-inheritance:
-SCons.Taskmaster module
------------------------
-
-.. automodule:: SCons.Taskmaster
- :members:
- :undoc-members:
- :show-inheritance:
-
SCons.Util module
-----------------
diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py
index 9db9095..5ad5448 100644
--- a/doc/sphinx/conf.py
+++ b/doc/sphinx/conf.py
@@ -38,7 +38,7 @@ extensions = [
'sphinx.ext.napoleon',
'sphinx.ext.todo',
'sphinx.ext.viewcode',
- 'sphinx_rtd_theme',
+ #'sphinx_rtd_theme',
'rst2pdf.pdfbuilder',
]
@@ -46,15 +46,30 @@ autosummary_generate = True
autodoc_default_options = {
"members": True,
- #"special-members": True,
+ "special-members": False,
"private-members": True,
"inherited-members": True,
"undoc-members": True,
"exclude-members": '__weakref__',
}
autodoc_exclude_members = ['*Tests']
-napoleon_include_special_with_doc = False
+
+# Napoleon settings. Nearly all defaults, listed explicitly to be safe.
+# See: https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html#configuration
+napoleon_google_docstring = True
+napoleon_numpy_docstring = True
+napoleon_include_init_with_doc = False
napoleon_include_private_with_doc = True
+napoleon_include_special_with_doc = True # not default
+napoleon_use_admonition_for_examples = True # not default
+napoleon_use_admonition_for_notes = False
+napoleon_use_admonition_for_references = False
+napoleon_use_ivar = True # not default
+napoleon_use_param = True
+napoleon_use_rtype = True
+napoleon_preprocess_types = False
+napoleon_type_aliases = None
+napoleon_attr_annotations = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -73,7 +88,7 @@ master_doc = 'index'
# General information about the project.
project = 'SCons'
-copyright = '2021, SCons Project'
+copyright = '2022, SCons Project'
author = 'SCons Project Team'
# The version info for the project you're documenting, acts as replacement for
@@ -92,7 +107,7 @@ version = '.'.join([major, minor])
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@@ -111,7 +126,8 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-html_theme = "sphinx_rtd_theme"
+#html_theme = "sphinx_rtd_theme"
+html_theme = "sphinx_book_theme"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -212,4 +228,4 @@ texinfo_documents = [
# -- for PDF
# Grouping the document tree into PDF files. List of tuples
# (source start file, target name, title, author, options).
-pdf_documents = [('index', u'scons-api', u'SCons API Docs', u'SCons Project'),]
+pdf_documents = [('index', 'scons-api', 'SCons API Docs', 'SCons Project'),]
diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst
index 6828154..f8d5f47 100644
--- a/doc/sphinx/index.rst
+++ b/doc/sphinx/index.rst
@@ -1,19 +1,25 @@
-.. SCons documentation master file, created by
+.. SCons documentation master file, originally created by
sphinx-quickstart on Mon Apr 30 09:36:53 2018.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-SCons Project API Documentation
-===============================
+SCons API Documentation
+=======================
-This is the internal API Documentation for SCons.
-The Documentation is generated using the Sphinx tool.
-The target audience is developers working on SCons itself,
-so it does not clearly delineate what is "Public API" -
-interfaces for use in your SCons configuration scripts
-which have a consistency guarantee, and what is internal,
-so always keep the SCons manual page around for helping
-with such determinations.
+.. Attention::
+ This is the **internal** API Documentation for SCons.
+ The documentation is automatically generated for each release
+ from the source code using the
+ `Sphinx <https://www.sphinx-doc.org>`_ tool.
+ Missing information is due to shortcomings in the docstrings in the code,
+ which are by no means complete (contributions welcomed!).
+
+ The target audience is developers working on SCons itself:
+ what is "Public API" is not clearly deliniated here.
+ The interfaces available for use in SCons configuration scripts,
+ which have a consistency guarantee, are those documented in the
+ `SCons Reference Manual
+ <https://scons.org/doc/production/HTML/scons-man.html>`_.
.. toctree::
:maxdepth: 2
@@ -25,6 +31,7 @@ with such determinations.
SCons.Platform
SCons.Scanner
SCons.Script
+ SCons.Taskmaster
SCons.Tool
SCons.Variables
diff --git a/doc/user/build-install.xml b/doc/user/build-install.xml
index c106dc5..697f5a2 100644
--- a/doc/user/build-install.xml
+++ b/doc/user/build-install.xml
@@ -1,4 +1,10 @@
<?xml version='1.0'?>
+<!--
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
+-->
+
<!DOCTYPE sconsdoc [
<!ENTITY % version SYSTEM "../version.xml">
@@ -22,46 +28,19 @@
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">
-<title>Building and Installing &SCons;</title>
-
-<!--
- __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.
-
--->
+<title>Building and Installing &SCons;</title>
<para>
This chapter will take you through the basic steps
- of installing &SCons; on your system,
- and building &SCons; if you don't have a
- pre-built package available
- (or simply prefer the flexibility of building it yourself).
+ of installing &SCons; so you can use it for your your projects.
Before that, however, this chapter will also describe the basic steps
- involved in installing Python on your system,
+ involved in installing &Python; on your system,
in case that is necessary.
- Fortunately, both &SCons; and Python
- are very easy to install on almost any system,
- and Python already comes installed on many systems.
+ Fortunately, both &SCons; and &Python;
+ are easy to install on almost any system,
+ and &Python; already comes installed on many systems.
</para>
@@ -70,31 +49,31 @@
<para>
Lastly, this chapter also contains a section that
- provides a brief overview of the Python programming language,
+ provides a brief overview of the &Python; programming language,
which is the language used to implement &SCons;,
and which forms the basis of the &SCons; configuration files.
- Becoming familiar with some Python concepts will make it easier
+ Becoming familiar with some &Python; concepts will make it easier
to understand many of the examples in this User's Guide.
Nevertheless, it <emphasis>is</emphasis> possible
- to configure simple &SCons; builds without knowing Python,
+ 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
- already familiar with Python.
+ already familiar with &Python;.
</para>
-->
- <section>
+ <section id="sect-install-python">
<title>Installing Python</title>
<para>
- Because &SCons; is written in Python,
- you need to have Python installed on your system
+ Because &SCons; is written in the &Python; programming language,
+ you need to have a &Python; interpreter available on your system
to use &SCons;.
- Before you try to install Python,
- you should check to see if Python is already
+ Before you try to install &Python;,
+ check to see if &Python; is already
available on your system by typing
<userinput>python -V</userinput>
(capital 'V')
@@ -106,28 +85,34 @@
<screen>
$ <userinput>python -V</userinput>
-Python 3.7.1
+Python 3.9.15
</screen>
<para>
- Note to Windows users: there are a number of different ways Python
+ If you get a version like 2.7.x, you may need to try using the
+ name <command>python3</command> - current &SCons; no longer
+ works with &Python; 2.
+ </para>
+
+ <para>
+ Note to Windows users: there are a number of different ways &Python;
can be installed or invoked on Windows, it is beyond the scope
- of this guide to unravel all of them. Many will have an additional
+ of this guide to unravel all of them. Some have an additional
program called the <firstterm>Python launcher</firstterm> (described,
somewhat technically, in
<ulink url="https://www.python.org/dev/peps/pep-0397/">PEP 397</ulink>):
try using the command name <command>py</command> instead of
<command>python</command>, if that is not available drop
- back to trying <command>python</command>.
+ back to trying <command>python</command>
</para>
<screen>
C:\><userinput>py -V</userinput>
-Python 3.7.1
+Python 3.9.15
</screen>
<para>
- If Python is not installed on your system,
+ If &Python; is not installed on your system,
or is not findable in the current search path,
you will see an error message
stating something like <computeroutput>"command not found"</computeroutput>
@@ -135,14 +120,14 @@ Python 3.7.1
or <computeroutput>"'python' is not recognized as an internal
or external command, operable progam or batch file"</computeroutput>
(on Windows <command>cmd</command>).
- In that case, you need to either install Python
+ In that case, you need to either install &Python;
or fix the search path
before you can install &SCons;.
</para>
<para>
- The canonical location for downloading Python
- from Python's own website is:
+ The link for downloading &Python; installers (Windows and Mac)
+ from the project's own website is:
<ulink url="https://www.python.org/download">https://www.python.org/download</ulink>.
There are useful system-specific entries on setup and
usage to be found at:
@@ -150,34 +135,56 @@ Python 3.7.1
</para>
<para>
- For Linux systems, Python is
- almost certainly available as a supported package, possibly
+ For Linux systems, &Python; is
+ almost certainly available as a supported package, probably
installed by default; this is often preferred over installing
- by other means, and is easier than installing from source code.
+ by other means as the system package will be built with
+ carefully chosen optimizations, and will be kept up to date
+ with bug fixes and security patches. In fact, the &Python;
+ project itself does not build installers for Linux for this reason.
Many such systems have separate packages for
- Python 2 and Python 3 - make sure the Python 3 package is
+ &Python; 2 and &Python; 3 - make sure the &Python; 3 package is
installed, as the latest &SCons; requires it.
Building from source may still be a
- useful option if you need a version that is not offered by
+ useful option if you need a specific version that is not offered by
the distribution you are using.
</para>
<para>
- &SCons; will work with Python 3.5 or later.
- If you need to install Python and have a choice,
- we recommend using the most recent Python version available.
- Newer Pythons have significant improvements
+ Recent versions of the Mac no longer come with &Python;
+ pre-installed; older versions came with a rather out of date
+ version (based on &Python; 2.7) which is insufficient to run
+ current &SCons;.
+ The python.org installer can be used on the Mac, but there are
+ also other sources such as MacPorts and Homebrew.
+ The Anaconda installation also comes with a bundled &Python;.
+ </para>
+
+ <para>
+ Windows has even more choices. The Python.org installer is
+ a traditional <filename>.exe</filename> style;
+ the same software is also released as a Windows application through
+ the Microsoft Store. Several alternative builds also exist
+ such as Chocolatey and ActiveState, and, again,
+ a version of Python comes with Anaconda.
+ </para>
+
+ <para>
+ &SCons; will work with &Python; 3.6 or later.
+ If you need to install &Python; and have a choice,
+ we recommend using the most recent &Python; version available.
+ Newer &Python; versions have significant improvements
that help speed up the performance of &SCons;.
</para>
</section>
- <section>
+ <section id="sect-install-scons">
<title>Installing &SCons;</title>
<para>
- The canonical way to install &SCons; is from the Python Package
- Index (PyPi):
+ The recommended way to install &SCons; is from the &Python; Package
+ Index (<ulink url="https://pypi.org/project/SCons/">PyPI</ulink>):
</para>
<screen>
@@ -185,9 +192,9 @@ Python 3.7.1
</screen>
<para>
- If you prefer not to install to the Python system location,
- or do not have privileges to do so, you can add a flag to
- install to a location specific to your own account:
+ If you prefer not to install to the &Python; system location,
+ or do not have privileges to do so, you can add a flag to install
+ to a location specific to your own account and &Python; version:
</para>
<screen>
@@ -197,7 +204,7 @@ Python 3.7.1
<para>
For those users using Anaconda or Miniconda, use the
<command>conda</command> installer instead, so the &scons;
- install location will match the version of Python that
+ install location will match the version of &Python; that
system will be using. For example:
</para>
@@ -206,43 +213,61 @@ Python 3.7.1
</screen>
<para>
- &SCons; comes pre-packaged for installation on many Linux systems.
- Check your package installation system
- to see if there is an &SCons; package available.
- Many people prefer to install distribution-native packages if available,
- as they provide a central point for management and updating.
- During the still-ongoing Python 2 to 3 transition,
- some distributions may still have two &SCons; packages available,
- one which uses Python 2 and one which uses Python 3. Since
- the latest &scons; only runs on Python 3, to get the current version
- you should choose the Python 3 package.
+ If you need a specific
+ version of &SCons; that is different from the current version,
+ <systemitem>pip</systemitem> has a version option
+ (e.g. <userinput>python -m pip install scons==3.1.2</userinput>),
+ or you can follow the instructions in the following sections.
</para>
<para>
- If you need a specific
- version of &SCons; that is different from the package available,
- <systemitem>pip</systemitem> has a version option or you can follow
- the instructions in the next section.
+ &SCons; does comes pre-packaged for installation on many Linux systems.
+ Check your package installation system
+ to see if there is an up-to-date &SCons; package available.
+ Many people prefer to install distribution-native packages if available,
+ as they provide a central point for management and updating;
+ however not all distributions update in a timely fashion.
+ During the still-ongoing &Python; 2 to 3 transition,
+ some distributions may still have two &SCons; packages available,
+ one which uses &Python; 2 and one which uses &Python; 3. Since
+ the latest &scons; only runs on &Python; 3, to get the current version
+ you should choose the &Python; 3 package.
</para>
</section>
- <section>
- <title>Building and Installing &SCons; on Any System</title>
+ <section id="sect-scons-no-install">
+ <title>Using &SCons; Without Installing</title>
<para>
- If a pre-built &SCons; package is not available for your system,
- and installing using <systemitem>pip</systemitem> is not suitable,
- then you can still easily build and install &SCons; using the native
- Python <systemitem>setuptools</systemitem> package.
+ You don't actually need to "install" &SCons; to use it.
+ Nor do you need to "build" it, unless you are interested in
+ producing the &SCons; documentation, which does use several
+ tools to produce HTML, PDF and other output formats from
+ files in the source tree.
+ All you need to do is
+ call the <filename>scons.py</filename> driver script in a
+ location that contains an &SCons; tree, and it will figure out
+ the rest. You can test that like this:
</para>
+ <screen>
+$ <userinput>python <replaceable>/path/to/unpacked</replaceable>/scripts/scons.py --version</userinput>
+ </screen>
+
<para>
- The first step is to download either the
+ To make use of an uninstalled &SCons;,
+ the first step is to download either the
<filename>scons-&buildversion;.tar.gz</filename>
or <filename>scons-&buildversion;.zip</filename>,
which are available from the SCons download page at
<ulink url="https://scons.org/pages/download.html">https://scons.org/pages/download.html</ulink>.
+ There is also a <literal>scons-local</literal> bundle you can make
+ use of. It is arranged a little bit differently, with the idea
+ that you can include it with your own project if you want people
+ to be able to do builds without having to download or install &SCons;.
+ Finally, you can also use a checkout of the git tree from GitHub
+ at a location to point to.
</para>
<para>
@@ -252,195 +277,88 @@ Python 3.7.1
or <application>WinZip</application> on Windows.
This will create a directory called
<filename>scons-&buildversion;</filename>,
- usually in your local directory.
- Then change your working directory to that directory
- and install &SCons; by executing the following commands:
+ usually in your local directory. The driver script
+ will be in a subdirectory named <filename>scripts</filename>,
+ unless you are using <literal>scons-local</literal>,
+ in which case it will be in the top directory.
+ Now you only need to call <filename>scons.py</filename> by
+ giving a full or relative path to it in order to use that
+ &SCons; version.
</para>
- <screen>
-# <userinput>cd scons-&buildversion;</userinput>
-# <userinput>python setup.py install</userinput>
- </screen>
-
<para>
-
- This will build &SCons;,
- install the &scons; script
- in the python which is used to run the setup.py's scripts directory
- (<filename>/usr/local/bin</filename> or
- <filename>C:\Python37\Scripts</filename>),
- and will install the &SCons; build engine
- in the corresponding library directory for the python used
- (<filename>/usr/local/lib/scons</filename> or
- <filename>C:\Python37\scons</filename>).
- Because these are system directories,
- you may need root (on Linux or UNIX) or Administrator (on Windows)
- privileges to install &SCons; like this.
-
+ Note that instructions for older versions may have suggested
+ running <userinput>python setup.py install</userinput> to
+ "build and install" &SCons;. This is no longer recommended
+ (in fact, it is not recommended by the wider &Python; packaging
+ community for <emphasis>any</emphasis> end-user installations
+ of &Python; software). There is a <filename>setup.py</filename> file,
+ but it is only tested and used for the automated procedure which
+ prepares an &SCons; bundle for making a release on PyPI,
+ and even that is not guaranteed to work in future.
</para>
- <!--
-
- <section>
- <title>Building and Installing &SCons; in the Standard Python Library Directories</title>
-
- <para>
-
- XXX
-
- </para>
-
- </section>
-
- -->
-
- <section>
- <title>Building and Installing Multiple Versions of &SCons; Side-by-Side</title>
-
- <para>
+ </section>
- The &SCons; <filename>setup.py</filename> script
- has some extensions that support
- easy installation of multiple versions of &SCons;
- in side-by-side locations.
- This makes it easier to download and
- experiment with different versions of &SCons;
- before moving your official build process to a new version,
- for example.
-
- </para>
+ <section id="sect-install-scons-multi">
+ <title>Running Multiple Versions of &SCons; Side-by-Side</title>
<para>
-
- To install &SCons; in a version-specific location,
- add the <option>--version-lib</option> option
- when you call <filename>setup.py</filename>:
-
+ In some cases you may need several versions of &SCons;
+ present on a system at the same time - perhaps you have
+ an older project to build that has not yet been "ported"
+ to a newer &SCons; version, or maybe you want to test a
+ new &SCons; release side-by-side with a previous one
+ before switching over.
+ The use of an "uninstalled" package as described in the
+ previous section can be of use for this purpose.
</para>
- <screen>
-# <userinput>python setup.py install --version-lib</userinput>
- </screen>
-
<para>
-
- This will install the &SCons; build engine
- in the
- <filename>/usr/lib/scons-&buildversion;</filename>
- or
- <filename>C:\Python27\scons-&buildversion;</filename>
- directory, for example.
-
+ Another approach to multiple versions is to create
+ &Python; virtualenvs, and install different &SCons; versions in each.
+ A Python <firstterm>virtual environment</firstterm>
+ is a directory with an isolated set of Python packages,
+ where packages you install/upgrade/remove inside the
+ environment do not affect anything outside it,
+ and those you install/upgrade/remove outside of it
+ do not affect anything inside it.
+ In other words, anything you do with <command>pip</command>
+ in the environment stays in that environment.
+ The &Python; standard library provides a module called
+ <systemitem>venv</systemitem> for creating these
+ (<ulink url="https://docs.python.org/e/library/venv.html"/>),
+ although there are also other tools which provide more precise
+ control of the setup.
</para>
<para>
-
- If you use the <option>--version-lib</option> option
- the first time you install &SCons;,
- you do not need to specify it each time you install
- a new version.
- The &SCons; <filename>setup.py</filename> script
- will detect the version-specific directory name(s)
- and assume you want to install all versions
- in version-specific directories.
- You can override that assumption in the future
- by explicitly specifying the <option>--standalone-lib</option> option.
-
+ Using a virtualenv can be useful even for a single version of
+ &SCons;, to gain the advantages of having an isolated environment.
+ It also gets around the problem of not having administrative
+ privileges on a particular system to install a distribution
+ package or use <command>pip</command> to install to a
+ system location, as the virtualenv is completely under your control.
</para>
- </section>
-
- <section>
- <title>Installing &SCons; in Other Locations</title>
-
<para>
-
- You can install &SCons; in locations other than
- the default by specifying the <option>--prefix=</option> option:
-
+ The following outline shows how this could be set up
+ on a Linux/POSIX system (the syntax will be a bit different
+ on Windows):
</para>
<screen>
-# <userinput>python setup.py install --prefix=/opt/scons</userinput>
+$ <emphasis>create virtualenv named scons3</emphasis>
+$ <emphasis>create virtualenv named scons4</emphasis>
+$ <userinput>source scons3/bin/activate</userinput>
+$ <userinput>pip install scons==3.1.2</userinput>
+$ <userinput>deactivate</userinput>
+$ <userinput>source scons4/bin/activate</userinput>
+$ <userinput>pip install scons</userinput>
+$ <userinput>deactivate</userinput>
+$ <emphasis>activate a virtualenv and run 'scons' to use that version</emphasis>
</screen>
- <para>
-
- This would
- install the <application>scons</application> script in
- <filename>/opt/scons/bin</filename>
- and the build engine in
- <filename>/opt/scons/lib/scons</filename>,
-
- </para>
-
- <para>
-
- Note that you can specify both the <option>--prefix=</option>
- and the <option>--version-lib</option> options
- at the same type,
- in which case <filename>setup.py</filename>
- will install the build engine
- in a version-specific directory
- relative to the specified prefix.
- Adding <option>--version-lib</option> to the
- above example would install the build engine in
- <filename>/opt/scons/lib/scons-&buildversion;</filename>.
-
- </para>
-
- </section>
-
- <section>
- <title>Building and Installing &SCons; Without Administrative Privileges</title>
-
- <para>
-
- If you don't have the right privileges to install &SCons;
- in a system location,
- simply use the <literal>--prefix=</literal> option
- to install it in a location of your choosing.
- For example,
- to install &SCons; in appropriate locations
- relative to the user's <literal>$HOME</literal> directory,
- the &scons; script in
- <filename>$HOME/bin</filename>
- and the build engine in
- <filename>$HOME/lib/scons</filename>,
- simply type:
-
- </para>
-
- <screen>
-$ <userinput>python setup.py install --prefix=$HOME</userinput>
- </screen>
-
- <para>
-
- You may, of course, specify any other location you prefer,
- and may use the <option>--version-lib</option> option
- if you would like to install version-specific directories
- relative to the specified prefix.
-
- </para>
-
- <para>
-
- This can also be used to experiment with a newer
- version of &SCons; than the one installed
- in your system locations.
- Of course, the location in which you install the
- newer version of the &scons; script
- (<filename>$HOME/bin</filename> in the above example)
- must be configured in your &PATH; variable
- before the directory containing
- the system-installed version
- of the &scons; script.
-
- </para>
-
- </section>
-
</section>
<!--
diff --git a/doc/user/command-line.xml b/doc/user/command-line.xml
index a14800d..500b2ef 100644
--- a/doc/user/command-line.xml
+++ b/doc/user/command-line.xml
@@ -1281,7 +1281,7 @@ vars = Variables('custom.py', ARGUMENTS)
<scons_example name="commandline_BoolVariable">
<file name="SConstruct" printme="1">
vars = Variables('custom.py')
-vars.Add(BoolVariable('RELEASE', help='Set to build for release', default=0))
+vars.Add(BoolVariable('RELEASE', help='Set to build for release', default=False))
env = Environment(variables=vars, CPPDEFINES={'RELEASE_BUILD': '${RELEASE}'})
env.Program('foo.c')
</file>
@@ -1964,7 +1964,7 @@ vars = Variables()
vars.AddVariables(
('RELEASE', 'Set to 1 to build for release', 0),
('CONFIG', 'Configuration file', '/etc/my_config'),
- BoolVariable('warnings', help='compilation with -Wall and similiar', default=1),
+ BoolVariable('warnings', help='compilation with -Wall and similiar', default=True),
EnumVariable(
'debug',
help='debug output and symbols',
diff --git a/doc/user/external.xml b/doc/user/external.xml
index b483196..ceeece0 100644
--- a/doc/user/external.xml
+++ b/doc/user/external.xml
@@ -129,13 +129,13 @@
<scons_example name="external_cdb_ex1">
<file name="SConstruct" printme="1">
- env = Environment(COMPILATIONDB_USE_ABSPATH=True)
- env.Tool('compilation_db')
- env.CompilationDatabase()
- env.Program('hello.c')
+env = Environment(COMPILATIONDB_USE_ABSPATH=True)
+env.Tool('compilation_db')
+env.CompilationDatabase()
+env.Program('hello.c')
</file>
<file name="hello.c">
- int main( int argc, char* argv[] )
+ int main(int argc, char* argv[])
{
return 0;
}
@@ -152,14 +152,14 @@
</para>
<programlisting language="json">
- [
- {
- "command": "gcc -o hello.o -c hello.c",
- "directory": "/home/user/sandbox",
- "file": "/home/user/sandbox/hello.c",
- "output": "/home/user/sandbox/hello.o"
- }
- ]
+[
+ {
+ "command": "gcc -o hello.o -c hello.c",
+ "directory": "/home/user/sandbox",
+ "file": "/home/user/sandbox/hello.c",
+ "output": "/home/user/sandbox/hello.o"
+ }
+]
</programlisting>
<para>
@@ -202,15 +202,15 @@
<scons_example name="external_cdb_ex2">
<file name="SConstruct" printme="1">
- env = Environment()
- env.Tool('compilation_db')
- cdb = env.CompilationDatabase('compile_database.json')
- Alias('cdb', cdb)
- env.Program('test_main.c')
+env = Environment()
+env.Tool('compilation_db')
+cdb = env.CompilationDatabase('compile_database.json')
+Alias('cdb', cdb)
+env.Program('test_main.c')
</file>
<file name="test_main.c">
#include "test_main.h"
- int main( int argc, char* argv[] )
+ int main(int argc, char* argv[])
{
return 0;
}
@@ -225,23 +225,21 @@
</scons_output>
<para>
- <filename>compile_database.json</filename>
- contains:
+ <filename>compile_database.json</filename> contains:
</para>
<programlisting language="json">
- [
- {
- "command": "gcc -o test_main.o -c test_main.c",
- "directory": "/home/user/sandbox",
- "file": "test_main.c",
- "output": "test_main.o"
- }
- ]
+[
+ {
+ "command": "gcc -o test_main.o -c test_main.c",
+ "directory": "/home/user/sandbox",
+ "file": "test_main.c",
+ "output": "test_main.o"
+ }
+]
</programlisting>
<para>
-
The following (incomplete) example shows using filtering
to separate build variants.
In the case of using variants,
@@ -251,20 +249,19 @@
and 64-bit build hinted at here.
For simplicity of presentation,
the example omits the setup details of the variant directories:
-
</para>
<sconstruct>
- env = Environment()
- env.Tool('compilation_db')
+env = Environment()
+env.Tool("compilation_db")
- env1 = env.Clone()
- env1['COMPILATIONDB_PATH_FILTER'] = 'build/linux32/*'
- env1.CompilationDatabase('compile_commands-linux32.json')
+env1 = env.Clone()
+env1["COMPILATIONDB_PATH_FILTER"] = "build/linux32/*"
+env1.CompilationDatabase("compile_commands-linux32.json")
- env2 = env.Clone()
- env2['COMPILATIONDB_PATH_FILTER'] = 'build/linux64/*'
- env2.CompilationDatabase('compile_commands-linux64.json')
+env2 = env.Clone()
+env2["COMPILATIONDB_PATH_FILTER"] = "build/linux64/*"
+env2.CompilationDatabase('compile_commands-linux64.json')
</sconstruct>
<para>
@@ -273,55 +270,70 @@
</para>
<programlisting language="json">
- [
- {
- "command": "gcc -m32 -o build/linux32/test_main.o -c test_main.c",
- "directory": "/home/user/sandbox",
- "file": "test_main.c",
- "output": "build/linux32/test_main.o"
- }
- ]
+[
+ {
+ "command": "gcc -o hello.o -c hello.c",
+ "directory": "/home/mats/github/scons/exp/compdb",
+ "file": "hello.c",
+ "output": "hello.o"
+ }
+]
</programlisting>
<para>
- <filename>compile_commands-linux64.json</filename>
- contains:
+ <filename>compile_commands-linux64.json</filename> contains:
</para>
<programlisting language="json">
- [
- {
- "command": "gcc -m64 -o build/linux64/test_main.o -c test_main.c",
- "directory": "/home/user/sandbox",
- "file": "test_main.c",
- "output": "build/linux64/test_main.o"
- }
- ]
+[
+ {
+ "command": "gcc -m64 -o build/linux64/test_main.o -c test_main.c",
+ "directory": "/home/user/sandbox",
+ "file": "test_main.c",
+ "output": "build/linux64/test_main.o"
+ }
+]
</programlisting>
</section>
<section>
<title>Ninja Build Generator</title>
-
- <note>
- <para>
+<note>
+ <para>
This is an experimental new feature.
It is subject to change and/or removal without a depreciation cycle.
- </para>
+ </para>
+ <para>
+ Loading the &t-link-ninja; tool into SCons will make significant changes
+ in SCons' normal functioning.
+ </para>
+ <itemizedlist>
+ <listitem>
<para>
- To enable this feature you'll need to use one of the following:
+ SCons will no longer execute any commands directly and will only create the <filename>build.ninja</filename> and
+ run ninja.
</para>
-<!-- NOTE DO NOT INDENT example_commands CONTENTS AS IT WILL ALTER THE FORMATTING-->
- <example_commands>
-# On the command line
---experimental=ninja
+ </listitem>
+ <listitem>
+ <para>
+ Any targets specified on the command line will be passed along to &ninja;
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ To enable this feature you'll need to use one of the following:
+ </para>
+ <!-- NOTE DO NOT INDENT example_commands CONTENTS AS IT WILL ALTER THE FORMATTING-->
+ <example_commands>
+# On the command line --experimental=ninja
# Or in your SConstruct
SetOption('experimental', 'ninja')
- </example_commands>
- </note>
+ </example_commands>
+</note>
<para>
Ninja is a small build system that tries to be fast
@@ -366,8 +378,10 @@ conda install -c conda-forge ninja
<para>
It is not expected that the &b-link-Ninja; builder will work for all builds at this point. It is still under active
- development. If you find that your build doesn't work with &ninja; please bring this to the users mailing list
- or <literal>#scons-help</literal> channel on our Discord server.
+ development. If you find that your build doesn't work with &ninja; please bring this to the <ulink url="https://pairlist4.pair.net/mailman/listinfo/scons-users">users mailing list</ulink>
+ or <ulink url="https://discord.gg/bXVpWAy">
+ <literal>#scons-help</literal>
+ </ulink> channel on our Discord server.
</para>
<para>
@@ -377,6 +391,17 @@ conda install -c conda-forge ninja
implement those actions via shell commands in the &ninja; build file.
</para>
+ <para>
+ When &ninja; runs the generated &ninja; build file, &ninja; will launch &scons; as a daemon and feed commands
+ to that &scons; process which &ninja; is unable to build directly. This daemon will stay alive until
+ explicitly killed, or it times out. The timeout is set by &cv-link-NINJA_SCONS_DAEMON_KEEP_ALIVE; .
+ </para>
+
+ <para>
+ The daemon will be restarted if any &SConscript; file(s) change or the build changes in a way that &ninja; determines
+ it needs to regenerate the build.ninja file
+ </para>
+
<para>See:</para>
<simplelist type="vert">
diff --git a/doc/user/factories.xml b/doc/user/factories.xml
index bb68504..362b6f0 100644
--- a/doc/user/factories.xml
+++ b/doc/user/factories.xml
@@ -190,10 +190,10 @@ touch $*
<scons_example name="factories_SymlinkCopy">
<file name ="SymlinkCopy" printme="1">
# Symbolic link shallow copied as a new symbolic link:
-Command("LinkIn", "LinkOut", Copy("$TARGET", "$SOURCE"[, True]))
+Command("LinkIn", "LinkOut", Copy("$TARGET", "$SOURCE", symlinks=True))
# Symbolic link target copied as a file or directory:
-Command("LinkIn", "FileOrDirectoryOut", Copy("$TARGET", "$SOURCE", False))
+Command("LinkIn", "FileOrDirectoryOut", Copy("$TARGET", "$SOURCE", symlinks=False))
</file>
</scons_example>
diff --git a/doc/user/hierarchy.xml b/doc/user/hierarchy.xml
index e63d230..17874d0 100644
--- a/doc/user/hierarchy.xml
+++ b/doc/user/hierarchy.xml
@@ -1,4 +1,10 @@
<?xml version='1.0'?>
+<!--
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
+-->
+
<!DOCTYPE sconsdoc [
<!ENTITY % scons SYSTEM "../scons.mod">
%scons;
@@ -18,32 +24,8 @@
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">
-<title>Hierarchical Builds</title>
-
-<!--
-
- __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.
-
--->
+<title>Hierarchical Builds</title>
<!--
@@ -193,11 +175,11 @@ make no difference to the build.
hierarchy of directories.
Organizing a large software build using &SCons;
involves creating a hierarchy of build scripts
- using the &SConscript; function.
+ which are connected together using the &f-link-SConscript; function.
</para>
- <section>
+ <section id="sect-sconscript-files">
<title>&SConscript; Files</title>
<para>
@@ -219,10 +201,14 @@ make no difference to the build.
</para>
<sconstruct>
-SConscript(['drivers/display/SConscript',
- 'drivers/mouse/SConscript',
- 'parser/SConscript',
- 'utilities/SConscript'])
+SConscript(
+ [
+ 'drivers/display/SConscript',
+ 'drivers/mouse/SConscript',
+ 'parser/SConscript',
+ 'utilities/SConscript',
+ ]
+)
</sconstruct>
<para>
@@ -241,9 +227,7 @@ SConscript(['drivers/display/SConscript',
</para>
<sconstruct>
-SConscript(['drivers/SConscript',
- 'parser/SConscript',
- 'utilities/SConscript'])
+SConscript(['drivers/SConscript', 'parser/SConscript', 'utilities/SConscript'])
</sconstruct>
<para>
@@ -255,8 +239,7 @@ SConscript(['drivers/SConscript',
</para>
<sconstruct>
-SConscript(['display/SConscript',
- 'mouse/SConscript'])
+SConscript(['display/SConscript', 'mouse/SConscript'])
</sconstruct>
<para>
@@ -272,7 +255,7 @@ SConscript(['display/SConscript',
</section>
- <section>
+ <section id="sect-sconscript-relative-paths">
<title>Path Names Are Relative to the &SConscript; Directory</title>
<para>
@@ -280,7 +263,7 @@ SConscript(['display/SConscript',
Subsidiary &SConscript; files make it easy to create a build
hierarchy because all of the file and directory names
in a subsidiary &SConscript; files are interpreted
- relative to the directory in which the &SConscript; file lives.
+ relative to the directory in which that &SConscript; file lives.
Typically, this allows the &SConscript; file containing the
instructions to build a target file
to live in the same directory as the source files
@@ -288,6 +271,8 @@ SConscript(['display/SConscript',
making it easy to update how the software is built
whenever files are added or deleted
(or other changes are made).
+ It also tends to keep scripts more readable as they don't
+ need to be filled with complex paths.
</para>
@@ -369,20 +354,27 @@ x
Notice the following:
First, you can have files with the same names
- in multiple directories, like main.c in the above example.
+ in multiple directories, like <filename>main.c</filename>
+ in the above example.
- Second, unlike standard recursive use of &Make;,
+ Second, when building,
&SCons; stays in the top-level directory
(where the &SConstruct; file lives)
and issues commands that use the path names
from the top-level directory to the
target and source files within the hierarchy.
+ This works because &SCons; reads all the SConscript files
+ in one pass, interpreting each in its local context,
+ building up a tree of information, before starting to
+ execute the needed builds in a second pass.
+ This is quite different than some other build tools
+ which implement a heirarcical build by recursing.
</para>
</section>
- <section>
+ <section id="sect-sconscript-top-relative-paths">
<title>Top-Relative Path Names in Subsidiary &SConscript; Files</title>
<para>
@@ -465,7 +457,8 @@ x
understand about it. This becomes immediately obvious
if you like to use <function>print</function> for debugging,
or write a Python function that wants to evaluate a path.
- You can force &SCons; to evaluate a top-relative path by
+ You can force &SCons; to evaluate a top-relative path
+ and produce a string that can be used by &Python; code by
creating a Node object from it:
</para>
@@ -492,7 +485,7 @@ print("force-interpreted path =", Entry(path))
</section>
- <section>
+ <section id="sect-sconscript-absolute-paths">
<title>Absolute Path Names</title>
<para>
@@ -544,7 +537,7 @@ x
</section>
- <section>
+ <section id="sect-sconscript-sharing">
<title>Sharing Environments (and Other Variables) Between &SConscript; Files</title>
<para>
@@ -573,7 +566,7 @@ x
</para>
- <section>
+ <section id="sect-sconscript-export">
<title>Exporting Variables</title>
<para>
@@ -701,8 +694,7 @@ SConscript('src/SConscript', exports='env')
</para>
<sconstruct>
-SConscript(['src1/SConscript',
- 'src2/SConscript'], exports='env')
+SConscript(['src1/SConscript', 'src2/SConscript'], exports='env')
</sconstruct>
<para>
@@ -716,7 +708,7 @@ SConscript(['src1/SConscript',
</section>
- <section>
+ <section id="sect-sconscript-import">
<title>Importing Variables</title>
<para>
@@ -818,7 +810,7 @@ env.Program('prog', ['prog.c'])
</section>
- <section>
+ <section id="sect-sconscript-return">
<title>Returning Values From an &SConscript; File</title>
<para>
diff --git a/doc/user/java.xml b/doc/user/java.xml
index 8017159..4f1beef 100644
--- a/doc/user/java.xml
+++ b/doc/user/java.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">
@@ -20,28 +20,9 @@
<title>Java Builds</title>
<!--
+SPDX-License-Identifier: MIT
- __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.
-
+Copyright The SCons Foundation
-->
<para>
@@ -206,7 +187,7 @@ public class AdditionalClass3
Will not only tell you reliably
that the <filename>.class</filename> files
- in the <filename>classes</filename> subdirectory
+ in the <filename>classes</filename> subdirectory
are up-to-date:
</para>
@@ -250,7 +231,7 @@ public class AdditionalClass3
variable to specify the version in use. With Java 1.6, the
one-liner example can then be defined like this:
- </para>
+ </para>
<sconstruct>
Java('classes', 'src', JAVAVERSION='1.6')
@@ -280,8 +261,8 @@ Java('classes', 'src', JAVAVERSION='1.6')
<scons_example name="java_jar1">
<file name="SConstruct" printme="1">
-Java(target = 'classes', source = 'src')
-Jar(target = 'test.jar', source = 'classes')
+Java(target='classes', source='src')
+Jar(target='test.jar', source='classes')
</file>
<file name="src/Example1.java">
public class Example1
@@ -344,10 +325,10 @@ public class Example3
<scons_example name="java_jar2">
<file name="SConstruct" printme="1">
-prog1_class_files = Java(target = 'classes', source = 'prog1')
-prog2_class_files = Java(target = 'classes', source = 'prog2')
-Jar(target = 'prog1.jar', source = prog1_class_files)
-Jar(target = 'prog2.jar', source = prog2_class_files)
+prog1_class_files = Java(target='classes', source='prog1')
+prog2_class_files = Java(target='classes', source='prog2')
+Jar(target='prog1.jar', source=prog1_class_files)
+Jar(target='prog2.jar', source=prog2_class_files)
</file>
<file name="prog1/Example1.java">
public class Example1
@@ -418,8 +399,8 @@ public class Example4
<scons_example name="java_javah">
<file name="SConstruct" printme="1">
-classes = Java(target = 'classes', source = 'src/pkg/sub')
-JavaH(target = 'native', source = classes)
+classes = Java(target='classes', source='src/pkg/sub')
+JavaH(target='native', source=classes)
</file>
<file name="src/pkg/sub/Example1.java">
package pkg.sub;
@@ -642,8 +623,8 @@ public class Example3
<scons_example name="java_RMIC">
<file name="SConstruct" printme="1">
-classes = Java(target = 'classes', source = 'src/pkg/sub')
-RMIC(target = 'outdir', source = classes)
+classes = Java(target='classes', source='src/pkg/sub')
+RMIC(target='outdir', source=classes)
</file>
<file name="src/pkg/sub/Example1.java">
package pkg.sub;
diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml
index 757ffac..3eda19c 100644
--- a/doc/user/less-simple.xml
+++ b/doc/user/less-simple.xml
@@ -236,9 +236,10 @@ void file2() { printf("file2.c\n"); }
<para>
- You can also use the &Glob; function to find all files matching a
+ You can also use the &f-link-Glob; function to find all files matching a
certain template, using the standard shell pattern matching
- characters <literal>*</literal>, <literal>?</literal>
+ characters <literal>*</literal> (to match everything),
+ <literal>?</literal> (to match a single character)
and <literal>[abc]</literal> to match any of
<literal>a</literal>, <literal>b</literal> or <literal>c</literal>.
<literal>[!abc]</literal> is also supported,
@@ -254,13 +255,14 @@ Program('program', Glob('*.c'))
<para>
- The SCons man page has more details on using &Glob;
- with variant directories
- (see <xref linkend="chap-variants"></xref>, below)
+ &f-Glob; has powerful capabilities - it matches even if the
+ file does not exist, but &SCons; can determine that it would
+ exist after a build.
+ You will meet it again reading about
+ variant directories
+ (see <xref linkend="chap-separate"/>)
and repositories
- (see <xref linkend="chap-repositories"></xref>, below),
- excluding some files
- and returning strings rather than Nodes.
+ (see <xref linkend="chap-repositories"/>).
</para>
diff --git a/doc/user/main.xml b/doc/user/main.xml
index 47250a9..5006339 100644
--- a/doc/user/main.xml
+++ b/doc/user/main.xml
@@ -54,10 +54,10 @@
<corpauthor>The SCons Development Team</corpauthor>
- <pubdate>Released: Mon, 21 Nov 2021 17:07:47 -0700</pubdate>
+ <pubdate>Released: Mon, 30 Jul 2022 14:49:54 -0700</pubdate>
<copyright>
- <year>2004 - 2021</year>
+ <year>2004 - 2022</year>
<holder>The SCons Foundation</holder>
</copyright>
diff --git a/doc/user/misc.xml b/doc/user/misc.xml
index 1b18d2f..b1e1507 100644
--- a/doc/user/misc.xml
+++ b/doc/user/misc.xml
@@ -256,11 +256,11 @@ hello.c
<para>
- The &FindFile; function searches for a file in a list of directories.
+ The &f-link-FindFile; function searches for a file in a list of directories.
If there is only one directory, it can be given as a simple string.
The function returns a File node if a matching file exists,
or None if no file is found.
- (See the documentation for the &Glob; function for an alternative way
+ (See the documentation for the &f-link-Glob; function for an alternative way
of searching for entries in a directory.)
</para>
diff --git a/doc/user/repositories.xml b/doc/user/repositories.xml
index 01edec7..77d75d3 100644
--- a/doc/user/repositories.xml
+++ b/doc/user/repositories.xml
@@ -203,6 +203,10 @@ int main() { printf("Hello, world!\n"); }
<para>
+ The &f-link-Glob; function understands about repositories,
+ and will use the same matching algorithm as described for
+ explicitly-listed sources.
+
</para>
</section>
diff --git a/doc/user/separate.xml b/doc/user/separate.xml
index 3b609f0..0485a55 100644
--- a/doc/user/separate.xml
+++ b/doc/user/separate.xml
@@ -1,4 +1,10 @@
<?xml version='1.0'?>
+<!--
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
+-->
+
<!DOCTYPE sconsdoc [
<!ENTITY % scons SYSTEM "../scons.mod">
%scons;
@@ -17,98 +23,111 @@
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">
+
<title>Separating Source and Build Trees: Variant Directories</title>
-<!--
+ <para>
- __COPYRIGHT__
+ It is often useful to keep built files completely
+ separate from the source files. Two main benefits are
+ the ability to have different configurations simultaneously
+ without build conflicts, and being version-control friendly.
- 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:
+ </para>
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
+ <para>
- 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.
+ Consider if you have a project to build an embedded
+ software system for a variety of different controller hardware.
+ The system is able to share a lot of code,
+ so it makes sense to use a common source tree,
+ but certain build options in the source code
+ and header files differ. For a regular in-place build,
+ the build outputs go in the same place as the source code.
+ If you build <emphasis>Controller A</emphasis> first,
+ followed by <emphasis>Controller B</emphasis>,
+ on the <emphasis>Controller B</emphasis> build everything that
+ uses different build options has to be rebuilt since those
+ objects will be different
+ (the build lines, including preprocessor defines, are part of
+ &SCons;'s out-of-date calculation for this reason).
+ If you go back and build for <emphasis>Controller A</emphasis> again,
+ things have to be rebuilt again for the same reason.
+ However, if you can separate the locations of the output files,
+ so each controller has its own location for build outputs,
+ this problem can be avoided.
--->
+ </para>
<para>
- It's often useful to keep any built files completely
- separate from the source files. Consider if you have a
- project to build software for a variety of different
- controller hardware. The boards are able to share a
- lot of code, so it makes sense to keep them in the same
- source tree, but certain build options in the source code
- and header files differ. If you build "Controller A" first,
- then "Controller B", on the "Controller B" build everything would
- have to be rebuilt, because &SCons; recognizes that the build
- instructions are different from those used in the "Controller A"
- build for each target - the build instructions are part of
- &SCons;'s out-of-date calculation.
- Now when you go back and build for "Controller A",
- things have to be rebuilt from scratch again for the same reason.
- However, if you can separate the locations of the output files,
- this problem can be avoided.
- You can even set up to do both builds in one invocation of &SCons;.
+ Having a separated build tree also helps you keep your source tree clean -
+ there is less chance of accidentally checking in build products
+ to version control that were not intended to be checked in.
+ You can add a separated build directory to your
+ version control system's list of items not to track.
+ You can even remove the whole build tree with a single command without
+ risking removing any of the source code.
</para>
<para>
- You can enable this separation by establishing one or more
- <firstterm>variant directory</firstterm> trees
- that are used to perform the build in, and thus provide a unique
- home for object files, libraries, and executable programs, etc.
- for a specific flavor, or variant, of build. &SCons; tracks
- targets by their path, so when the variant directory is included,
- objects belonging to "Controller A" can have different
- build instructions than those belonging to "Controller B" without
- triggering ping-ponging rebuilds.
+ The key to making this separation work is the ability to
+ do out-of-tree builds: building under a separate root
+ than the sources being built.
+ You set up out of tree builds by establishing what &SCons;
+ calls a <firstterm>variant directory</firstterm>,
+ a place where you can build a single variant of your software
+ (of course you can define more than one of these if you need to).
+ Since &SCons; tracks targets by their path, it is able to distinguish
+ build products like <filename>build/A/network.obj</filename>
+ of the <emphasis>Controller A</emphasis> build
+ from <filename>build/B/network.obj</filename>
+ of the <emphasis>Controller B</emphasis> build,
+ thus avoiding conflicts.
</para>
<para>
- &SCons; provides two ways to do this,
- one through the &f-link-SConscript; function that we've already seen,
+ &SCons; provides two ways to establish variant directories,
+ one through the &f-link-SConscript; function that we have already seen,
and the second through a more flexible &f-link-VariantDir; function.
</para>
<para>
- Historical note: the &VariantDir; function
- used to be called &BuildDir;, a name which was
- removed because the &SCons; functionality
+ The variant directory mechanism does support doing multiple builds
+ in one invocation of &SCons;, but the remainder of this chapter
+ will focus on setting up a single build. You can combine these
+ techniques with ones from the previous chapter and elsewhere
+ in this Guide to set up more complex scenarios.
+
+ </para>
+
+ <note> <para>
+
+ The &VariantDir; function used to be called &BuildDir;,
+ a name which was changed because it turned out to be confusing:
+ the &SCons; functionality
differs from a familiar model of a "build directory"
- implemented by other build systems like GNU Autotools.
+ implemented by certain other build systems like GNU Autotools.
You might still find references to the old name on
the Internet in postings about &SCons;, but it no longer works.
- </para>
+ </para> </note>
- <section>
+ <section id="sect-variant-sconscript">
<title>Specifying a Variant Directory Tree as Part of an &SConscript; Call</title>
<para>
The most straightforward way to establish a variant directory tree
- relies the fact that the usual way to
+ relies on the fact that the usual way to
set up a build hierarchy is to have an
- SConscript file in the source subdirectory.
+ &SConscript; file in the source directory.
If you pass a &variant_dir; argument to the
&f-link-SConscript; function call:
@@ -130,7 +149,7 @@ int main() { printf("Hello, world!\n"); }
<para>
&SCons; will then build all of the files in
- the &build; subdirectory:
+ the &build; directory:
</para>
@@ -143,16 +162,14 @@ int main() { printf("Hello, world!\n"); }
<para>
- No files were built in &src;, they went to &build;.
- The build output might show a bit of a surprise:
+ No files were built in &src;:
the object file
<filename>build/hello.o</filename>
and the executable file
<filename>build/hello</filename>
- were built in the &build; subdirectory,
- as expected.
- But even though our &hello_c; file lives in the &src; subdirectory,
- &SCons; has actually compiled a
+ were built in the &build; directory, as expected.
+ But notice that even though our &hello_c; file actually
+ lives in the &src; directory, &SCons; has compiled a
<filename>build/hello.c</filename> file
to create the object file,
and that file is now seen in &build;.
@@ -161,28 +178,57 @@ int main() { printf("Hello, world!\n"); }
<para>
+ You can ask &SCons; to show the dependency tree to illustrate
+ a bit more:
+
+ </para>
+
+ <scons_output example="separate_ex1" suffix="2">
+ <scons_output_command>scons -Q --tree=prune</scons_output_command>
+ </scons_output>
+
+ <para>
+
What's happened is that &SCons; has <emphasis>duplicated</emphasis>
- the &hello_c; file from the &src; subdirectory
- to the &build; subdirectory,
+ the &hello_c; file from the &src; directory
+ to the &build; directory,
and built the program from there (it also duplicated &SConscript;).
The next section explains why &SCons; does this.
</para>
+ <para>
+
+ The nice thing about the &SConscript; approach is it is almost
+ invisible to you:
+ this build looks just like an ordinary in-place build
+ except for the extra &variant_dir; argument in the
+ &f-link-SConscript; call.
+ &SCons; handles all the path adjustments for the
+ out of tree &build; directory while it processes that SConscript file.
+
+ </para>
+
</section>
- <section>
+ <section id="sect-variant-duplication">
<title>Why &SCons; Duplicates Source Files in a Variant Directory Tree</title>
<para>
- The important thing to understand is that when you set up a variant directory,
- &SCons; performs the build <emphasis>in that directory</emphasis>.
- It turns out it's easiest to ensure where build products end up
- by just building in place.
- Since the build is happening in a place different from where the
- sources are, the most straightforward way to guarantee a correct build
- is for &SCons; to copy them there.
+ When you set up a variant directory &SCons; conceptually behaves as
+ if you requested a build in that directory.
+ As noted in the previous chapter,
+ all builds actually happen from the top level directory,
+ but as an aid to understanding how &SCons; operates, think
+ of it as <emphasis>build in place in the variant directory</emphasis>,
+ not <emphasis>build in source but send build artifacts
+ to the variant directory</emphasis>.
+ It turns out in place builds are easier to get right than out
+ of tree builds - so by default &SCons; simulates an in place build
+ by making the variant directory look just like the source directory.
+ The most straightforward way to do that is by making copies
+ of the files needed for the build.
</para>
@@ -192,7 +238,11 @@ int main() { printf("Hello, world!\n"); }
in variant directories
is simply that some tools (mostly older versions)
are written to only build their output files
- in the same directory as the source files.
+ in the same directory as the source files - such tools often don't
+ have any option to specify the output file, and the tool just
+ uses a predefined output file name,
+ or uses a derived variant of the source file name,
+ dropping the result in the same directory.
In this case, the choices are either
to build the output file in the source directory
and move it to the variant directory,
@@ -204,9 +254,9 @@ int main() { printf("Hello, world!\n"); }
Additionally,
relative references between files
- can cause problems if we don't
- just duplicate the hierarchy of source files
- in the variant directory.
+ can cause problems which are resolved by
+ just duplicating the hierarchy of source files
+ into the variant directory.
You can see this at work in
use of the C preprocessor <literal>#include</literal>
mechanism with double quotes, not angle brackets:
@@ -240,8 +290,8 @@ int main() { printf("Hello, world!\n"); }
<para>
Although source-file duplication guarantees a correct build
- even in these end-cases,
- it <emphasis>can</emphasis> usually be safely disabled.
+ even in these edge cases,
+ it can <emphasis>usually</emphasis> be safely disabled.
The next section describes
how you can disable the duplication of source files
in the variant directory.
@@ -250,19 +300,19 @@ int main() { printf("Hello, world!\n"); }
</section>
- <section>
+ <section id="sect-variant-no-duplication">
<title>Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree</title>
<para>
In most cases and with most tool sets,
- &SCons; can place its target files in a build subdirectory
+ &SCons; can use sources directly from the source directory
<emphasis>without</emphasis>
- duplicating the source files
+ duplicating them into the variant directory before building,
and everything will work just fine.
- You can disable the default &SCons; behavior
+ You can disable the default &SCons; duplication behavior
by specifying <literal>duplicate=False</literal>
- when you call the &SConscript; function:
+ when you call the &f-link-SConscript; function:
</para>
@@ -272,11 +322,11 @@ SConscript('src/SConscript', variant_dir='build', duplicate=False)
<para>
- When this flag is specified,
- &SCons; uses the variant directory
- like most people expect--that is,
- the output files are placed in the variant directory
- while the source files stay in the source directory:
+ When this flag is specified, the results of a build
+ look more like the mental model people may have from other
+ build systems - that is,
+ the output files end up in the variant directory
+ while the source files do not.
</para>
@@ -292,15 +342,23 @@ hello
hello.o
</screen>
+ <para>
+
+ If disabling duplication causes any problems,
+ just return to the more cautious approach by letting
+ &SCons; go back to duplicating files.
+
+ </para>
+
</section>
- <section>
+ <section id="sect-variantdir-function">
<title>The &VariantDir; Function</title>
<para>
- Use the &VariantDir; function to establish that target
- files should be built in a separate directory
+ You can also use the &f-link-VariantDir; function to establish
+ that target files should be built in a separate directory tree
from the source files:
</para>
@@ -318,13 +376,18 @@ int main() { printf("Hello, world!\n"); }
<para>
- Note that when you're not using
- an &SConscript; file in the &src; subdirectory,
- you must actually specify that
- the program must be built from
- the <filename>build/hello.c</filename>
- file that &SCons; will duplicate in the
- &build; subdirectory.
+ When using this form, you have to tell &SCons; that
+ sources and targets are in the variant directory,
+ and those references will trigger the remapping,
+ necessary file copying, etc. for an already established
+ variant directory. Here is the same example in a more
+ spelled out form to show this more clearly:
+
+ <programlisting language="python">
+VariantDir('build', 'src')
+env = Environment()
+env.Program(target='build/hello', source=['build/hello.c'])
+ </programlisting>
</para>
@@ -345,7 +408,7 @@ int main() { printf("Hello, world!\n"); }
<para>
You can specify the same <literal>duplicate=False</literal> argument
- that you can specify for an &SConscript; call:
+ that you can specify for an &f-link-SConscript; call:
</para>
@@ -375,13 +438,13 @@ int main() { printf("Hello, world!\n"); }
</section>
- <section>
+ <section id="sect-variantdir-sconscript">
<title>Using &VariantDir; With an &SConscript; File</title>
<para>
- Even when using the &VariantDir; function,
- it's more natural to use it with
+ Even when using the &f-link-VariantDir; function,
+ it is more natural to use it with
a subsidiary &SConscript; file,
because then you don't have to adjust your individual
build instructions to use the variant directory path.
@@ -428,21 +491,26 @@ int main() { printf("Hello, world!\n"); }
<para>
- Notice that this is completely equivalent
- to the use of &SConscript; that we
- learned about in the previous section.
+ This is completely equivalent
+ to the use of &f-link-SConscript; with the
+ <parameter>variant_dir</parameter> argument
+ from earlier in this chapter,
+ but did require callng the SConscript using the already established
+ variant directory path to trigger that behavior.
+ If you call <userinput>SConscript('src/SConscript')</userinput>
+ you would get a normal in-place build in &src;.
</para>
</section>
- <section>
+ <section id="sect-variantdir-glob">
<title>Using &Glob; with &VariantDir;</title>
<para>
The &f-link-Glob; file name pattern matching function
- works just as usual when using &VariantDir;.
+ works just as usual when using &f-link-VariantDir;.
For example, if the
<filename>src/SConscript</filename>
looks like this:
@@ -496,7 +564,7 @@ const char * f2();
<!--
- <section>
+ <section id="sect-variantdir-over-sconscript">
<title>Why You'd Want to Call &VariantDir; Instead of &SConscript;</title>
<para>
diff --git a/doc/user/variants.xml b/doc/user/variants.xml
index 6cf8c3d..5096745 100644
--- a/doc/user/variants.xml
+++ b/doc/user/variants.xml
@@ -1,4 +1,10 @@
<?xml version='1.0'?>
+<!--
+SPDX-License-Identifier: MIT
+
+Copyright The SCons Foundation
+-->
+
<!DOCTYPE sconsdoc [
<!ENTITY % scons SYSTEM "../scons.mod">
%scons;
@@ -13,36 +19,12 @@
%variables-mod;
]>
-<section id="sect-variants"
+<section id="sect-variant-examples"
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">
-<title>Variant Build Examples</title>
-
-<!--
- __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.
-
--->
+<title>Variant Build Examples</title>
<para>
diff --git a/debian/changelog b/packaging/debian/changelog
index 7a0ecd3..ba38e64 100755
--- a/debian/changelog
+++ b/packaging/debian/changelog
@@ -1,10 +1,15 @@
+scons (4.4.0) unstable; urgency=low
+
+ * Feature release
+
+ -- William Deegan <bill@baddogconsulting.com> Sat, 30 Jul 2022 14:08:29 -0700
+
scons (4.3.0) unstable; urgency=low
* Feature release
-- William Deegan <bill@baddogconsulting.com> Tue, 16 Nov 2021 18:12:46 -0700
-
scons (4.2.0) unstable; urgency=low
* Feature release
diff --git a/debian/compat b/packaging/debian/compat
index 7ed6ff8..7ed6ff8 100644
--- a/debian/compat
+++ b/packaging/debian/compat
diff --git a/debian/control b/packaging/debian/control
index d0a61fa..d0a61fa 100644
--- a/debian/control
+++ b/packaging/debian/control
diff --git a/debian/copyright b/packaging/debian/copyright
index c4cc1ef..c4cc1ef 100644
--- a/debian/copyright
+++ b/packaging/debian/copyright
diff --git a/debian/dirs b/packaging/debian/dirs
index 93a451b..93a451b 100644
--- a/debian/dirs
+++ b/packaging/debian/dirs
diff --git a/debian/docs b/packaging/debian/docs
index 09547ff..09547ff 100644
--- a/debian/docs
+++ b/packaging/debian/docs
diff --git a/debian/postinst b/packaging/debian/postinst
index 6a7d335..6a7d335 100644
--- a/debian/postinst
+++ b/packaging/debian/postinst
diff --git a/debian/prerm b/packaging/debian/prerm
index 62531a3..62531a3 100644
--- a/debian/prerm
+++ b/packaging/debian/prerm
diff --git a/debian/rules b/packaging/debian/rules
index 9d83723..9d83723 100644
--- a/debian/rules
+++ b/packaging/debian/rules
diff --git a/packaging/etc/README.txt b/packaging/etc/README.txt
new file mode 100644
index 0000000..ffb9cc1
--- /dev/null
+++ b/packaging/etc/README.txt
@@ -0,0 +1,10 @@
+This directory contains a number of scripts/files useful when building/packageing SCons
+
+To force SCons to propagate SOURCE_DATE_EPOCH from the shell running SCons we're providing
+a script to create a ~/.scons/site_scons/site_init.py.
+Note that reproducible_install.sh will NOT overwite an existing ~/.scons/site_scons/site_init.py
+This supports https://reproducible-builds.org/specs/source-date-epoch/
+If you wanted to include this in your build tree you would place in site_scons/site_init.py relative
+to your SConstruct.
+* reproducible_install.sh
+* reproducible_site_init.py \ No newline at end of file
diff --git a/packaging/etc/reproducible_install.sh b/packaging/etc/reproducible_install.sh
new file mode 100755
index 0000000..90d9d37
--- /dev/null
+++ b/packaging/etc/reproducible_install.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+TARGET_FILE=~/.scons/site_scons/site_init.py
+
+mkdir -p ~/.scons/site_scons
+
+if [ ! -f "${TARGET_FILE}" ]
+then
+ echo "File ${TARGET_FILE} does not exist"
+ echo "We will add one which supports reproducible builds"
+ cp ${SCRIPT_DIR}/reproducible_site_init.py ${TARGET_FILE}
+else
+ echo "File ${TARGET_FILE} already exists"
+ echo "We will not overwrite it. Please copy the content"
+ echo "from ${SCRIPT_DIR}/reproducible_site_init.py"
+fi
diff --git a/packaging/etc/reproducible_site_init.py b/packaging/etc/reproducible_site_init.py
new file mode 100644
index 0000000..2b6b42a
--- /dev/null
+++ b/packaging/etc/reproducible_site_init.py
@@ -0,0 +1,24 @@
+"""
+Use this file as your ~/.site_scons/scons_init.py to enable reprodicble builds as described at
+https://reproducible-builds.org/specs/source-date-epoch/
+"""
+
+import os
+import SCons.Environment
+
+old_init = SCons.Environment.Base.__init__
+
+print("Adding logic to propagate SOURCE_DATE_EPOCH from the shell environment when building with SCons")
+
+
+def new_init(self, **kw):
+ """
+ This logic will add SOURCE_DATE_EPOCH to the execution environment used to run
+ all the build commands.
+ """
+ old_init(self, **kw)
+ if 'SOURCE_DATE_EPOCH' in os.environ:
+ self._dict['ENV']['SOURCE_DATE_EPOCH'] = os.environ['SOURCE_DATE_EPOCH']
+
+
+SCons.Environment.Base.__init__ = new_init
diff --git a/rpm/scons.spec.in b/packaging/rpm/scons.spec.in
index 9b97a07..9b97a07 100644
--- a/rpm/scons.spec.in
+++ b/packaging/rpm/scons.spec.in
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..1725456
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,13 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = ["setuptools", "wheel"]
+
+# for black and mypy, set the lowest Python version supported
+[tool.black]
+quiet = true
+target-version = ['py36']
+skip-string-normalization = true
+
+[mypy]
+python_version = 3.6
+
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..82faa28
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,14 @@
+# Dependencies for development - mainly for running tests
+
+-r requirements.txt
+
+# for now keep pinning "known working" lxml,
+# it's been a troublesome component in the past.
+# Skip lxml for win32 as no tests which require it currently pass on win32
+lxml==4.9.2; python_version < '3.12' and sys_platform != 'win32'
+
+ninja
+
+# Needed for test/Parallel/failed-build/failed-build.py
+# Also for test/ninja/shutdown_scons_daemon.py
+psutil
diff --git a/requirements-pkg.txt b/requirements-pkg.txt
new file mode 100644
index 0000000..ae71cde
--- /dev/null
+++ b/requirements-pkg.txt
@@ -0,0 +1,13 @@
+# Dependencies for packaging, and distribution, including the docs build
+
+-r requirements-dev.txt
+
+# Check if the README.rst will render o.k. on pypi and not block an upload
+# Can be used with twinecheck
+# See: https://github.com/pypa/readme_renderer
+readme-renderer
+
+# sphinx pinned because it has broken several times on new releases
+sphinx < 6.0
+sphinx-book-theme
+rst2pdf
diff --git a/requirements.txt b/requirements.txt
index 1be070c..e2aa5ca 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,17 +1 @@
-# Packages needed for development, packaging, and distribution, but not for running SCons
-
-# This will check if the README.rst will render o.k. on pypi and not block an upload
-# Can be used with twinecheck
-# See: https://github.com/pypa/readme_renderer
-readme-renderer
-sphinx
-sphinx_rtd_theme
-rst2pdf
-# for now keep pinning "known working" lxml,
-# it's been a troublesome component in the past.
-lxml==4.7.1
-rst2pdf
-ninja
-
-# Needed for test/Parallel/failed-build/failed-build.py
-psutil
+# No dependencies for running SCons
diff --git a/runtest.py b/runtest.py
index c05e1c1..a2ece7e 100755
--- a/runtest.py
+++ b/runtest.py
@@ -22,7 +22,6 @@ performs test discovery and processes tests according to options.
import argparse
import glob
import os
-import re
import stat
import subprocess
import sys
@@ -311,15 +310,6 @@ if sys.platform == 'win32':
return buf.value
-_ws = re.compile(r'\s')
-
-def escape(s):
- if _ws.search(s):
- s = '"' + s + '"'
- s = s.replace('\\', '\\\\')
- return s
-
-
if not catch_output:
# Without any output suppressed, we let the subprocess
# write its stuff freely to stdout/stderr.
@@ -432,8 +422,7 @@ class PopenExecutor(RuntestBase):
A bit of a misnomer as the Popen call is now wrapped
by calling subprocess.run (behind the covers uses Popen.
- Very similar to SystemExecutor, but uses command_str
- instead of command_args, and doesn't allow for not catching
+ Very similar to SystemExecutor, but doesn't allow for not catching
the output).
"""
# For an explanation of the following 'if ... else'
@@ -447,7 +436,7 @@ class PopenExecutor(RuntestBase):
tmp_stderr = tempfile.TemporaryFile(mode='w+t')
# Start subprocess...
cp = subprocess.run(
- self.command_str.split(),
+ self.command_args,
stdout=tmp_stdout,
stderr=tmp_stderr,
shell=False,
@@ -470,7 +459,7 @@ class PopenExecutor(RuntestBase):
def execute(self, env):
cp = subprocess.run(
- self.command_str.split(),
+ self.command_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=False,
@@ -784,7 +773,7 @@ def run_test(t, io_lock=None, run_async=True):
if args.runner and t.path in unittests:
# For example --runner TestUnit.TAPTestRunner
command_args.append('--runner ' + args.runner)
- t.command_args = [escape(args.python)] + command_args
+ t.command_args = [args.python] + command_args
t.command_str = " ".join(t.command_args)
if args.printcommand:
if args.print_progress:
diff --git a/scripts/scons-configure-cache.py b/scripts/scons-configure-cache.py
index da0e848..8f81d8a 100644
--- a/scripts/scons-configure-cache.py
+++ b/scripts/scons-configure-cache.py
@@ -2,7 +2,9 @@
#
# SCons - a Software Constructor
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -47,7 +49,7 @@ import os
import sys
# python compatibility check
-if sys.version_info < (3, 5, 0):
+if sys.version_info < (3, 6, 0):
msg = "scons: *** SCons version %s does not run under Python version %s.\n\
Python >= 3.5 is required.\n"
sys.stderr.write(msg % (__version__, sys.version.split()[0]))
@@ -92,4 +94,4 @@ sys.path = libs + sys.path
from SCons.Utilities.ConfigureCache import main
if __name__ == "__main__":
- main() \ No newline at end of file
+ main()
diff --git a/scripts/scons.bat b/scripts/scons.bat
index cf7a5ea..a91cd5d 100644
--- a/scripts/scons.bat
+++ b/scripts/scons.bat
@@ -1,37 +1,37 @@
-@REM __COPYRIGHT__
-@echo off
-set SCONS_ERRORLEVEL=
-if "%OS%" == "Windows_NT" goto WinNT
-
-@REM for 9x/Me you better not have more than 9 args
-python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %*
-@REM no way to set exit status of this script for 9x/Me
-goto endscons
-
-@REM Credit where credit is due: we return the exit code despite our
-@REM use of setlocal+endlocal using a technique from Bear's Journal:
-@REM http://code-bear.com/bearlog/2007/06/01/getting-the-exit-code-from-a-batch-file-that-is-run-from-a-python-program/
-
-:WinNT
-setlocal
-@REM ensure the script will be executed with the Python it was installed for
-pushd %~dp0..
-set path=%~dp0;%CD%;%path%
-popd
-@REM try the script named as the .bat file in current dir, then in Scripts subdir
-set scriptname=%~dp0%~n0.py
-if not exist "%scriptname%" set scriptname=%~dp0Scripts\%~n0.py
-@REM Handle when running from wheel where the script has no .py extension
-if not exist "%scriptname%" set scriptname=%~dp0%~n0
-python "%scriptname%" %*
-endlocal & set SCONS_ERRORLEVEL=%ERRORLEVEL%
-
-if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto returncode
-if errorlevel 9009 echo you do not have python in your PATH
-goto endscons
-
-:returncode
-exit /B %SCONS_ERRORLEVEL%
-
-:endscons
-call :returncode %SCONS_ERRORLEVEL%
+@REM __COPYRIGHT__
+@echo off
+set SCONS_ERRORLEVEL=
+if "%OS%" == "Windows_NT" goto WinNT
+
+@REM for 9x/Me you better not have more than 9 args
+python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %*
+@REM no way to set exit status of this script for 9x/Me
+goto endscons
+
+@REM Credit where credit is due: we return the exit code despite our
+@REM use of setlocal+endlocal using a technique from Bear's Journal:
+@REM http://code-bear.com/bearlog/2007/06/01/getting-the-exit-code-from-a-batch-file-that-is-run-from-a-python-program/
+
+:WinNT
+setlocal
+@REM ensure the script will be executed with the Python it was installed for
+pushd %~dp0..
+set path=%~dp0;%CD%;%path%
+popd
+@REM try the script named as the .bat file in current dir, then in Scripts subdir
+set scriptname=%~dp0%~n0.py
+if not exist "%scriptname%" set scriptname=%~dp0Scripts\%~n0.py
+@REM Handle when running from wheel where the script has no .py extension
+if not exist "%scriptname%" set scriptname=%~dp0%~n0
+python "%scriptname%" %*
+endlocal & set SCONS_ERRORLEVEL=%ERRORLEVEL%
+
+if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto returncode
+if errorlevel 9009 echo you do not have python in your PATH
+goto endscons
+
+:returncode
+exit /B %SCONS_ERRORLEVEL%
+
+:endscons
+call :returncode %SCONS_ERRORLEVEL%
diff --git a/scripts/scons.py b/scripts/scons.py
index 86a6bf6..6a36ef7 100755
--- a/scripts/scons.py
+++ b/scripts/scons.py
@@ -41,9 +41,8 @@ __developer__ = "__DEVELOPER__"
import os
import sys
-
# Python compatibility check
-if sys.version_info < (3, 5, 0):
+if sys.version_info < (3, 6, 0):
msg = "scons: *** SCons version %s does not run under Python version %s.\n\
Python >= 3.5 is required.\n"
sys.stderr.write(msg % (__version__, sys.version.split()[0]))
diff --git a/scripts/sconsign.py b/scripts/sconsign.py
index 0edd05c..19e0c82 100644
--- a/scripts/sconsign.py
+++ b/scripts/sconsign.py
@@ -2,7 +2,9 @@
#
# SCons - a Software Constructor
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -39,7 +41,7 @@ import os
import sys
# python compatibility check
-if sys.version_info < (3, 5, 0):
+if sys.version_info < (3, 6, 0):
msg = "scons: *** SCons version %s does not run under Python version %s.\n\
Python >= 3.5 is required.\n"
sys.stderr.write(msg % (__version__, sys.version.split()[0]))
diff --git a/setup.cfg b/setup.cfg
index 941db34..f177d6f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -31,6 +31,8 @@ classifiers =
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
+ Programming Language :: Python :: 3.11
+ Programming Language :: Python :: 3.12
Environment :: Console
Intended Audience :: Developers
License :: OSI Approved :: MIT License
diff --git a/site_scons/scons_local_package.py b/site_scons/scons_local_package.py
index aaf058a..8eca758 100644
--- a/site_scons/scons_local_package.py
+++ b/site_scons/scons_local_package.py
@@ -23,22 +23,23 @@
from glob import glob
import os.path
-from zip_utils import zipit
+from zip_utils import zipit, zipappit
from Utilities import is_windows
def get_local_package_file_list():
- """
- Get list of all files which should be included in scons-local package
- """
+ """Get list of all files which should be included in scons-local package"""
+
s_files = glob("SCons/**", recursive=True)
# import pdb; pdb.set_trace()
non_test = [f for f in s_files if "Tests.py" not in f]
- non_test_non_doc = [f for f in non_test if '.xml' not in f or "SCons/Tool/docbook" in f]
+ non_test_non_doc = [
+ f for f in non_test if '.xml' not in f or "SCons/Tool/docbook" in f
+ ]
filtered_list = [f for f in non_test_non_doc if 'pyc' not in f]
- filtered_list = [f for f in filtered_list if '__pycache__' not in f ]
+ filtered_list = [f for f in filtered_list if '__pycache__' not in f]
filtered_list = [f for f in filtered_list if not os.path.isdir(f)]
return filtered_list
@@ -50,47 +51,71 @@ def install_local_package_files(env):
files = get_local_package_file_list()
target_dir = '#/build/scons-local/scons-local-$VERSION'
for f in files:
- all_local_installed.extend(env.Install(os.path.join(target_dir, os.path.dirname(f)),
- f))
-
- basedir_files = ['scripts/scons.bat',
- 'scripts/scons.py',
- 'scripts/scons-configure-cache.py',
- 'scripts/sconsign.py',
- 'bin/scons-time.py']
+ all_local_installed.extend(
+ env.Install(os.path.join(target_dir, os.path.dirname(f)), f)
+ )
+
+ basedir_files = [
+ 'scripts/scons.bat',
+ 'scripts/scons.py',
+ 'scripts/scons-configure-cache.py',
+ 'scripts/sconsign.py',
+ 'bin/scons-time.py',
+ ]
for bf in basedir_files:
fn = os.path.basename(bf)
- all_local_installed.append(env.SCons_revision('#/build/scons-local/%s'%fn, bf))
+ all_local_installed.append(
+ env.SCons_revision(f'#/build/scons-local/{fn}', bf)
+ )
# Now copy manpages into scons-local package
- built_manpage_files = env.Glob('build/doc/man/*.1')
+ built_manpage_files = env.Glob('build/doc/man/*.1')
for bmp in built_manpage_files:
fn = os.path.basename(str(bmp))
- all_local_installed.append(env.SCons_revision('#/build/scons-local/%s'%fn, bmp))
-
- rename_files = [('scons-${VERSION}.bat', 'scripts/scons.bat'),
- ('scons-README', 'README-local'),
- ('scons-LICENSE', 'LICENSE-local')]
+ all_local_installed.append(
+ env.SCons_revision(f'#/build/scons-local/{fn}', bmp)
+ )
+
+ rename_files = [
+ ('scons-${VERSION}.bat', 'scripts/scons.bat'),
+ ('scons-README', 'README-local'),
+ ('scons-LICENSE', 'LICENSE-local'),
+ ]
for t, f in rename_files:
- target_file = "#/build/scons-local/%s"%t
+ target_file = f"#/build/scons-local/{t}"
all_local_installed.append(env.SCons_revision(target_file, f))
return all_local_installed
def create_local_packages(env):
- # Add SubstFile builder
- env.Tool('textfile')
[env.Tool(x) for x in ['packaging', 'filesystem', 'zip']]
installed_files = install_local_package_files(env)
build_local_dir = 'build/scons-local'
- package = env.Command('#build/dist/scons-local-${VERSION}.zip',
- installed_files,
- zipit,
- CD=build_local_dir,
- PSV='.',
- )
+ package = env.Command(
+ '#build/dist/scons-local-${VERSION}.zip',
+ installed_files,
+ zipit,
+ CD=build_local_dir,
+ PSV='.',
+ )
+
+ do_zipapp = True # Q: maybe an external way to specify whether to build?
+ if do_zipapp:
+ # We need to descend into the versioned directory for zipapp,
+ # but we don't know the version. env.Glob lets us expand that.
+ # The action isn't going to use the sources, but including
+ # them makes sure SCons has populated the dir we're going to zip.
+ app_dir = env.Glob(f"{build_local_dir}/scons-local-*")[0]
+ zipapp = env.Command(
+ target='#build/dist/scons-local-${VERSION}.pyz',
+ source=installed_files,
+ action=zipappit,
+ CD=app_dir,
+ PSV='.',
+ entry='SCons.Script.Main:main',
+ )
if is_windows():
# avoid problem with tar interpreting c:/ as a remote machine
@@ -98,13 +123,12 @@ def create_local_packages(env):
else:
tar_cargs = '-czf'
- env.Command('#build/dist/scons-local-${VERSION}.tar.gz',
- installed_files,
- "cd %s && tar %s $( ${TARGET.abspath} $) *" % (build_local_dir, tar_cargs))
-
- print("Package:%s"%package)
-
-
-
-
+ env.Command(
+ '#build/dist/scons-local-${VERSION}.tar.gz',
+ installed_files,
+ "cd %s && tar %s $( ${TARGET.abspath} $) *" % (build_local_dir, tar_cargs),
+ )
+ print(f"Package:{package}")
+ if do_zipapp:
+ print(f"Zipapp:{zipapp}")
diff --git a/site_scons/zip_utils.py b/site_scons/zip_utils.py
index 1a0f843..a38a68f 100644
--- a/site_scons/zip_utils.py
+++ b/site_scons/zip_utils.py
@@ -1,54 +1,101 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Actions to zip and unzip, for working with SCons release bundles
+Action for creating a zipapp
+"""
+
import os.path
+import zipfile
+import zipapp
-zcat = 'gzip -d -c'
+def zipit(env, target, source):
+ """Action function to zip *source* into *target*
-#
-# Figure out if we can handle .zip files.
-#
-zipit = None
-unzipit = None
-try:
- import zipfile
-
- def zipit(env, target, source):
- print("Zipping %s:" % str(target[0]))
- def visit(arg, dirname, filenames):
- for filename in filenames:
- path = os.path.join(dirname, filename)
- if os.path.isfile(path):
- arg.write(path)
- # default ZipFile compression is ZIP_STORED
- zf = zipfile.ZipFile(str(target[0]), 'w', compression=zipfile.ZIP_DEFLATED)
- olddir = os.getcwd()
- os.chdir(env.Dir(env['CD']).abspath)
- try:
- for dirname, dirnames, filenames in os.walk(env['PSV']):
- visit(zf, dirname, filenames)
- finally:
- os.chdir(olddir)
- zf.close()
-
- def unzipit(env, target, source):
- print("Unzipping %s:" % str(source[0]))
- zf = zipfile.ZipFile(str(source[0]), 'r')
- for name in zf.namelist():
- dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
- dir = os.path.dirname(dest)
- try:
- os.makedirs(dir)
- except:
- pass
- print(dest,name)
- # if the file exists, then delete it before writing
- # to it so that we don't end up trying to write to a symlink:
- if os.path.isfile(dest) or os.path.islink(dest):
- os.unlink(dest)
- if not os.path.isdir(dest):
- with open(dest, 'wb') as fp:
- fp.write(zf.read(name))
-
-except ImportError:
- if unzip and zip:
- zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV"
- unzipit = "$UNZIP $UNZIPFLAGS $SOURCES"
+ Values extracted from *env*:
+ *CD*: the directory to work in
+ *PSV*: the directory name to walk down to find the files
+ """
+ print(f"Zipping {target[0]}:")
+
+ def visit(arg, dirname, filenames):
+ for filename in filenames:
+ path = os.path.join(dirname, filename)
+ if os.path.isfile(path):
+ arg.write(path)
+
+ # default ZipFile compression is ZIP_STORED
+ zf = zipfile.ZipFile(str(target[0]), 'w', compression=zipfile.ZIP_DEFLATED)
+ olddir = os.getcwd()
+ os.chdir(env.Dir(env['CD']).abspath)
+ try:
+ for dirname, dirnames, filenames in os.walk(env['PSV']):
+ visit(zf, dirname, filenames)
+ finally:
+ os.chdir(olddir)
+ zf.close()
+
+
+def unzipit(env, target, source):
+ """Action function to unzip *source*"""
+
+ print(f"Unzipping {source[0]}:")
+ zf = zipfile.ZipFile(str(source[0]), 'r')
+ for name in zf.namelist():
+ dest = os.path.join(env['UNPACK_ZIP_DIR'], name)
+ dir = os.path.dirname(dest)
+ os.makedirs(dir, exist_ok=True)
+ print(dest, name)
+ # if the file exists, then delete it before writing
+ # to it so that we don't end up trying to write to a symlink:
+ if os.path.isfile(dest) or os.path.islink(dest):
+ os.unlink(dest)
+ if not os.path.isdir(dest):
+ with open(dest, 'wb') as fp:
+ fp.write(zf.read(name))
+
+
+def zipappit(env, target, source):
+ """Action function to Create a zipapp *target* from specified directory.
+
+ Values extracted from *env*:
+ *CD*: the Dir node for the place we want to work.
+ *PSV*: the directory name to point :meth:`zipapp.create_archive` at
+ (note *source* is unused here in favor of PSV)
+ *main*: the entry point for the zipapp
+ """
+ print(f"Creating zipapp {target[0]}:")
+ dest = target[0].abspath
+ olddir = os.getcwd()
+ os.chdir(env['CD'].abspath)
+ try:
+ zipapp.create_archive(
+ source=env['PSV'],
+ target=dest,
+ main=env['entry'],
+ interpreter="/usr/bin/env python",
+ )
+ finally:
+ os.chdir(olddir)
diff --git a/template/RELEASE.txt b/template/RELEASE.txt
index 89d634c..439c863 100755
--- a/template/RELEASE.txt
+++ b/template/RELEASE.txt
@@ -1,10 +1,17 @@
-A new SCons release, 4.1.0, is now available
-on the SCons download page:
+If you are reading this in the git repository, the contents
+refer to *unreleased* changes since the last SCons release.
+Past official release announcements appear at:
- https://scons.org/pages/download.html
+ https://scons.org/tag/releases.html
+==================================================================
-Here is a summary of the changes since 4.1.0:
+A new SCons release, 4.4.1, is now available on the SCons download page:
+
+ https://scons.org/pages/download.html
+
+
+Here is a summary of the changes since 4.4.0:
NEW FUNCTIONALITY
-----------------
diff --git a/test/Actions/actions.py b/test/Actions/actions.py
index 3bac188..72317ca 100644
--- a/test/Actions/actions.py
+++ b/test/Actions/actions.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -40,6 +39,7 @@ sys.exit(0)
test.write('SConstruct', """
B = Builder(action = r'%(_python_)s build.py $TARGET 1 $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
""" % locals())
@@ -54,6 +54,7 @@ test.up_to_date(arguments = '.')
test.write('SConstruct', """
B = Builder(action = r'%(_python_)s build.py $TARGET 2 $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
""" % locals())
@@ -73,6 +74,7 @@ def func(env, target, source):
cp = subprocess.run(cmd, shell=True)
return cp.returncode
B = Builder(action = func)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
""" % locals())
@@ -98,6 +100,7 @@ class bld:
return self.cmd %% (' '.join(map(str, target)),
' '.join(map(str, source)))
B = Builder(action = bld())
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
""" % locals())
@@ -112,6 +115,8 @@ test.up_to_date(arguments = '.')
test.write('SConstruct', """\
def func(env, target, source):
pass
+
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(S = Action('foo'),
F = Action(func),
L = Action(['arg1', 'arg2']))
diff --git a/test/Actions/subst_shell_env-fixture/SConstruct b/test/Actions/subst_shell_env-fixture/SConstruct
index 6e48add..5ba822e 100644
--- a/test/Actions/subst_shell_env-fixture/SConstruct
+++ b/test/Actions/subst_shell_env-fixture/SConstruct
@@ -1,24 +1,36 @@
import sys
-def custom_environment_expansion(env, target, source):
- ENV = env['ENV'].copy()
- ENV['EXPANDED_SHELL_VAR'] = env.subst(env['ENV']['EXPANDED_SHELL_VAR'], target=target, source=source)
+def custom_environment_expansion1(env, target, source, shell_env):
+ ENV = shell_env.copy()
+ ENV['EXPANDED_SHELL_VAR1'] = env.subst(env['ENV']['EXPANDED_SHELL_VAR1'], target=target, source=source)
+ return ENV
+
+def custom_environment_expansion2(env, target, source, shell_env):
+ ENV = shell_env.copy()
+ ENV['EXPANDED_SHELL_VAR2'] = env.subst(env['ENV']['EXPANDED_SHELL_VAR2'], target=target, source=source)
return ENV
def expand_this_generator(env, target, source, for_signature):
return "I_got_expanded_to_" + str(target[0])
+def expand_that_generator(env, target, source, for_signature):
+ return str(target[0]) + "_is_from_expansion"
+
env = Environment(tools=['textfile'])
-env['SHELL_ENV_GENERATOR'] = custom_environment_expansion
+env['SHELL_ENV_GENERATORS'] = [custom_environment_expansion1, custom_environment_expansion2]
env['EXPAND_THIS'] = expand_this_generator
-env['ENV']['EXPANDED_SHELL_VAR'] = "$EXPAND_THIS"
+env['EXPAND_THAT'] = expand_that_generator
+
+env['ENV']['EXPANDED_SHELL_VAR1'] = "$EXPAND_THIS"
+env['ENV']['EXPANDED_SHELL_VAR2'] = "$EXPAND_THAT"
env['ENV']['NON_EXPANDED_SHELL_VAR'] = "$EXPAND_THIS"
env.Textfile('expand_script.py', [
'import os',
- 'print(os.environ["EXPANDED_SHELL_VAR"])',
+ 'print(os.environ["EXPANDED_SHELL_VAR1"])',
+ 'print(os.environ["EXPANDED_SHELL_VAR2"])',
'print(os.environ["NON_EXPANDED_SHELL_VAR"])',
])
env.Command('out.txt', 'expand_script.py', fr'{sys.executable} $SOURCE > $TARGET')
diff --git a/test/Actions/subst_shell_env.py b/test/Actions/subst_shell_env.py
index 9f5c5db..02ba640 100644
--- a/test/Actions/subst_shell_env.py
+++ b/test/Actions/subst_shell_env.py
@@ -25,7 +25,7 @@
"""
Verify that shell environment variables can be expanded per target/source
-when exectuting actions on the command line.
+when executing actions on the command line.
"""
import os
@@ -36,8 +36,8 @@ test = TestSCons.TestSCons()
test.dir_fixture('subst_shell_env-fixture')
test.run(arguments = ['-Q'])
-test.must_match('out.txt', f"I_got_expanded_to_out.txt{os.linesep}$EXPAND_THIS{os.linesep}")
-test.must_match('out2.txt', f"I_got_expanded_to_out2.txt{os.linesep}$EXPAND_THIS{os.linesep}")
+test.must_match('out.txt', f"I_got_expanded_to_out.txt{os.linesep}out.txt_is_from_expansion{os.linesep}$EXPAND_THIS{os.linesep}")
+test.must_match('out2.txt', f"I_got_expanded_to_out2.txt{os.linesep}out2.txt_is_from_expansion{os.linesep}$EXPAND_THIS{os.linesep}")
test.pass_test()
diff --git a/test/Alias/Alias.py b/test/Alias/Alias.py
index c371625..1bb52f6 100644
--- a/test/Alias/Alias.py
+++ b/test/Alias/Alias.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -44,6 +43,7 @@ sys.exit(0)
test.write('SConstruct', """
B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env['BUILDERS']['B'] = B
env.B(target = 'f1.out', source = 'f1.in')
@@ -136,6 +136,7 @@ test.up_to_date(arguments = 'f1.out')
test.write('SConstruct', """
Decider('content')
B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env['BUILDERS']['B'] = B
env.B(target = 'f1.out', source = 'f1.in')
diff --git a/test/Alias/Depends.py b/test/Alias/Depends.py
index 715374c..b1051ad 100644
--- a/test/Alias/Depends.py
+++ b/test/Alias/Depends.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -44,6 +43,7 @@ sys.exit(0)
test.write('SConstruct', """
B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env['BUILDERS']['B'] = B
env.B(target = 'f1.out', source = 'f1.in')
diff --git a/test/Alias/action.py b/test/Alias/action.py
index def27a7..8212448 100644
--- a/test/Alias/action.py
+++ b/test/Alias/action.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,14 +22,11 @@
# 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.
-#
"""
Test that Aliases with actions work.
"""
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
import TestSCons
test = TestSCons.TestSCons()
@@ -52,6 +51,7 @@ def bar(target, source, env):
with open('bar', 'wb') as f:
f.write(bytearray("bar(%s, %s)\\n" % (target, source),'utf-8'))
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = {'Cat':Builder(action=cat)})
env.Alias(target = ['build-f1'], source = 'f1.out', action = foo)
f1 = env.Cat('f1.out', 'f1.in')
diff --git a/test/Batch/changed_sources_main.cpp b/test/Batch/changed_sources_main.cpp
index 038fea3..810625a 100644
--- a/test/Batch/changed_sources_main.cpp
+++ b/test/Batch/changed_sources_main.cpp
@@ -1,7 +1,7 @@
-
-#include <iostream>
-
-int main()
-{
- std::cout << "Hello, world!\n";
-}
+
+#include <iostream>
+
+int main()
+{
+ std::cout << "Hello, world!\n";
+}
diff --git a/test/CPPPATH/CPPPATH.py b/test/CPPPATH/CPPPATH.py
index efa64d2..73e0e09 100644
--- a/test/CPPPATH/CPPPATH.py
+++ b/test/CPPPATH/CPPPATH.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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.path
@@ -45,6 +44,7 @@ test.subdir('foobar',
'inc2')
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(CPPPATH = ['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
FOO='include')
obj = env.Object(target='foobar/prog', source='subdir/prog.c')
@@ -246,6 +246,7 @@ test.up_to_date(arguments = args)
# Change CPPPATH and make sure we don't rebuild because of it.
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(CPPPATH = Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'))
obj = env.Object(target='foobar/prog', source='subdir/prog.c')
env.Program(target='prog', source=obj)
diff --git a/test/CacheDir/option--cs.py b/test/CacheDir/option--cs.py
index a2b09ed..c3e5e5b 100644
--- a/test/CacheDir/option--cs.py
+++ b/test/CacheDir/option--cs.py
@@ -54,7 +54,6 @@ with open(sys.argv[1], 'w') as f:
cache = test.workpath('cache')
test.write(['src1', 'SConstruct'], """
-DefaultEnvironment(tools=[])
def cat(env, source, target):
target = str(target[0])
with open('cat.out', 'a') as f:
@@ -63,6 +62,8 @@ def cat(env, source, target):
for src in source:
with open(str(src), "r") as f2:
f.write(f2.read())
+
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools=[],
BUILDERS={'Internal':Builder(action=cat),
'External':Builder(action=r'%(_python_)s build.py $TARGET $SOURCES')})
@@ -156,6 +157,7 @@ hello_obj = 'hello' + _obj
src2_hello = test.workpath('src2', hello_exe)
test.write(['src2', 'SConstruct'], """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env.Program('hello.c')
CacheDir(r'%s')
diff --git a/test/CacheDir/timestamp-match.py b/test/CacheDir/timestamp-match.py
index 4b64137..fd9c659 100644
--- a/test/CacheDir/timestamp-match.py
+++ b/test/CacheDir/timestamp-match.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 CAcheDir() works when using 'timestamp-match' decisions.
@@ -41,21 +40,15 @@ Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE'))
test.write('file.in', "file.in\n")
-test.run(arguments = '--cache-show --debug=explain .')
-
+test.run(arguments='--cache-show --debug=explain .')
test.must_match('file.out', "file.in\n")
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('file.in')
-
-test.not_up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
+test.not_up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
test.pass_test()
diff --git a/test/CacheDir/timestamp-newer.py b/test/CacheDir/timestamp-newer.py
index 618f467..567078e 100644
--- a/test/CacheDir/timestamp-newer.py
+++ b/test/CacheDir/timestamp-newer.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 CAcheDir() works when using 'timestamp-newer' decisions.
@@ -41,21 +40,16 @@ Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE'))
test.write('file.in', "file.in\n")
-test.run(arguments = '--cache-show --debug=explain .')
-
+test.run(arguments='--cache-show --debug=explain .')
test.must_match('file.out', "file.in\n")
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('file.in')
-test.not_up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
-
-test.up_to_date(options = '--cache-show --debug=explain', arguments = '.')
+test.not_up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
+test.up_to_date(options='--cache-show --debug=explain', arguments='.')
test.pass_test()
diff --git a/test/Chmod.py b/test/Chmod.py
index 64f4ed9..7af95b4 100644
--- a/test/Chmod.py
+++ b/test/Chmod.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the Chmod() Action works.
@@ -43,26 +42,36 @@ Execute(Chmod('f1', 0o666))
Execute(Chmod(('f1-File'), 0o666))
Execute(Chmod('d2', 0o777))
Execute(Chmod(Dir('d2-Dir'), 0o777))
+
def cat(env, source, target):
target = str(target[0])
with open(target, "wb") as f:
for src in source:
with open(str(src), "rb") as infp:
f.write(infp.read())
+
Cat = Action(cat)
env = Environment()
-env.Command('bar.out', 'bar.in', [Cat,
- Chmod("f3", 0o666),
- Chmod("d4", 0o777)])
-env = Environment(FILE = 'f5')
+env.Command(
+ 'bar.out',
+ 'bar.in',
+ [Cat, Chmod("f3", 0o666), Chmod("d4", 0o777)],
+)
+env = Environment(FILE='f5')
env.Command('f6.out', 'f6.in', [Chmod('$FILE', 0o666), Cat])
-env.Command('f7.out', 'f7.in', [Cat,
- Chmod('Chmod-$SOURCE', 0o666),
- Chmod('${TARGET}-Chmod', 0o666)])
+env.Command(
+ 'f7.out',
+ 'f7.in',
+ [Cat, Chmod('Chmod-$SOURCE', 0o666), Chmod('${TARGET}-Chmod', 0o666)],
+)
# Make sure Chmod works with a list of arguments
-env = Environment(FILE = 'f9')
-env.Command('f8.out', 'f8.in', [Chmod(['$FILE', File('f10')], 0o666), Cat])
+env = Environment(FILE='f9')
+env.Command(
+ 'f8.out',
+ 'f8.in',
+ [Chmod(['$FILE', File('f10')], 0o666), Cat],
+)
Execute(Chmod(['d11', Dir('d12')], 0o777))
Execute(Chmod('f13', "a=r"))
Execute(Chmod('f14', "ogu+w"))
@@ -117,28 +126,30 @@ os.chmod(test.workpath('f15'), 0o444)
os.chmod(test.workpath('d16'), 0o555)
os.chmod(test.workpath('d17'), 0o555)
os.chmod(test.workpath('d18'), 0o555)
-expect = test.wrap_stdout(read_str = """\
-Chmod("f1", 0666)
-Chmod("f1-File", 0666)
-Chmod("d2", 0777)
-Chmod("d2-Dir", 0777)
-Chmod(["d11", "d12"], 0777)
+
+expect = test.wrap_stdout(
+ read_str = """\
+Chmod("f1", 0o666)
+Chmod("f1-File", 0o666)
+Chmod("d2", 0o777)
+Chmod("d2-Dir", 0o777)
+Chmod(["d11", "d12"], 0o777)
Chmod("f13", "a=r")
Chmod("f14", "ogu+w")
Chmod("f15", "ug=rw, go+ rw")
Chmod("d16", "0777")
Chmod(["d17", "d18"], "ogu = rwx")
""",
- build_str = """\
+ build_str = """\
cat(["bar.out"], ["bar.in"])
-Chmod("f3", 0666)
-Chmod("d4", 0777)
-Chmod("f5", 0666)
+Chmod("f3", 0o666)
+Chmod("d4", 0o777)
+Chmod("f5", 0o666)
cat(["f6.out"], ["f6.in"])
cat(["f7.out"], ["f7.in"])
-Chmod("Chmod-f7.in", 0666)
-Chmod("f7.out-Chmod", 0666)
-Chmod(["f9", "f10"], 0666)
+Chmod("Chmod-f7.in", 0o666)
+Chmod("f7.out-Chmod", 0o666)
+Chmod(["f9", "f10"], 0o666)
cat(["f8.out"], ["f8.in"])
""")
test.run(options = '-n', arguments = '.', stdout = expect)
diff --git a/test/CompilationDatabase/TEMPFILE.py b/test/CompilationDatabase/TEMPFILE.py
new file mode 100644
index 0000000..45e4521
--- /dev/null
+++ b/test/CompilationDatabase/TEMPFILE.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test that CompilationDatabase works when TEMPFILE is being used to handle long
+commandlines for compilers/linkers/etc
+"""
+
+import sys
+import os
+import os.path
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.file_fixture('mygcc.py')
+test.file_fixture('test_main.c')
+test.file_fixture('fixture/SConstruct_tempfile', 'SConstruct')
+
+test.run()
+
+rel_files = [
+ 'compile_commands_only_arg.json',
+]
+
+example_rel_file = """[
+ {
+ "command": "%s mygcc.py cc -o test_main.o -c test_main.c",
+ "directory": "%s",
+ "file": "test_main.c",
+ "output": "test_main.o"
+ }
+]
+""" % (sys.executable, test.workdir)
+
+if sys.platform == 'win32':
+ example_rel_file = example_rel_file.replace('\\', '\\\\')
+
+for f in rel_files:
+ # print("Checking:%s" % f)
+ test.must_exist(f)
+ test.must_match(f, example_rel_file, mode='r')
+
+test.pass_test()
diff --git a/test/CompilationDatabase/basic.py b/test/CompilationDatabase/basic.py
index 2f76752..6bf0d1a 100644
--- a/test/CompilationDatabase/basic.py
+++ b/test/CompilationDatabase/basic.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,7 +22,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.
-#
+
"""
Test CompilationDatabase and several variations of ways to call it
and values of COMPILATIONDB_USE_ABSPATH
@@ -46,7 +48,7 @@ rel_files = [
'compile_commands_target.json',
'compile_commands.json',
'compile_commands_over_rel.json',
- 'compile_commands_over_abs_0.json'
+ 'compile_commands_over_abs_0.json',
]
abs_files = [
@@ -63,7 +65,8 @@ example_rel_file = """[
"file": "test_main.c",
"output": "test_main.o"
}
-]""" % (sys.executable, test.workdir)
+]
+""" % (sys.executable, test.workdir)
if sys.platform == 'win32':
example_rel_file = example_rel_file.replace('\\', '\\\\')
@@ -80,7 +83,8 @@ example_abs_file = """[
"file": "%s",
"output": "%s"
}
-]""" % (sys.executable, test.workdir, os.path.join(test.workdir, 'test_main.c'), os.path.join(test.workdir, 'test_main.o'))
+]
+""" % (sys.executable, test.workdir, os.path.join(test.workdir, 'test_main.c'), os.path.join(test.workdir, 'test_main.o'))
if sys.platform == 'win32':
example_abs_file = example_abs_file.replace('\\', '\\\\')
diff --git a/test/CompilationDatabase/fixture/SConstruct b/test/CompilationDatabase/fixture/SConstruct
index ea23bc3..bd2f780 100644
--- a/test/CompilationDatabase/fixture/SConstruct
+++ b/test/CompilationDatabase/fixture/SConstruct
@@ -7,7 +7,7 @@ env = Environment(
LINKFLAGS=[],
CC='$PYTHON mygcc.py cc',
CXX='$PYTHON mygcc.py c++',
- tools=['gcc','g++','gnulink'],
+ tools=['gcc', 'g++', 'gnulink'],
)
env.Tool('compilation_db')
diff --git a/test/CompilationDatabase/fixture/SConstruct_tempfile b/test/CompilationDatabase/fixture/SConstruct_tempfile
new file mode 100644
index 0000000..6b95977
--- /dev/null
+++ b/test/CompilationDatabase/fixture/SConstruct_tempfile
@@ -0,0 +1,29 @@
+import sys
+
+DefaultEnvironment(tools=[])
+env = Environment(
+ PYTHON=sys.executable,
+ LINK='$PYTHON mylink.py',
+ LINKFLAGS=[],
+ CC='$PYTHON mygcc.py cc',
+ tools=['gcc'],
+ MAXLINELENGTH=10,
+ OBJSUFFIX='.o',
+)
+
+# make sure TempFileMunge is used
+if 'TEMPFILE' not in env['CCCOM']:
+ env['CCCOM'] = '${TEMPFILE("%s")}' % (env['CCCOM'])
+
+env.Tool('compilation_db')
+
+outputs = []
+
+# Should be relative paths
+outputs += env.CompilationDatabase('compile_commands_only_arg.json')
+
+env.Object('test_main.c')
+
+# Prevent actual call of $PYTHON @tempfile since "mygcc.py cc ..." is not a proper python statement
+# Interesting outputs are json databases
+env.Default(outputs)
diff --git a/test/CompilationDatabase/variant_dir.py b/test/CompilationDatabase/variant_dir.py
index 6a2675b..21eb78b 100644
--- a/test/CompilationDatabase/variant_dir.py
+++ b/test/CompilationDatabase/variant_dir.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,7 +22,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.
-#
+
"""
Test CompilationDatabase and several variations of ways to call it
and values of COMPILATIONDB_USE_ABSPATH
@@ -77,7 +79,8 @@ example_rel_file = """[
"file": "%(src_file)s",
"output": "%(output2_file)s"
}
-]""" % {'exe': sys.executable,
+]
+""" % {'exe': sys.executable,
'workdir': test.workdir,
'src_file': os.path.join('src', 'test_main.c'),
'output_file': os.path.join('build', 'test_main.o'),
@@ -106,7 +109,8 @@ example_abs_file = """[
"file": "%(abs_src_file)s",
"output": "%(abs_output2_file)s"
}
-]""" % {'exe': sys.executable,
+]
+""" % {'exe': sys.executable,
'workdir': test.workdir,
'src_file': os.path.join('src', 'test_main.c'),
'abs_src_file': os.path.join(test.workdir, 'src', 'test_main.c'),
@@ -130,7 +134,8 @@ example_filter_build_file = """[
"file": "%(src_file)s",
"output": "%(output_file)s"
}
-]""" % {'exe': sys.executable,
+]
+""" % {'exe': sys.executable,
'workdir': test.workdir,
'src_file': os.path.join('src', 'test_main.c'),
'output_file': os.path.join('build', 'test_main.o'),
@@ -151,7 +156,8 @@ example_filter_build2_file = """[
"file": "%(src_file)s",
"output": "%(output2_file)s"
}
-]""" % {'exe': sys.executable,
+]
+""" % {'exe': sys.executable,
'workdir': test.workdir,
'src_file': os.path.join('src', 'test_main.c'),
'output2_file': os.path.join('build2', 'test_main.o'),
diff --git a/test/Configure/issue-3469/fixture/SConstruct b/test/Configure/issue-3469/fixture/SConstruct
index 4b5bedc..ae1fb30 100644
--- a/test/Configure/issue-3469/fixture/SConstruct
+++ b/test/Configure/issue-3469/fixture/SConstruct
@@ -1,3 +1,7 @@
+# SPDX-License-Identifier: MIT
+#
+# Copyright The SCons Foundation
+
"""
This tests if we add/remove a test in between other tests if a rerun will properly cache the results.
Github issue #3469
@@ -6,7 +10,7 @@ Github issue #3469
DefaultEnvironment(tools=[])
vars = Variables()
-vars.Add(BoolVariable('SKIP', 'Skip Middle Conf test', 0))
+vars.Add(BoolVariable('SKIP', 'Skip Middle Conf test', False))
env = Environment(variables=vars)
conf = Configure(env)
diff --git a/test/Copy-Action.py b/test/Copy-Action.py
index 4bfa0da..1b1356e 100644
--- a/test/Copy-Action.py
+++ b/test/Copy-Action.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the Copy() Action works, and preserves file modification
@@ -37,29 +36,36 @@ import TestSCons
test = TestSCons.TestSCons()
-test.write('SConstruct', """
+test.write('SConstruct', """\
Execute(Copy('f1.out', 'f1.in'))
Execute(Copy(File('d2.out'), 'd2.in'))
Execute(Copy('d3.out', File('f3.in')))
+# Issue #3009: make sure it's not mangled if src is a list.
+# make sure both list-of-str and list-of-Node work
+Execute(Copy('d7.out', ['f10.in', 'f11.in']))
+Execute(Copy('d7.out', Glob('f?.in')))
+
def cat(env, source, target):
target = str(target[0])
with open(target, "w") as f:
for src in source:
with open(str(src), "r") as ifp:
f.write(ifp.read())
+
Cat = Action(cat)
env = Environment()
-env.Command('bar.out', 'bar.in', [Cat,
- Copy("f4.out", "f4.in"),
- Copy("d5.out", "d5.in"),
- Copy("d6.out", "f6.in")])
-env = Environment(OUTPUT = 'f7.out', INPUT = 'f7.in')
+env.Command(
+ 'bar.out',
+ 'bar.in',
+ [Cat, Copy("f4.out", "f4.in"), Copy("d5.out", "d5.in"), Copy("d6.out", "f6.in")],
+)
+env = Environment(OUTPUT='f7.out', INPUT='f7.in')
env.Command('f8.out', 'f8.in', [Copy('$OUTPUT', '$INPUT'), Cat])
env.Command('f9.out', 'f9.in', [Cat, Copy('${TARGET}-Copy', '$SOURCE')])
-env.CopyTo( 'd4', 'f10.in' )
-env.CopyAs( 'd4/f11.out', 'f11.in')
-env.CopyAs( 'd4/f12.out', 'd5/f12.in')
+env.CopyTo('d4', 'f10.in')
+env.CopyAs('d4/f11.out', 'f11.in')
+env.CopyAs('d4/f12.out', 'd5/f12.in')
env.Command('f 13.out', 'f 13.in', Copy('$TARGET', '$SOURCE'))
""")
@@ -69,6 +75,7 @@ test.subdir('d2.in')
test.write(['d2.in', 'file'], "d2.in/file\n")
test.write('f3.in', "f3.in\n")
test.subdir('d3.out')
+test.subdir('d7.out')
test.write('bar.in', "bar.in\n")
test.write('f4.in', "f4.in\n")
test.subdir('d5.in')
@@ -87,19 +94,22 @@ test.write('f 13.in', "f 13.in\n")
os.chmod('f1.in', 0o646)
os.chmod('f4.in', 0o644)
-test.sleep()
+test.sleep() # delay for timestamps
d4_f10_in = os.path.join('d4', 'f10.in')
d4_f11_out = os.path.join('d4', 'f11.out')
d4_f12_out = os.path.join('d4', 'f12.out')
d5_f12_in = os.path.join('d5', 'f12.in')
-expect = test.wrap_stdout(read_str = """\
+expect = test.wrap_stdout(
+ read_str="""\
Copy("f1.out", "f1.in")
Copy("d2.out", "d2.in")
Copy("d3.out", "f3.in")
+Copy("d7.out", ["f10.in", "f11.in"])
+Copy("d7.out", ["f1.in", "f3.in", "f4.in", "f6.in", "f7.in", "f8.in", "f9.in"])
""",
- build_str = """\
+ build_str="""\
cat(["bar.out"], ["bar.in"])
Copy("f4.out", "f4.in")
Copy("d5.out", "d5.in")
@@ -112,13 +122,16 @@ Copy("f7.out", "f7.in")
cat(["f8.out"], ["f8.in"])
cat(["f9.out"], ["f9.in"])
Copy("f9.out-Copy", "f9.in")
-""" % locals())
+""" % locals(),
+)
-test.run(options = '-n', arguments = '.', stdout = expect)
+test.run(options='-n', arguments='.', stdout=expect)
test.must_not_exist('f1.out')
test.must_not_exist('d2.out')
test.must_not_exist(os.path.join('d3.out', 'f3.in'))
+test.must_not_exist(os.path.join('d7.out', 'f7.in'))
+test.must_not_exist(os.path.join('d7.out', 'f11.in'))
test.must_not_exist('f4.out')
test.must_not_exist('d5.out')
test.must_not_exist(os.path.join('d6.out', 'f6.in'))
@@ -137,6 +150,8 @@ test.run()
test.must_match('f1.out', "f1.in\n", mode='r')
test.must_match(['d2.out', 'file'], "d2.in/file\n", mode='r')
test.must_match(['d3.out', 'f3.in'], "f3.in\n", mode='r')
+test.must_match(['d7.out', 'f7.in'], "f7.in\n", mode='r')
+test.must_match(['d7.out', 'f11.in'], "f11.in\n", mode='r')
test.must_match('f4.out', "f4.in\n", mode='r')
test.must_match(['d5.out', 'file'], "d5.in/file\n", mode='r')
test.must_match(['d6.out', 'f6.in'], "f6.in\n", mode='r')
@@ -162,23 +177,21 @@ def must_be_same(f1, f2):
for value in ['ST_MODE', 'ST_MTIME']:
v = getattr(stat, value)
if s1[v] != s2[v]:
- msg = '%s[%s] %s != %s[%s] %s\n' % \
- (repr(f1), value, s1[v],
- repr(f2), value, s2[v],)
+ msg = f"{f1!r}[{value}] {s1[v]} != {f2!r}[{value}] {s2[v]}\n"
sys.stderr.write(msg)
- errors = errors + 1
-
-must_be_same('f1.out', 'f1.in')
-must_be_same(['d2.out', 'file'], ['d2.in', 'file'])
-must_be_same(['d3.out', 'f3.in'], 'f3.in')
-must_be_same('f4.out', 'f4.in')
-must_be_same(['d5.out', 'file'], ['d5.in', 'file'])
-must_be_same(['d6.out', 'f6.in'], 'f6.in')
-must_be_same('f7.out', 'f7.in')
-must_be_same(['d4', 'f10.in'], 'f10.in')
-must_be_same(['d4', 'f11.out'], 'f11.in')
-must_be_same(['d4', 'f12.out'], ['d5', 'f12.in'])
-must_be_same('f 13.out', 'f 13.in')
+ errors += 1
+
+must_be_same('f1.out', 'f1.in')
+must_be_same(['d2.out', 'file'], ['d2.in', 'file'])
+must_be_same(['d3.out', 'f3.in'], 'f3.in')
+must_be_same('f4.out', 'f4.in')
+must_be_same(['d5.out', 'file'], ['d5.in', 'file'])
+must_be_same(['d6.out', 'f6.in'], 'f6.in')
+must_be_same('f7.out', 'f7.in')
+must_be_same(['d4', 'f10.in'], 'f10.in')
+must_be_same(['d4', 'f11.out'], 'f11.in')
+must_be_same(['d4', 'f12.out'], ['d5', 'f12.in'])
+must_be_same('f 13.out', 'f 13.in')
if errors:
test.fail_test()
diff --git a/test/Copy-Option.py b/test/Copy-Option.py
index f06c3b0..170dbbe 100644
--- a/test/Copy-Option.py
+++ b/test/Copy-Option.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 setting Variables in an Environment doesn't prevent the
@@ -37,20 +36,22 @@ test.write('SConstruct', """
gpib_options = ['NI_GPIB', 'NI_ENET']
gpib_include = '/'
-#0.96 broke copying ListVariables ???
+# 0.96 broke copying ListVariables ???
opts = Variables('config.py', ARGUMENTS)
opts.AddVariables(
- BoolVariable('gpib', 'enable gpib support', 1),
- ListVariable('gpib_options',
+ BoolVariable('gpib', 'enable gpib support', True),
+ ListVariable(
+ 'gpib_options',
'whether and what kind of gpib support shall be enabled',
'all',
- gpib_options),
- )
-env = Environment(options = opts, CPPPATH = ['#/'])
-new_env=env.Clone()
+ gpib_options,
+ ),
+)
+env = Environment(options=opts, CPPPATH=['#/'])
+new_env = env.Clone()
""")
-test.run(arguments = '.')
+test.run(arguments='.')
test.pass_test()
diff --git a/test/Decider/MD5-timestamp-Repository.py b/test/Decider/MD5-timestamp-Repository.py
index 1826f68..201bdfe 100644
--- a/test/Decider/MD5-timestamp-Repository.py
+++ b/test/Decider/MD5-timestamp-Repository.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 behavior of the MD5-timestamp Decider() setting when combined with Repository() usage
@@ -38,13 +37,11 @@ test = TestSCons.TestSCons()
test.subdir('Repository', 'work')
repository = test.workpath('Repository')
-
test.write(['Repository','content1.in'], "content1.in 1\n")
test.write(['Repository','content2.in'], "content2.in 1\n")
test.write(['Repository','content3.in'], "content3.in 1\n")
# test.writable('Repository', 0)
-
test.write(['work','SConstruct'], """\
Repository(r'%s')
DefaultEnvironment(tools=[])
@@ -53,18 +50,14 @@ m.Decider('MD5-timestamp')
m.Command('content1.out', 'content1.in', Copy('$TARGET', '$SOURCE'))
m.Command('content2.out', 'content2.in', Copy('$TARGET', '$SOURCE'))
m.Command('content3.out', 'content3.in', Copy('$TARGET', '$SOURCE'))
-"""%repository)
+""" % repository)
test.run(chdir='work',arguments='.')
-
test.up_to_date(chdir='work',arguments='.')
-test.sleep()
-
+test.sleep() # delay for timestamps
test.write(['Repository','content1.in'], "content1.in 2\n")
-
test.touch(['Repository','content2.in'])
-
time_content = os.stat(os.path.join(repository,'content3.in'))[stat.ST_MTIME]
test.write(['Repository','content3.in'], "content3.in 2\n")
test.touch(['Repository','content3.in'], time_content)
@@ -76,10 +69,9 @@ test.touch(['Repository','content3.in'], time_content)
expect = test.wrap_stdout("""\
Copy("content1.out", "%s")
-"""%os.path.join(repository,'content1.in'))
+""" % os.path.join(repository, 'content1.in'))
test.run(chdir='work', arguments='.', stdout=expect)
-
test.up_to_date(chdir='work', arguments='.')
test.pass_test()
diff --git a/test/Decider/MD5-timestamp.py b/test/Decider/MD5-timestamp.py
index 6fcdb42..3815639 100644
--- a/test/Decider/MD5-timestamp.py
+++ b/test/Decider/MD5-timestamp.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 behavior of the MD5-timestamp Decider() setting.
@@ -49,15 +48,10 @@ test.write('content2.in', "content2.in 1\n")
test.write('content3.in', "content3.in 1\n")
test.run(arguments = '.')
-
test.up_to_date(arguments = '.')
-
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.write('content1.in', "content1.in 2\n")
-
test.touch('content2.in')
time_content = os.stat('content3.in')[stat.ST_MTIME]
@@ -73,11 +67,8 @@ expect = test.wrap_stdout("""\
Copy("content1.out", "content1.in")
""")
-test.run(arguments = '.', stdout=expect)
-
-test.up_to_date(arguments = '.')
-
-
+test.run(arguments='.', stdout=expect)
+test.up_to_date(arguments='.')
test.pass_test()
diff --git a/test/Decider/timestamp.py b/test/Decider/timestamp.py
index e528d77..d713a62 100644
--- a/test/Decider/timestamp.py
+++ b/test/Decider/timestamp.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 various interactions of the timestamp-match and timestamp-newer
@@ -54,27 +53,21 @@ test.write('newer1.in', "newer1.in\n")
test.write('newer2.in', "newer2.in\n")
test.run(arguments = '.')
-
test.up_to_date(arguments = '.')
time_match = os.stat('match2.out')[stat.ST_MTIME]
time_newer = os.stat('newer2.out')[stat.ST_MTIME]
-
-
# Now make all the source files newer than (different timestamps from)
# the last time the targets were built, and touch the target files
# of match1.out and newer1.out to see the different effects.
-
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('match1.in')
test.touch('newer1.in')
test.touch('match2.in')
test.touch('newer2.in')
-test.sleep()
-
+test.sleep() # delay for timestamps
test.touch('match1.out')
test.touch('newer1.out')
@@ -90,7 +83,7 @@ Copy("match2.out", "match2.in")
Copy("newer2.out", "newer2.in")
""")
-test.run(arguments = '.', stdout=expect)
+test.run(arguments='.', stdout=expect)
# Now, for the somewhat pathological case, reset the match2.out and
# newer2.out timestamps to the older timestamp when the targets were
@@ -107,9 +100,7 @@ expect = test.wrap_stdout("""\
Copy("newer2.out", "newer2.in")
""")
-test.run(arguments = '.', stdout=expect)
-
-
+test.run(arguments='.', stdout=expect)
test.pass_test()
diff --git a/test/Default.py b/test/Default.py
index b9896f8..f3fe533 100644
--- a/test/Default.py
+++ b/test/Default.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 various combinations of arguments to Default() work properly.
@@ -50,6 +49,7 @@ with open(sys.argv[1], 'w') as f, open(sys.argv[2], 'r') as ifp:
#
test.write(['one', 'SConstruct'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
@@ -58,6 +58,7 @@ Default('foo.out')
test.write(['two', 'SConstruct'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
@@ -66,6 +67,7 @@ Default('foo.out', 'bar.out')
test.write(['three', 'SConstruct'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
@@ -74,6 +76,7 @@ Default(Split('foo.out bar.out'))
test.write(['four', 'SConstruct'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = ['foo bar'], source = 'foo.in')
env.B(target = 'foo', source = 'foo.in')
@@ -83,6 +86,7 @@ Default(['foo bar'])
test.write(['five', 'SConstruct'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
Default(env.B(target = 'foo.out', source = 'foo.in'))
Default(env.B(target = 'bar.out', source = 'bar.in'))
@@ -112,6 +116,7 @@ test.must_match(test.workpath('five', 'bar.out'), "five/bar.in\n", mode='r')
# Test how a None Default() argument works to disable/reset default targets.
test.write(['six', 'SConstruct'], """\
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
foo = env.B(target = 'foo.out', source = 'foo.in')
bar = env.B(target = 'bar.out', source = 'bar.in')
@@ -123,6 +128,7 @@ test.run(chdir='six', status=2,
test.write(['seven', 'SConstruct'], """\
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
foo = env.B(target = 'foo.out', source = 'foo.in')
bar = env.B(target = 'bar.out', source = 'bar.in')
@@ -134,6 +140,7 @@ test.run(chdir='seven', status=2,
test.write(['eight', 'SConstruct'], """\
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
foo = env.B(target = 'foo.out', source = 'foo.in')
bar = env.B(target = 'bar.out', source = 'bar.in')
@@ -150,6 +157,7 @@ test.subdir('nine', ['nine', 'sub1'])
test.write(['nine', 'SConstruct'], """\
B = Builder(action = r'%(_python_)s build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'xxx.out', source = 'xxx.in')
SConscript('sub1/SConscript')
@@ -159,6 +167,7 @@ test.write(['nine', 'xxx.in'], "xxx.in\n")
test.write(['nine', 'sub1', 'SConscript'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'xxx.out', source = 'xxx.in')
Default('xxx.out')
@@ -177,6 +186,7 @@ test.subdir('ten', ['ten', 'sub2'])
test.write(['ten', 'SConstruct'], """\
Default('sub2')
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'xxx.out', source = 'xxx.in')
SConscript('sub2/SConscript')
@@ -186,6 +196,7 @@ test.write(['ten', 'xxx.in'], "xxx.in\n")
test.write(['ten', 'sub2', 'SConscript'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
env.B(target = 'xxx.out', source = 'xxx.in')
""" % locals())
@@ -202,6 +213,7 @@ test.subdir('eleven')
test.write(['eleven', 'SConstruct'], """
B = Builder(action = r'%(_python_)s ../build.py $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo.out')
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
diff --git a/test/Dir/PyPackageDir/PyPackageDir.py b/test/Dir/PyPackageDir/PyPackageDir.py
index ca3bd01..b430ea0 100644
--- a/test/Dir/PyPackageDir/PyPackageDir.py
+++ b/test/Dir/PyPackageDir/PyPackageDir.py
@@ -1,54 +1,54 @@
-#!/usr/bin/env python
-#
-# MIT License
-#
-# Copyright The SCons Foundation
-#
-# 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.
-
-import TestSCons
-
-test = TestSCons.TestSCons()
-
-test.dir_fixture('image')
-
-test.run(arguments = '.', stdout = """\
-scons: Reading SConscript files ...
-Test identification of directory for a given python package
-testmod1
-.
-submod1
-submod1/submod2
-Test parameter substitution
-submod1/submod2
-submod1/submod2
-scons: done reading SConscript files.
-scons: Building targets ...
-scons: `.' is up to date.
-scons: done building targets.
-""")
-
-test.pass_test()
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+
+test.run(arguments = '.', stdout = """\
+scons: Reading SConscript files ...
+Test identification of directory for a given python package
+testmod1
+.
+submod1
+submod1/submod2
+Test parameter substitution
+submod1/submod2
+submod1/submod2
+scons: done reading SConscript files.
+scons: Building targets ...
+scons: `.' is up to date.
+scons: done building targets.
+""")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/Dir/PyPackageDir/image/SConstruct b/test/Dir/PyPackageDir/image/SConstruct
index 7e841ac..aceb245 100644
--- a/test/Dir/PyPackageDir/image/SConstruct
+++ b/test/Dir/PyPackageDir/image/SConstruct
@@ -1,31 +1,31 @@
-import sys, os
-
-oldsyspath = sys.path
-dir_path = Dir('.').srcnode().abspath
-dir_path = os.path.join(dir_path, 'syspath')
-sys.path.append(dir_path)
-
-def TestPyPackageDir(env, modname):
- packagepath = env.PyPackageDir(modname).abspath
- # Convert from an absolute path back to a relative one for testing
- commonprefix = os.path.commonprefix([dir_path, packagepath])
- relpath = os.path.relpath(packagepath, commonprefix)
- relpath = relpath.replace(os.sep, '/')
- print(relpath)
-
-DefaultEnvironment(tools=[])
-
-print("Test identification of directory for a given python package")
-env = Environment(tools=[])
-TestPyPackageDir(env, 'testmod1')
-TestPyPackageDir(env, 'testmod2')
-TestPyPackageDir(env, 'submod1.testmod3')
-TestPyPackageDir(env, 'submod1.submod2.testmod4')
-
-print("Test parameter substitution")
-env = Environment(tools=[], FOO = 'submod1.submod2.testmod4')
-TestPyPackageDir(env, '${FOO}')
-env = Environment(tools=[], FOO = 'submod1.submod2', BAR = 'testmod4')
-TestPyPackageDir(env, '${FOO}.${BAR}')
-
-sys.path = oldsyspath
+import sys, os
+
+oldsyspath = sys.path
+dir_path = Dir('.').srcnode().abspath
+dir_path = os.path.join(dir_path, 'syspath')
+sys.path.append(dir_path)
+
+def TestPyPackageDir(env, modname):
+ packagepath = env.PyPackageDir(modname).abspath
+ # Convert from an absolute path back to a relative one for testing
+ commonprefix = os.path.commonprefix([dir_path, packagepath])
+ relpath = os.path.relpath(packagepath, commonprefix)
+ relpath = relpath.replace(os.sep, '/')
+ print(relpath)
+
+DefaultEnvironment(tools=[])
+
+print("Test identification of directory for a given python package")
+env = Environment(tools=[])
+TestPyPackageDir(env, 'testmod1')
+TestPyPackageDir(env, 'testmod2')
+TestPyPackageDir(env, 'submod1.testmod3')
+TestPyPackageDir(env, 'submod1.submod2.testmod4')
+
+print("Test parameter substitution")
+env = Environment(tools=[], FOO = 'submod1.submod2.testmod4')
+TestPyPackageDir(env, '${FOO}')
+env = Environment(tools=[], FOO = 'submod1.submod2', BAR = 'testmod4')
+TestPyPackageDir(env, '${FOO}.${BAR}')
+
+sys.path = oldsyspath
diff --git a/test/Dir/source.py b/test/Dir/source.py
index c35d169..a0ba987 100644
--- a/test/Dir/source.py
+++ b/test/Dir/source.py
@@ -42,15 +42,16 @@ test.subdir('tstamp', [ 'tstamp', 'subdir' ],
test.write('SConstruct', """\
DefaultEnvironment(tools=[])
+
def writeTarget(target, source, env):
- f=open(str(target[0]), 'w')
+ f = open(str(target[0]), 'w')
f.write("stuff\\n")
f.close()
return 0
-test_bld_dir = Builder(action=writeTarget,
- source_factory=Dir,
- source_scanner=DirScanner)
+test_bld_dir = Builder(
+ action=writeTarget, source_factory=Dir, source_scanner=DirScanner
+)
test_bld_file = Builder(action=writeTarget)
env = Environment(tools=[])
env['BUILDERS']['TestDir'] = test_bld_dir
@@ -61,34 +62,36 @@ env_tstamp.Decider('timestamp-newer')
env_tstamp.TestFile(source='junk.txt', target='tstamp/junk.out')
env_tstamp.TestDir(source='tstamp', target='tstamp.out')
env_tstamp.Command('cmd-tstamp-noscan.out', 'cmd-tstamp', writeTarget)
-env_tstamp.Command('cmd-tstamp.out', 'cmd-tstamp', writeTarget,
- source_scanner=DirScanner)
+env_tstamp.Command(
+ 'cmd-tstamp.out', 'cmd-tstamp', writeTarget, source_scanner=DirScanner
+)
env_content = env.Clone()
env_content.Decider('content')
env_content.TestFile(source='junk.txt', target='content/junk.out')
env_content.TestDir(source='content', target='content.out')
env_content.Command('cmd-content-noscan.out', 'cmd-content', writeTarget)
-env_content.Command('cmd-content.out', 'cmd-content', writeTarget,
- source_scanner=DirScanner)
+env_content.Command(
+ 'cmd-content.out', 'cmd-content', writeTarget, source_scanner=DirScanner
+)
""")
-test.write([ 'tstamp', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'tstamp', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'tstamp', 'subdir', 'bar.txt'], 'bar.txt 1\n')
-test.write([ 'tstamp', 'subdir', '#hash.txt'], 'hash.txt 1\n')
-test.write([ 'content', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'content', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'content', 'subdir', 'bar.txt' ], 'bar.txt 1\n')
-test.write([ 'content', 'subdir', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'cmd-tstamp', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'cmd-tstamp', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'cmd-tstamp', 'subdir', 'bar.txt' ], 'bar.txt 1\n')
-test.write([ 'cmd-tstamp', 'subdir', '#hash.txt' ], 'hash.txt 1\n')
-test.write([ 'cmd-content', 'foo.txt' ], 'foo.txt 1\n')
-test.write([ 'cmd-content', '#hash.txt' ], '#hash.txt 1\n')
-test.write([ 'cmd-content', 'subdir', 'bar.txt' ], 'bar.txt 1\n')
-test.write([ 'cmd-content', 'subdir', '#hash.txt' ], 'hash.txt 1\n')
+test.write(['tstamp', 'foo.txt'], 'foo.txt 1\n')
+test.write(['tstamp', '#hash.txt'], 'hash.txt 1\n')
+test.write(['tstamp', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['tstamp', 'subdir', '#hash.txt'], 'hash.txt 1\n')
+test.write(['content', 'foo.txt'], 'foo.txt 1\n')
+test.write(['content', '#hash.txt'], 'hash.txt 1\n')
+test.write(['content', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['content', 'subdir', '#hash.txt'], 'hash.txt 1\n')
+test.write(['cmd-tstamp', 'foo.txt'], 'foo.txt 1\n')
+test.write(['cmd-tstamp', '#hash.txt'], 'hash.txt 1\n')
+test.write(['cmd-tstamp', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['cmd-tstamp', 'subdir', '#hash.txt'], 'hash.txt 1\n')
+test.write(['cmd-content', 'foo.txt'], 'foo.txt 1\n')
+test.write(['cmd-content', '#hash.txt'], '#hash.txt 1\n')
+test.write(['cmd-content', 'subdir', 'bar.txt'], 'bar.txt 1\n')
+test.write(['cmd-content', 'subdir', '#hash.txt'], 'hash.txt 1\n')
test.write('junk.txt', 'junk.txt\n')
test.run(arguments=".", stderr=None)
@@ -106,61 +109,61 @@ test.up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.sleep()
+test.sleep() # delay for timestamps
-test.write([ 'tstamp', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['tstamp', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'tstamp', 'new.txt' ], 'new.txt\n')
+test.write(['tstamp', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'content', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['content', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'content', 'new.txt' ], 'new.txt\n')
+test.write(['content', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'cmd-tstamp', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['cmd-tstamp', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-tstamp', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-tstamp', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-content', 'foo.txt' ], 'foo.txt 2\n')
+test.write(['cmd-content', 'foo.txt'], 'foo.txt 2\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.write([ 'cmd-content', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-content', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.write([ 'tstamp', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['tstamp', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'tstamp', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['tstamp', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='tstamp.out')
-test.write([ 'content', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['content', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'content', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['content', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='content.out')
-test.write([ 'cmd-tstamp', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['cmd-tstamp', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-tstamp', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-tstamp', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-tstamp.out')
test.up_to_date(arguments='cmd-tstamp-noscan.out')
-test.write([ 'cmd-content', 'subdir', 'bar.txt' ], 'bar.txt 2\n')
+test.write(['cmd-content', 'subdir', 'bar.txt'], 'bar.txt 2\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
-test.write([ 'cmd-content', 'subdir', 'new.txt' ], 'new.txt\n')
+test.write(['cmd-content', 'subdir', 'new.txt'], 'new.txt\n')
test.not_up_to_date(arguments='cmd-content.out')
test.up_to_date(arguments='cmd-content-noscan.out')
diff --git a/test/Errors/Exception.py b/test/Errors/Exception.py
index c08a09e..cdd6a3c 100644
--- a/test/Errors/Exception.py
+++ b/test/Errors/Exception.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -53,7 +52,7 @@ test.write('exit.in', 'exit\n')
# so make sure the proper variations are supported in the following
# regexp.
expect = r"""scons: \*\*\* \[exit.out\] Exception : exit
-Traceback \((most recent call|innermost) last\):
+Traceback \(most recent call last\):
( File ".+", line \d+, in \S+
[^\n]+
)*( File ".+", line \d+, in \S+
diff --git a/test/Errors/InternalError.py b/test/Errors/InternalError.py
index a709597..d37178e 100644
--- a/test/Errors/InternalError.py
+++ b/test/Errors/InternalError.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 exit status and error output if an SConstruct file
@@ -41,7 +40,7 @@ raise InternalError('error inside')
""")
test.run(stdout = "scons: Reading SConscript files ...\ninternal error\n",
- stderr = r"""Traceback \((most recent call|innermost) last\):
+ stderr = r"""Traceback \(most recent call last\):
File ".+", line \d+, in .+
File ".+", line \d+, in .+
File ".+", line \d+, in .+
diff --git a/test/Fortran/F77PATH.py b/test/Fortran/F77PATH.py
index 260782c..cce5f13 100644
--- a/test/Fortran/F77PATH.py
+++ b/test/Fortran/F77PATH.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -39,30 +38,28 @@ test = TestSCons.TestSCons()
fc = 'f77'
if not test.detect_tool(fc):
- test.skip_test('Could not find a f77 tool; skipping test.\n')
-
-test.subdir('include',
- 'subdir',
- ['subdir', 'include'],
- 'foobar',
- 'inc2')
-
-
-
-test.write('SConstruct', """
-env = Environment(F77 = '%s',
- F77PATH = ['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
- FOO='include',
- F77FLAGS = '-x f77')
+ # gfortran names all variants the same, try it too
+ fc = 'gfortran'
+ if not test.detect_tool(fc):
+ test.skip_test('Could not find a f77 tool; skipping test.\n')
+
+test.subdir('include', 'subdir', ['subdir', 'include'], 'foobar', 'inc2')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(
+ F77='%s',
+ F77PATH=['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
+ FOO='include',
+ F77FLAGS='-x f77',
+)
obj = env.Object(target='foobar/prog', source='subdir/prog.f77')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
VariantDir('variant', 'subdir', 0)
include = Dir('include')
-env = Environment(F77 = '%s',
- F77PATH=[include, '#foobar', '#subdir'],
- F77FLAGS = '-x f77')
+env = Environment(F77='%s', F77PATH=[include, '#foobar', '#subdir'], F77FLAGS='-x f77')
SConscript('variant/SConscript', "env")
""" % (fc, fc))
@@ -115,8 +112,6 @@ r"""
PRINT *, 'subdir/ttt.f77'
""")
-
-
test.run(arguments = args)
test.run(program = test.workpath(prog),
@@ -151,8 +146,6 @@ test.must_not_exist(test.workpath('variant', 'prog.f77'))
test.up_to_date(arguments = args)
-
-
test.write(['include', 'foo.f77'],
r"""
PRINT *, 'include/foo.f77 2'
@@ -193,9 +186,6 @@ test.must_not_exist(test.workpath('variant', 'prog.f77'))
test.up_to_date(arguments = args)
-
-
-#
test.write(['include', 'bar.f77'],
r"""
PRINT *, 'include/bar.f77 2'
@@ -235,22 +225,23 @@ test.must_not_exist(test.workpath('variant', 'prog.f77'))
test.up_to_date(arguments = args)
-
-
# Change F77PATH and make sure we don't rebuild because of it.
-test.write('SConstruct', """
-env = Environment(F77 = '%s',
- F77PATH = Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'),
- F77FLAGS = '-x f77')
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(
+ F77='%s',
+ F77PATH=Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'),
+ F77FLAGS='-x f77',
+)
obj = env.Object(target='foobar/prog', source='subdir/prog.f77')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
VariantDir('variant', 'subdir', 0)
include = Dir('include')
-env = Environment(F77 = '%s',
- F77PATH=['inc2', include, '#foobar', '#subdir'],
- F77FLAGS = '-x f77')
+env = Environment(
+ F77='%s', F77PATH=['inc2', include, '#foobar', '#subdir'], F77FLAGS='-x f77'
+)
SConscript('variant/SConscript', "env")
""" % (fc, fc))
@@ -294,20 +285,16 @@ test.run(program = test.workpath(variant_prog),
test.up_to_date(arguments = args)
-
-
# Check that a null-string F77PATH doesn't blow up.
-test.write('SConstruct', """
-env = Environment(tools = ['f77'], F77PATH = '', F77FLAGS = '-x f77')
-env.Object('foo', source = 'empty.f77')
-""")
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(tools=['%s'], F77PATH='', F77FLAGS='-x f77')
+env.Object('foo', source='empty.f77')
+""" % fc)
test.write('empty.f77', '')
-
test.run(arguments = '.')
-
-
test.pass_test()
# Local Variables:
diff --git a/test/Fortran/F90PATH.py b/test/Fortran/F90PATH.py
index 341b241..c10e134 100644
--- a/test/Fortran/F90PATH.py
+++ b/test/Fortran/F90PATH.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -39,32 +38,30 @@ test = TestSCons.TestSCons()
fc = 'f90'
if not test.detect_tool(fc):
- test.skip_test('Could not find a f90 tool; skipping test.\n')
-
-test.subdir('include',
- 'subdir',
- ['subdir', 'include'],
- 'foobar',
- 'inc2')
-
+ # gfortran names all variants the same, try it too
+ fc = 'gfortran'
+ if not test.detect_tool(fc):
+ test.skip_test('Could not find a f90 tool; skipping test.\n')
+test.subdir('include', 'subdir', ['subdir', 'include'], 'foobar', 'inc2')
test.write('SConstruct', """
-env = Environment(F90 = r'%s',
- F90PATH = ['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
- LINK = '$F90',
- FOO='include')
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(
+ F90=r'%s',
+ F90PATH=['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
+ LINK='$F90',
+ FOO='include',
+)
obj = env.Object(target='foobar/prog', source='subdir/prog.f90')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
VariantDir('variant', 'subdir', 0)
include = Dir('include')
-env = Environment(F90 = r'%s',
- F90PATH=[include, '#foobar', '#subdir'],
- LINK = '$F90')
+env = Environment(F90=r'%s', F90PATH=[include, '#foobar', '#subdir'], LINK='$F90')
SConscript('variant/SConscript', "env")
-""" % (fc, fc, ))
+""" % (fc, fc))
test.write(['subdir', 'SConscript'],
"""
@@ -115,8 +112,6 @@ r"""
PRINT *, 'subdir/ttt.f90'
""")
-
-
test.run(arguments = args)
test.run(program = test.workpath(prog),
@@ -151,8 +146,6 @@ test.must_not_exist(test.workpath('variant', 'prog.f90'))
test.up_to_date(arguments = args)
-
-
test.write(['include', 'foo.f90'],
r"""
PRINT *, 'include/foo.f90 2'
@@ -193,9 +186,6 @@ test.must_not_exist(test.workpath('variant', 'prog.f90'))
test.up_to_date(arguments = args)
-
-
-#
test.write(['include', 'bar.f90'],
r"""
PRINT *, 'include/bar.f90 2'
@@ -238,27 +228,29 @@ test.up_to_date(arguments = args)
# Change F90PATH and make sure we don't rebuild because of it.
-test.write('SConstruct', """
-env = Environment(F90 = r'%s',
- F90PATH = Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'),
- LINK = '$F90')
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(
+ F90=r'%s',
+ F90PATH=Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'),
+ LINK='$F90'
+)
obj = env.Object(target='foobar/prog', source='subdir/prog.f90')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
VariantDir('variant', 'subdir', 0)
include = Dir('include')
-env = Environment(F90 = r'%s',
- F90PATH=['inc2', include, '#foobar', '#subdir'],
- LINK = '$F90')
+env = Environment(
+ F90=r'%s',
+ F90PATH=['inc2', include, '#foobar', '#subdir'],
+ LINK='$F90',
+)
SConscript('variant/SConscript', "env")
""" % (fc, fc))
test.up_to_date(arguments = args)
-
-
-#
test.write(['inc2', 'foo.f90'],
r"""
PRINT *, 'inc2/foo.f90 1'
@@ -296,8 +288,6 @@ test.run(program = test.workpath(variant_prog),
test.up_to_date(arguments = args)
-
-
test.pass_test()
# Local Variables:
diff --git a/test/Fortran/F95FLAGS.py b/test/Fortran/F95FLAGS.py
index 2853cc9..e706264 100644
--- a/test/Fortran/F95FLAGS.py
+++ b/test/Fortran/F95FLAGS.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -31,26 +30,30 @@ _python_ = TestSCons._python_
test = TestSCons.TestSCons()
_exe = TestSCons._exe
+# ref: test/fixture/mylink.py
test.file_fixture('mylink.py')
+# ref: test/Fortran/fixture/myfortran_flags.py
test.file_fixture(['fixture', 'myfortran_flags.py'])
test.write('SConstruct', """
-env = Environment(LINK = r'%(_python_)s mylink.py',
- LINKFLAGS = [],
- F95 = r'%(_python_)s myfortran_flags.py g95',
- F95FLAGS = '-x',
- FORTRAN = r'%(_python_)s myfortran_flags.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 = 'test13', source = 'test13.f95')
-env.Program(target = 'test14', source = 'test14.F95')
+env = Environment(
+ LINK=r'%(_python_)s mylink.py',
+ LINKFLAGS=[],
+ F95=r'%(_python_)s myfortran_flags.py g95',
+ F95FLAGS='-x',
+ FORTRAN=r'%(_python_)s myfortran_flags.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='test13', source='test13.f95')
+env.Program(target='test14', source='test14.F95')
""" % locals())
test.write('test01.f', "This is a .f file.\n#link\n#fortran\n")
@@ -80,24 +83,22 @@ test.must_match('test14' + _exe, " -c -x\nThis is a .F95 file.\n")
fc = 'f95'
g95 = test.detect_tool(fc)
-
-
if g95:
test.subdir('x')
-
test.write(['x','dummy.i'],
"""
# Exists only such that -Ix finds the directory...
""")
+ # ref: test/fixture/wrapper.py
test.file_fixture('wrapper.py')
test.write('SConstruct', """
-foo = Environment(F95 = '%(fc)s')
+foo = Environment(F95='%(fc)s')
f95 = foo.Dictionary('F95')
-bar = foo.Clone(F95 = r'%(_python_)s wrapper.py ' + f95, F95FLAGS = '-Ix')
-foo.Program(target = 'foo', source = 'foo.f95')
-bar.Program(target = 'bar', source = 'bar.f95')
+bar = foo.Clone(F95=r'%(_python_)s wrapper.py ' + f95, F95FLAGS='-Ix')
+foo.Program(target='foo', source='foo.f95')
+bar.Program(target='bar', source='bar.f95')
""" % locals())
test.write('foo.f95', r"""
@@ -114,21 +115,18 @@ bar.Program(target = 'bar', source = 'bar.f95')
END
""")
-
- test.run(arguments = 'foo' + _exe, stderr = None)
-
- test.run(program = test.workpath('foo'), stdout = " foo.f95\n")
-
+ test.run(arguments='foo' + _exe, stderr=None)
+ test.run(program=test.workpath('foo'), stdout=" foo.f95\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.f95\n")
+ if sys.platform.startswith('sunos'):
+ test.run(arguments='bar' + _exe, stderr=None)
+ else:
+ test.run(arguments='bar' + _exe)
+ test.run(program=test.workpath('bar'), stdout=" bar.f95\n")
test.must_match('wrapper.out', "wrapper.py\n")
test.pass_test()
diff --git a/test/Fortran/FORTRANCOMMONFLAGS.py b/test/Fortran/FORTRANCOMMONFLAGS.py
new file mode 100644
index 0000000..9030709
--- /dev/null
+++ b/test/Fortran/FORTRANCOMMONFLAGS.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test that while FORTRANFLAGS is not passed to another dialect,
+FORTRANCOMMONFLAGS is passed to both.
+"""
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+_exe = TestSCons._exe
+
+test.file_fixture('mylink.py')
+test.file_fixture(['fixture', 'myfortran_flags.py'])
+
+test.write('SConstruct', """
+env = Environment(
+ LINK=r'%(_python_)s mylink.py',
+ LINKFLAGS=[],
+ F90=r'%(_python_)s myfortran_flags.py g90',
+ F90FLAGS='-x',
+ FORTRAN=r'%(_python_)s myfortran_flags.py fortran',
+ FORTRANFLAGS='-y',
+ FORTRANCOMMONFLAGS='-z',
+)
+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='test11', source='test11.f90')
+env.Program(target='test12', source='test12.F90')
+""" % 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('test11.f90', "This is a .f90 file.\n#link\n#g90\n")
+test.write('test12.F90', "This is a .F90 file.\n#link\n#g90\n")
+
+test.run(arguments = '.', stderr = None)
+
+test.must_match('test01' + _exe, " -c -z -y\nThis is a .f file.\n")
+test.must_match('test02' + _exe, " -c -z -y\nThis is a .F file.\n")
+test.must_match('test03' + _exe, " -c -z -y\nThis is a .for file.\n")
+test.must_match('test04' + _exe, " -c -z -y\nThis is a .FOR file.\n")
+test.must_match('test05' + _exe, " -c -z -y\nThis is a .ftn file.\n")
+test.must_match('test06' + _exe, " -c -z -y\nThis is a .FTN file.\n")
+test.must_match('test07' + _exe, " -c -z -y\nThis is a .fpp file.\n")
+test.must_match('test08' + _exe, " -c -z -y\nThis is a .FPP file.\n")
+test.must_match('test11' + _exe, " -c -z -x\nThis is a .f90 file.\n")
+test.must_match('test12' + _exe, " -c -z -x\nThis is a .F90 file.\n")
+
+
+fc = 'f90'
+g90 = test.detect_tool(fc)
+if g90:
+ test.file_fixture('wrapper.py')
+
+ test.write('SConstruct', """
+foo = Environment(F90='%(fc)s')
+f90 = foo.Dictionary('F90')
+bar = foo.Clone(F90=r'%(_python_)s wrapper.py ' + f90)
+foo.Program(target='foo', source='foo.f90')
+bar.Program(target='bar', source='bar.f90')
+""" % locals())
+
+ test.write('foo.f90', r"""
+ PROGRAM FOO
+ PRINT *,'foo.f90'
+ END
+""")
+
+ test.write('bar.f90', r"""
+ PROGRAM BAR
+ PRINT *,'bar.f90'
+ END
+""")
+
+ test.run(arguments='foo' + _exe, stderr=None)
+ test.run(program=test.workpath('foo'), stdout=" foo.f90\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.f90\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/FORTRANFILESUFFIXES2.py b/test/Fortran/FORTRANFILESUFFIXES2.py
index a57dd2a..15abd2d 100644
--- a/test/Fortran/FORTRANFILESUFFIXES2.py
+++ b/test/Fortran/FORTRANFILESUFFIXES2.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,14 +22,11 @@
# 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_
-_exe = TestSCons._exe
+_exe = TestSCons._exe
test = TestSCons.TestSCons()
@@ -35,28 +34,30 @@ test.file_fixture('mylink.py')
test.file_fixture(['fixture', 'myfortran.py'])
# Test non default file suffix: .f, .f90 and .f95 for FORTRAN
-test.write('SConstruct', """
-env = Environment(LINK = r'%(_python_)s mylink.py',
- LINKFLAGS = [],
- F77 = r'%(_python_)s myfortran.py g77',
- FORTRAN = r'%(_python_)s myfortran.py fortran',
- FORTRANFILESUFFIXES = ['.f', '.f95', '.f90', '.ffake'],
- tools = ['default', 'fortran'])
-#print(env.Dump())
-env.Program(target = 'test01', source = 'test01.f')
-env.Program(target = 'test02', source = 'test02.f90')
-env.Program(target = 'test03', source = 'test03.f95')
-env.Program(target = 'test04', source = 'test04.ffake')
-env.Program(target = 'test05', source = 'test05.f77')
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+env = Environment(
+ LINK=r'%(_python_)s mylink.py',
+ LINKFLAGS=[],
+ F77=r'%(_python_)s myfortran.py g77',
+ FORTRAN=r'%(_python_)s myfortran.py fortran',
+ FORTRANFILESUFFIXES=['.f', '.f95', '.f90', '.ffake'],
+ tools=['default', 'fortran'],
+)
+env.Program(target='test01', source='test01.f')
+env.Program(target='test02', source='test02.f90')
+env.Program(target='test03', source='test03.f95')
+env.Program(target='test04', source='test04.ffake')
+env.Program(target='test05', source='test05.f77')
""" % locals())
-test.write('test01.f', "This is a .f file.\n#link\n#fortran\n")
-test.write('test02.f90', "This is a .f90 file.\n#link\n#fortran\n")
+test.write('test01.f', "This is a .f file.\n#link\n#fortran\n")
+test.write('test02.f90', "This is a .f90 file.\n#link\n#fortran\n")
test.write('test03.f95', "This is a .f95 file.\n#link\n#fortran\n")
test.write('test04.ffake', "This is a .ffake file.\n#link\n#fortran\n")
test.write('test05.f77', "This is a .f77 file.\n#link\n#g77\n")
-test.run(arguments = '.', stderr = None)
+test.run(arguments='.', stderr=None)
test.must_match('test01' + _exe, "This is a .f file.\n")
test.must_match('test02' + _exe, "This is a .f90 file.\n")
diff --git a/test/Fortran/FORTRANPATH.py b/test/Fortran/FORTRANPATH.py
index ee01aee..f0f8b3e 100644
--- a/test/Fortran/FORTRANPATH.py
+++ b/test/Fortran/FORTRANPATH.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -39,28 +38,27 @@ test = TestSCons.TestSCons()
fc = 'f77'
if not test.detect_tool(fc):
- test.skip_test('Could not find a f77 tool; skipping test.\n')
-
-test.subdir('include',
- 'subdir',
- ['subdir', 'include'],
- 'foobar',
- 'inc2')
-
-
-
-test.write('SConstruct', """
-env = Environment(FORTRAN = '%s',
- FORTRANPATH = ['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
- FOO='include')
+ # gfortran names all variants the same, try it too
+ fc = 'gfortran'
+ if not test.detect_tool(fc):
+ test.skip_test('Could not find a f77 tool; skipping test.\n')
+
+test.subdir('include', 'subdir', ['subdir', 'include'], 'foobar', 'inc2')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(
+ FORTRAN='%s',
+ FORTRANPATH=['$FOO', '${TARGET.dir}', '${SOURCE.dir}'],
+ FOO='include'
+)
obj = env.Object(target='foobar/prog', source='subdir/prog.f')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
VariantDir('variant', 'subdir', 0)
include = Dir('include')
-env = Environment(FORTRAN = '%s',
- FORTRANPATH=[include, '#foobar', '#subdir'])
+env = Environment(FORTRAN='%s', FORTRANPATH=[include, '#foobar', '#subdir'])
SConscript('variant/SConscript', "env")
""" % (fc, fc))
@@ -153,8 +151,6 @@ test.must_not_exist(test.workpath('variant', 'prog.f'))
test.up_to_date(arguments = args)
-
-
test.write(['include', 'foo.f'],
r"""
PRINT *, 'include/foo.f 2'
@@ -199,9 +195,6 @@ test.must_not_exist(test.workpath('variant', 'prog.f'))
test.up_to_date(arguments = args)
-
-
-#
test.write(['include', 'bar.f'],
r"""
PRINT *, 'include/bar.f 2'
@@ -249,25 +242,27 @@ test.up_to_date(arguments = args)
# Change FORTRANPATH and make sure we don't rebuild because of it.
-test.write('SConstruct', """
-env = Environment(FORTRAN = '%s',
- FORTRANPATH = Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'))
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(
+ FORTRAN='%s',
+ FORTRANPATH=Split('inc2 include ${TARGET.dir} ${SOURCE.dir}'),
+)
obj = env.Object(target='foobar/prog', source='subdir/prog.f')
env.Program(target='prog', source=obj)
SConscript('subdir/SConscript', "env")
VariantDir('variant', 'subdir', 0)
include = Dir('include')
-env = Environment(FORTRAN = '%s',
- FORTRANPATH=['inc2', include, '#foobar', '#subdir'])
+env = Environment(
+ FORTRAN='%s',
+ FORTRANPATH=['inc2', include, '#foobar', '#subdir'],
+)
SConscript('variant/SConscript', "env")
""" % (fc, fc))
test.up_to_date(arguments = args)
-
-
-#
test.write(['inc2', 'foo.f'],
r"""
PRINT *, 'inc2/foo.f 1'
@@ -310,10 +305,9 @@ test.run(program = test.workpath(variant_prog),
test.up_to_date(arguments = args)
-
-
# Check that a null-string FORTRANPATH doesn't blow up.
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(FORTRANPATH = '')
env.Object('foo', source = 'empty.f')
""")
diff --git a/test/Fortran/SHF95FLAGS.py b/test/Fortran/SHF95FLAGS.py
index 56744d6..dcec49b 100644
--- a/test/Fortran/SHF95FLAGS.py
+++ b/test/Fortran/SHF95FLAGS.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -32,23 +31,25 @@ _obj = TestSCons._shobj
obj_ = TestSCons.shobj_
test = TestSCons.TestSCons()
+# ref: test/Fortran/fixture/myfortran_flags.py
test.file_fixture(['fixture', 'myfortran_flags.py'])
test.write('SConstruct', """
-env = Environment(SHF95 = r'%(_python_)s myfortran_flags.py g95',
- SHFORTRAN = r'%(_python_)s myfortran_flags.py fortran')
-env.Append(SHF95FLAGS = '-x',
- SHFORTRANFLAGS = '-y')
-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 = 'test13', source = 'test13.f95')
-env.SharedObject(target = 'test14', source = 'test14.F95')
+env = Environment(
+ SHF95=r'%(_python_)s myfortran_flags.py g95',
+ SHFORTRAN=r'%(_python_)s myfortran_flags.py fortran',
+)
+env.Append(SHF95FLAGS='-x', SHFORTRANFLAGS='-y')
+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='test13', source='test13.f95')
+env.SharedObject(target='test14', source='test14.F95')
""" % locals())
test.write('test01.f', "This is a .f file.\n#fortran\n")
@@ -62,8 +63,7 @@ test.write('test08.FPP', "This is a .FPP file.\n#fortran\n")
test.write('test13.f95', "This is a .f95 file.\n#g95\n")
test.write('test14.F95', "This is a .F95 file.\n#g95\n")
-test.run(arguments = '.', stderr = None)
-
+test.run(arguments='.', stderr=None)
test.must_match(obj_ + 'test01' + _obj, " -c -y\nThis is a .f file.\n")
test.must_match(obj_ + 'test02' + _obj, " -c -y\nThis is a .F file.\n")
test.must_match(obj_ + 'test03' + _obj, " -c -y\nThis is a .for file.\n")
@@ -75,29 +75,24 @@ test.must_match(obj_ + 'test08' + _obj, " -c -y\nThis is a .FPP file.\n")
test.must_match(obj_ + 'test13' + _obj, " -c -x\nThis is a .f95 file.\n")
test.must_match(obj_ + 'test14' + _obj, " -c -x\nThis is a .F95 file.\n")
-
-
fc = 'f95'
g95 = test.detect_tool(fc)
-
if g95:
-
test.subdir('x')
-
test.write(['x','dummy.i'],
"""
# Exists only such that -Ix finds the directory...
""")
+ # ref: test/fixture/wrapper.py
test.file_fixture('wrapper.py')
-
test.write('SConstruct', """
-foo = Environment(SHF95 = '%(fc)s')
+foo = Environment(SHF95='%(fc)s')
shf95 = foo.Dictionary('SHF95')
-bar = foo.Clone(SHF95 = r'%(_python_)s wrapper.py ' + shf95)
-bar.Append(SHF95FLAGS = '-Ix')
-foo.SharedLibrary(target = 'foo/foo', source = 'foo.f95')
-bar.SharedLibrary(target = 'bar/bar', source = 'bar.f95')
+bar = foo.Clone(SHF95=r'%(_python_)s wrapper.py ' + shf95)
+bar.Append(SHF95FLAGS='-Ix')
+foo.SharedLibrary(target='foo/foo', source='foo.f95')
+bar.SharedLibrary(target='bar/bar', source='bar.f95')
""" % locals())
test.write('foo.f95', r"""
@@ -114,17 +109,15 @@ bar.SharedLibrary(target = 'bar/bar', source = 'bar.f95')
END
""")
-
- test.run(arguments = 'foo', stderr = None)
-
+ 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')
+ if sys.platform.startswith('sunos'):
+ test.run(arguments='bar', stderr=None)
+ else:
+ test.run(arguments='bar')
test.must_match('wrapper.out', "wrapper.py\n")
test.pass_test()
diff --git a/test/Fortran/fixture/myfortran.py b/test/Fortran/fixture/myfortran.py
index 08d1489..6b4e5ef 100644
--- a/test/Fortran/fixture/myfortran.py
+++ b/test/Fortran/fixture/myfortran.py
@@ -1,14 +1,17 @@
import getopt
import sys
+
print(sys.argv)
comment = ('#' + sys.argv[1]).encode()
-length = len(comment)
+
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[:length] != comment:
- outfile.write(l)
+ if opt == '-o':
+ out = arg
+
+with open(args[0], 'rb') as infile, open(out, 'wb') as outfile:
+ for l in infile:
+ if not l.startswith(comment):
+ outfile.write(l)
+
sys.exit(0)
diff --git a/test/Fortran/fixture/myfortran_flags.py b/test/Fortran/fixture/myfortran_flags.py
index b972e35..2b433ea 100644
--- a/test/Fortran/fixture/myfortran_flags.py
+++ b/test/Fortran/fixture/myfortran_flags.py
@@ -1,16 +1,19 @@
import getopt
import sys
+
comment = ('#' + sys.argv[1]).encode()
-opts, args = getopt.getopt(sys.argv[2:], 'cf:o:xy')
+
+opts, args = getopt.getopt(sys.argv[2:], 'cf:o:xyz')
optstring = ''
length = len(comment)
for opt, arg in opts:
if opt == '-o': out = arg
elif opt not in ('-f', '-K'): optstring = optstring + ' ' + opt
-infile = open(args[0], 'rb')
-outfile = open(out, 'wb')
-outfile.write((optstring + "\n").encode())
-for l in infile.readlines():
- if l[:length] != comment:
- outfile.write(l)
+
+with open(args[0], 'rb') as infile, open(out, 'wb') as outfile:
+ outfile.write((optstring + "\n").encode())
+ for l in infile:
+ if not l.startswith(comment):
+ outfile.write(l)
+
sys.exit(0)
diff --git a/test/Fortran/link-with-cxx.py b/test/Fortran/link-with-cxx.py
index 22d9081..2f19e82 100644
--- a/test/Fortran/link-with-cxx.py
+++ b/test/Fortran/link-with-cxx.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 smart_link() warning messages used when attempting to link
@@ -41,6 +40,7 @@ test = TestSCons.TestSCons(match = TestSCons.match_re)
test.write('test_linker.py', """\
import sys
+
if sys.argv[1] == '-o':
with open(sys.argv[2], 'wb') as ofp:
for infile in sys.argv[3:]:
@@ -54,9 +54,9 @@ elif sys.argv[1][:5] == '/OUT:':
sys.exit(0)
""")
-
test.write('test_fortran.py', """\
import sys
+
with open(sys.argv[2], 'wb') as ofp:
for infile in sys.argv[4:]:
with open(infile, 'rb') as ifp:
@@ -64,35 +64,35 @@ with open(sys.argv[2], 'wb') as ofp:
sys.exit(0)
""")
-
test.write('SConstruct', """
import SCons.Tool.link
+
def copier(target, source, env):
s = str(source[0])
t = str(target[0])
with open(t, 'wb') as ofp, open(s, 'rb') as ifp:
ofp.write(ifp.read())
-env = Environment(CXX = r'%(_python_)s test_linker.py',
- CXXCOM = Action(copier),
- SMARTLINK = SCons.Tool.link.smart_link,
- LINK = r'$SMARTLINK',
- LINKFLAGS = '',
- # We want to re-define this as follows (so as to
- # not rely on a real Fortran compiler) but can't
- # because $FORTRANCOM is defined with an extra space
- # so it ends up as a CommandAction, not a LazyAction.
- # Must look into changing that after 1.0 is out.
- #FORTRANCOM = Action(copier))
- FORTRAN = r'%(_python_)s test_fortran.py')
+env = Environment(
+ CXX=r'%(_python_)s test_linker.py',
+ CXXCOM=Action(copier),
+ SMARTLINK=SCons.Tool.link.smart_link,
+ LINK=r'$SMARTLINK',
+ LINKFLAGS='',
+ # We want to re-define this as follows (so as to
+ # not rely on a real Fortran compiler) but can't
+ # because $FORTRANCOM is defined with an extra space
+ # so it ends up as a CommandAction, not a LazyAction.
+ # Must look into changing that after 1.0 is out.
+ # FORTRANCOM = Action(copier))
+ FORTRAN=r'%(_python_)s test_fortran.py',
+)
env.Program('prog1.exe', ['f1.cpp', 'f2.f'])
env.Program('prog2.exe', ['f1.cpp', 'f2.f'])
if ARGUMENTS.get('NO_LINK'):
- # Can remove no-deprecated when we drop Python1.5
- SetOption('warn', ['no-link', 'no-deprecated'])
+ SetOption('warn', ['no-link'])
if ARGUMENTS.get('NO_MIX'):
- # Can remove no-deprecated when we drop Python1.5
- SetOption('warn', ['no-fortran-cxx-mix', 'no-deprecated'])
+ SetOption('warn', ['no-fortran-cxx-mix'])
""" % locals())
test.write('f1.cpp', "f1.cpp\n")
@@ -100,52 +100,43 @@ test.write('f2.f', "f2.f\n")
expect = ("""
scons: warning: Using \\$CXX to link Fortran and C\\+\\+ code together.
-\tThis may generate a buggy executable if the '%s test_linker.py'
-\tcompiler does not know how to deal with Fortran runtimes.
+ This may generate a buggy executable if the '%s test_linker.py'
+ compiler does not know how to deal with Fortran runtimes.
""" % re.escape(_python_)) + TestSCons.file_expr
test.run(arguments = '.', stderr=expect)
-
test.must_match('prog1.exe', "f1.cpp\nf2.f\n")
test.must_match('prog2.exe', "f1.cpp\nf2.f\n")
test.run(arguments = '-c .', stderr=expect)
-
test.must_not_exist('prog1.exe')
test.must_not_exist('prog2.exe')
test.run(arguments = '--warning=no-link .')
-
test.must_match('prog1.exe', "f1.cpp\nf2.f\n")
test.must_match('prog2.exe', "f1.cpp\nf2.f\n")
test.run(arguments = '-c .', stderr=expect)
-
test.must_not_exist('prog1.exe')
test.must_not_exist('prog2.exe')
test.run(arguments = '--warning=no-fortran-cxx-mix .')
-
test.must_match('prog1.exe', "f1.cpp\nf2.f\n")
test.must_match('prog2.exe', "f1.cpp\nf2.f\n")
test.run(arguments = '-c .', stderr=expect)
-
test.must_not_exist('prog1.exe')
test.must_not_exist('prog2.exe')
test.run(arguments = 'NO_LINK=1 .')
-
test.must_match('prog1.exe', "f1.cpp\nf2.f\n")
test.must_match('prog2.exe', "f1.cpp\nf2.f\n")
test.run(arguments = '-c .', stderr=expect)
-
test.must_not_exist('prog1.exe')
test.must_not_exist('prog2.exe')
test.run(arguments = 'NO_MIX=1 .')
-
test.must_match('prog1.exe', "f1.cpp\nf2.f\n")
test.must_match('prog2.exe', "f1.cpp\nf2.f\n")
diff --git a/test/GetBuildFailures/serial.py b/test/GetBuildFailures/serial.py
index 4aecc12..ab8fbb5 100644
--- a/test/GetBuildFailures/serial.py
+++ b/test/GetBuildFailures/serial.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,7 +22,6 @@
# 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.
-#
"""
Verify that the GetBuildFailures() function returns a list of
@@ -28,8 +29,6 @@ BuildError exceptions. Also verify printing the BuildError
attributes we expect to be most commonly used.
"""
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
import TestSCons
import re
@@ -196,7 +195,7 @@ scons: *** [f12] f12: My SConsEnvironmentError
scons: *** [f13] f13: My SConsEnvironmentError
scons: *** [f14] InternalError : My InternalError
""") + \
-r"""Traceback \((most recent call|innermost) last\):
+r"""Traceback \(most recent call last\):
( File ".+", line \d+, in \S+
[^\n]+
)*( File ".+", line \d+, in \S+
diff --git a/test/Interactive/taskmastertrace.py b/test/Interactive/taskmastertrace.py
index 93ee068..04e95fd 100644
--- a/test/Interactive/taskmastertrace.py
+++ b/test/Interactive/taskmastertrace.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,8 +22,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__"
+
"""
Verify use of the --taskmastertrace= option to the "build" command
@@ -42,7 +43,6 @@ Command('2', [], Touch('$TARGET'))
test.write('foo.in', "foo.in 1\n")
-
scons = test.start(arguments = '-Q --interactive')
scons.send("build foo.out 1\n")
@@ -101,7 +101,6 @@ Task.postprocess(): node <executed 0 'foo.out'>
Taskmaster: Looking for a node to evaluate
Taskmaster: No candidate anymore.
-
scons>>> Touch("2")
scons>>> scons: `foo.out' is up to date.
scons>>>
@@ -109,8 +108,6 @@ scons>>>
test.finish(scons, stdout = expect_stdout)
-
-
test.pass_test()
# Local Variables:
diff --git a/test/Java/JAVAPROCESSORPATH.py b/test/Java/JAVAPROCESSORPATH.py
new file mode 100644
index 0000000..2b8f04d
--- /dev/null
+++ b/test/Java/JAVAPROCESSORPATH.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Verify that use of $JAVAPROCESSORPATH sets the -processorpath option
+on javac compilations.
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+where_javac, java_version = test.java_where_javac()
+
+test.write('SConstruct', """
+DefaultEnvironment(tools=[])
+env = Environment(tools=['javac'], JAVAPROCESSORPATH=['dir1', 'dir2'])
+j1 = env.Java(target='class', source='com/Example1.java')
+j2 = env.Java(target='class', source='com/Example2.java')
+""")
+
+test.subdir('com')
+
+test.write(['com', 'Example1.java'], """\
+package com;
+
+public class Example1
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+test.write(['com', 'Example2.java'], """\
+package com;
+
+public class Example2
+{
+
+ public static void main(String[] args)
+ {
+
+ }
+
+}
+""")
+
+# Setting -processorpath messes with the Java runtime environment, so
+# we'll just take the easy way out and examine the -n output to see if
+# the expected option shows up on the command line.
+
+processorpath = os.pathsep.join(['dir1', 'dir2'])
+
+expect = """\
+javac -processorpath %(processorpath)s -d class -sourcepath com com.Example1\\.java
+javac -processorpath %(processorpath)s -d class -sourcepath com com.Example2\\.java
+""" % locals()
+
+test.run(arguments = '-Q -n .', stdout = expect, 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/Java/inner-cacheable-live.py b/test/Java/inner-cacheable-live.py
new file mode 100644
index 0000000..e0391d2
--- /dev/null
+++ b/test/Java/inner-cacheable-live.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test Java inner classes can be cached. Requires a working JDK.
+
+Regression test: one iteration of CacheDir left it unable to deal
+with class names from the emitter which contained an embedded '$'.
+Led to error like:
+
+SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$Inner.class'
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+where_javac, java_version = test.java_where_javac()
+
+# Work around javac 1.4 not reporting its version:
+java_version = java_version or "1.4"
+
+# Skip this test as SCons doesn't (currently) predict the generated
+# inner/anonymous class generated .class files generated by gcj
+# and so will always fail.
+if test.javac_is_gcj:
+ test.skip_test('Test not valid for gcj (gnu java); skipping test(s).\n')
+
+test.write(
+ 'SConstruct',
+ """
+DefaultEnvironment(tools=[])
+env = Environment()
+env.CacheDir("cache")
+env.Java("classes", "source")
+""",
+)
+
+test.subdir('source')
+
+test.write(
+ ['source', 'Test.java'],
+ """\
+class Test { class Inner {} }
+""",
+)
+
+test.run(arguments='.')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LEX/FLEXFLAGS.py b/test/LEX/FLEXFLAGS.py
new file mode 100644
index 0000000..b803ec7
--- /dev/null
+++ b/test/LEX/FLEXFLAGS.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test that detection of file-writing options in LEXFLAGS works.
+Also test that construction vars for the same purpose work.
+"""
+
+import sysconfig
+from pathlib import Path
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+test = TestSCons.TestSCons()
+
+test.subdir('sub1')
+test.subdir('sub2')
+
+test.file_fixture('mylex.py')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+SConscript(dirs=['sub1', 'sub2'])
+""")
+
+# this SConscript is for the options-in-flags version
+test.write(['sub1', 'SConscript'], f"""\
+import sys
+
+env = Environment(
+ LEX=r'{_python_} mylex.py',
+ LEXFLAGS='-x --header-file=header.h --tables-file=tables.t',
+ tools=['default', 'lex'],
+)
+targs = env.CFile(target='aaa', source='aaa.l')
+t = [str(target) for target in targs]
+# fail ourselves if the two extra files were not detected
+if not all((len(t) == 3, "header.h" in t, "tables.t" in t)):
+ sys.exit(1)
+""")
+test.write(['sub1', 'aaa.l'], "aaa.l\nLEXFLAGS\n")
+
+# this SConscript is for the construction var version
+test.write(['sub2', 'SConscript'], f"""\
+import sys
+
+env = Environment(
+ LEX=r'{_python_} mylex.py',
+ LEXFLAGS='-x',
+ tools=['default', 'lex'],
+)
+env.CFile(
+ target='aaa',
+ source='aaa.l',
+ LEX_HEADER_FILE='header.h',
+ LEX_TABLES_FILE='tables.t',
+)
+""")
+test.write(['sub2', 'aaa.l'], "aaa.l\nLEXFLAGS\n")
+
+test.run('.', stderr=None)
+
+lexflags = ' -x --header-file=header.h --tables-file=tables.t -t'
+if IS_WINDOWS and not sysconfig.get_platform() in ("mingw",):
+ lexflags = ' --nounistd' + lexflags
+# Read in with mode='r' because mylex.py implicitly wrote to stdout
+# with mode='w'.
+test.must_match(['sub1', 'aaa.c'], "aaa.l\n%s\n" % lexflags, mode='r')
+
+# NOTE: this behavior is "wrong" but we're keeping it for compat.
+# the generated files should go into 'sub1', not the topdir.
+test.must_match(['header.h'], 'lex header\n')
+test.must_match(['tables.t'], 'lex table\n')
+
+# To confirm the files from the file-output options were tracked,
+# we should do a clean and make sure they got removed.
+# As noted, they currently don't go into the tracked location,
+# so using the check in the SConscript instead.
+#test.run(arguments='-c .')
+#test.must_not_exist(test.workpath(['sub1', 'header.h']))
+#test.must_not_exist(test.workpath(['sub1', 'tables.t']))
+
+sub2 = Path('sub2')
+headerfile = sub2 / 'header.h'
+tablefile = sub2 / 'tables.t'
+lexflags = f' -x --header-file={headerfile} --tables-file={tablefile} -t'
+if IS_WINDOWS and not sysconfig.get_platform() in ("mingw",):
+ lexflags = ' --nounistd' + lexflags
+# Read in with mode='r' because mylex.py implicitly wrote to stdout
+# with mode='w'.
+test.must_match(['sub2', 'aaa.c'], "aaa.l\n%s\n" % lexflags, mode='r')
+test.must_match(['sub2', 'header.h'], 'lex header\n')
+test.must_match(['sub2', 'tables.t'], 'lex table\n')
+
+# To confirm the files from the file-output options were tracked,
+# do a clean and make sure they got removed.
+test.run(arguments='-c .', stderr=None)
+test.must_not_exist(test.workpath('sub2', 'header.h'))
+test.must_not_exist(test.workpath('sub2', 'tables.t'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/LEX/LEX.py b/test/LEX/LEX.py
index 7dc8436..3eb7960 100644
--- a/test/LEX/LEX.py
+++ b/test/LEX/LEX.py
@@ -33,6 +33,7 @@ test = TestSCons.TestSCons()
test.file_fixture('mylex.py')
test.write('SConstruct', """
+DefaultEnvironment(tools=[])
env = Environment(LEX=r'%(_python_)s mylex.py', tools=['default', 'lex'])
env.CFile(target='aaa', source='aaa.l')
env.CFile(target='bbb', source='bbb.lex')
diff --git a/test/LEX/LEXCOM.py b/test/LEX/LEXCOM.py
index 75f21b7..d4ed91b 100644
--- a/test/LEX/LEXCOM.py
+++ b/test/LEX/LEXCOM.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the ability to configure the $LEXCOM construction variable.
@@ -37,16 +36,19 @@ test = TestSCons.TestSCons()
test.file_fixture('mycompile.py')
test.write('SConstruct', """
-env = Environment(tools=['default', 'lex'],
- LEXCOM = r'%(_python_)s mycompile.py lex $TARGET $SOURCES')
-env.CFile(target = 'aaa', source = 'aaa.l')
-env.CFile(target = 'bbb', source = 'bbb.lex')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'lex'],
+ LEXCOM=r'%(_python_)s mycompile.py lex $TARGET $SOURCES',
+)
+env.CFile(target='aaa', source='aaa.l')
+env.CFile(target='bbb', source='bbb.lex')
""" % locals())
test.write('aaa.l', "aaa.l\n/*lex*/\n")
test.write('bbb.lex', "bbb.lex\n/*lex*/\n")
-test.run(arguments = '.')
+test.run(arguments='.')
test.must_match('aaa.c', "aaa.l\n")
test.must_match('bbb.c', "bbb.lex\n")
diff --git a/test/LEX/LEXCOMSTR.py b/test/LEX/LEXCOMSTR.py
index 2130d60..b888db8 100644
--- a/test/LEX/LEXCOMSTR.py
+++ b/test/LEX/LEXCOMSTR.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the $LEXCOMSTR construction variable allows you to customize
@@ -38,17 +37,20 @@ test = TestSCons.TestSCons()
test.file_fixture('mycompile.py')
test.write('SConstruct', """
-env = Environment(tools=['default', 'lex'],
- LEXCOM = r'%(_python_)s mycompile.py lex $TARGET $SOURCES',
- LEXCOMSTR = 'Lexing $TARGET from $SOURCE')
-env.CFile(target = 'aaa', source = 'aaa.l')
-env.CFile(target = 'bbb', source = 'bbb.lex')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'lex'],
+ LEXCOM=r'%(_python_)s mycompile.py lex $TARGET $SOURCES',
+ LEXCOMSTR='Lexing $TARGET from $SOURCE',
+)
+env.CFile(target='aaa', source='aaa.l')
+env.CFile(target='bbb', source='bbb.lex')
""" % locals())
test.write('aaa.l', "aaa.l\n/*lex*/\n")
test.write('bbb.lex', "bbb.lex\n/*lex*/\n")
-test.run(stdout = test.wrap_stdout("""\
+test.run(stdout=test.wrap_stdout("""\
Lexing aaa.c from aaa.l
Lexing bbb.c from bbb.lex
"""))
diff --git a/test/LEX/LEXFLAGS.py b/test/LEX/LEXFLAGS.py
index f7d16c1..9cb16ab 100644
--- a/test/LEX/LEXFLAGS.py
+++ b/test/LEX/LEXFLAGS.py
@@ -27,6 +27,7 @@ import sys
import sysconfig
import TestSCons
+from TestCmd import IS_WINDOWS
_python_ = TestSCons._python_
_exe = TestSCons._exe
@@ -37,7 +38,8 @@ test.subdir('in')
test.file_fixture('mylex.py')
-test.write('SConstruct', """
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
env = Environment(
LEX=r'%(_python_)s mylex.py',
LEXFLAGS='-x -I${TARGET.dir} -I${SOURCE.dir}',
@@ -48,12 +50,12 @@ env.CFile(target='out/aaa', source='in/aaa.l')
test.write(['in', 'aaa.l'], "aaa.l\nLEXFLAGS\nI_ARGS\n")
-test.run('.', stderr = None)
+test.run('.', stderr=None)
lexflags = ' -x -t'
-if sys.platform == 'win32' and not sysconfig.get_platform() in ("mingw",):
+if IS_WINDOWS and not sysconfig.get_platform() in ("mingw",):
lexflags = ' --nounistd' + lexflags
-# Read in with mode='r' because mylex.py implicitley wrote to stdout
+# Read in with mode='r' because mylex.py implicitly wrote to stdout
# with mode='w'.
test.must_match(['out', 'aaa.c'], "aaa.l\n%s\n out in\n" % lexflags, mode='r')
diff --git a/test/print_statement.py b/test/LEX/lex_headerfile.py
index 2205059..7ff035a 100644
--- a/test/print_statement.py
+++ b/test/LEX/lex_headerfile.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,32 +22,22 @@
# 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 the headerfile option for lex tool.
+"""
-import sys
import TestSCons
test = TestSCons.TestSCons()
+lex = test.where_is('win_flex') or test.where_is('lex') or test.where_is('flex')
+if not lex:
+ test.skip_test('No lex or flex found; skipping test.\n')
-test.write('SConstruct', """\
-print('python 3 style statement')
-Exit(0)
-""")
-
-test.run()
-
-test.write('SConstruct', """\
-print 'python 2 style statement'
-Exit(0)
-""")
+test.dir_fixture('lex_headerfile')
-if sys.version_info >= (3,0):
- test.skip_test('Python 2 print statement test, skipping on Python 3.\n')
-else:
- test.run()
+test.run(chdir='spaced path', arguments='.')
test.pass_test()
diff --git a/test/LEX/lex_headerfile/spaced path/SConstruct b/test/LEX/lex_headerfile/spaced path/SConstruct
new file mode 100644
index 0000000..aa4aca0
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/SConstruct
@@ -0,0 +1,2 @@
+DefaultEnvironment(tools=[])
+SConscript("src/SConscript") \ No newline at end of file
diff --git a/test/LEX/lex_headerfile/spaced path/src/SConscript b/test/LEX/lex_headerfile/spaced path/src/SConscript
new file mode 100644
index 0000000..a3f4bfd
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/src/SConscript
@@ -0,0 +1,10 @@
+env = Environment(tools=['lex'])
+
+def make_header_path(env, target, source, for_signature):
+ return target[1]
+
+env.Replace(LEX_HEADER_FILE_GEN=make_header_path)
+env.Append(LEXFLAGS=['--header-file=$LEX_HEADER_FILE_GEN'])
+
+env.CFile(target=['#gen_src/lexer.c', '#gen_src/lexer.l.h'], source='lexer.l')
+env.CFile(target=['#gen_src/lexer2.c', '#gen_src/lexer2.l.h'], source='lexer2.l') \ No newline at end of file
diff --git a/test/LEX/lex_headerfile/spaced path/src/lexer.l b/test/LEX/lex_headerfile/spaced path/src/lexer.l
new file mode 100644
index 0000000..66b82a4
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/src/lexer.l
@@ -0,0 +1 @@
+%% \ No newline at end of file
diff --git a/test/LEX/lex_headerfile/spaced path/src/lexer2.l b/test/LEX/lex_headerfile/spaced path/src/lexer2.l
new file mode 100644
index 0000000..66b82a4
--- /dev/null
+++ b/test/LEX/lex_headerfile/spaced path/src/lexer2.l
@@ -0,0 +1 @@
+%% \ No newline at end of file
diff --git a/test/LEX/live.py b/test/LEX/live.py
index 91a2d49..d223043 100644
--- a/test/LEX/live.py
+++ b/test/LEX/live.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 LEX and LEXFLAGS with a live lex.
@@ -36,19 +35,18 @@ _python_ = TestSCons._python_
test = TestSCons.TestSCons()
lex = test.where_is('win_flex') or test.where_is('lex') or test.where_is('flex')
-
if not lex:
test.skip_test('No lex or flex found; skipping test.\n')
test.file_fixture('wrapper.py')
test.write('SConstruct', """
+DefaultEnvironment(tools=[])
foo = Environment()
lex = foo.Dictionary('LEX')
-bar = Environment(LEX = r'%(_python_)s wrapper.py ' + lex,
- LEXFLAGS = '-b')
-foo.Program(target = 'foo', source = 'foo.l')
-bar.Program(target = 'bar', source = 'bar.l')
+bar = Environment(LEX=r'%(_python_)s wrapper.py ' + lex, LEXFLAGS='-b')
+foo.Program(target='foo', source='foo.l')
+bar.Program(target='bar', source='bar.l')
""" % locals())
lex = r"""
@@ -70,22 +68,18 @@ main()
"""
test.write('foo.l', lex % ('foo.l', 'foo.l'))
-
test.write('bar.l', lex % ('bar.l', 'bar.l'))
-test.run(arguments = 'foo' + _exe, stderr = None)
-
+test.run(arguments='foo' + _exe, stderr=None)
test.must_not_exist(test.workpath('wrapper.out'))
test.must_not_exist(test.workpath('lex.backup'))
-test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n")
-
-test.run(arguments = 'bar' + _exe)
-
+test.run(program=test.workpath('foo'), stdin="a\n", stdout="Afoo.lA\n")
+test.run(arguments='bar' + _exe)
test.must_match(test.workpath('wrapper.out'), "wrapper.py\n")
test.must_exist(test.workpath('lex.backup'))
-test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n")
+test.run(program=test.workpath('bar'), stdin="b\n", stdout="Bbar.lB\n")
test.pass_test()
diff --git a/test/LEX/live_mingw.py b/test/LEX/live_mingw.py
index 5c64eb8..6c865e8 100644
--- a/test/LEX/live_mingw.py
+++ b/test/LEX/live_mingw.py
@@ -30,13 +30,14 @@ Test LEX and LEXFLAGS and unistd.h with a live lex in mingw environment.
import sys
import TestSCons
+from TestCmd import IS_WINDOWS
_exe = TestSCons._exe
_python_ = TestSCons._python_
test = TestSCons.TestSCons()
-if sys.platform != 'win32':
+if not IS_WINDOWS:
test.skip_test('Not windows environment; skipping test.\n')
if not test.where_is('gcc'):
@@ -50,6 +51,7 @@ if not lex:
test.file_fixture('wrapper.py')
test.write('SConstruct', """
+DefaultEnvironment(tools=[])
foo = Environment(tools=['default', 'mingw', 'lex'], LEXUNISTD="")
lex = foo.Dictionary('LEX')
bar = Environment(
diff --git a/test/LEX/no_lex.py b/test/LEX/no_lex.py
index 89ffdc7..e53b02e 100644
--- a/test/LEX/no_lex.py
+++ b/test/LEX/no_lex.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 Environments are functional and return None when no lex tool is found.
@@ -35,7 +34,7 @@ test = TestSCons.TestSCons()
test.write('SConstruct', """
import SCons
-def no_lex(env, key_program, default_paths=[]):
+def no_lex(env, key_program, default_paths=[], add_path=False):
return None
class TestEnvironment(SCons.Environment.Environment):
@@ -44,11 +43,12 @@ class TestEnvironment(SCons.Environment.Environment):
SCons.Tool.find_program_path = no_lex
+DefaultEnvironment(tools=[])
foo = TestEnvironment(tools=['default', 'lex'])
print(foo.Dictionary('LEX'))
""" % locals())
-test.run(arguments = '-Q -s', stdout = 'None\n' )
+test.run(arguments='-Q -s', stdout='None\n')
test.pass_test()
diff --git a/test/Libs/LIBPATH.py b/test/Libs/LIBPATH.py
index b5a1b54..74e9219 100644
--- a/test/Libs/LIBPATH.py
+++ b/test/Libs/LIBPATH.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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.path
import time
@@ -40,20 +39,18 @@ test.subdir('lib1', 'lib2')
prog1 = test.workpath('prog') + _exe
prog2 = test.workpath(dll_ + 'shlib') + _dll
-test.write('SConstruct', """
-env1 = Environment(LIBS = [ 'foo1' ],
- LIBPATH = [ '$FOO' ],
- FOO='./lib1')
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env1 = Environment(LIBS=['foo1'], LIBPATH=['$FOO'], FOO='./lib1')
f1 = env1.SharedObject('f1', 'f1.c')
-env1.Program(target = 'prog', source = 'prog.c')
-env1.Library(target = './lib1/foo1', source = f1)
+env1.Program(target='prog', source='prog.c')
+env1.Library(target='./lib1/foo1', source=f1)
-env2 = Environment(LIBS = 'foo2',
- LIBPATH = '.')
-env2.SharedLibrary(target = 'shlib', source = 'shlib.c', no_import_lib = 1)
-env2.Library(target = 'foo2', source = f1)
+env2 = Environment(LIBS='foo2', LIBPATH='.')
+env2.SharedLibrary(target='shlib', source='shlib.c', no_import_lib=1)
+env2.Library(target='foo2', source=f1)
""")
test.write('f1.c', r"""
@@ -99,8 +96,8 @@ test.run(program = prog1,
oldtime1 = os.path.getmtime(prog1)
oldtime2 = os.path.getmtime(prog2)
-time.sleep(2)
-test.run(arguments = '.')
+test.sleep() # delay for timestamps
+test.run(arguments='.')
test.fail_test(oldtime1 != os.path.getmtime(prog1))
test.fail_test(oldtime2 != os.path.getmtime(prog2))
@@ -115,30 +112,26 @@ f1(void)
}
""")
-test.run(arguments = '.',
- stderr=TestSCons.noisy_ar,
- match=TestSCons.match_re_dotall)
-test.run(program = prog1,
- stdout = "f1.c 1\nprog.c\n")
+test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall)
+test.run(program=prog1, stdout="f1.c 1\nprog.c\n")
test.fail_test(oldtime2 == os.path.getmtime(prog2))
#test.up_to_date(arguments = '.')
# Change LIBPATH and make sure we don't rebuild because of it.
-test.write('SConstruct', """
-env1 = Environment(LIBS = [ 'foo1' ],
- LIBPATH = [ './lib1', './lib2' ])
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env1 = Environment(LIBS=['foo1'], LIBPATH=['./lib1', './lib2'])
f1 = env1.SharedObject('f1', 'f1.c')
-env1.Program(target = 'prog', source = 'prog.c')
-env1.Library(target = './lib1/foo1', source = f1)
+env1.Program(target='prog', source='prog.c')
+env1.Library(target='./lib1/foo1', source=f1)
-env2 = Environment(LIBS = 'foo2',
- LIBPATH = Split('. ./lib2'))
-env2.SharedLibrary(target = 'shlib', source = 'shlib.c', no_import_lib = 1)
-env2.Library(target = 'foo2', source = f1)
+env2 = Environment(LIBS='foo2', LIBPATH=Split('. ./lib2'))
+env2.SharedLibrary(target='shlib', source='shlib.c', no_import_lib=1)
+env2.Library(target='foo2', source=f1)
""")
-test.up_to_date(arguments = '.', stderr=None)
+test.up_to_date(arguments='.', stderr=None)
test.write('f1.c', r"""
#include <stdio.h>
@@ -150,27 +143,23 @@ f1(void)
}
""")
-test.run(arguments = '.',
- stderr=TestSCons.noisy_ar,
- match=TestSCons.match_re_dotall)
-test.run(program = prog1,
- stdout = "f1.c 2\nprog.c\n")
+test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall)
+test.run(program=prog1, stdout="f1.c 2\nprog.c\n")
-test.up_to_date(arguments = '.')
+test.up_to_date(arguments='.')
# We need at least one file for some implementations of the Library
# builder, notably the SGI one.
test.write('empty.c', 'int a=0;\n')
# Check that a null-string LIBPATH doesn't blow up.
-test.write('SConstruct', """
-env = Environment(LIBPATH = '')
-env.Library('foo', source = 'empty.c')
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
+env = Environment(LIBPATH='')
+env.Library('foo', source='empty.c')
""")
-test.run(arguments = '.',
- stderr=TestSCons.noisy_ar,
- match=TestSCons.match_re_dotall)
+test.run(arguments='.', stderr=TestSCons.noisy_ar, match=TestSCons.match_re_dotall)
test.pass_test()
diff --git a/test/Libs/LIBS.py b/test/Libs/LIBS.py
index 3d9eb29..f5cef77 100644
--- a/test/Libs/LIBS.py
+++ b/test/Libs/LIBS.py
@@ -51,6 +51,7 @@ foo5_exe = test.workpath('foo5' + _exe)
slprog_exe = test.workpath('slprog' + _exe)
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(LIBS=['bar'], LIBPATH = '.')
env.Program(target='foo1', source='foo1.c')
env2 = Environment(LIBS=[File(r'%s')], LIBPATH = '.')
@@ -161,6 +162,7 @@ test.run(program=slprog_exe, stdout='sl.c\nslprog.c\n')
#
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(LIBS=['baz'])
env.Program(target='foo1', source='foo1.c', LIBS=['$LIBS', 'bar'], LIBPATH = '.')
SConscript('sub1/SConscript', 'env')
@@ -173,6 +175,7 @@ test.run(program=foo1_exe, stdout='sub1/bar.c\nsub2/baz.c\n')
#
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(LIBS=['bar', 'baz'], LIBPATH = '.')
env.Program(target='foo1', source='foo1.c')
SConscript('sub1/SConscript', 'env')
@@ -191,6 +194,7 @@ test.run(program=foo1_exe, stdout='sub1/bar.c\nsub1/baz.c\n')
#
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env.Program(target='foo1', source='foo1.c', LIBS=['bar', 'baz'], LIBPATH = '.')
SConscript('sub1/SConscript', 'env')
diff --git a/test/Libs/SharedLibraryIxes.py b/test/Libs/SharedLibraryIxes.py
index 93d67ea..55fcd27 100644
--- a/test/Libs/SharedLibraryIxes.py
+++ b/test/Libs/SharedLibraryIxes.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 build shared libraries and link against shared
@@ -50,6 +49,7 @@ if isWindows:
isWindows = False
isMingw = True
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
# Make sure that the shared library can be located at runtime.
diff --git a/test/MSVC/MSVC_NOTFOUND_POLICY.py b/test/MSVC/MSVC_NOTFOUND_POLICY.py
new file mode 100644
index 0000000..f5a8de2
--- /dev/null
+++ b/test/MSVC/MSVC_NOTFOUND_POLICY.py
@@ -0,0 +1,126 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test the MSVC_NOTFOUND_POLICY construction variable and functions.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+import textwrap
+
+# Test construction variable with valid symbols
+test.write('SConstruct', textwrap.dedent(
+ """
+ env_list = []
+ DefaultEnvironment(tools=[])
+ for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']:
+ for policy in [symbol, symbol.upper(), symbol.lower()]:
+ env = Environment(MSVC_NOTFOUND_POLICY=policy, tools=['msvc'])
+ env_list.append(env)
+ """
+))
+test.run(arguments='-Q -s', stdout='')
+
+# Test construction variable with invalid symbol
+test.write('SConstruct', textwrap.dedent(
+ """
+ env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Undefined', tools=['msvc'])
+ """
+))
+test.run(arguments='-Q -s', status=2, stderr=None)
+expect = "MSVCArgumentError: Value specified for MSVC_NOTFOUND_POLICY is not supported: 'Undefined'."
+test.must_contain_all(test.stderr(), expect)
+
+# Test environment construction with global policy
+test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_notfound_policy
+ msvc_set_notfound_policy('Exception')
+ env = Environment(MSVC_VERSION='12.9', tools=['msvc'])
+ """
+))
+test.run(arguments='-Q -s', status=2, stderr=None)
+expect = "MSVCVersionNotFound: MSVC version '12.9' was not found."
+test.must_contain_all(test.stderr(), expect)
+
+# Test environment construction with global policy and construction variable ignored
+test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_notfound_policy
+ msvc_set_notfound_policy('Exception')
+ env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY=None, tools=['msvc'])
+ """
+))
+test.run(arguments='-Q -s', status=2, stderr=None)
+expect = "MSVCVersionNotFound: MSVC version '12.9' was not found."
+test.must_contain_all(test.stderr(), expect)
+
+# Test environment construction with construction variable
+test.write('SConstruct', textwrap.dedent(
+ """
+ env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Error', tools=['msvc'])
+ """
+))
+test.run(arguments='-Q -s', status=2, stderr=None)
+expect = "MSVCVersionNotFound: MSVC version '12.9' was not found."
+test.must_contain_all(test.stderr(), expect)
+
+# Test environment construction with global policy
+test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_notfound_policy
+ msvc_set_notfound_policy('Warning')
+ env = Environment(MSVC_VERSION='12.9', tools=['msvc'])
+ """
+))
+test.run(arguments="-Q -s --warn=visual-c-missing .", status=0, stderr=None)
+expect = "scons: warning: MSVC version '12.9' was not found."
+test.must_contain_all(test.stderr(), expect)
+
+# Test environment construction with construction variable
+test.write('SConstruct', textwrap.dedent(
+ """
+ env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Warning', tools=['msvc'])
+ """
+))
+test.run(arguments="-Q -s --warn=visual-c-missing .", status=0, stderr=None)
+expect = "scons: warning: MSVC version '12.9' was not found."
+test.must_contain_all(test.stderr(), expect)
+
+# Test environment construction with construction variable (override global)
+test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_notfound_policy
+ msvc_set_notfound_policy('Exception')
+ env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Ignore', tools=['msvc'])
+ """
+))
+test.run(arguments='-Q -s', stdout='')
+
+test.pass_test()
+
diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py
new file mode 100644
index 0000000..5aa1682
--- /dev/null
+++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py
@@ -0,0 +1,141 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test the MSVC_SCRIPTERROR_POLICY construction variable and functions.
+"""
+
+import TestSCons
+import textwrap
+
+from SCons.Tool.MSCommon.vc import get_installed_vcs_components
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+installed_versions = get_installed_vcs_components()
+
+default_version = installed_versions[0]
+
+# Test construction variable with valid symbols
+test.write('SConstruct', textwrap.dedent(
+ """
+ env_list = []
+ DefaultEnvironment(tools=[])
+ for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']:
+ for policy in [symbol, symbol.upper(), symbol.lower()]:
+ env = Environment(MSVC_SCRIPTERROR_POLICY=policy, tools=['msvc'])
+ env_list.append(env)
+ """
+))
+test.run(arguments='-Q -s', stdout='')
+
+if default_version.msvc_vernum >= 14.1:
+ # Need VS2017 or later for MSVC_SCRIPT_ARGS
+
+ # Test environment construction with construction variable (invalid)
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Undefined', tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: Value specified for MSVC_SCRIPTERROR_POLICY is not supported: 'Undefined'."
+ test.must_contain_all(test.stderr(), expect)
+
+ # Test environment construction with construction variable (override global)
+ test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_scripterror_policy
+ DefaultEnvironment(tools=[])
+ msvc_set_scripterror_policy('Exception')
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Ignore', tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # Test environment construction with global policy
+ test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_scripterror_policy
+ DefaultEnvironment(tools=[])
+ msvc_set_scripterror_policy('Exception')
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCScriptExecutionError: vc script errors detected:"
+ test.must_contain_all(test.stderr(), expect)
+
+ # Test environment construction with global policy and construction variable ignored
+ test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_scripterror_policy
+ DefaultEnvironment(tools=[])
+ msvc_set_scripterror_policy('Exception')
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY=None, tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCScriptExecutionError: vc script errors detected:"
+ test.must_contain_all(test.stderr(), expect)
+
+ # Test environment construction with construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Error', tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCScriptExecutionError: vc script errors detected:"
+ test.must_contain_all(test.stderr(), expect)
+
+ # Test environment construction with global policy
+ test.write('SConstruct', textwrap.dedent(
+ """
+ from SCons.Tool.MSCommon import msvc_set_scripterror_policy
+ DefaultEnvironment(tools=[])
+ msvc_set_scripterror_policy('Warning')
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', status=0, stderr=None)
+ expect = "scons: warning: vc script errors detected:"
+ test.must_contain_all(test.stderr(), expect)
+
+ # Test environment construction with construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Warning', tools=['msvc'])
+ """
+ ))
+ test.run(arguments='-Q -s', status=0, stderr=None)
+ expect = "scons: warning: vc script errors detected:"
+ test.must_contain_all(test.stderr(), expect)
+
+test.pass_test()
+
diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py
new file mode 100644
index 0000000..e2f78be
--- /dev/null
+++ b/test/MSVC/MSVC_SDK_VERSION.py
@@ -0,0 +1,241 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test the MSVC_SDK_VERSION construction variable.
+"""
+import textwrap
+
+from SCons.Tool.MSCommon.vc import get_installed_vcs_components
+from SCons.Tool.MSCommon import msvc_sdk_versions
+from SCons.Tool.MSCommon import msvc_toolset_versions
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+
+installed_versions = get_installed_vcs_components()
+
+default_version = installed_versions[0]
+
+GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0]
+LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0]
+
+default_sdk_versions_uwp = msvc_sdk_versions(version=None, msvc_uwp_app=True)
+default_sdk_versions_def = msvc_sdk_versions(version=None, msvc_uwp_app=False)
+
+have_140 = any([v.msvc_verstr == '14.0' for v in GE_VS2015_versions])
+
+def version_major(version):
+ components = version.split('.')
+ if len(components) >= 2:
+ return components[0] + '.' + components[1][0]
+ if len(components) == 1:
+ return components[0] + '.0'
+ return version
+
+def version_major_list(version_list):
+ versions = []
+ seen_major = set()
+ for version in version_list:
+ major = version_major(version)
+ if major in seen_major:
+ continue
+ versions.append(version)
+ seen_major.add(major)
+ return versions
+
+if GE_VS2015_versions:
+
+ for supported in GE_VS2015_versions:
+
+ sdk_versions_uwp = msvc_sdk_versions(version=supported.msvc_version, msvc_uwp_app=True)
+ sdk_versions_def = msvc_sdk_versions(version=supported.msvc_version, msvc_uwp_app=False)
+
+ # find sdk version for each major SDK
+ sdk_versions = version_major_list(sdk_versions_def)
+
+ for sdk_version in sdk_versions:
+
+ # sdk version construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={0}, MSVC_SDK_VERSION={1}, tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\{2}\\\\' not in lib_path:
+ raise RuntimeError("{1} not found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version), repr(sdk_version), sdk_version)
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # sdk version script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={0}, MSVC_SCRIPT_ARGS={1}, tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\{2}\\\\' not in lib_path:
+ raise RuntimeError("{1} not found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version), repr(sdk_version), sdk_version)
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # sdk version construction variable and script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(sdk_version), repr(sdk_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}:".format(
+ repr(sdk_version), repr(sdk_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # sdk version is not supported
+ invalid_sdk_version = '9.1'
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(invalid_sdk_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_SDK_VERSION ({}) is not supported:".format(
+ repr(invalid_sdk_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # sdk version not found
+ missing_sdk_version = '10.0.12345.6'
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(missing_sdk_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCSDKVersionNotFound: MSVC_SDK_VERSION {} not found for platform type 'Desktop':".format(
+ repr(missing_sdk_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # platform contraints: 8.1 and UWP
+ if '8.1' in sdk_versions:
+
+ if supported.msvc_vernum > 14.0:
+
+ toolset_full_versions = msvc_toolset_versions(supported.msvc_version, full=True, sxs=False)
+ toolset_versions = version_major_list(toolset_full_versions)
+
+ # toolset msvc_version != current msvc_version and toolset msvc_version != 14.0
+ toolset_candidates = [v for v in toolset_versions if version_major(v) not in (supported.msvc_verstr, '14.0')]
+ toolset_version = toolset_candidates[0] if toolset_candidates else None
+
+ # sdk version 8.1, UWP, and msvc_verson > VS2015
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc'])
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: MSVC_VERSION {} > '14.0' VS2015:".format(
+ repr(supported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ if toolset_version:
+
+ # sdk version 8.1, UWP, and msvc_toolset_verson > VS2015
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(toolset_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: toolset version {} > '14.0' VS2015:".format(
+ repr(toolset_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ if have_140:
+
+ # sdk version 8.1, UWP, and msvc_toolset_version > VS2015
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_TOOLSET_VERSION='14.0', MSVC_UWP_APP=True, tools=['msvc'])
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ elif supported.msvc_vernum == 14.0:
+
+ # sdk version 8.1, UWP, and msvc_verson == VS2015
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc'])
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+if LT_VS2015_versions:
+
+ for unsupported in LT_VS2015_versions:
+ # must be VS2015 or later
+
+ sdk_version = default_sdk_versions_def[0] if default_sdk_versions_def else '8.1'
+
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc'])
+ """.format(repr(unsupported.msvc_version), repr(sdk_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format(
+ repr(sdk_version), repr(unsupported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc'])
+ """.format(repr(unsupported.msvc_version), repr(sdk_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format(
+ repr(sdk_version), repr(unsupported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+test.pass_test()
+
diff --git a/test/MSVC/MSVC_SPECTRE_LIBS.py b/test/MSVC/MSVC_SPECTRE_LIBS.py
new file mode 100644
index 0000000..ea98fd5
--- /dev/null
+++ b/test/MSVC/MSVC_SPECTRE_LIBS.py
@@ -0,0 +1,152 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test the MSVC_SPECTRE_LIBS construction variable.
+"""
+
+import TestSCons
+import textwrap
+
+from SCons.Tool.MSCommon.vc import get_installed_vcs_components
+from SCons.Tool.MSCommon import msvc_toolset_versions_spectre
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+installed_versions = get_installed_vcs_components()
+
+GE_VS2017_versions = [v for v in installed_versions if v.msvc_vernum >= 14.1]
+LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1]
+
+if GE_VS2017_versions:
+ # VS2017 and later for toolset argument
+
+ for supported in GE_VS2017_versions:
+
+ spectre_toolset_versions = msvc_toolset_versions_spectre(supported.msvc_version)
+ spectre_toolset_version = spectre_toolset_versions[0] if spectre_toolset_versions else None
+
+ if spectre_toolset_version:
+
+ # spectre libs using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\lib\\\\spectre\\\\' not in lib_path.lower():
+ raise RuntimeError("'spectre' not found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # spectre libs using script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\lib\\\\spectre\\\\' not in lib_path.lower():
+ raise RuntimeError("'spectre' not found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # error construction variable and script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', tools=['msvc'])
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: multiple spectre declarations: MSVC_SPECTRE_LIBS=True and MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre':"
+ test.must_contain_all(test.stderr(), expect)
+
+ else:
+
+ # spectre libs using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc'])
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ if not test.stderr().strip().startswith('MSVCSpectreLibsNotFound: Spectre libraries not found'):
+ test.fail_test()
+
+ # spectre libs using script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', MSVC_SCRIPTERROR_POLICY='error', tools=['msvc'])
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ if not test.stderr().strip().startswith('MSVCScriptExecutionError: vc script errors detected:'):
+ test.fail_test()
+
+ # spectre libs using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=False, tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\lib\\\\spectre\\\\' in lib_path.lower():
+ raise RuntimeError("'spectre' found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+if LT_VS2017_versions:
+ # VS2015 and earlier for toolset argument error
+
+ for unsupported in LT_VS2017_versions:
+
+ # must be VS2017 or later
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc'])
+ """.format(repr(unsupported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ if not test.stderr().strip().startswith('MSVCArgumentError: MSVC_SPECTRE_LIBS (True) constraint violation:'):
+ test.fail_test()
+
+ for disabled in (False, None):
+
+ # ignore
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS={}, tools=['msvc'])
+ """.format(repr(unsupported.msvc_version), disabled)
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+test.pass_test()
+
diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py
new file mode 100644
index 0000000..db40c95
--- /dev/null
+++ b/test/MSVC/MSVC_TOOLSET_VERSION.py
@@ -0,0 +1,233 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test the MSVC_TOOLSET_VERSION construction variable.
+"""
+import textwrap
+
+from SCons.Tool.MSCommon.vc import get_installed_vcs_components
+from SCons.Tool.MSCommon import msvc_toolset_versions
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+installed_versions = get_installed_vcs_components()
+
+default_version = installed_versions[0]
+
+GE_VS2017_versions = [v for v in installed_versions if v.msvc_vernum >= 14.1]
+LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1]
+LT_VS2015_versions = [v for v in LT_VS2017_versions if v.msvc_vernum < 14.0]
+
+if GE_VS2017_versions:
+ # VS2017 and later for toolset argument
+
+ for supported in GE_VS2017_versions:
+
+ toolset_full_versions = msvc_toolset_versions(supported.msvc_version, full=True, sxs=False)
+ toolset_full_version = toolset_full_versions[0] if toolset_full_versions else None
+
+ toolset_sxs_versions = msvc_toolset_versions(supported.msvc_version, full=False, sxs=True)
+ toolset_sxs_version = toolset_sxs_versions[0] if toolset_sxs_versions else None
+
+ if toolset_full_version:
+
+ # toolset version using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={0}, MSVC_TOOLSET_VERSION={1}, tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\{2}\\\\' not in lib_path:
+ raise RuntimeError("{1} not found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version), repr(toolset_full_version), toolset_full_version)
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # toolset version using script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={0}, MSVC_SCRIPT_ARGS='-vcvars_ver={1}', tools=['msvc'])
+ lib_path = env['ENV']['LIB']
+ if '\\\\{1}\\\\' not in lib_path:
+ raise RuntimeError("'{1}' not found in lib path " + lib_path)
+ """.format(repr(supported.msvc_version), toolset_full_version)
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ if toolset_sxs_version:
+
+ # sxs toolset version using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(toolset_sxs_version))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # msvc_version as toolset version
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(supported.msvc_verstr))
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # msvc_version as toolset version using script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc'])
+ """.format(repr(supported.msvc_version), supported.msvc_verstr)
+ ))
+ test.run(arguments='-Q -s', stdout='')
+
+ # error toolset version and script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(supported.msvc_verstr), supported.msvc_verstr)
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS='-vcvars_ver={}':".format(
+ repr(supported.msvc_verstr), supported.msvc_verstr
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # msvc_toolset_version does not exist (hopefully)
+ missing_toolset_version = supported.msvc_verstr + '9.99999'
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(missing_toolset_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCToolsetVersionNotFound: MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}:".format(
+ repr(missing_toolset_version), repr(supported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # msvc_toolset_version is invalid (format)
+ invalid_toolset_version = supported.msvc_verstr + '9.99999.99999'
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(invalid_toolset_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) format is not supported:".format(
+ repr(invalid_toolset_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # msvc_toolset_version is invalid (version greater than msvc version)
+ invalid_toolset_vernum = round(supported.msvc_vernum + 0.1, 1)
+ invalid_toolset_version = str(invalid_toolset_vernum)
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(invalid_toolset_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION:".format(
+ repr(invalid_toolset_version), repr(invalid_toolset_version), repr(supported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # msvc_toolset_version is invalid (version less than 14.0)
+ invalid_toolset_version = '12.0'
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(invalid_toolset_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < '14.0' VS2015:".format(
+ repr(invalid_toolset_version), repr(invalid_toolset_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+ # 14.0 toolset is invalid (toolset version != 14.0)
+ invalid_toolset_version = '14.00.00001'
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(invalid_toolset_version))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0':".format(
+ repr(invalid_toolset_version), repr(invalid_toolset_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+if LT_VS2017_versions:
+ # VS2015 and earlier for toolset argument error
+
+ for unsupported in LT_VS2017_versions:
+
+ # must be VS2017 or later
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc'])
+ """.format(repr(unsupported.msvc_version), repr(unsupported.msvc_verstr))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.1' VS2017:".format(
+ repr(unsupported.msvc_verstr), repr(unsupported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+if LT_VS2015_versions:
+ # VS2013 and earlier for script argument error
+
+ for unsupported in LT_VS2015_versions:
+
+ # must be VS2015 or later for MSVC_SCRIPT_ARGS
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc'])
+ """.format(repr(unsupported.msvc_version), unsupported.msvc_verstr)
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ('-vcvars_ver={}') constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format(
+ unsupported.msvc_verstr, repr(unsupported.msvc_version)
+ )
+ test.must_contain_all(test.stderr(), expect)
+
+test.pass_test()
+
diff --git a/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct b/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct
index 68ec4b0..54d140e 100644
--- a/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct
+++ b/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct
@@ -1,5 +1,8 @@
import os
+if 'SCONS_CACHE_MSVC_CONFIG' in os.environ:
+ del os.environ['SCONS_CACHE_MSVC_CONFIG']
+
os.environ['SCONS_MSCOMMON_DEBUG']='MSDEBUG_OUTPUT.log'
DefaultEnvironment(tools=[])
diff --git a/test/MSVC/MSVC_UWP_APP.py b/test/MSVC/MSVC_UWP_APP.py
index 6ccd117..645fc0e 100644
--- a/test/MSVC/MSVC_UWP_APP.py
+++ b/test/MSVC/MSVC_UWP_APP.py
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+# MIT License
#
-# __COPYRIGHT__
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,190 +20,148 @@
# 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 the ability to configure the $MSVC_UWP_APP construction variable with
-the desired effect.
+Test the MSVC_UWP_APP construction variable.
"""
+import textwrap
+import re
+from SCons.Tool.MSCommon.vc import get_installed_vcs_components
import TestSCons
-import SCons.Tool.MSCommon.vc as msvc
-from SCons.Tool.MSCommon.vc import get_msvc_version_numeric
-
-def AreVCStoreLibPathsInLIBPATH(output):
- libpath = None
- msvc_version = None
- UWP_APP = None
- lines = output.splitlines()
- for line in lines:
- if 'env[ENV][LIBPATH]=' in line:
- libpath = line.split('=')[1]
- elif 'env[MSVC_VERSION]=' in line:
- msvc_version = line.split('=')[1]
- elif 'env[ENV][VSCMD_ARG_app_plat]=' in line:
- UWP_APP = line.split('=')[1]
-
- if not libpath or not msvc_version:
- # Couldn't find the libpath or msvc version in the output
- return (False, False, None)
-
- libpaths = libpath.lower().split(';')
- msvc_num = float(get_msvc_version_numeric(msvc_version))
-
- (vclibstore_path_present, vclibstorerefs_path_present) = (False, False)
- for path in libpaths:
- # Look for the Store VC Lib paths in the LIBPATH:
- # [VS install path]\VC\LIB\store[\arch] and
- # [VS install path]\VC\LIB\store\references
- # For example,
- # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\amd64
- # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\references
-
- if msvc_num <= 14:
- if r'vc\lib\store\references' in path:
- vclibstorerefs_path_present = True
- elif r'vc\lib\store' in path:
- vclibstore_path_present = True
- elif msvc_num > 14:
- if UWP_APP == "UWP":
- if(r'\lib\x86\store\references' in path
- or r'\lib\x64\store' in path):
- vclibstorerefs_path_present = True
- vclibstore_path_present = True
-
- return (vclibstore_path_present, vclibstorerefs_path_present, msvc_version)
-
-_python_ = TestSCons._python_
test = TestSCons.TestSCons()
test.skip_if_not_msvc()
-installed_msvc_versions = msvc.get_installed_vcs()
-# MSVC guaranteed to be at least one version on the system or else
-# skip_if_not_msvc() function would have skipped the test
-
-msvc_140 = '14.0' in installed_msvc_versions
-msvc_141 = '14.1' in installed_msvc_versions
-msvc_142 = '14.2' in installed_msvc_versions
-msvc_143 = '14.3' in installed_msvc_versions
-
-if not any((msvc_140, msvc_141, msvc_142, msvc_143)):
- test.skip_test("Available MSVC doesn't support App store\n")
-
-if msvc_140:
- test.write('SConstruct', """\
-if ARGUMENTS.get('MSVC_UWP_APP'):
- help_vars = Variables()
- help_vars.Add(EnumVariable(
- 'MSVC_UWP_APP',
- 'Build a Universal Windows Platform (UWP) Application',
- '0',
- allowed_values=('0', '1')))
-else:
- help_vars = None
-env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.0')
-# Print the ENV LIBPATH to stdout
-print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH'))
-print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION'))
-""")
-
- # Test setting MSVC_UWP_APP is '1' (True)
- test.run(arguments = "MSVC_UWP_APP=1")
- (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout())
- test.fail_test((vclibstore_path_present is False) or (vclibstorerefs_path_present is False),
- message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version)
-
- # Test setting MSVC_UWP_APP is '0' (False)
- test.run(arguments = "MSVC_UWP_APP=0")
- (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout())
- test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True),
- message='VC Store LIBPATHs present when MSVC_UWP_APP=0 (msvc_version=%s)' % msvc_version)
-
- # Test not setting MSVC_UWP_APP
- test.run(arguments = "")
- (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout())
- test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True),
- message='VC Store LIBPATHs present when MSVC_UWP_APP not set (msvc_version=%s)' % msvc_version)
-
-if msvc_141 or msvc_142 or msvc_143:
- if msvc_143:
- test.write('SConstruct', """\
-if ARGUMENTS.get('MSVC_UWP_APP'):
- help_vars = Variables()
- help_vars.Add(EnumVariable(
- 'MSVC_UWP_APP',
- 'Build a Universal Windows Platform (UWP) Application',
- '0',
- allowed_values=('0', '1')))
-else:
- help_vars = None
-env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.3')
-# Print the ENV LIBPATH to stdout
-print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH'))
-print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION'))
-print('env[ENV][VSCMD_ARG_app_plat]=%s' % env.get('ENV').get('VSCMD_ARG_app_plat'))
-""")
- elif msvc_142:
- test.write('SConstruct', """\
-if ARGUMENTS.get('MSVC_UWP_APP'):
- help_vars = Variables()
- help_vars.Add(EnumVariable(
- 'MSVC_UWP_APP',
- 'Build a Universal Windows Platform (UWP) Application',
- '0',
- allowed_values=('0', '1')))
-else:
- help_vars = None
-env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.2')
-# Print the ENV LIBPATH to stdout
-print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH'))
-print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION'))
-print('env[ENV][VSCMD_ARG_app_plat]=%s' % env.get('ENV').get('VSCMD_ARG_app_plat'))
-""")
- elif msvc_141:
- test.write('SConstruct', """\
-if ARGUMENTS.get('MSVC_UWP_APP'):
- help_vars = Variables()
- help_vars.Add(EnumVariable(
- 'MSVC_UWP_APP',
- 'Build a Universal Windows Platform (UWP) Application',
- '0',
- allowed_values=('0', '1')))
-else:
- help_vars = None
-env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.1')
-# Print the ENV LIBPATH to stdout
-print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH'))
-print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION'))
-print('env[ENV][VSCMD_ARG_app_plat]=%s' % env.get('ENV').get('VSCMD_ARG_app_plat'))
-""")
-
- # Test setting MSVC_UWP_APP is '1' (True)
- test.run(arguments = "MSVC_UWP_APP=1")
- (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout())
- test.fail_test((vclibstore_path_present is False) or (vclibstorerefs_path_present is False),
- message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version)
-
- # Test setting MSVC_UWP_APP is '0' (False)
- test.run(arguments = "MSVC_UWP_APP=0")
- (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout())
- test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True),
- message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version)
-
- # Test not setting MSVC_UWP_APP
- test.run(arguments = "")
- (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout())
- test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True),
- message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version)
+
+installed_versions = get_installed_vcs_components()
+
+GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0]
+LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0]
+
+# Look for the Store VC Lib paths in the LIBPATH:
+# [VS install path]\VC\LIB\store[\arch] and
+# [VS install path]\VC\LIB\store\references
+# For example,
+# C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\amd64
+# C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\references
+
+re_lib_eq2015_1 = re.compile(r'\\vc\\lib\\store\\references', re.IGNORECASE)
+re_lib_eq2015_2 = re.compile(r'\\vc\\lib\\store', re.IGNORECASE)
+
+re_lib_ge2017_1 = re.compile(r'\\lib\\x86\\store\\references', re.IGNORECASE)
+re_lib_ge2017_2 = re.compile(r'\\lib\\x64\\store', re.IGNORECASE)
+
+
+def check_libpath(msvc, active, output):
+
+ def _check_libpath(msvc, output):
+ outdict = {key.strip(): val.strip() for key, val in [line.split('|') for line in output.splitlines()]}
+ platform = outdict.get('PLATFORM', '')
+ libpath = outdict.get('LIBPATH', '')
+ n_matches = 0
+ if msvc.msvc_verstr == '14.0':
+ for regex in (re_lib_eq2015_1, re_lib_eq2015_2):
+ if regex.search(libpath):
+ n_matches += 1
+ return n_matches >= 2, 'store', libpath
+ elif platform == 'UWP':
+ for regex in (re_lib_ge2017_1, re_lib_ge2017_2):
+ if regex.search(libpath):
+ n_matches += 1
+ return n_matches > 0, 'uwp', libpath
+ return False, 'uwp', libpath
+
+ found, kind, libpath = _check_libpath(msvc, output)
+
+ failmsg = None
+
+ if active and not found:
+ failmsg = 'msvc version {} {} paths not found in lib path {}'.format(
+ repr(msvc.msvc_version), repr(kind), repr(libpath)
+ )
+ elif not active and found:
+ failmsg = 'msvc version {} {} paths found in lib path {}'.format(
+ repr(msvc.msvc_version), repr(kind), repr(libpath)
+ )
+
+ return failmsg
+
+if GE_VS2015_versions:
+ # VS2015 and later for uwp/store argument
+
+ for supported in GE_VS2015_versions:
+
+ for msvc_uwp_app in (True, '1', False, '0', None):
+
+ active = msvc_uwp_app in (True, '1')
+
+ # uwp using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_UWP_APP={}, tools=['msvc'])
+ print('LIBPATH|' + env['ENV'].get('LIBPATH', ''))
+ print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat',''))
+ """.format(repr(supported.msvc_version), repr(msvc_uwp_app))
+ ))
+ test.run(arguments='-Q -s', stdout=None)
+ failmsg = check_libpath(supported, active, test.stdout())
+ if failmsg:
+ test.fail_test(message=failmsg)
+
+ if not active:
+ continue
+
+ # error construction variable and script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_UWP_APP={}, MSVC_SCRIPT_ARGS='store', tools=['msvc'])
+ """.format(repr(supported.msvc_version), repr(msvc_uwp_app))
+ ))
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ if not test.stderr().strip().startswith("MSVCArgumentError: multiple uwp declarations:"):
+ test.fail_test(message='Expected MSVCArgumentError')
+
+ # uwp using script argument
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='store', tools=['msvc'])
+ print('LIBPATH|' + env['ENV'].get('LIBPATH', ''))
+ print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat',''))
+ """.format(repr(supported.msvc_version))
+ ))
+ test.run(arguments='-Q -s', stdout=None)
+ failmsg = check_libpath(supported, True, test.stdout())
+ if failmsg:
+ test.fail_test(message=failmsg)
+
+if LT_VS2015_versions:
+ # VS2013 and earlier for uwp/store error
+
+ for unsupported in LT_VS2015_versions:
+
+ for msvc_uwp_app in (True, '1', False, '0', None):
+
+ active = msvc_uwp_app in (True, '1')
+
+ # uwp using construction variable
+ test.write('SConstruct', textwrap.dedent(
+ """
+ DefaultEnvironment(tools=[])
+ env = Environment(MSVC_VERSION={0}, MSVC_UWP_APP={1}, tools=['msvc'])
+ """.format(repr(unsupported.msvc_version), repr(msvc_uwp_app))
+ ))
+ if not active:
+ test.run(arguments='-Q -s', stdout=None)
+ else:
+ test.run(arguments='-Q -s', status=2, stderr=None)
+ expect = 'MSVCArgumentError: MSVC_UWP_APP ({}) constraint violation:'.format(repr(msvc_uwp_app))
+ if not test.stderr().strip().startswith(expect):
+ test.fail_test(message='Expected MSVCArgumentError')
test.pass_test()
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVC/PCH-source.py b/test/MSVC/PCH-source.py
index ccab66d..a51dd33 100644
--- a/test/MSVC/PCH-source.py
+++ b/test/MSVC/PCH-source.py
@@ -1,102 +1,102 @@
-#!/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 use of pre-compiled headers when the source .cpp file shows
-up in both the env.PCH() and the env.Program() source list.
-
-Issue 2505: http://github.com/SCons/scons/issues/2505
-"""
-
-import TestSCons
-
-test = TestSCons.TestSCons()
-
-test.skip_if_not_msvc()
-
-test.write('SConstruct', """\
-env = Environment(tools=['msvc', 'mslink'])
-env['PCH'] = env.PCH('Source1.cpp')[0]
-env['PCHSTOP'] = 'Header1.hpp'
-env.Program('foo', ['foo.cpp', 'Source2.cpp', 'Source1.cpp'])
-""" % locals())
-
-test.write('Header1.hpp', r"""
-""")
-
-test.write('Source1.cpp', r"""
-#include <stdio.h>
-
-#include "Header1.hpp"
-
-void
-Source1(void) {
- printf("Source1.cpp\n");
-}
-""")
-
-test.write('Source2.cpp', r"""
-#include <stdio.h>
-
-#include "Header1.hpp"
-
-void
-Source2(void) {
- printf("Source2.cpp\n");
-}
-""")
-
-test.write('foo.cpp', r"""
-#include <stdio.h>
-
-#include "Header1.hpp"
-
-void Source1(void);
-void Source2(void);
-
-int
-main(int argc, char *argv[])
-{
- Source1();
- Source2();
- printf("foo.cpp\n");
-}
-""")
-
-test.run(arguments = ".")
-
-test.run(program=test.workpath('foo'+TestSCons._exe),
- stdout="Source1.cpp\nSource2.cpp\nfoo.cpp\n")
-
-
-
-test.pass_test()
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
+#!/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 use of pre-compiled headers when the source .cpp file shows
+up in both the env.PCH() and the env.Program() source list.
+
+Issue 2505: http://github.com/SCons/scons/issues/2505
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+test.write('SConstruct', """\
+env = Environment(tools=['msvc', 'mslink'])
+env['PCH'] = env.PCH('Source1.cpp')[0]
+env['PCHSTOP'] = 'Header1.hpp'
+env.Program('foo', ['foo.cpp', 'Source2.cpp', 'Source1.cpp'])
+""" % locals())
+
+test.write('Header1.hpp', r"""
+""")
+
+test.write('Source1.cpp', r"""
+#include <stdio.h>
+
+#include "Header1.hpp"
+
+void
+Source1(void) {
+ printf("Source1.cpp\n");
+}
+""")
+
+test.write('Source2.cpp', r"""
+#include <stdio.h>
+
+#include "Header1.hpp"
+
+void
+Source2(void) {
+ printf("Source2.cpp\n");
+}
+""")
+
+test.write('foo.cpp', r"""
+#include <stdio.h>
+
+#include "Header1.hpp"
+
+void Source1(void);
+void Source2(void);
+
+int
+main(int argc, char *argv[])
+{
+ Source1();
+ Source2();
+ printf("foo.cpp\n");
+}
+""")
+
+test.run(arguments = ".")
+
+test.run(program=test.workpath('foo'+TestSCons._exe),
+ stdout="Source1.cpp\nSource2.cpp\nfoo.cpp\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/MSVC/embed-manifest.py b/test/MSVC/embed-manifest.py
index c0f8408..b4f5314 100644
--- a/test/MSVC/embed-manifest.py
+++ b/test/MSVC/embed-manifest.py
@@ -1,94 +1,94 @@
-#!/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 manifest files get embedded correctly in EXEs and DLLs
-"""
-
-import TestSCons
-
-_exe = TestSCons._exe
-_dll = TestSCons._dll
-_lib = TestSCons._lib
-
-test = TestSCons.TestSCons()
-
-test.skip_if_not_msvc()
-
-test.write('SConstruct', """\
-env=Environment(WINDOWS_EMBED_MANIFEST=True)
-env.Append(CCFLAGS = '/MD')
-env.Append(LINKFLAGS = '/MANIFEST')
-env.Append(SHLINKFLAGS = '/MANIFEST')
-exe=env.Program('test.cpp')
-dll=env.SharedLibrary('testdll.cpp')
-env.Command('exe-extracted.manifest', exe,
- '$MT /nologo -inputresource:${SOURCE};1 -out:${TARGET}')
-env.Command('dll-extracted.manifest', dll,
- '$MT /nologo -inputresource:${SOURCE};2 -out:${TARGET}')
-env2=Environment(WINDOWS_EMBED_MANIFEST=True) # no /MD here
-env2.Program('test-nomanifest', env2.Object('test-nomanifest', 'test.cpp'))
-""")
-
-test.write('test.cpp', """\
-#include <stdio.h>
-#include <stdlib.h>
-int
-main(int argc, char *argv)
-{
- printf("test.cpp\\n");
- exit (0);
-}
-""")
-
-test.write('testdll.cpp', """\
-#include <stdio.h>
-#include <stdlib.h>
-
-__declspec(dllexport) int
-testdll(int argc, char *argv)
-{
- printf("testdll.cpp\\n");
- return 0;
-}
-""")
-
-test.run(arguments='.')
-
-test.must_exist('test%s' % _exe)
-test.must_exist('test%s.manifest' % _exe)
-test.must_contain('exe-extracted.manifest', '</assembly>', mode='r')
-test.must_exist('testdll%s' % _dll)
-test.must_exist('testdll%s.manifest' % _dll)
-test.must_contain('dll-extracted.manifest', '</assembly>', mode='r')
-
-test.pass_test()
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
+#!/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 manifest files get embedded correctly in EXEs and DLLs
+"""
+
+import TestSCons
+
+_exe = TestSCons._exe
+_dll = TestSCons._dll
+_lib = TestSCons._lib
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+test.write('SConstruct', """\
+env=Environment(WINDOWS_EMBED_MANIFEST=True)
+env.Append(CCFLAGS = '/MD')
+env.Append(LINKFLAGS = '/MANIFEST')
+env.Append(SHLINKFLAGS = '/MANIFEST')
+exe=env.Program('test.cpp')
+dll=env.SharedLibrary('testdll.cpp')
+env.Command('exe-extracted.manifest', exe,
+ '$MT /nologo -inputresource:${SOURCE};1 -out:${TARGET}')
+env.Command('dll-extracted.manifest', dll,
+ '$MT /nologo -inputresource:${SOURCE};2 -out:${TARGET}')
+env2=Environment(WINDOWS_EMBED_MANIFEST=True) # no /MD here
+env2.Program('test-nomanifest', env2.Object('test-nomanifest', 'test.cpp'))
+""")
+
+test.write('test.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+ printf("test.cpp\\n");
+ exit (0);
+}
+""")
+
+test.write('testdll.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+
+__declspec(dllexport) int
+testdll(int argc, char *argv)
+{
+ printf("testdll.cpp\\n");
+ return 0;
+}
+""")
+
+test.run(arguments='.')
+
+test.must_exist('test%s' % _exe)
+test.must_exist('test%s.manifest' % _exe)
+test.must_contain('exe-extracted.manifest', '</assembly>', mode='r')
+test.must_exist('testdll%s' % _dll)
+test.must_exist('testdll%s.manifest' % _dll)
+test.must_contain('dll-extracted.manifest', '</assembly>', mode='r')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVC/msvc_badversion.py b/test/MSVC/msvc_badversion.py
new file mode 100644
index 0000000..65dc789
--- /dev/null
+++ b/test/MSVC/msvc_badversion.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test scons with an invalid MSVC version when at least one MSVC is present.
+"""
+
+import sys
+
+import TestSCons
+import SCons.Tool.MSCommon.vc as msvc
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+ test.skip_test("Not win32 platform. Skipping test\n")
+
+test.skip_if_not_msvc()
+
+installed_msvc_versions = msvc.get_installed_vcs()
+# MSVC guaranteed to be at least one version on the system or else
+# skip_if_not_msvc() function would have skipped the test
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+env = Environment(MSVC_VERSION='12.9')
+""")
+test.run(arguments='-Q -s', stdout='')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='ignore')
+""")
+test.run(arguments='-Q -s', stdout='')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='warning')
+""")
+test.run(arguments='-Q -s', stdout='')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='error')
+""")
+test.run(arguments='-Q -s', status=2, stderr=r"^.*MSVCVersionNotFound.+", match=TestSCons.match_re_dotall)
+
+test.write('SConstruct', """\
+env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='bad_value')
+""")
+test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_NOTFOUND_POLICY.+", match=TestSCons.match_re_dotall)
+
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/MSVC/msvc_cache_force_defaults.py b/test/MSVC/msvc_cache_force_defaults.py
new file mode 100644
index 0000000..a8396d9
--- /dev/null
+++ b/test/MSVC/msvc_cache_force_defaults.py
@@ -0,0 +1,80 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test SCONS_CACHE_MSVC_FORCE_DEFAULTS system environment variable.
+"""
+
+import textwrap
+
+from SCons.Tool.MSCommon.vc import get_installed_vcs_components
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_if_not_msvc()
+
+installed_versions = get_installed_vcs_components()
+
+default_version = installed_versions[0]
+
+if default_version.msvc_vernum >= 14.0:
+ # VS2015 and later
+
+ # force SDK version and toolset version as msvc batch file arguments
+ test.write('SConstruct', textwrap.dedent(
+ """
+ import os
+ import json
+
+ cache_file = 'MSCACHE.json'
+
+ os.environ['SCONS_CACHE_MSVC_CONFIG']=cache_file
+ os.environ['SCONS_CACHE_MSVC_FORCE_DEFAULTS']='1'
+
+ DefaultEnvironment(tools=[])
+ env = Environment(tools=['msvc'])
+
+ envcache_keys = []
+ with open(cache_file, 'r') as file:
+ envcache_list = json.load(file)
+ envcache_keys = [tuple(d['key']) for d in envcache_list]
+
+ if envcache_keys:
+ # key = (script, arguments)
+ print("SCRIPT_ARGS: {}".format(envcache_keys[0][-1]))
+ """
+ ))
+ test.run(arguments = "-Q -s", status=0, stdout=None)
+
+ cache_arg = test.stdout().strip()
+
+ if default_version.msvc_verstr == '14.0':
+ # VS2015: target_arch msvc_sdk_version
+ expect = r'^SCRIPT_ARGS: .* [0-9.]+$'
+ else:
+ # VS2017+ msvc_sdk_version msvc_toolset_version
+ expect = r'^SCRIPT_ARGS: [0-9.]+ -vcvars_ver=[0-9.]+$'
+
+ test.must_contain_all(cache_arg, expect, find=TestSCons.match_re)
+
diff --git a/test/MSVC/no_msvc.py b/test/MSVC/no_msvc.py
index d1161c6..e255c65 100644
--- a/test/MSVC/no_msvc.py
+++ b/test/MSVC/no_msvc.py
@@ -48,4 +48,39 @@ test.run(arguments='-Q -s')
if 'MSVC_VERSION=None' not in test.stdout():
test.fail_test()
-test.pass_test() \ No newline at end of file
+# test msvc version number request with no msvc's
+test.file_fixture('no_msvc/no_msvcs_sconstruct_version.py', 'SConstruct')
+test.run(arguments='-Q -s', status=2, stderr=r"^.*MSVCVersionNotFound.+", match=TestSCons.match_re_dotall)
+
+# test that MSVCVersionNotFound is not raised for default msvc tools
+# when a non-msvc tool list is used
+test.subdir('site_scons', ['site_scons', 'site_tools'])
+
+test.write(['site_scons', 'site_tools', 'myignoredefaultmsvctool.py'], """
+import SCons.Tool
+def generate(env):
+ env['MYIGNOREDEFAULTMSVCTOOL']='myignoredefaultmsvctool'
+def exists(env):
+ return 1
+""")
+
+test.file_fixture('no_msvc/no_msvcs_sconstruct_tools.py', 'SConstruct')
+test.run(arguments='-Q -s')
+
+# test no msvc's and msvc_sdk_version() call
+test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py', 'SConstruct')
+test.run(arguments='-Q -s')
+test.must_contain_all(test.stdout(), 'sdk_version_list=[]')
+
+# test no msvc's and msvc_sdk_version() call
+test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py', 'SConstruct')
+test.run(arguments='-Q -s')
+test.must_contain_all(test.stdout(), 'toolset_version_list=[]')
+
+# test no msvc's and msvc_query_version_toolset() call
+test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py', 'SConstruct')
+test.run(arguments='-Q -s')
+test.must_contain_all(test.stdout(), 'msvc_version=None, msvc_toolset_version=None')
+
+test.pass_test()
+
diff --git a/test/MSVS/CPPPATH-Dirs.py b/test/MSVS/CPPPATH-Dirs.py
index 53c7a8b..3883331 100644
--- a/test/MSVS/CPPPATH-Dirs.py
+++ b/test/MSVS/CPPPATH-Dirs.py
@@ -1,94 +1,94 @@
-
-#!/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 MSVS generation works when CPPPATH contains Dir nodes.
-Also make sure changing CPPPATH causes rebuild.
-"""
-
-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)
-
-import SCons.Tool.MSCommon as msc
-if not msc.msvs_exists():
- msg = "No MSVS toolchain found...skipping test\n"
- test.skip_test(msg)
-
-SConscript_contents = """\
-env = Environment()
-
-sources = ['main.cpp']
-
-program = env.Program(target = 'hello', source = sources)
-
-if ARGUMENTS.get('moreincludes'):
- env.AppendUnique(CPPPATH = [env.Dir('.'), env.Dir('myincludes')])
-else:
- env.AppendUnique(CPPPATH = [env.Dir('.')])
-
-env.MSVSProject(target = 'Hello' + env['MSVSPROJECTSUFFIX'],
- srcs = sources,
- buildtarget = program,
- variant = 'Release')
-"""
-
-test.write('SConstruct', SConscript_contents)
-
-test.write('main.cpp', """\
-#include <stdio.h>
-int main(void) {
- printf("hello, world!\\n");
-}
-""")
-
-test.run()
-
-if not os.path.exists(test.workpath('Hello.vcproj')) and \
- not os.path.exists(test.workpath('Hello.vcxproj')):
- test.fail_test("Failed to create Visual Studio project Hello.vcproj or Hello.vcxproj")
-test.must_exist(test.workpath('Hello.sln'))
-# vcproj = test.read('Test.vcproj', 'r')
-
-test.run(arguments='moreincludes=1')
-test.must_not_contain_any_line(test.stdout(), ['is up to date'])
-test.must_contain_all_lines(test.stdout(), ['Adding', 'Hello'])
-
-test.pass_test()
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
+
+#!/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 MSVS generation works when CPPPATH contains Dir nodes.
+Also make sure changing CPPPATH causes rebuild.
+"""
+
+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)
+
+import SCons.Tool.MSCommon as msc
+if not msc.msvs_exists():
+ msg = "No MSVS toolchain found...skipping test\n"
+ test.skip_test(msg)
+
+SConscript_contents = """\
+env = Environment()
+
+sources = ['main.cpp']
+
+program = env.Program(target = 'hello', source = sources)
+
+if ARGUMENTS.get('moreincludes'):
+ env.AppendUnique(CPPPATH = [env.Dir('.'), env.Dir('myincludes')])
+else:
+ env.AppendUnique(CPPPATH = [env.Dir('.')])
+
+env.MSVSProject(target = 'Hello' + env['MSVSPROJECTSUFFIX'],
+ srcs = sources,
+ buildtarget = program,
+ variant = 'Release')
+"""
+
+test.write('SConstruct', SConscript_contents)
+
+test.write('main.cpp', """\
+#include <stdio.h>
+int main(void) {
+ printf("hello, world!\\n");
+}
+""")
+
+test.run()
+
+if not os.path.exists(test.workpath('Hello.vcproj')) and \
+ not os.path.exists(test.workpath('Hello.vcxproj')):
+ test.fail_test("Failed to create Visual Studio project Hello.vcproj or Hello.vcxproj")
+test.must_exist(test.workpath('Hello.sln'))
+# vcproj = test.read('Test.vcproj', 'r')
+
+test.run(arguments='moreincludes=1')
+test.must_not_contain_any_line(test.stdout(), ['is up to date'])
+test.must_contain_all_lines(test.stdout(), ['Adding', 'Hello'])
+
+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 fe6a2bc..1cf9577 100644
--- a/test/NoClean.py
+++ b/test/NoClean.py
@@ -1,97 +1,97 @@
-#!/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__"
-
-#
-# This test ensures that NoClean works correctly, even when it's applied to
-# a single target in the return list of an multi-target Builder.
-#
-import TestSCons
-
-test = TestSCons.TestSCons()
-
-test.write('SConstruct', """
-def action(target, source, env):
- for t in target:
- with open(t.get_internal_path(), 'w'):
- pass
-Command('1.out', 'SConstruct', action)
-NoClean('1.out')
-""")
-
-test.write('SConstruct.force', """
-def action(target, source, env):
- for t in target:
- with open(t.get_internal_path(), 'w'):
- pass
- with open('4.out', 'w'):
- pass
-res = Command('3.out', 'SConstruct.force', action)
-Clean('4.out', res)
-NoClean('4.out')
-""")
-
-test.write('SConstruct.multi', """
-def action(target, source, env):
- for t in target:
- with open(t.get_internal_path(), 'w'):
- pass
-Command(['5.out', '6.out'], 'SConstruct.multi', action)
-NoClean('6.out')
-""")
-
-#
-# Basic check: NoClean keeps files
-#
-test.run()
-test.run(arguments='-c')
-
-test.must_exist('1.out')
-
-#
-# Check: NoClean overrides Clean
-#
-test.run(arguments=['-f', 'SConstruct.force'])
-test.run(arguments=['-f', 'SConstruct.force', '-c'])
-
-test.must_not_exist('3.out')
-test.must_exist('4.out')
-
-#
-# Check: NoClean works for multi-target Builders
-#
-test.run(arguments=['-f', 'SConstruct.multi'])
-test.run(arguments=['-f', 'SConstruct.multi', '-c'])
-
-test.must_not_exist('5.out')
-test.must_exist('6.out')
-
-test.pass_test()
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
+#!/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__"
+
+#
+# This test ensures that NoClean works correctly, even when it's applied to
+# a single target in the return list of an multi-target Builder.
+#
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+def action(target, source, env):
+ for t in target:
+ with open(t.get_internal_path(), 'w'):
+ pass
+Command('1.out', 'SConstruct', action)
+NoClean('1.out')
+""")
+
+test.write('SConstruct.force', """
+def action(target, source, env):
+ for t in target:
+ with open(t.get_internal_path(), 'w'):
+ pass
+ with open('4.out', 'w'):
+ pass
+res = Command('3.out', 'SConstruct.force', action)
+Clean('4.out', res)
+NoClean('4.out')
+""")
+
+test.write('SConstruct.multi', """
+def action(target, source, env):
+ for t in target:
+ with open(t.get_internal_path(), 'w'):
+ pass
+Command(['5.out', '6.out'], 'SConstruct.multi', action)
+NoClean('6.out')
+""")
+
+#
+# Basic check: NoClean keeps files
+#
+test.run()
+test.run(arguments='-c')
+
+test.must_exist('1.out')
+
+#
+# Check: NoClean overrides Clean
+#
+test.run(arguments=['-f', 'SConstruct.force'])
+test.run(arguments=['-f', 'SConstruct.force', '-c'])
+
+test.must_not_exist('3.out')
+test.must_exist('4.out')
+
+#
+# Check: NoClean works for multi-target Builders
+#
+test.run(arguments=['-f', 'SConstruct.multi'])
+test.run(arguments=['-f', 'SConstruct.multi', '-c'])
+
+test.must_not_exist('5.out')
+test.must_exist('6.out')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/NodeOps.py b/test/NodeOps.py
index a5300f1..a2db429 100644
--- a/test/NodeOps.py
+++ b/test/NodeOps.py
@@ -50,6 +50,7 @@ test = TestSCons.TestSCons()
test.subdir('bld', 'src', ['src', 'subsrcdir'])
sconstruct = r"""
+DefaultEnvironment(tools=[]) # test speedup
foo = Environment(SHOBJPREFIX='', WINDOWS_INSERT_DEF=1)
foo.Append(SHCXXFLAGS = '-DFOO')
bar = Environment(SHOBJPREFIX='', WINDOWS_INSERT_DEF=1)
diff --git a/test/Parallel/multiple-parents.py b/test/Parallel/multiple-parents.py
index 5a52f28..3712638 100644
--- a/test/Parallel/multiple-parents.py
+++ b/test/Parallel/multiple-parents.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,14 +22,11 @@
# 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.
-#
"""
Verify that a failed build action with -j works as expected.
"""
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
import sys
import TestSCons
@@ -58,67 +57,77 @@ test = TestSCons.TestSCons()
test.write('SConstruct', """
vars = Variables()
-vars.Add( BoolVariable('interrupt', 'Interrupt the build.', 0 ) )
+vars.Add(BoolVariable('interrupt', 'Interrupt the build.', False))
+DefaultEnvironment(tools=[]) # test speedup
varEnv = Environment(variables=vars)
-def fail_action(target = None, source = None, env = None):
+def fail_action(target=None, source=None, env=None):
return 2
-def simulate_keyboard_interrupt(target = None, source = None, env = None):
+def simulate_keyboard_interrupt(target=None, source=None, env=None):
# Directly invoked the SIGINT handler to simulate a
# KeyboardInterrupt. This hack is necessary because there is no
# easy way to get access to the current Job/Taskmaster object.
import signal
+
handler = signal.getsignal(signal.SIGINT)
handler(signal.SIGINT, None)
return 0
-interrupt = Command(target='interrupt', source='', action=simulate_keyboard_interrupt)
+interrupt = Command(target='interrupt', source='', action=simulate_keyboard_interrupt)
touch0 = Touch('${TARGETS[0]}')
touch1 = Touch('${TARGETS[1]}')
touch2 = Touch('${TARGETS[2]}')
-failed0 = Command(target='failed00', source='', action=fail_action)
-ok0 = Command(target=['ok00a', 'ok00b', 'ok00c'],
- source='',
- action=[touch0, touch1, touch2])
-prereq0 = Command(target='prereq00', source='', action=touch0)
-ignore0 = Command(target='ignore00', source='', action=touch0)
-igreq0 = Command(target='igreq00', source='', action=touch0)
+failed0 = Command(target='failed00', source='', action=fail_action)
+ok0 = Command(
+ target=['ok00a', 'ok00b', 'ok00c'],
+ source='',
+ action=[touch0, touch1, touch2],
+)
+prereq0 = Command(target='prereq00', source='', action=touch0)
+ignore0 = Command(target='ignore00', source='', action=touch0)
+igreq0 = Command(target='igreq00', source='', action=touch0)
missing0 = Command(target='missing00', source='MissingSrc', action=touch0)
-withSE0 = Command(target=['withSE00a', 'withSE00b', 'withSE00c'],
- source='',
- action=[touch0, touch1, touch2, Touch('side_effect')])
-SideEffect('side_effect', withSE0)
-
-prev_level = failed0 + ok0 + ignore0 + missing0 + withSE0
+withSE0 = Command(
+ target=['withSE00a', 'withSE00b', 'withSE00c'],
+ source='',
+ action=[touch0, touch1, touch2, Touch('side_effect')],
+)
+SideEffect('side_effect', withSE0)
+
+prev_level = failed0 + ok0 + ignore0 + missing0 + withSE0
prev_prereq = prereq0
prev_ignore = ignore0
-prev_igreq = igreq0
+prev_igreq = igreq0
if varEnv['interrupt']:
prev_level = prev_level + interrupt
-for i in range(1,20):
-
- failed = Command(target='failed%02d' % i, source='', action=fail_action)
- ok = Command(target=['ok%02da' % i, 'ok%02db' % i, 'ok%02dc' % i],
- source='',
- action=[touch0, touch1, touch2])
- prereq = Command(target='prereq%02d' % i, source='', action=touch0)
- ignore = Command(target='ignore%02d' % i, source='', action=touch0)
- igreq = Command(target='igreq%02d' % i, source='', action=touch0)
- missing = Command(target='missing%02d' %i, source='MissingSrc', action=touch0)
- withSE = Command(target=['withSE%02da' % i, 'withSE%02db' % i, 'withSE%02dc' % i],
- source='',
- action=[touch0, touch1, touch2, Touch('side_effect')])
- SideEffect('side_effect', withSE)
+for i in range(1, 20):
+
+ failed = Command(target='failed%02d' % i, source='', action=fail_action)
+ ok = Command(
+ target=['ok%02da' % i, 'ok%02db' % i, 'ok%02dc' % i],
+ source='',
+ action=[touch0, touch1, touch2],
+ )
+ prereq = Command(target='prereq%02d' % i, source='', action=touch0)
+ ignore = Command(target='ignore%02d' % i, source='', action=touch0)
+ igreq = Command(target='igreq%02d' % i, source='', action=touch0)
+ missing = Command(target='missing%02d' % i, source='MissingSrc', action=touch0)
+ withSE = Command(
+ target=['withSE%02da' % i, 'withSE%02db' % i, 'withSE%02dc' % i],
+ source='',
+ action=[touch0, touch1, touch2, Touch('side_effect')],
+ )
+ SideEffect('side_effect', withSE)
next_level = failed + ok + ignore + igreq + missing + withSE
- for j in range(1,10):
- a = Alias('a%02d%02d' % (i,j), prev_level)
+ for j in range(1, 10):
+ a = Alias('a%02d%02d' % (i, j), prev_level)
Requires(a, prev_prereq)
Ignore(a, prev_ignore)
@@ -128,18 +137,18 @@ for i in range(1,20):
next_level = next_level + a
- prev_level = next_level
+ prev_level = next_level
prev_prereq = prereq
prev_ignore = ignore
- prev_igreq = igreq
+ prev_igreq = igreq
all = Alias('all', prev_level)
Requires(all, prev_prereq)
-Ignore(all, prev_ignore)
+Ignore(all, prev_ignore)
Requires(all, prev_igreq)
-Ignore(all, prev_igreq)
+Ignore(all, prev_igreq)
Default(all)
""")
diff --git a/test/PharLap.py b/test/PharLap.py
index 8e56f21..0a54151 100644
--- a/test/PharLap.py
+++ b/test/PharLap.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 sys
@@ -284,10 +283,10 @@ test.write([ "baz", "bar.lnk"],"""
@asm.emb
""")
-test.write("SConstruct", """
-env=Environment(tools = [ 'linkloc', '386asm' ],
- ASFLAGS='-twocase -cvsym',
- LINKFLAGS='@foo.lnk')
+test.write("SConstruct", """\
+env = Environment(
+ tools=['linkloc', '386asm'], ASFLAGS='-twocase -cvsym', LINKFLAGS='@foo.lnk'
+)
env.Program(target='minasm', source='minasm.asm')
""")
@@ -304,8 +303,8 @@ test.write([ "baz", "bar.lnk"],"""
""")
oldtime = os.path.getmtime(test.workpath('minasm.exe'))
-time.sleep(2) # Give the time stamp time to change
-test.run(arguments = '.')
+test.sleep() # delay for timestamps
+test.run(arguments='.')
test.fail_test(oldtime == os.path.getmtime(test.workpath('minasm.exe')))
test.pass_test()
diff --git a/test/Program.py b/test/Program.py
index 15fd0c3..d977bef 100644
--- a/test/Program.py
+++ b/test/Program.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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.path
import time
@@ -40,13 +39,14 @@ foo4 = test.workpath('foo4' + _exe)
foo5 = test.workpath('foo5' + _exe)
foo_args = 'foo1%s foo2%s foo3%s foo4%s foo5%s' % (_exe, _exe, _exe, _exe, _exe)
-test.write('SConstruct', """
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
-env.Program(target = 'foo1', source = 'f1.c')
-env.Program(target = 'foo2', source = Split('f2a.c f2b.c f2c.c'))
+env.Program(target='foo1', source='f1.c')
+env.Program(target='foo2', source=Split('f2a.c f2b.c f2c.c'))
f3a = File('f3a.c')
f3b = File('f3b.c')
-Program(target = 'foo3', source = [f3a, [f3b, 'f3c.c']])
+env.Program(target='foo3', source=[f3a, [f3b, 'f3c.c']])
env.Program('foo4', 'f4.c')
env.Program('foo5.c')
""")
@@ -156,15 +156,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = '.')
+test.run(arguments='.')
-test.run(program = foo1, stdout = "f1.c\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c\n")
-test.run(program = foo5, stdout = "foo5.c\n")
-
-test.up_to_date(arguments = '.')
+test.run(program=foo1, stdout="f1.c\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c\n")
+test.run(program=foo5, stdout="foo5.c\n")
+test.up_to_date(arguments='.')
test.write('f1.c', r"""
#include <stdio.h>
@@ -211,15 +210,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = '.')
-
-test.run(program = foo1, stdout = "f1.c X\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c X\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c X\n")
-test.run(program = foo5, stdout = "foo5.c X\n")
+test.run(arguments='.')
-test.up_to_date(arguments = '.')
+test.run(program=foo1, stdout="f1.c X\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c X\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c X\n")
+test.run(program=foo5, stdout="foo5.c X\n")
+test.up_to_date(arguments='.')
# make sure the programs didn't get rebuilt, because nothing changed:
oldtime1 = os.path.getmtime(foo1)
@@ -228,10 +226,8 @@ oldtime3 = os.path.getmtime(foo3)
oldtime4 = os.path.getmtime(foo4)
oldtime5 = os.path.getmtime(foo5)
-time.sleep(2) # introduce a small delay, to make the test valid
-
-test.run(arguments = foo_args)
-
+test.sleep() # delay for timestamps
+test.run(arguments=foo_args)
test.fail_test(oldtime1 != os.path.getmtime(foo1))
test.fail_test(oldtime2 != os.path.getmtime(foo2))
test.fail_test(oldtime3 != os.path.getmtime(foo3))
@@ -284,15 +280,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = foo_args)
-
-test.run(program = foo1, stdout = "f1.c Y\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c Y\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c Y\n")
-test.run(program = foo5, stdout = "foo5.c Y\n")
+test.run(arguments=foo_args)
-test.up_to_date(arguments = foo_args)
+test.run(program=foo1, stdout="f1.c Y\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c Y\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c Y\n")
+test.run(program=foo5, stdout="foo5.c Y\n")
+test.up_to_date(arguments=foo_args)
test.write('f1.c', r"""
#include <stdio.h>
@@ -340,15 +335,14 @@ main(int argc, char *argv[])
}
""")
-test.run(arguments = foo_args)
-
-test.run(program = foo1, stdout = "f1.c Z\n")
-test.run(program = foo2, stdout = "f2a.c\nf2b.c\nf2c.c\n")
-test.run(program = foo3, stdout = "f3a.c\nf3b.c Z\nf3c.c\n")
-test.run(program = foo4, stdout = "f4.c Z\n")
-test.run(program = foo5, stdout = "foo5.c Z\n")
+test.run(arguments=foo_args)
-test.up_to_date(arguments = foo_args)
+test.run(program=foo1, stdout="f1.c Z\n")
+test.run(program=foo2, stdout="f2a.c\nf2b.c\nf2c.c\n")
+test.run(program=foo3, stdout="f3a.c\nf3b.c Z\nf3c.c\n")
+test.run(program=foo4, stdout="f4.c Z\n")
+test.run(program=foo5, stdout="foo5.c Z\n")
+test.up_to_date(arguments=foo_args)
# make sure the programs didn't get rebuilt, because nothing changed:
oldtime1 = os.path.getmtime(foo1)
@@ -357,10 +351,8 @@ oldtime3 = os.path.getmtime(foo3)
oldtime4 = os.path.getmtime(foo4)
oldtime5 = os.path.getmtime(foo5)
-time.sleep(2) # introduce a small delay, to make the test valid
-
-test.run(arguments = foo_args)
-
+test.sleep() # delay for timestamps
+test.run(arguments=foo_args)
test.fail_test(not (oldtime1 == os.path.getmtime(foo1)))
test.fail_test(not (oldtime2 == os.path.getmtime(foo2)))
test.fail_test(not (oldtime3 == os.path.getmtime(foo3)))
diff --git a/test/Repository/Java.py b/test/Repository/Java.py
index 290d742..17ef960 100644
--- a/test/Repository/Java.py
+++ b/test/Repository/Java.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 building Java applications when using Repositories.
@@ -58,6 +57,7 @@ opts = '-Y ' + test.workpath('rep1')
#
test.write(['rep1', 'SConstruct'], """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools = ['javac'],
JAVAC = r'"%s"')
env.Java(target = 'classes', source = 'src')
diff --git a/test/Repository/JavaH.py b/test/Repository/JavaH.py
index fea5f5b..a81049c 100644
--- a/test/Repository/JavaH.py
+++ b/test/Repository/JavaH.py
@@ -79,6 +79,7 @@ opts = '-Y ' + test.workpath('rep1')
#
test.write(['rep1', 'SConstruct'], """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools=['javac', 'javah'], JAVAC=r'"%s"', JAVAH=r'"%s"')
classes = env.Java(target='classes', source='src')
env.JavaH(target='outdir', source=classes)
diff --git a/test/Repository/Program.py b/test/Repository/Program.py
index 1b06a5d..1fc0dcf 100644
--- a/test/Repository/Program.py
+++ b/test/Repository/Program.py
@@ -36,7 +36,7 @@ for implicit_deps in ['0', '1', '2', '\"all\"']:
work1_foo_c = test.workpath('work1', 'foo.c')
test.write(['work1', 'SConstruct'], r"""
-DefaultEnvironment(tools=[])
+DefaultEnvironment(tools=[]) # test speedup
Repository(r'%s')
env = Environment(IMPLICIT_COMMAND_DEPENDENCIES=%s)
env.Program(target= 'foo', source = Split('aaa.c bbb.c foo.c'))
@@ -179,7 +179,7 @@ repository/foo.c
work2_foo = test.workpath('work2', 'foo' + _exe)
test.write(['work2', 'SConstruct'], r"""
-DefaultEnvironment(tools=[])
+DefaultEnvironment(tools=[]) # test speedup
Repository(r'%s')
Repository(r'%s')
env = Environment()
diff --git a/test/Repository/RMIC.py b/test/Repository/RMIC.py
index 7c37dd9..92421be 100644
--- a/test/Repository/RMIC.py
+++ b/test/Repository/RMIC.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 building Java applications when using Repositories.
@@ -92,6 +91,7 @@ opts = '-Y ' + test.workpath('rep1')
#
test.write(['rep1', 'SConstruct'], """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools = ['javac', 'rmic'],
JAVAC = r'"%s"',
RMIC = r'"%s"')
@@ -360,6 +360,7 @@ test.up_to_date(chdir = 'work2', options = opts, arguments = ".")
#
test.write(['work3', 'SConstruct'], """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools = ['javac', 'rmic'],
JAVAC = r'"%s"',
RMIC = r'"%s"')
diff --git a/test/Repository/StaticLibrary.py b/test/Repository/StaticLibrary.py
index c4a901b..a0360ff 100644
--- a/test/Repository/StaticLibrary.py
+++ b/test/Repository/StaticLibrary.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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.path
import TestSCons
@@ -54,6 +53,8 @@ for implicit_deps in ['0', '1', '2', '\"all\"']:
#
test.write(['repository', 'SConstruct'], """
+
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(LIBS = ['xxx'], LIBPATH = '.',
IMPLICIT_COMMAND_DEPENDENCIES=%s)
env.Library(target = 'xxx', source = ['aaa.c', 'bbb.c'])
diff --git a/test/Repository/VariantDir.py b/test/Repository/VariantDir.py
index 327e550..eb33aa1 100644
--- a/test/Repository/VariantDir.py
+++ b/test/Repository/VariantDir.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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.path
@@ -38,6 +37,7 @@ opts = "-Y " + test.workpath('repository')
#
test.write(['repository', 'SConstruct'], r"""
+DefaultEnvironment(tools=[]) # test speedup
VariantDir('build0', 'src', duplicate=0)
VariantDir('build1', 'src', duplicate=1)
SConscript('build0/SConscript')
diff --git a/test/Repository/include.py b/test/Repository/include.py
index ae0bc04..176b0a2 100644
--- a/test/Repository/include.py
+++ b/test/Repository/include.py
@@ -37,6 +37,7 @@ work_foo_h = test.workpath('work', 'foo.h')
test.write(['work', 'SConstruct'], """
Repository(r'%s')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(CPPPATH = ['.'])
env.Program(target = 'foo', source = 'foo.c')
""" % repository)
diff --git a/test/Repository/no-SConsignFile.py b/test/Repository/no-SConsignFile.py
index d7f570c..6b5b3e2 100644
--- a/test/Repository/no-SConsignFile.py
+++ b/test/Repository/no-SConsignFile.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 using Repository() works even when the Repository has no
@@ -63,13 +62,10 @@ test.write(['src', 'foo.h'], """\
# Make sure it's past the max_drift time,
# so the source file signatures get saved.
-test.sleep(2)
+test.sleep() # delay for timestamps
test.run(chdir='build', arguments='.')
-
-test.run(program=test.workpath('build', 'foo'),
- stdout="src/foo.h\nsrc/foo.c\n")
-
+test.run(program=test.workpath('build', 'foo'), stdout="src/foo.h\nsrc/foo.c\n")
test.up_to_date(chdir='build', arguments='.')
test.pass_test()
diff --git a/test/Repository/variants.py b/test/Repository/variants.py
index c95e853..6f0b627 100644
--- a/test/Repository/variants.py
+++ b/test/Repository/variants.py
@@ -31,20 +31,22 @@ from TestSCons import TestSCons, _exe, _obj
test = TestSCons()
-test.subdir('repository',
- ['repository', 'src1'],
- ['repository', 'src2'],
- ['repository', 'src2', 'include'],
- ['repository', 'src2', 'xxx'],
- ['repository', 'build2'],
- ['repository', 'build2', 'foo'],
- ['repository', 'build2', 'bar'],
- 'work1',
- ['work1', 'src1'],
- 'work2',
- ['work2', 'src2'],
- ['work2', 'src2', 'include'],
- ['work2', 'src2', 'xxx'])
+test.subdir(
+ 'repository',
+ ['repository', 'src1'],
+ ['repository', 'src2'],
+ ['repository', 'src2', 'include'],
+ ['repository', 'src2', 'xxx'],
+ ['repository', 'build2'],
+ ['repository', 'build2', 'foo'],
+ ['repository', 'build2', 'bar'],
+ 'work1',
+ ['work1', 'src1'],
+ 'work2',
+ ['work2', 'src2'],
+ ['work2', 'src2', 'include'],
+ ['work2', 'src2', 'xxx'],
+)
aaa_obj = 'aaa' + _obj
bbb_obj = 'bbb' + _obj
@@ -56,14 +58,18 @@ repository_build1_foo_xxx = test.workpath('repository', 'build1', 'foo', 'xxx')
work1_build1_foo_xxx = test.workpath('work1', 'build1', 'foo', 'xxx')
work1_build1_bar_xxx = test.workpath('work1', 'build1', 'bar', 'xxx')
-repository_build2_foo_src2_xxx_xxx = test.workpath('repository', 'build2',
- 'foo', 'src2', 'xxx', 'xxx')
-repository_build2_bar_src2_xxx_xxx = test.workpath('repository', 'build2',
- 'bar', 'src2', 'xxx', 'xxx')
-work2_build2_foo_src2_xxx_xxx = test.workpath('work2', 'build2',
- 'foo', 'src2', 'xxx', 'xxx')
-work2_build2_bar_src2_xxx_xxx = test.workpath('work2', 'build2',
- 'bar', 'src2', 'xxx', 'xxx')
+repository_build2_foo_src2_xxx_xxx = test.workpath(
+ 'repository', 'build2', 'foo', 'src2', 'xxx', 'xxx'
+)
+repository_build2_bar_src2_xxx_xxx = test.workpath(
+ 'repository', 'build2', 'bar', 'src2', 'xxx', 'xxx'
+)
+work2_build2_foo_src2_xxx_xxx = test.workpath(
+ 'work2', 'build2', 'foo', 'src2', 'xxx', 'xxx'
+)
+work2_build2_bar_src2_xxx_xxx = test.workpath(
+ 'work2', 'build2', 'bar', 'src2', 'xxx', 'xxx'
+)
opts = "-Y " + test.workpath('repository')
@@ -71,14 +77,16 @@ opts = "-Y " + test.workpath('repository')
test.write(['repository', 'SConstruct'], r"""
OS = ARGUMENTS.get('OS', '')
build1_os = "#build1/" + OS
+DefaultEnvironment(tools=[]) # test speedup
default = Environment()
ccflags = {
- '' : '',
- 'foo' : '-DFOO',
- 'bar' : '-DBAR',
+ '': '',
+ 'foo': '-DFOO',
+ 'bar': '-DBAR',
}
-env1 = Environment(CCFLAGS = default.subst('$CCFLAGS %s' % ccflags[OS]),
- CPPPATH = build1_os)
+env1 = Environment(
+ CCFLAGS=default.subst('$CCFLAGS %s' % ccflags[OS]), CPPPATH=build1_os
+)
VariantDir(build1_os, 'src1')
SConscript(build1_os + '/SConscript', "env1")
@@ -95,8 +103,9 @@ test.write(['repository', 'build2', 'foo', 'SConscript'], r"""
VariantDir('src2', '#src2')
default = Environment()
-env2 = Environment(CCFLAGS = default.subst('$CCFLAGS -DFOO'),
- CPPPATH = ['#src2/xxx', '#src2/include'])
+env2 = Environment(
+ CCFLAGS=default.subst('$CCFLAGS -DFOO'), CPPPATH=['#src2/xxx', '#src2/include']
+)
SConscript('src2/xxx/SConscript', "env2")
""")
@@ -105,8 +114,9 @@ test.write(['repository', 'build2', 'bar', 'SConscript'], r"""
VariantDir('src2', '#src2')
default = Environment()
-env2 = Environment(CCFLAGS = default.subst('$CCFLAGS -DBAR'),
- CPPPATH = ['#src2/xxx', '#src2/include'])
+env2 = Environment(
+ CCFLAGS=default.subst('$CCFLAGS -DBAR'), CPPPATH=['#src2/xxx', '#src2/include']
+)
SConscript('src2/xxx/SConscript', "env2")
""")
@@ -216,12 +226,9 @@ repository/src1/bbb.c: REPOSITORY_FOO
repository/src1/main.c: REPOSITORY_FOO
""")
-database_name=test.get_sconsignname()
-
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+database_name = test.get_sconsignname()
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
@@ -237,10 +244,8 @@ repository/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
@@ -251,15 +256,10 @@ test.writable('repository', 0)
#
test.up_to_date(chdir='work1', options=opts + " OS=foo", arguments='build1')
-test.fail_test(os.path.exists(
- test.workpath('work1', 'build1', 'foo', aaa_obj)))
-test.fail_test(os.path.exists(
- test.workpath('work1', 'build1', 'foo', bbb_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work1', 'build1', 'foo', main_obj)))
-
-test.fail_test(os.path.exists(
- test.workpath('work1', 'build1', 'foo', xxx_exe)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', aaa_obj)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', bbb_obj)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', main_obj)))
+test.fail_test(os.path.exists(test.workpath('work1', 'build1', 'foo', xxx_exe)))
#
test.run(chdir='work1', options=opts, arguments='OS=bar .')
@@ -271,18 +271,13 @@ repository/src1/bbb.c: REPOSITORY_BAR
repository/src1/main.c: REPOSITORY_BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-
test.up_to_date(chdir='work1', options=opts + " OS=bar", arguments='build1')
-# Ensure file time stamps will be newer.
-time.sleep(2)
-
+test.sleep() # delay for timestamps
test.write(['work1', 'src1', 'iii.h'], r"""
#ifdef FOO
#define STRING "WORK_FOO"
@@ -302,13 +297,10 @@ repository/src1/bbb.c: WORK_BAR
repository/src1/main.c: WORK_BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-
test.up_to_date(chdir='work1', options=opts + " OS=bar", arguments='build1')
#
@@ -320,38 +312,39 @@ repository/src1/bbb.c: WORK_FOO
repository/src1/main.c: WORK_FOO
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-
test.up_to_date(chdir='work1', options=opts + " OS=foo", arguments='build1')
-
-#
test.up_to_date(chdir='work2', options=opts, arguments='build2')
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', aaa_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', bbb_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', main_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'foo', 'src2', 'xxx', xxx_exe)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', aaa_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', bbb_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', main_obj)))
-test.fail_test(os.path.exists(test.workpath(
- 'work2', 'build2', 'bar', 'src2', 'xxx', xxx_exe)))
-
-# Ensure file time stamps will be newer.
-time.sleep(2)
-
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', aaa_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', bbb_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', main_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'foo', 'src2', 'xxx', xxx_exe))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', aaa_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', bbb_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', main_obj))
+)
+test.fail_test(
+ os.path.exists(test.workpath('work2', 'build2', 'bar', 'src2', 'xxx', xxx_exe))
+)
+
+test.sleep() # delay for timestamps
test.write(['work2', 'src2', 'include', 'my_string.h'], r"""
#ifdef FOO
#define INCLUDE_OS "FOO"
@@ -362,7 +355,6 @@ test.write(['work2', 'src2', 'include', 'my_string.h'], r"""
#define INCLUDE_STRING "work2/src2/include/my_string.h: %s\n"
""")
-#
test.run(chdir='work2', options=opts, arguments='build2')
test.run(program=work2_build2_foo_src2_xxx_xxx, stdout="""\
@@ -377,16 +369,12 @@ repository/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-# Ensure file time stamps will be newer.
-time.sleep(2)
-
+test.sleep() # delay for timestamps
test.write(['work2', 'src2', 'xxx', 'include.h'], r"""
#include <my_string.h>
#ifdef FOO
@@ -412,38 +400,37 @@ work2/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
""")
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-#
test.unlink(['work2', 'src2', 'include', 'my_string.h'])
-
test.run(chdir='work2', options=opts, arguments='build2')
-test.run(program=work2_build2_foo_src2_xxx_xxx, stdout="""\
+test.run(
+ program=work2_build2_foo_src2_xxx_xxx,
+ stdout="""\
repository/src2/include/my_string.h: FOO
work2/src2/xxx/include.h: FOO
repository/src2/xxx/main.c: FOO
-""")
+""",
+)
-test.run(program=work2_build2_bar_src2_xxx_xxx, stdout="""\
+test.run(
+ program=work2_build2_bar_src2_xxx_xxx,
+ stdout="""\
repository/src2/include/my_string.h: BAR
work2/src2/xxx/include.h: BAR
repository/src2/xxx/main.c: BAR
-""")
+""",
+)
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src1', database_name)))
-test.fail_test(os.path.exists(
- test.workpath('repository', 'src2', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src1', database_name)))
+test.fail_test(os.path.exists(test.workpath('repository', 'src2', database_name)))
test.fail_test(os.path.exists(test.workpath('work1', 'src1', database_name)))
test.fail_test(os.path.exists(test.workpath('work2', 'src2', database_name)))
-#
test.pass_test()
# Local Variables:
diff --git a/test/TAR/TAR.py b/test/TAR/TAR.py
index 159f047..b05dc3b 100644
--- a/test/TAR/TAR.py
+++ b/test/TAR/TAR.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -58,6 +57,7 @@ sys.exit(0)
""")
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools = ['tar'], TAR = r'%(_python_)s mytar.py')
env.Tar(target = 'aaa.tar', source = ['file1', 'file2'])
env.Tar(target = 'aaa.tar', source = 'file3')
@@ -89,6 +89,7 @@ if tar:
test.file_fixture('wrapper.py')
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
foo = Environment()
tar = foo.Dictionary('TAR')
bar = Environment(TAR = r'%(_python_)s wrapper.py ' + tar)
diff --git a/test/TARGETS.py b/test/TARGETS.py
index 3cc2f0f..b67c97a 100644
--- a/test/TARGETS.py
+++ b/test/TARGETS.py
@@ -66,6 +66,7 @@ test.run(arguments = 'bbb ccc=xyz -n aaa', stdout = expect)
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
print(list(map(str, DEFAULT_TARGETS)))
print(list(map(str, BUILD_TARGETS)))
diff --git a/test/TEX/biber_biblatex2.py b/test/TEX/biber_biblatex2.py
index 92c7ea9..049f06e 100644
--- a/test/TEX/biber_biblatex2.py
+++ b/test/TEX/biber_biblatex2.py
@@ -72,7 +72,7 @@ sources_bib_content = r"""
"""
test.write(['ref.bib'],sources_bib_content % '2013' )
-test.write(['bibertest.tex'],r"""
+sources_tex_content = r"""
\documentclass{article}
\usepackage{biblatex}
@@ -80,13 +80,14 @@ test.write(['bibertest.tex'],r"""
\begin{document}
-Hello. This is boring.
+Hello. This is %s boring.
\cite{mybook}
And even more boring.
\printbibliography
\end{document}
-""")
+"""
+test.write(['bibertest.tex'], sources_tex_content % "")
test.run()
@@ -110,7 +111,17 @@ for f in files:
pdf_output_1 = test.read('bibertest.pdf')
+# Change tex, but don't change bib. In this case, pdf should still be rebuilt.
+test.write(['bibertest.tex'], sources_tex_content % "very")
+test.run()
+pdf_output_1a = test.read('bibertest.pdf')
+# If the PDF file is the same as it was previously, then it didn't
+# pick up the change in the tex file, so fail.
+test.fail_test(pdf_output_1 == pdf_output_1a)
+
+# Change bib.
+test.write(['bibertest.tex'], sources_tex_content % "")
test.write(['ref.bib'],sources_bib_content % '1982')
test.run()
diff --git a/test/TEX/bibtex-latex-rerun.py b/test/TEX/bibtex-latex-rerun.py
index f0f8c34..7334f1e 100644
--- a/test/TEX/bibtex-latex-rerun.py
+++ b/test/TEX/bibtex-latex-rerun.py
@@ -47,14 +47,14 @@ env = Environment(tools=['pdftex', 'tex'])
env.PDF( 'bibtest.tex' )
""")
-test.write(['bibtest.tex'], r"""
+sources_tex_content = r"""
\documentclass{article}
\begin{document}
-Learn about cool math in \cite{koblitz:elliptic_curves}.
+Learn about %s cool math in \cite{koblitz:elliptic_curves}.
\bibliographystyle{alpha}
\bibliography{sources}
\end{document}
-""")
+"""
sources_bib_content = r"""
@book{koblitz:elliptic_curves,
@@ -67,14 +67,24 @@ sources_bib_content = r"""
+test.write(['bibtest.tex'], sources_tex_content % "")
test.write('sources.bib', sources_bib_content % '1981')
test.run()
pdf_output_1 = test.read('bibtest.pdf')
+# Change tex, but don't change bib. In this case, pdf should still be rebuilt.
+test.write(['bibtest.tex'], sources_tex_content % "really")
+test.run()
+pdf_output_1a = test.read('bibtest.pdf')
+# If the PDF file is the same as it was previously, then it didn't
+# pick up the change in the tex file, so fail.
+test.fail_test(pdf_output_1 == pdf_output_1a)
+# Change bib.
+test.write(['bibtest.tex'], sources_tex_content % "")
test.write('sources.bib', sources_bib_content % '1982')
test.run()
diff --git a/test/Touch.py b/test/Touch.py
index 431cd6c..3538c7d 100644
--- a/test/Touch.py
+++ b/test/Touch.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the Touch() Action works.
@@ -34,30 +33,29 @@ import TestSCons
test = TestSCons.TestSCons()
-test.write('SConstruct', """
+test.write('SConstruct', """\
Execute(Touch('f1'))
Execute(Touch(File('f1-File')))
+
def cat(env, source, target):
target = str(target[0])
with open(target, "wb") as f:
for src in source:
with open(str(src), "rb") as ifp:
f.write(ifp.read())
+
Cat = Action(cat)
env = Environment()
env.Command('f2.out', 'f2.in', [Cat, Touch("f3")])
env = Environment(FILE='f4')
env.Command('f5.out', 'f5.in', [Touch("$FILE"), Cat])
-env.Command('f6.out', 'f6.in', [Cat,
- Touch("Touch-$SOURCE"),
- Touch("$TARGET-Touch")])
+env.Command('f6.out', 'f6.in', [Cat, Touch("Touch-$SOURCE"), Touch("$TARGET-Touch")])
# Make sure Touch works with a list of arguments
env = Environment()
-env.Command('f7.out', 'f7.in', [Cat,
- Touch(["Touch-$SOURCE",
- "$TARGET-Touch",
- File("f8")])])
+env.Command(
+ 'f7.out', 'f7.in', [Cat, Touch(["Touch-$SOURCE", "$TARGET-Touch", File("f8")])]
+)
""")
test.write('f1', "f1\n")
@@ -70,11 +68,12 @@ test.write('f7.in', "f7.in\n")
old_f1_time = os.path.getmtime(test.workpath('f1'))
old_f1_File_time = os.path.getmtime(test.workpath('f1-File'))
-expect = test.wrap_stdout(read_str = """\
+expect = test.wrap_stdout(
+ read_str="""\
Touch("f1")
Touch("f1-File")
""",
- build_str = """\
+ build_str="""\
cat(["f2.out"], ["f2.in"])
Touch("f3")
Touch("f4")
@@ -84,11 +83,11 @@ Touch("Touch-f6.in")
Touch("f6.out-Touch")
cat(["f7.out"], ["f7.in"])
Touch(["Touch-f7.in", "f7.out-Touch", "f8"])
-""")
-test.run(options = '-n', arguments = '.', stdout = expect)
-
-test.sleep(2)
+""",
+)
+test.run(options='-n', arguments='.', stdout=expect)
+test.sleep() # delay for timestamps
new_f1_time = os.path.getmtime(test.workpath('f1'))
test.fail_test(old_f1_time != new_f1_time)
new_f1_File_time = os.path.getmtime(test.workpath('f1-File'))
diff --git a/test/ValidateOptions.py b/test/ValidateOptions.py
new file mode 100644
index 0000000..9b53c09
--- /dev/null
+++ b/test/ValidateOptions.py
@@ -0,0 +1,68 @@
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test ValidateOptions().
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.file_fixture('fixture/SConstruct-check-valid-options', 'SConstruct')
+
+# Should see "This is in SConstruct" because all options specified (none) are valid and
+# so ValidatedOptions() won't exit before it's printed.
+test.run()
+test.must_contain_single_instance_of(test.stdout(), ["This is in SConstruct"])
+
+# Should see "This is in SConstruct" because all options specified (--testing=abc) are valid and
+# so ValidatedOptions() won't exit before it's printed.
+test.run(arguments="--testing=abc")
+test.must_contain_single_instance_of(test.stdout(), ["This is in SConstruct"])
+
+# Should not see "This is in SConstruct" because the option specified (--garbage=xyz) is invalid and
+# so ValidatedOptions() will exit before it's printed.
+test.run(arguments="--garbage=xyz", status=2, stderr=".*SCons Error: no such option: --garbage.*",
+ match=TestSCons.match_re_dotall)
+test.fail_test(("This is in SConstruct" in test.stdout()),
+ message='"This is in SConstruct" should not be output. This means ValidateOptions() did not error out before this was printed')
+
+# Now we'll test having ValidateOptions raise a SConsBadOptionError exception
+test.run(arguments="--garbage=xyz raise=1", status=2,
+ stderr=".*SConsBadOptionError: no such option: no such option: --garbage.*",
+ match=TestSCons.match_re_dotall)
+test.fail_test(("This is in SConstruct" in test.stdout()),
+ message='"This is in SConstruct" should not be output. This means ValidateOptions() did not error out before this was printed')
+
+# Now we'll test having ValidateOptions raise a SConsBadOptionError exception and catching that exception
+test.run(arguments="--garbage=xyz raise=2", status=3,
+ stdout=".*Parser is SConsOptionParser:True.*Message is .no such option. --garbage.*",
+ match=TestSCons.match_re_dotall)
+test.fail_test(("This is in SConstruct" in test.stdout()),
+ message='"This is in SConstruct" should not be output. This means ValidateOptions() did not error out before this was printed')
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/Value.py b/test/Value.py
index 7a702b0..78cd152 100644
--- a/test/Value.py
+++ b/test/Value.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -21,8 +23,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.
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
import re
import TestSCons
@@ -49,6 +49,7 @@ def create(target, source, env):
with open(str(target[0]), 'wb') as f:
f.write(source[0].get_contents())
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env['BUILDERS']['B'] = Builder(action = create)
env['BUILDERS']['S'] = Builder(action = r'%(_python_)s put.py $SOURCES into $TARGET')
diff --git a/test/Variables/BoolVariable.py b/test/Variables/BoolVariable.py
index eaf496a..9a95d85 100644
--- a/test/Variables/BoolVariable.py
+++ b/test/Variables/BoolVariable.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the BoolVariable canned Variable type.
@@ -40,18 +39,18 @@ def check(expect):
assert result[1:len(expect)+1] == expect, (result[1:len(expect)+1], expect)
-
test.write(SConstruct_path, """\
from SCons.Variables.BoolVariable import BoolVariable
+
BV = BoolVariable
from SCons.Variables import BoolVariable
opts = Variables(args=ARGUMENTS)
opts.AddVariables(
- BoolVariable('warnings', 'compilation with -Wall and similiar', 1),
- BV('profile', 'create profiling informations', 0),
- )
+ BoolVariable('warnings', 'compilation with -Wall and similiar', True),
+ BV('profile', 'create profiling informations', False),
+)
env = Environment(variables=opts)
Help(opts.GenerateHelpText(env))
@@ -62,8 +61,6 @@ print(env['profile'])
Default(env.Alias('dummy', None))
""")
-
-
test.run()
check([str(True), str(False)])
@@ -73,12 +70,10 @@ check([str(False), str(True)])
expect_stderr = """
scons: *** Error converting option: warnings
Invalid value for boolean option: irgendwas
-""" + test.python_file_line(SConstruct_path, 12)
+""" + test.python_file_line(SConstruct_path, 13)
test.run(arguments='warnings=irgendwas', stderr = expect_stderr, status=2)
-
-
test.pass_test()
# Local Variables:
diff --git a/test/Variables/ListVariable.py b/test/Variables/ListVariable.py
index e463840..0067c56 100644
--- a/test/Variables/ListVariable.py
+++ b/test/Variables/ListVariable.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the ListVariable canned Variable type.
@@ -62,6 +61,7 @@ opts.AddVariables(
LV('listvariable', 'listvariable help', 'all', names=['l1', 'l2', 'l3'])
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
opts.Save(optsfile, env)
Help(opts.GenerateHelpText(env))
@@ -115,7 +115,7 @@ check(['gl,qt', '0', 'gl qt', 'gl qt', "['gl qt']"])
expect_stderr = """
scons: *** Error converting option: shared
Invalid value(s) for option: foo
-""" + test.python_file_line(SConstruct_path, 19)
+""" + test.python_file_line(SConstruct_path, 20)
test.run(arguments='shared=foo', stderr=expect_stderr, status=2)
@@ -124,28 +124,28 @@ test.run(arguments='shared=foo', stderr=expect_stderr, status=2)
expect_stderr = """
scons: *** Error converting option: shared
Invalid value(s) for option: foo
-""" + test.python_file_line(SConstruct_path, 19)
+""" + test.python_file_line(SConstruct_path, 20)
test.run(arguments='shared=foo,ical', stderr=expect_stderr, status=2)
expect_stderr = """
scons: *** Error converting option: shared
Invalid value(s) for option: foo
-""" + test.python_file_line(SConstruct_path, 19)
+""" + test.python_file_line(SConstruct_path, 20)
test.run(arguments='shared=ical,foo', stderr=expect_stderr, status=2)
expect_stderr = """
scons: *** Error converting option: shared
Invalid value(s) for option: foo
-""" + test.python_file_line(SConstruct_path, 19)
+""" + test.python_file_line(SConstruct_path, 20)
test.run(arguments='shared=ical,foo,x11', stderr=expect_stderr, status=2)
expect_stderr = """
scons: *** Error converting option: shared
Invalid value(s) for option: foo,bar
-""" + test.python_file_line(SConstruct_path, 19)
+""" + test.python_file_line(SConstruct_path, 20)
test.run(arguments='shared=foo,x11,,,bar', stderr=expect_stderr, status=2)
@@ -162,6 +162,7 @@ opts.AddVariables(
names = ['ENET', 'GPIB', 'LINUX_GPIB', 'NO_GPIB']),
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
Help(opts.GenerateHelpText(env))
diff --git a/test/Variables/PathVariable.py b/test/Variables/PathVariable.py
index 753aa11..ea5e15f 100644
--- a/test/Variables/PathVariable.py
+++ b/test/Variables/PathVariable.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the PathVariable canned option type, with tests for its
@@ -61,6 +60,7 @@ opts.AddVariables(
PV('qt_libraries', 'where the Qt library is installed', r'%s'),
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
Help(opts.GenerateHelpText(env))
@@ -92,7 +92,7 @@ test.run(arguments=['qtdir=%s' % qtpath, 'qt_libraries=%s' % libpath])
check([qtpath, libpath, libpath])
qtpath = os.path.join(workpath, 'non', 'existing', 'path')
-SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 14)[:-1]
+SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 15)[:-1]
expect_stderr = """
scons: *** Path for option qtdir does not exist: %(qtpath)s
@@ -130,6 +130,7 @@ opts.AddVariables(
PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathAccept),
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
print(env['X'])
@@ -163,6 +164,7 @@ opts.AddVariables(
PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathIsFile),
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
print(env['X'])
@@ -170,7 +172,7 @@ print(env['X'])
Default(env.Alias('dummy', None))
""" % default_file)
-SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 6)[:-1]
+SConstruct_file_line = test.python_file_line(test.workpath('SConstruct'), 7)[:-1]
expect_stderr = """
scons: *** File path for option X does not exist: %(default_file)s
@@ -209,6 +211,7 @@ opts.AddVariables(
PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathIsDir),
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
print(env['X'])
@@ -257,6 +260,7 @@ opts.AddVariables(
PathVariable('X', 'X variable', r'%s', validator=PathVariable.PathIsDirCreate),
)
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
print(env['X'])
diff --git a/test/Variables/Variables.py b/test/Variables/Variables.py
index bb7b237..f6832c0 100644
--- a/test/Variables/Variables.py
+++ b/test/Variables/Variables.py
@@ -28,6 +28,7 @@ import TestSCons
test = TestSCons.TestSCons()
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
print(env['CC'])
print(" ".join(env['CCFLAGS']))
@@ -94,6 +95,7 @@ def test_tool(env):
env.Append(CCFLAGS = '-g')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts, tools=['default', test_tool])
Help('Variables settable in custom.py or on the command line:\\n' + opts.GenerateHelpText(env))
@@ -217,6 +219,7 @@ opts.Add('DEBUG_BUILD',
opts.Add('UNSPECIFIED',
'An option with no value')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables = opts)
print(env['RELEASE_BUILD'])
@@ -275,6 +278,7 @@ opts.Add('LISTOPTION_TEST',
'none',
names = ['a','b','c',])
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables = opts)
print(env['RELEASE_BUILD'])
@@ -319,6 +323,7 @@ opts.Add('CC',
opts.Add('UNSPECIFIED',
'An option with no value')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(variables=opts)
def compare(a, b):
@@ -355,6 +360,7 @@ Use scons -H for help about command-line options.
test.write('SConstruct', """
import SCons.Variables
+DefaultEnvironment(tools=[]) # test speedup
env1 = Environment(variables = Variables())
env2 = Environment(variables = SCons.Variables.Variables())
""")
diff --git a/test/Variables/help.py b/test/Variables/help.py
index bee6011..cab7a67 100644
--- a/test/Variables/help.py
+++ b/test/Variables/help.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the Variables help messages.
@@ -46,37 +45,52 @@ test.subdir(qtpath)
test.subdir(libpath)
test.write('SConstruct', """
-from SCons.Variables import BoolVariable, EnumVariable, ListVariable, \
- PackageVariable, PathVariable
+from SCons.Variables import (
+ BoolVariable,
+ EnumVariable,
+ ListVariable,
+ PackageVariable,
+ PathVariable,
+)
list_of_libs = Split('x11 gl qt ical')
qtdir = r'%(qtpath)s'
opts = Variables(args=ARGUMENTS)
opts.AddVariables(
- BoolVariable('warnings', 'compilation with -Wall and similiar', 1),
- BoolVariable('profile', 'create profiling informations', 0),
- EnumVariable('debug', 'debug output and symbols', 'no',
- allowed_values=('yes', 'no', 'full'),
- map={}, ignorecase=0), # case sensitive
- EnumVariable('guilib', 'gui lib to use', 'gtk',
- allowed_values=('motif', 'gtk', 'kde'),
- map={}, ignorecase=1), # case insensitive
- EnumVariable('some', 'some option', 'xaver',
- allowed_values=('xaver', 'eins'),
- map={}, ignorecase=2), # make lowercase
- ListVariable('shared',
- 'libraries to build as shared libraries',
- 'all',
- names = list_of_libs),
- PackageVariable('x11',
- 'use X11 installed here (yes = search some places)',
- 'yes'),
+ BoolVariable('warnings', 'compilation with -Wall and similiar', True),
+ BoolVariable('profile', 'create profiling informations', False),
+ EnumVariable(
+ 'debug',
+ 'debug output and symbols',
+ 'no',
+ allowed_values=('yes', 'no', 'full'),
+ map={},
+ ignorecase=0,
+ ), # case sensitive
+ EnumVariable(
+ 'guilib',
+ 'gui lib to use',
+ 'gtk',
+ allowed_values=('motif', 'gtk', 'kde'),
+ map={},
+ ignorecase=1,
+ ), # case insensitive
+ EnumVariable(
+ 'some',
+ 'some option',
+ 'xaver',
+ allowed_values=('xaver', 'eins'),
+ map={},
+ ignorecase=2,
+ ), # make lowercase
+ ListVariable(
+ 'shared', 'libraries to build as shared libraries', 'all', names=list_of_libs
+ ),
+ PackageVariable('x11', 'use X11 installed here (yes = search some places)', 'yes'),
PathVariable('qtdir', 'where the root of Qt is installed', qtdir),
- PathVariable('qt_libraries',
- 'where the Qt library is installed',
- r'%(libdirvar)s'),
- )
+ PathVariable('qt_libraries', 'where the Qt library is installed', r'%(libdirvar)s'),
+)
env = Environment(variables=opts)
Help(opts.GenerateHelpText(env))
@@ -96,11 +110,11 @@ scons: Reading SConscript files ...
scons: done reading SConscript files.
warnings: compilation with -Wall and similiar (yes|no)
- default: 1
+ default: True
actual: %(str_True)s
profile: create profiling informations (yes|no)
- default: 0
+ default: False
actual: %(str_False)s
debug: debug output and symbols (yes|no|full)
diff --git a/test/VariantDir/VariantDir.py b/test/VariantDir/VariantDir.py
index a988831..bd329d8 100644
--- a/test/VariantDir/VariantDir.py
+++ b/test/VariantDir/VariantDir.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -21,8 +23,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.
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
import TestSCons
_exe = TestSCons._exe
@@ -56,7 +56,6 @@ bar52 = test.workpath('build', 'var5', 'bar2' + _exe)
test.subdir('work1', 'work2', 'work3')
test.write(['work1', 'SConstruct'], """
-DefaultEnvironment(tools=[])
src = Dir('src')
var2 = Dir('build/var2')
var3 = Dir('build/var3')
@@ -64,6 +63,7 @@ var4 = Dir('build/var4')
var5 = Dir('../build/var5')
var6 = Dir('../build/var6')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILD = 'build', SRC = 'src')
VariantDir('build/var1', src)
@@ -119,7 +119,7 @@ except:
if fortran and env.Detect(fortran):
if sys.platform =='win32':
- env_prog=Environment(tools=['mingw'],
+ env_prog = Environment(tools=['mingw'],
# BUILD = env['BUILD'], SRC = ENV['src'],
CPPPATH=env['CPPPATH'], FORTRANPATH=env['FORTRANPATH'] )
else:
@@ -333,6 +333,7 @@ scons: `%s' is up to date.
#
test.write(['work2', 'SConstruct'], """\
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env.Program('prog.c')
""")
@@ -356,6 +357,7 @@ test.up_to_date(chdir='work2', arguments='.')
#
test.write(['work2', 'SConstruct'], """\
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
VariantDir('build', '.')
Export('env')
diff --git a/test/YACC/BISONFLAGS.py b/test/YACC/BISONFLAGS.py
new file mode 100644
index 0000000..cae673d
--- /dev/null
+++ b/test/YACC/BISONFLAGS.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+#
+# MIT License
+#
+# Copyright The SCons Foundation
+#
+# 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.
+
+"""
+Test that detection of file-writing options in YACCFLAGS works.
+Also test that the construction vars for the same purpose work.
+"""
+
+from pathlib import Path
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+if IS_WINDOWS:
+ compiler = 'msvc'
+ linker = 'mslink'
+else:
+ compiler = 'gcc'
+ linker = 'gnulink'
+
+test = TestSCons.TestSCons()
+
+test.subdir('sub1')
+test.subdir('sub2')
+
+test.dir_fixture('YACCFLAGS-fixture')
+
+test.write('SConstruct', """\
+DefaultEnvironment(tools=[])
+SConscript(dirs=['sub1', 'sub2'])
+""")
+
+# this SConscript is for the options-in-flags version
+test.write(['sub1', 'SConscript'], """\
+import sys
+
+env = Environment(
+ YACC=r'%(_python_)s myyacc.py',
+ YACCFLAGS='-x --header=header.h --graph=graph.g',
+ tools=['yacc', '%(linker)s', '%(compiler)s'],
+)
+targs = env.CFile(target='aaa', source='aaa.y')
+t = [str(target) for target in targs]
+# fail ourselves if the two extra files were not detected
+if not all((len(t) == 3, "header.h" in t, "graph.g" in t)):
+ sys.exit(1)
+""" % locals())
+test.write(['sub1', 'aaa.y'], "aaa.y\nYACCFLAGS\n")
+
+# this SConscript is for the construction var version
+test.write(['sub2', 'SConscript'], """\
+import sys
+
+env = Environment(
+ YACC=r'%(_python_)s myyacc.py',
+ YACCFLAGS='-x',
+ tools=['yacc', '%(linker)s', '%(compiler)s'],
+)
+env.CFile(
+ target='aaa',
+ source='aaa.y',
+ YACC_HEADER_FILE='header.h',
+ YACC_GRAPH_FILE='graph.g',
+)
+""" % locals())
+test.write(['sub2', 'aaa.y'], "aaa.y\nYACCFLAGS\n")
+
+test.run('.', stderr=None)
+test.must_match(['sub1', 'aaa.c'], "aaa.y\n -x --header=header.h --graph=graph.g\n")
+
+# NOTE: this behavior is "wrong" but we're keeping it for compat:
+# the generated files should go into 'sub1', not the topdir.
+test.must_match(['header.h'], 'yacc header\n')
+test.must_match(['graph.g'], 'yacc graph\n')
+
+# To confirm the files from the file-output options were tracked,
+# we should do a clean and make sure they got removed.
+# As noted, they currently don't go into the tracked location,
+# so using the check in the SConscript instead.
+#test.run(arguments='-c .')
+#test.must_not_exist(test.workpath(['sub1', 'header.h']))
+#test.must_not_exist(test.workpath(['sub1', 'graph.g']))
+
+sub2 = Path('sub2')
+headerfile = sub2 / 'header.h'
+graphfile = sub2 / 'graph.g'
+yaccflags = f"aaa.y\n -x --header={headerfile} --graph={graphfile}\n"
+test.must_match(['sub2', 'aaa.c'], yaccflags)
+test.must_match(['sub2', 'header.h'], 'yacc header\n')
+test.must_match(['sub2', 'graph.g'], 'yacc graph\n')
+
+# To confirm the files from the file-output options were tracked,
+# do a clean and make sure they got removed. As noted, they currently
+# don't go into the tracked location, so using the the SConscript check instead.
+test.run(arguments='-c .')
+test.must_not_exist(test.workpath('sub2', 'header.h'))
+test.must_not_exist(test.workpath('sub2', 'graph.g'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/YACC/YACC-fixture/myyacc.py b/test/YACC/YACC-fixture/myyacc.py
index d0ab95e..77f80ea 100644
--- a/test/YACC/YACC-fixture/myyacc.py
+++ b/test/YACC/YACC-fixture/myyacc.py
@@ -1,13 +1,19 @@
import getopt
import sys
+
cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:', [])
opt_string = ''
for opt, arg in cmd_opts:
- if opt == '-o': out = arg
- else: opt_string = opt_string + ' ' + opt
+ if opt == '-o':
+ out = arg
+ else:
+ opt_string = opt_string + ' ' + opt
+
with open(out, 'w') as ofp:
for a in args:
with open(a, 'r') as ifp:
contents = ifp.read()
- ofp.write(contents.replace('YACC', 'myyacc.py'))
+ contents = contents.replace('YACC', 'myyacc.py')
+ ofp.write(contents)
+
sys.exit(0)
diff --git a/test/YACC/YACC.py b/test/YACC/YACC.py
index d4cb40e..a5b290c 100644
--- a/test/YACC/YACC.py
+++ b/test/YACC/YACC.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,19 +22,17 @@
# 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 sys
+from TestCmd import IS_WINDOWS
import TestSCons
_python_ = TestSCons._python_
_exe = TestSCons._exe
-if sys.platform == 'win32':
+if IS_WINDOWS:
compiler = 'msvc'
linker = 'mslink'
else:
@@ -44,22 +44,26 @@ test = TestSCons.TestSCons()
test.dir_fixture('YACC-fixture')
test.write('SConstruct', """
-env = Environment(YACC = r'%(_python_)s myyacc.py', tools=['default', 'yacc'])
-env.CFile(target = 'aaa', source = 'aaa.y')
-env.CFile(target = 'bbb', source = 'bbb.yacc')
-env.CXXFile(target = 'ccc', source = 'ccc.yy')
-env.CFile(target = 'ddd', source = 'ddd.ym')
+DefaultEnvironment(tools=[])
+env = Environment(YACC=r'%(_python_)s myyacc.py', tools=['default', 'yacc'])
+env.CFile(target='aaa', source='aaa.y')
+env.CFile(target='bbb', source='bbb.yacc')
+env.CXXFile(target='ccc', source='ccc.yy')
+env.CFile(target='ddd', source='ddd.ym')
""" % locals())
-test.run(arguments = '.', stderr = None)
+test.run(arguments='.', stderr=None)
-test.must_match('aaa.c', "aaa.y" + os.linesep + "myyacc.py" + os.linesep)
-test.must_match('bbb.c', "bbb.yacc" + os.linesep + "myyacc.py" + os.linesep)
-test.must_match('ccc.cc', "ccc.yacc" + os.linesep + "myyacc.py" + os.linesep)
-test.must_match('ddd.m', "ddd.yacc" + os.linesep + "myyacc.py" + os.linesep)
+test.must_match('aaa.c', "aaa.y" + os.linesep + "myyacc.py" + os.linesep)
+test.must_match('bbb.c', "bbb.yacc" + os.linesep + "myyacc.py" + os.linesep)
+test.must_match('ccc.cc', "ccc.yacc" + os.linesep + "myyacc.py" + os.linesep)
+test.must_match('ddd.m', "ddd.yacc" + os.linesep + "myyacc.py" + os.linesep)
test.run(arguments="-n -f SConstruct_YACC_before")
-test.fail_test('SOMETHING_DUMB' not in test.stdout(), "YACC is not overridden to be SOMETHING_DUMB")
+test.fail_test(
+ 'SOMETHING_DUMB' not in test.stdout(),
+ "YACC is not overridden to be SOMETHING_DUMB"
+)
test.pass_test()
diff --git a/test/YACC/YACCCOM.py b/test/YACC/YACCCOM.py
index e9fb47f..cc30ca7 100644
--- a/test/YACC/YACCCOM.py
+++ b/test/YACC/YACCCOM.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the ability to configure the $YACCCOM construction variable.
@@ -37,16 +36,19 @@ test = TestSCons.TestSCons()
test.file_fixture('mycompile.py')
test.write('SConstruct', """
-env = Environment(tools=['default', 'yacc'],
- YACCCOM = r'%(_python_)s mycompile.py yacc $TARGET $SOURCES')
-env.CFile(target = 'aaa', source = 'aaa.y')
-env.CFile(target = 'bbb', source = 'bbb.yacc')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'yacc'],
+ YACCCOM=r'%(_python_)s mycompile.py yacc $TARGET $SOURCES',
+)
+env.CFile(target='aaa', source='aaa.y')
+env.CFile(target='bbb', source='bbb.yacc')
""" % locals())
test.write('aaa.y', 'aaa.y\n/*yacc*/\n')
test.write('bbb.yacc', 'bbb.yacc\n/*yacc*/\n')
-test.run(arguments = '.')
+test.run(arguments='.')
test.must_match('aaa.c', "aaa.y\n")
test.must_match('bbb.c', "bbb.yacc\n")
diff --git a/test/YACC/YACCCOMSTR.py b/test/YACC/YACCCOMSTR.py
index bded560..de26025 100644
--- a/test/YACC/YACCCOMSTR.py
+++ b/test/YACC/YACCCOMSTR.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the $YACCCOMSTR construction variable allows you to customize
@@ -38,17 +37,20 @@ test = TestSCons.TestSCons()
test.file_fixture('mycompile.py')
test.write('SConstruct', """
-env = Environment(tools=['default', 'yacc'],
- YACCCOM = r'%(_python_)s mycompile.py yacc $TARGET $SOURCES',
- YACCCOMSTR = 'Yaccing $TARGET from $SOURCE')
-env.CFile(target = 'aaa', source = 'aaa.y')
-env.CFile(target = 'bbb', source = 'bbb.yacc')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'yacc'],
+ YACCCOM=r'%(_python_)s mycompile.py yacc $TARGET $SOURCES',
+ YACCCOMSTR='Yaccing $TARGET from $SOURCE',
+)
+env.CFile(target='aaa', source='aaa.y')
+env.CFile(target='bbb', source='bbb.yacc')
""" % locals())
test.write('aaa.y', 'aaa.y\n/*yacc*/\n')
test.write('bbb.yacc', 'bbb.yacc\n/*yacc*/\n')
-test.run(stdout = test.wrap_stdout("""\
+test.run(stdout=test.wrap_stdout("""\
Yaccing aaa.c from aaa.y
Yaccing bbb.c from bbb.yacc
"""))
diff --git a/test/YACC/YACCFLAGS-fixture/myyacc.py b/test/YACC/YACCFLAGS-fixture/myyacc.py
index 43024f1..3bc1375 100644
--- a/test/YACC/YACCFLAGS-fixture/myyacc.py
+++ b/test/YACC/YACCFLAGS-fixture/myyacc.py
@@ -1,17 +1,65 @@
import getopt
import sys
-cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:I:x', [])
-opt_string = ''
-i_arguments = ''
-for opt, arg in cmd_opts:
- if opt == '-o': out = arg
- elif opt == '-I': i_arguments = i_arguments + ' ' + arg
- else: opt_string = opt_string + ' ' + opt
-with open(out, 'wb') as ofp:
- for a in args:
- with open(a, 'rb') as ifp:
- contents = ifp.read()
- contents = contents.replace(b'YACCFLAGS', opt_string.encode())
- contents = contents.replace(b'I_ARGS', i_arguments.encode())
- ofp.write(contents)
-sys.exit(0)
+from pathlib import Path
+
+
+def make_side_effect(path, text):
+ p = Path(path)
+ if str(p.parent) != '.':
+ p.parent.mkdir(parents=True, exist_ok=True)
+ with p.open(mode="wb") as f:
+ f.write(text)
+
+
+def fake_yacc():
+ make_header = None
+ make_graph = None
+
+ longopts = ["defines=", "header=", "graph="]
+ cmd_opts, args = getopt.getopt(sys.argv[1:], 'o:I:x', longopts)
+ opt_string = ''
+ i_arguments = ''
+
+ for opt, arg in cmd_opts:
+ if opt == '-o':
+ out = arg
+ elif opt == '-I':
+ i_arguments = f'{i_arguments} {arg}'
+ elif opt in ('--defines', '--header'):
+ make_header = arg
+ opt_string = f'{opt_string} {opt}={arg}'
+ elif opt == '--graph':
+ make_graph = arg
+ opt_string = f'{opt_string} {opt}={arg}'
+ else:
+ opt_string = f'{opt_string} {opt}'
+
+ with open(out, 'wb') as ofp:
+ for a in args:
+ with open(a, 'rb') as ifp:
+ contents = ifp.read()
+ contents = contents.replace(b'YACCFLAGS', opt_string.encode())
+ contents = contents.replace(b'YACC', b'myyacc.py')
+ contents = contents.replace(b'I_ARGS', i_arguments.encode())
+ ofp.write(contents)
+
+ # Extra bits:
+ if make_header:
+ make_side_effect(make_header, b"yacc header\n")
+ if make_graph:
+ make_side_effect(make_graph, b"yacc graph\n")
+
+
+if __name__ == '__main__':
+ fake_yacc()
+ sys.exit(0)
+
+# If -d is specified on the command line, yacc will emit a .h
+# or .hpp file with the same name as the .c or .cpp output file.
+
+# If -g is specified on the command line, yacc will emit a .vcg
+# file with the same base name as the .y, .yacc, .ym or .yy 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 (issue #2558)
diff --git a/test/YACC/YACCFLAGS.py b/test/YACC/YACCFLAGS.py
index 1ab8c0d..1f3ad78 100644
--- a/test/YACC/YACCFLAGS.py
+++ b/test/YACC/YACCFLAGS.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,18 +22,16 @@
# 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 sys
import TestSCons
+from TestCmd import IS_WINDOWS
_python_ = TestSCons._python_
_exe = TestSCons._exe
-if sys.platform == 'win32':
+if IS_WINDOWS:
compiler = 'msvc'
linker = 'mslink'
else:
@@ -45,19 +45,18 @@ test.subdir('in')
test.dir_fixture('YACCFLAGS-fixture')
test.write('SConstruct', """
-env = Environment(YACC = r'%(_python_)s myyacc.py',
- YACCFLAGS = '-x -I${TARGET.dir} -I${SOURCE.dir}',
- tools=['yacc', '%(linker)s', '%(compiler)s'])
-env.CFile(target = 'out/aaa', source = 'in/aaa.y')
+DefaultEnvironment(tools=[])
+env = Environment(
+ YACC=r'%(_python_)s myyacc.py',
+ YACCFLAGS='-x -I${TARGET.dir} -I${SOURCE.dir}',
+ tools=['yacc', '%(linker)s', '%(compiler)s'],
+)
+env.CFile(target='out/aaa', source='in/aaa.y')
""" % locals())
-test.write(['in', 'aaa.y'], "aaa.y\nYACCFLAGS\nI_ARGS\n")
-
-test.run('.', stderr = None)
-
-test.must_match(['out', 'aaa.c'], "aaa.y\n -x\n out in\n")
-
-
+test.write(['in', 'aaa.y'], "aaa.y\nYACCFLAGS\nI_ARGS\n")
+test.run('.', stderr=None)
+test.must_match(['out', 'aaa.c'], "aaa.y\n -x\n out in\n")
test.pass_test()
diff --git a/test/YACC/YACCHFILESUFFIX.py b/test/YACC/YACCHFILESUFFIX.py
index da3416c..e37bd66 100644
--- a/test/YACC/YACCHFILESUFFIX.py
+++ b/test/YACC/YACCHFILESUFFIX.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 setting the YACCHFILESUFFIX variable can reflect a yacc
@@ -35,12 +34,11 @@ _python_ = TestSCons._python_
test = TestSCons.TestSCons()
-
-
test.write('myyacc.py', """\
import getopt
import os.path
import sys
+
opts, args = getopt.getopt(sys.argv[1:], 'do:')
for o, a in opts:
if o == '-o':
@@ -51,33 +49,34 @@ for f in args:
outfile.write(l)
outfile.close()
base, ext = os.path.splitext(args[0])
-with open(base+'.hsuffix', 'wb') as outfile:
+with open(base + '.hsuffix', 'wb') as outfile:
outfile.write((" ".join(sys.argv) + '\\n').encode())
sys.exit(0)
""")
test.write('SConstruct', """
-env = Environment(tools=['default', 'yacc'],
- YACC = r'%(_python_)s myyacc.py',
- YACCFLAGS = '-d',
- YACCHFILESUFFIX = '.hsuffix')
-env.CFile(target = 'aaa', source = 'aaa.y')
-env.CFile(target = 'bbb', source = 'bbb.yacc')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'yacc'],
+ YACC=r'%(_python_)s myyacc.py',
+ YACCFLAGS='-d',
+ YACCHFILESUFFIX='.hsuffix',
+)
+env.CFile(target='aaa', source='aaa.y')
+env.CFile(target='bbb', source='bbb.yacc')
""" % locals())
test.write('aaa.y', "aaa.y\n/*yacc*/\n")
test.write('bbb.yacc', "bbb.yacc\n/*yacc*/\n")
-test.run(arguments = '.')
+test.run(arguments='.')
test.must_match('aaa.c', "aaa.y\n")
test.must_contain('aaa.hsuffix', "myyacc.py -d -o aaa.c aaa.y\n")
test.must_match('bbb.c', "bbb.yacc\n")
test.must_contain('bbb.hsuffix', "myyacc.py -d -o bbb.c bbb.yacc\n")
-test.up_to_date(arguments = '.')
-
-
+test.up_to_date(arguments='.')
test.pass_test()
diff --git a/test/YACC/YACCHXXFILESUFFIX.py b/test/YACC/YACCHXXFILESUFFIX.py
index 3ee70ee..3028d70 100644
--- a/test/YACC/YACCHXXFILESUFFIX.py
+++ b/test/YACC/YACCHXXFILESUFFIX.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 setting the YACCHXXFILESUFFIX variable can reflect a yacc
@@ -35,12 +34,11 @@ _python_ = TestSCons._python_
test = TestSCons.TestSCons()
-
-
test.write('myyacc.py', """\
import getopt
import os.path
import sys
+
opts, args = getopt.getopt(sys.argv[1:], 'do:')
for o, a in opts:
if o == '-o':
@@ -51,29 +49,30 @@ for f in args:
outfile.write(l)
outfile.close()
base, ext = os.path.splitext(args[0])
-with open(base+'.hxxsuffix', 'wb') as outfile:
- outfile.write((" ".join(sys.argv)+'\\n').encode())
+with open(base + '.hxxsuffix', 'wb') as outfile:
+ outfile.write((" ".join(sys.argv) + '\\n').encode())
sys.exit(0)
""")
test.write('SConstruct', """
-env = Environment(tools=['default', 'yacc'],
- YACC = r'%(_python_)s myyacc.py',
- YACCFLAGS = '-d',
- YACCHXXFILESUFFIX = '.hxxsuffix')
-env.CXXFile(target = 'aaa', source = 'aaa.yy')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'yacc'],
+ YACC=r'%(_python_)s myyacc.py',
+ YACCFLAGS='-d',
+ YACCHXXFILESUFFIX='.hxxsuffix',
+)
+env.CXXFile(target='aaa', source='aaa.yy')
""" % locals())
test.write('aaa.yy', "aaa.yy\n/*yacc*/\n")
-test.run(arguments = '.')
+test.run(arguments='.')
test.must_match('aaa.cc', "aaa.yy\n")
test.must_contain('aaa.hxxsuffix', "myyacc.py -d -o aaa.cc aaa.yy\n")
-test.up_to_date(arguments = '.')
-
-
+test.up_to_date(arguments='.')
test.pass_test()
diff --git a/test/YACC/YACCVCGFILESUFFIX.py b/test/YACC/YACCVCGFILESUFFIX.py
index 32c3440..2fb953b 100644
--- a/test/YACC/YACCVCGFILESUFFIX.py
+++ b/test/YACC/YACCVCGFILESUFFIX.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 setting the YACCVCGFILESUFFIX variable.
@@ -34,12 +33,11 @@ _python_ = TestSCons._python_
test = TestSCons.TestSCons()
-
-
test.write('myyacc.py', """\
import getopt
import os.path
import sys
+
vcg = None
opts, args = getopt.getopt(sys.argv[1:], 'go:')
for o, a in opts:
@@ -54,23 +52,26 @@ for f in args:
outfile.close()
if vcg:
base, ext = os.path.splitext(args[0])
- with open(base+'.vcgsuffix', 'wb') as outfile:
- outfile.write((" ".join(sys.argv)+'\\n').encode())
+ with open(base + '.vcgsuffix', 'wb') as outfile:
+ outfile.write((" ".join(sys.argv) + '\\n').encode())
sys.exit(0)
""")
test.write('SConstruct', """
-env = Environment(tools=['default', 'yacc'],
- YACC = r'%(_python_)s myyacc.py',
- YACCVCGFILESUFFIX = '.vcgsuffix')
-env.CXXFile(target = 'aaa', source = 'aaa.yy')
-env.CXXFile(target = 'bbb', source = 'bbb.yy', YACCFLAGS = '-g')
+DefaultEnvironment(tools=[])
+env = Environment(
+ tools=['default', 'yacc'],
+ YACC=r'%(_python_)s myyacc.py',
+ YACCVCGFILESUFFIX='.vcgsuffix',
+)
+env.CXXFile(target='aaa', source='aaa.yy')
+env.CXXFile(target='bbb', source='bbb.yy', YACCFLAGS='-g')
""" % locals())
test.write('aaa.yy', "aaa.yy\n/*yacc*/\n")
test.write('bbb.yy', "bbb.yy\n/*yacc*/\n")
-test.run(arguments = '.')
+test.run(arguments='.')
test.must_match('aaa.cc', "aaa.yy\n")
test.must_not_exist('aaa.vcg')
@@ -80,9 +81,7 @@ test.must_match('bbb.cc', "bbb.yy\n")
test.must_not_exist('bbb.vcg')
test.must_contain('bbb.vcgsuffix', "myyacc.py -g -o bbb.cc bbb.yy\n")
-test.up_to_date(arguments = '.')
-
-
+test.up_to_date(arguments='.')
test.pass_test()
diff --git a/test/YACC/live-check-output-cleaned.py b/test/YACC/live-check-output-cleaned.py
index 9adaaf0..aef47de 100644
--- a/test/YACC/live-check-output-cleaned.py
+++ b/test/YACC/live-check-output-cleaned.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 .output file is cleaned
@@ -77,14 +76,14 @@ newline: '\n';
test.write('foo.y', yacc % 'foo.y')
-test.run('.', stderr = None)
+test.run('.', stderr=None)
-test.up_to_date(arguments = 'foo.c')
+test.up_to_date(arguments='foo.c')
test.must_exist(test.workpath('foo.output'))
test.must_exist(test.workpath('foo.c'))
-test.run(arguments = '-c .')
+test.run(arguments='-c .')
test.must_not_exist(test.workpath('foo.output'))
test.must_not_exist(test.workpath('foo.c'))
diff --git a/test/YACC/live.py b/test/YACC/live.py
index 6314e6c..9214369 100644
--- a/test/YACC/live.py
+++ b/test/YACC/live.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 YACC and YACCFLAGS with a live yacc compiler.
@@ -44,14 +43,14 @@ test.file_fixture('wrapper.py')
test.write('SConstruct', """
DefaultEnvironment(tools=[])
-foo = Environment(YACCFLAGS='-d', tools = ['default', 'yacc'])
+foo = Environment(YACCFLAGS='-d', tools=['default', 'yacc'])
yacc = foo.Dictionary('YACC')
-bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc, tools = ['default', 'yacc'])
-foo.Program(target = 'foo', source = 'foo.y')
-bar.Program(target = 'bar', source = 'bar.y')
-foo.Program(target = 'hello', source = ['hello.cpp'])
-foo.CXXFile(target = 'file.cpp', source = ['file.yy'], YACCFLAGS='-d')
-foo.CFile(target = 'not_foo', source = 'foo.y')
+bar = Environment(YACC=r'%(_python_)s wrapper.py ' + yacc, tools=['default', 'yacc'])
+foo.Program(target='foo', source='foo.y')
+bar.Program(target='bar', source='bar.y')
+foo.Program(target='hello', source=['hello.cpp'])
+foo.CXXFile(target='file.cpp', source=['file.yy'], YACCFLAGS='-d')
+foo.CFile(target='not_foo', source='foo.y')
""" % locals())
yacc = r"""
diff --git a/test/builderrors.py b/test/builderrors.py
index 635f448..4cdd1c1 100644
--- a/test/builderrors.py
+++ b/test/builderrors.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -21,8 +23,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.
-__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-
import os
import TestSCons
@@ -45,6 +45,7 @@ sys.exit(exitval)
test.write(['one', 'SConstruct'], """
B0 = Builder(action = r'%(_python_)s ../build.py 0 $TARGET $SOURCES')
B1 = Builder(action = r'%(_python_)s ../build.py 1 $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B0' : B0, 'B1' : B1 })
env.B1(target = 'f1.out', source = 'f1.in')
env.B0(target = 'f2.out', source = 'f2.in')
@@ -65,6 +66,7 @@ test.fail_test(os.path.exists(test.workpath('f3.out')))
test.write(['two', 'SConstruct'], """
B0 = Builder(action = r'%(_python_)s ../build.py 0 $TARGET $SOURCES')
B1 = Builder(action = r'%(_python_)s ../build.py 1 $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B0': B0, 'B1' : B1 })
env.B0(target = 'f1.out', source = 'f1.in')
env.B1(target = 'f2.out', source = 'f2.in')
@@ -85,6 +87,7 @@ test.fail_test(os.path.exists(test.workpath('f3.out')))
test.write(['three', 'SConstruct'], """
B0 = Builder(action = r'%(_python_)s ../build.py 0 $TARGET $SOURCES')
B1 = Builder(action = r'%(_python_)s ../build.py 1 $TARGET $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B0' : B0, 'B1': B1 })
env.B0(target = 'f1.out', source = 'f1.in')
env.B0(target = 'f2.out', source = 'f2.in')
@@ -103,6 +106,7 @@ test.must_match(['three', 'f2.out'], "three/f2.in\n", mode='r')
test.fail_test(os.path.exists(test.workpath('f3.out')))
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env=Environment()
env['ENV']['PATH'] = ''
env.Command(target='foo.out', source=[], action='not_a_program')
@@ -116,6 +120,7 @@ test.must_not_contain_any_line(test.stderr(), ['Exception', 'Traceback'])
# but that shouldn't cause a scons traceback.
long_cmd = 'xyz ' + "foobarxyz" * 100000
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env=Environment()
env.Command(target='longcmd.out', source=[], action='echo %s')
"""%long_cmd)
@@ -137,6 +142,7 @@ test.must_not_contain_any_line(test.stderr(), ['Exception', 'Traceback'])
# This will also give an exit status not in exitvalmap,
# with error "Permission denied" or "No such file or directory".
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env=Environment()
env['SHELL'] = 'one'
env.Command(target='badshell.out', source=[], action='foo')
@@ -156,6 +162,7 @@ test.must_contain_any_line(test.stderr(), expect)
# Should not give traceback.
test.write('SConstruct', """
import os
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(ENV = os.environ)
env.Command('dummy.txt', None, ['python -c "import sys; sys.exit(-1)"'])
""")
@@ -170,6 +177,7 @@ test.must_not_contain_any_line(test.stderr(), ['Exception', 'Traceback'])
test.write('SConstruct', """
import atexit
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env2 = env.Clone()
@@ -190,6 +198,7 @@ test.must_not_contain_any_line(test.stderr(), ['Exception', 'Traceback'])
# Bug #1053: Alias is called "all", but default is the File "all"
test.write('SConstruct', """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env.Default("all")
env.Alias("all", env.Install("dir", "file.txt"))
diff --git a/test/chained-build.py b/test/chained-build.py
index 871a593..10d0b46 100644
--- a/test/chained-build.py
+++ b/test/chained-build.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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
@@ -58,23 +57,20 @@ test.write(['w1', 'SConstruct1'], SConstruct1_contents)
test.write(['w1', 'SConstruct2'], SConstruct2_contents)
test.write(['w1', 'foo.in'], "foo.in 1")
-test.run(chdir='w1',
- arguments="--max-drift=0 -f SConstruct1 foo.mid",
- stdout = test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'))
-
-test.run(chdir='w1',
- arguments="--max-drift=0 -f SConstruct2 foo.out",
- stdout = test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'))
-
-test.up_to_date(chdir='w1',
- options="--max-drift=0 -f SConstruct1",
- arguments="foo.mid")
-
-test.up_to_date(chdir='w1',
- options="--max-drift=0 -f SConstruct2",
- arguments="foo.out")
-
-test.sleep() # make sure foo.in rewrite has new mod-time
+test.run(
+ chdir='w1',
+ arguments="--max-drift=0 -f SConstruct1 foo.mid",
+ stdout=test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'),
+)
+test.run(
+ chdir='w1',
+ arguments="--max-drift=0 -f SConstruct2 foo.out",
+ stdout=test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'),
+)
+test.up_to_date(chdir='w1', options="--max-drift=0 -f SConstruct1", arguments="foo.mid")
+test.up_to_date(chdir='w1', options="--max-drift=0 -f SConstruct2", arguments="foo.out")
+
+test.sleep() # delay for timestamps
test.write(['w1', 'foo.in'], "foo.in 2")
# Because we're using --max-drift=0, we use the cached csig value
@@ -86,17 +82,19 @@ test.up_to_date(chdir='w1',
# Now try with --max-drift disabled. The build of foo.out should still
# be considered up-to-date, but the build of foo.mid now detects the
# change and rebuilds, too, which then causes a rebuild of foo.out.
-test.up_to_date(chdir='w1',
- options="--max-drift=-1 -f SConstruct2",
- arguments="foo.out")
-
-test.run(chdir='w1',
- arguments="--max-drift=-1 -f SConstruct1 foo.mid",
- stdout = test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'))
-
-test.run(chdir='w1',
- arguments="--max-drift=-1 -f SConstruct2 foo.out",
- stdout = test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'))
+test.up_to_date(
+ chdir='w1', options="--max-drift=-1 -f SConstruct2", arguments="foo.out"
+)
+test.run(
+ chdir='w1',
+ arguments="--max-drift=-1 -f SConstruct1 foo.mid",
+ stdout=test.wrap_stdout('build(["foo.mid"], ["foo.in"])\n'),
+)
+test.run(
+ chdir='w1',
+ arguments="--max-drift=-1 -f SConstruct2 foo.out",
+ stdout=test.wrap_stdout('build(["foo.out"], ["foo.mid"])\n'),
+)
test.pass_test()
diff --git a/test/diskcheck.py b/test/diskcheck.py
index 36cfa4e..c07dc6b 100644
--- a/test/diskcheck.py
+++ b/test/diskcheck.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,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__"
"""
Test that the --diskcheck option and SetOption('diskcheck') correctly
@@ -41,16 +41,23 @@ test.write('file', "file\n")
test.write('SConstruct', """
-SetOption('diskcheck', 'none')
+
+if GetOption('diskcheck') == ['match'] or ARGUMENTS.get('setoption_none',0):
+ SetOption('diskcheck', 'none')
File('subdir')
""")
-test.run()
+test.run(status=2, stderr=None)
+test.must_contain_all_lines(test.stderr(), ["found where file expected"])
test.run(arguments='--diskcheck=match', status=2, stderr=None)
test.must_contain_all_lines(test.stderr(), ["found where file expected"])
+# Test that setting --diskcheck to none via command line also works.
+test.run(arguments='--diskcheck=none')
+# Test that SetOption('diskcheck','none') works to override default as well
+test.run(arguments='setoption_none=1')
test.pass_test()
diff --git a/test/exceptions.py b/test/exceptions.py
index 79d869c..842959a 100644
--- a/test/exceptions.py
+++ b/test/exceptions.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 re
@@ -37,17 +36,18 @@ SConstruct_path = test.workpath('SConstruct')
test.write(SConstruct_path, """\
def func(source = None, target = None, env = None):
raise Exception("func exception")
-B = Builder(action = func)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'foo.out', source = 'foo.in')
+B = Builder(action=func)
+env = Environment(BUILDERS={'B': B})
+env.B(target='foo.out', source='foo.in')
""")
test.write('foo.in', "foo.in\n")
expected_stderr = r"""scons: \*\*\* \[foo.out\] Exception : func exception
-Traceback \((most recent call|innermost) last\):
+Traceback \(most recent call last\):
( File ".+", line \d+, in \S+
[^\n]+
+ [^\n]+
)*( File ".+", line \d+, in \S+
)*( File ".+", line \d+, in \S+
[^\n]+
@@ -56,10 +56,8 @@ Traceback \((most recent call|innermost) last\):
Exception: func exception
""" % re.escape(SConstruct_path)
-test.run(arguments = "foo.out", stderr = expected_stderr, status = 2)
-
-test.run(arguments = "-j2 foo.out", stderr = expected_stderr, status = 2)
-
+test.run(arguments="foo.out", stderr=expected_stderr, status=2)
+test.run(arguments="-j2 foo.out", stderr=expected_stderr, status=2)
# Verify that exceptions caused by exit values of builder actions are
# correctly signalled, for both Serial and Parallel jobs.
@@ -70,29 +68,28 @@ sys.exit(1)
""")
test.write(SConstruct_path, """
-Fail = Builder(action = r'%(_python_)s myfail.py $TARGETS $SOURCE')
-env = Environment(BUILDERS = { 'Fail' : Fail })
-env.Fail(target = 'out.f1', source = 'in.f1')
+Fail = Builder(action=r'%(_python_)s myfail.py $TARGETS $SOURCE')
+env = Environment(BUILDERS={'Fail': Fail})
+env.Fail(target='out.f1', source='in.f1')
""" % locals())
test.write('in.f1', "in.f1\n")
expected_stderr = "scons: \\*\\*\\* \\[out.f1\\] Error 1\n"
-test.run(arguments = '.', status = 2, stderr = expected_stderr)
-test.run(arguments = '-j2 .', status = 2, stderr = expected_stderr)
-
+test.run(arguments='.', status=2, stderr=expected_stderr)
+test.run(arguments='-j2 .', status=2, stderr=expected_stderr)
# Verify that all exceptions from simultaneous tasks are reported,
# even if the exception is raised during the Task.prepare()
# [Node.prepare()]
test.write(SConstruct_path, """
-Fail = Builder(action = r'%(_python_)s myfail.py $TARGETS $SOURCE')
-env = Environment(BUILDERS = { 'Fail' : Fail })
-env.Fail(target = 'out.f1', source = 'in.f1')
-env.Fail(target = 'out.f2', source = 'in.f2')
-env.Fail(target = 'out.f3', source = 'in.f3')
+Fail = Builder(action=r'%(_python_)s myfail.py $TARGETS $SOURCE')
+env = Environment(BUILDERS={'Fail': Fail})
+env.Fail(target='out.f1', source='in.f1')
+env.Fail(target='out.f2', source='in.f2')
+env.Fail(target='out.f3', source='in.f3')
""" % locals())
# in.f2 is not created to cause a Task.prepare exception
@@ -100,7 +97,7 @@ test.write('in.f1', 'in.f1\n')
test.write('in.f3', 'in.f3\n')
# In Serial task mode, get the first exception and stop
-test.run(arguments = '.', status = 2, stderr = expected_stderr)
+test.run(arguments='.', status=2, stderr=expected_stderr)
# In Parallel task mode, we will get all three exceptions.
@@ -117,11 +114,9 @@ expected_stderr_list = [
# walk of '.' and are already considered up-to-date when we kick off the
# "simultaneous" builds of the output (target) files.
-test.run(arguments = '-j7 -k .', status = 2, stderr = None)
-
+test.run(arguments='-j7 -k .', status=2, stderr=None)
test.must_contain_all_lines(test.stderr(), expected_stderr_list)
-
test.pass_test()
# Local Variables:
diff --git a/test/fixture/SConstruct-check-valid-options b/test/fixture/SConstruct-check-valid-options
new file mode 100644
index 0000000..2c935a2
--- /dev/null
+++ b/test/fixture/SConstruct-check-valid-options
@@ -0,0 +1,21 @@
+import sys
+from SCons.Script.SConsOptions import SConsOptionParser, SConsBadOptionError
+
+AddOption(
+ '--testing',
+ help='Test arg',
+)
+
+if ARGUMENTS.get('raise', 0) == '1':
+ ValidateOptions(throw_exception=True)
+elif ARGUMENTS.get('raise', 0) == '2':
+ try:
+ ValidateOptions(throw_exception=True)
+ except SConsBadOptionError as e:
+ print("Parser is SConsOptionParser:%s" % (isinstance(e.parser, SConsOptionParser)))
+ print("Message is :%s" % e.opt_str)
+ Exit(3)
+else:
+ ValidateOptions()
+
+print("This is in SConstruct")
diff --git a/test/fixture/mylex.py b/test/fixture/mylex.py
index 8f47049..b5e94b1 100644
--- a/test/fixture/mylex.py
+++ b/test/fixture/mylex.py
@@ -4,25 +4,47 @@ Phony lex for testing SCons.
Writes the contents of input file to stdout,
after "substituting" $LEXFLAGS and $I_ARGS
-Intended for use as $LEX
+Needs to understand all the lex/flex options the testcases might use.
"""
import getopt
import sys
+from pathlib import Path
+
+
+def make_side_effect(path, text):
+ p = Path(path)
+ if str(p.parent) != '.':
+ p.parent.mkdir(parents=True, exist_ok=True)
+ with p.open(mode="wb") as f:
+ f.write(text)
+
def fake_lex():
+ make_header = None
+ make_table = None
+
if sys.platform == 'win32':
longopts = ['nounistd']
else:
longopts = []
+ longopts.extend(['header-file=', 'tables-file='])
cmd_opts, args = getopt.getopt(sys.argv[1:], 'I:tx', longopts)
opt_string = ''
i_arguments = ''
+
for opt, arg in cmd_opts:
if opt == '-I':
i_arguments = f'{i_arguments} {arg}'
+ elif opt == '--header-file':
+ make_header = arg
+ opt_string = f'{opt_string} {opt}={arg}'
+ elif opt == '--tables-file':
+ make_table = arg
+ opt_string = f'{opt_string} {opt}={arg}'
else:
opt_string = f'{opt_string} {opt}'
+
for arg in args:
with open(arg, 'rb') as ifp:
contents = ifp.read().decode(encoding='utf-8')
@@ -31,6 +53,13 @@ def fake_lex():
contents = contents.replace('I_ARGS', i_arguments)
sys.stdout.write(contents)
+ # Extra bits:
+ if make_header:
+ make_side_effect(make_header, b"lex header\n")
+ if make_table:
+ make_side_effect(make_table, b"lex table\n")
+
+
if __name__ == '__main__':
fake_lex()
sys.exit(0)
diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py
new file mode 100644
index 0000000..8e3c65f
--- /dev/null
+++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py
@@ -0,0 +1,15 @@
+import SCons
+import SCons.Tool.MSCommon
+
+def DummyVsWhere(msvc_version, env):
+ # not testing versions with vswhere, so return none
+ return None
+
+for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR:
+ SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')]
+
+SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere
+
+msvc_version, msvc_toolset_version = SCons.Tool.MSCommon.msvc_query_version_toolset()
+
+print('msvc_version={}, msvc_toolset_version={}'.format(repr(msvc_version), repr(msvc_toolset_version))) \ No newline at end of file
diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py
new file mode 100644
index 0000000..7953ce6
--- /dev/null
+++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py
@@ -0,0 +1,15 @@
+import SCons
+import SCons.Tool.MSCommon
+
+def DummyVsWhere(msvc_version, env):
+ # not testing versions with vswhere, so return none
+ return None
+
+for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR:
+ SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')]
+
+SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere
+
+sdk_version_list = SCons.Tool.MSCommon.msvc_sdk_versions()
+
+print('sdk_version_list='+repr(sdk_version_list))
diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py
new file mode 100644
index 0000000..fd209fd
--- /dev/null
+++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py
@@ -0,0 +1,15 @@
+import SCons
+import SCons.Tool.MSCommon
+
+def DummyVsWhere(msvc_version, env):
+ # not testing versions with vswhere, so return none
+ return None
+
+for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR:
+ SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')]
+
+SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere
+
+toolset_version_list = SCons.Tool.MSCommon.msvc_toolset_versions()
+
+print('toolset_version_list='+repr(toolset_version_list))
diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py b/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py
new file mode 100644
index 0000000..9aa924b
--- /dev/null
+++ b/test/fixture/no_msvc/no_msvcs_sconstruct_tools.py
@@ -0,0 +1,14 @@
+import SCons
+import SCons.Tool.MSCommon
+
+def DummyVsWhere(msvc_version, env):
+ # not testing versions with vswhere, so return none
+ return None
+
+for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR:
+ SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')]
+
+SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere
+
+env = SCons.Environment.Environment(tools=['myignoredefaultmsvctool'])
+
diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py
new file mode 100644
index 0000000..b586d6c
--- /dev/null
+++ b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py
@@ -0,0 +1,19 @@
+import SCons
+import SCons.Tool.MSCommon
+
+
+def DummyVsWhere(msvc_version, env):
+ # not testing versions with vswhere, so return none
+ return None
+
+
+for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR:
+ SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')]
+
+SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere
+
+SCons.Tool.MSCommon.msvc_set_notfound_policy('error')
+
+env = SCons.Environment.Environment(MSVC_VERSION='14.3')
+
+
diff --git a/test/ninja/build_libraries.py b/test/ninja/build_libraries.py
index 325031b..0a1941a 100644
--- a/test/ninja/build_libraries.py
+++ b/test/ninja/build_libraries.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python
#
+# MIT License
+#
# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
@@ -34,10 +36,6 @@ try:
except ImportError:
test.skip_test("Could not find ninja module. Skipping test.\n")
-ninja_binary = test.where_is('ninja')
-if not ninja_binary:
- test.skip_test("Could not find ninja executable. Skipping test.\n")
-
ninja_bin = os.path.abspath(os.path.join(
ninja.__file__,
os.pardir,
diff --git a/test/ninja/command_line_targets.py b/test/ninja/command_line_targets.py
new file mode 100644
index 0000000..9fa77ea
--- /dev/null
+++ b/test/ninja/command_line_targets.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# 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.
+#
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find module in python")
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(os.path.join(
+ ninja.__file__,
+ os.pardir,
+ 'data',
+ 'bin',
+ 'ninja' + _exe))
+
+test.dir_fixture('ninja-fixture')
+
+test.file_fixture('ninja_test_sconscripts/sconstruct_default_targets', 'SConstruct')
+
+# generate simple build
+test.run(arguments=['out1.txt'], stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_contain_all(test.stdout(), 'build.ninja -j1 out1.txt')
+test.must_exist([test.workpath('out1.txt')])
+test.must_not_exist([test.workpath('out2.txt')])
+
+# clean build and ninja files
+test.run(arguments='-c', stdout=None)
+test.must_contain_all_lines(test.stdout(), [
+ 'Removed out1.txt',
+ 'Removed build.ninja'])
+
+# generate simple build
+test.run(arguments=['out2.txt'], stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_contain_all(test.stdout(), 'build.ninja -j1 out2.txt')
+test.must_not_exist([test.workpath('out1.txt')])
+test.must_exist([test.workpath('out2.txt')])
+
+# clean build and ninja files
+test.run(arguments='-c', stdout=None)
+test.must_contain_all_lines(test.stdout(), [
+ 'Removed out2.txt',
+ 'Removed build.ninja'])
+
+test.run(arguments=['out1.txt', 'out2.txt'], stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_contain_all(test.stdout(), 'build.ninja -j1 out1.txt out2.txt')
+test.must_exist([test.workpath('out1.txt')])
+test.must_exist([test.workpath('out2.txt')])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/ninja/default_targets.py b/test/ninja/default_targets.py
new file mode 100644
index 0000000..dc7c7c1
--- /dev/null
+++ b/test/ninja/default_targets.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# 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.
+#
+
+import os
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+test = TestSCons.TestSCons()
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find module in python")
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(os.path.join(
+ ninja.__file__,
+ os.pardir,
+ 'data',
+ 'bin',
+ 'ninja' + _exe))
+
+test.dir_fixture('ninja-fixture')
+
+test.file_fixture('ninja_test_sconscripts/sconstruct_default_targets', 'SConstruct')
+
+# generate simple build
+test.run(stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_not_exist([test.workpath('out1.txt')])
+test.must_exist([test.workpath('out2.txt')])
+
+# clean build and ninja files
+test.run(arguments='-c', stdout=None)
+test.must_contain_all_lines(test.stdout(), [
+ 'Removed out2.txt',
+ 'Removed build.ninja'])
+
+# only generate the ninja file
+test.run(arguments='--disable-execute-ninja', stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_not_exist(test.workpath('out1.txt'))
+test.must_not_exist(test.workpath('out2.txt'))
+
+# run ninja independently
+program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+test.run(program=program, stdout=None)
+test.must_not_exist([test.workpath('out1.txt')])
+test.must_exist(test.workpath('out2.txt'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/ninja/force_scons_callback.py b/test/ninja/force_scons_callback.py
index c99ed58..e0da0dd 100644
--- a/test/ninja/force_scons_callback.py
+++ b/test/ninja/force_scons_callback.py
@@ -80,7 +80,7 @@ test.must_match("out2.txt", "test2.cpp" + os.linesep)
# only generate the ninja file with specific NINJA_SCONS_DAEMON_PORT
test.run(arguments="PORT=9999 --disable-execute-ninja", stdout=None)
# Verify that port # propagates to call to ninja_run_daemon.py
-test.must_contain(test.workpath("build.ninja"), "ninja_run_daemon.py 9999")
+test.must_contain(test.workpath("build.ninja"), "PORT = 9999")
test.pass_test()
diff --git a/test/ninja/generate_and_build.py b/test/ninja/generate_and_build.py
index 91be108..e1c26d4 100644
--- a/test/ninja/generate_and_build.py
+++ b/test/ninja/generate_and_build.py
@@ -49,12 +49,17 @@ test.dir_fixture('ninja-fixture')
test.file_fixture('ninja_test_sconscripts/sconstruct_generate_and_build', 'SConstruct')
# generate simple build
-test.run(stdout=None)
+test.run(stdout=None, arguments='NINJA_CMD_ARGS=-v')
test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
test.must_contain_all(test.stdout(), 'Executing:')
test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_contain_all(test.stdout(), ' -j1 -v')
test.run(program=test.workpath('foo' + _exe), stdout="foo.c")
+# Test multiple args for NINJA_CMD_ARGS
+test.run(stdout=None, arguments={'NINJA_CMD_ARGS':"-v -j3"})
+test.must_contain_all(test.stdout(), ' -v -j3')
+
# clean build and ninja files
test.run(arguments='-c', stdout=None)
test.must_contain_all_lines(test.stdout(), [
diff --git a/test/ninja/ninja_file_deterministic.py b/test/ninja/ninja_file_deterministic.py
new file mode 100644
index 0000000..9832f22
--- /dev/null
+++ b/test/ninja/ninja_file_deterministic.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# 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.
+#
+
+import os
+import shutil
+import filecmp
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+test = TestSCons.TestSCons()
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find module in python")
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(os.path.join(
+ ninja.BIN_DIR,
+ 'ninja' + _exe))
+
+test.dir_fixture('ninja-fixture')
+
+test.file_fixture('ninja_test_sconscripts/sconstruct_ninja_determinism', 'SConstruct')
+
+# generate simple build
+test.run(stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+shutil.copyfile(test.workpath('build.ninja'), test.workpath('build.ninja.orig'))
+
+ninja_file_mtime = os.path.getmtime(test.workpath('build.ninja'))
+
+# generate same build again
+test.run(stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja', 'ninja: no work to do.'])
+test.must_contain_all(test.stdout(), 'Executing:')
+test.must_contain_all(test.stdout(), 'ninja%(_exe)s -f' % locals())
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+if os.path.getmtime(test.workpath('build.ninja')) != ninja_file_mtime:
+ test.fail_test(message="build.ninja file has been updated (mtime changed) and should not have been")
+
+# make sure the ninja file was deterministic
+if not filecmp.cmp(test.workpath('build.ninja'), test.workpath('build.ninja.orig')):
+ test.fail_test()
+
+# clean build and ninja files
+os.unlink(test.workpath('build.ninja.orig'))
+test.run(arguments='-c', stdout=None)
+
+# only generate the ninja file
+test.run(arguments='--disable-execute-ninja', stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_not_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# run ninja independently
+program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+test.run(program=program, stdout=None)
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+shutil.copyfile(test.workpath('build.ninja'), test.workpath('build.ninja.orig'))
+
+# only generate the ninja file again
+test.run(arguments='--disable-execute-ninja', stdout=None)
+test.must_contain_all_lines(test.stdout(), ['Generating: build.ninja'])
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# run ninja independently again
+program = test.workpath('run_ninja_env.bat') if IS_WINDOWS else ninja_bin
+test.run(program=program, stdout=None)
+test.must_contain_all_lines(test.stdout(), ['ninja: no work to do.'])
+test.must_exist([test.workpath('out1.txt'), test.workpath('out2.txt')])
+
+# make sure the ninja file was deterministic
+if not filecmp.cmp(test.workpath('build.ninja'), test.workpath('build.ninja.orig')):
+ test.fail_test()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_default_targets b/test/ninja/ninja_test_sconscripts/sconstruct_default_targets
new file mode 100644
index 0000000..8963270
--- /dev/null
+++ b/test/ninja/ninja_test_sconscripts/sconstruct_default_targets
@@ -0,0 +1,14 @@
+import SCons
+
+SetOption('experimental','ninja')
+DefaultEnvironment(tools=[])
+
+env = Environment(tools=[])
+
+env.Tool('ninja')
+
+env.Command('out1.txt', 'foo.c', 'echo test > $TARGET' )
+out2_node = env.Command('out2.txt', 'foo.c', 'echo test > $TARGET', NINJA_FORCE_SCONS_BUILD=True)
+alias = env.Alias('def', out2_node)
+
+env.Default(alias)
diff --git a/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism b/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism
new file mode 100644
index 0000000..3d3eecb
--- /dev/null
+++ b/test/ninja/ninja_test_sconscripts/sconstruct_ninja_determinism
@@ -0,0 +1,19 @@
+import random
+
+SetOption('experimental', 'ninja')
+SetOption('skip_ninja_regen', True)
+DefaultEnvironment(tools=[])
+
+env = Environment(tools=[])
+
+env.Tool('ninja')
+
+# make the dependency list vary in order. Ninja tool should sort them to be deterministic.
+for i in range(1, 10):
+ node = env.Command(f'out{i}.txt', 'foo.c', 'echo test > $TARGET')
+ deps = list(range(1, i))
+ random.shuffle(deps)
+ for j in deps:
+ if j == i:
+ continue
+ env.Depends(node, f'out{j}.txt')
diff --git a/test/ninja/shell_command.py b/test/ninja/shell_command.py
index a6926c7..a6c48c4 100644
--- a/test/ninja/shell_command.py
+++ b/test/ninja/shell_command.py
@@ -53,6 +53,12 @@ SetOption('experimental','ninja')
DefaultEnvironment(tools=[])
env = Environment()
+
+# Added to verify that SCons Ninja tool is sanitizing the shell environment
+# before it spawns a new shell
+env['ENV']['ZPATH']=['/a/b/c','/c/d/e']
+
+
env.Tool('ninja')
prog = env.Program(target = 'foo', source = 'foo.c')
env.Command('foo.out', prog, '%(shell)sfoo%(_exe)s > foo.out')
diff --git a/test/ninja/shutdown_scons_daemon.py b/test/ninja/shutdown_scons_daemon.py
new file mode 100644
index 0000000..64ec2c7
--- /dev/null
+++ b/test/ninja/shutdown_scons_daemon.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+#
+# Copyright The SCons Foundation
+#
+# 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.
+#
+
+import os
+from timeit import default_timer as timer
+
+import TestSCons
+from TestCmd import IS_WINDOWS
+
+test = TestSCons.TestSCons()
+
+try:
+ import ninja
+except ImportError:
+ test.skip_test("Could not find ninja module in python")
+
+try:
+ import psutil
+except ImportError:
+ test.skip_test("Could not find psutil module in python")
+
+
+_python_ = TestSCons._python_
+_exe = TestSCons._exe
+
+ninja_bin = os.path.abspath(
+ os.path.join(ninja.__file__, os.pardir, "data", "bin", "ninja" + _exe)
+)
+
+test.dir_fixture("ninja-fixture")
+
+test.file_fixture(
+ "ninja_test_sconscripts/sconstruct_force_scons_callback", "SConstruct"
+)
+
+test.run(stdout=None)
+pid = None
+test.must_exist(test.workpath('.ninja/scons_daemon_dirty'))
+with open(test.workpath('.ninja/scons_daemon_dirty')) as f:
+ pid = int(f.read())
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ test.fail_test(message="daemon not running!")
+
+program = test.workpath("run_ninja_env.bat") if IS_WINDOWS else ninja_bin
+test.run(program=program, arguments='shutdown-ninja-scons-daemon', stdout=None)
+
+wait_time = 10
+start_time = timer()
+while True:
+ if wait_time > (timer() - start_time):
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ break
+ else:
+ test.fail_test(message=f"daemon still not shutdown after {wait_time} seconds.")
+
+test.must_not_exist(test.workpath('.ninja/scons_daemon_dirty'))
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/no-global-dependencies.py b/test/no-global-dependencies.py
index 18b674c..95761e7 100644
--- a/test/no-global-dependencies.py
+++ b/test/no-global-dependencies.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 files are correctly located in the variant directory even when
diff --git a/test/option/debug-stacktrace.py b/test/option/debug-stacktrace.py
index 490fecf..f0f92c1 100644
--- a/test/option/debug-stacktrace.py
+++ b/test/option/debug-stacktrace.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 the --debug=stacktrace option.
@@ -34,37 +33,27 @@ test = TestSCons.TestSCons()
test.write('SConstruct', """\
DefaultEnvironment(tools=[])
+
def kfile_scan(node, env, target):
raise Exception("kfile_scan error")
-kscan = Scanner(name = 'kfile',
- function = kfile_scan,
- skeys = ['.k'])
-
+kscan = Scanner(name='kfile', function=kfile_scan, skeys=['.k'])
env = Environment(tools=[])
env.Append(SCANNERS = [kscan])
env.Command('foo', 'foo.k', Copy('$TARGET', '$SOURCE'))
""")
-
test.write('foo.k', "foo.k\n")
test.run(status = 2, stderr = "scons: *** [foo] Exception : kfile_scan error\n")
-
-test.run(arguments = "--debug=stacktrace",
- status = 2,
- stderr = None)
-
+test.run(arguments="--debug=stacktrace", status=2, stderr=None)
lines = [
"scons: *** [foo] Exception : kfile_scan error",
"scons: internal stack trace:",
'raise Exception("kfile_scan error")',
]
-
test.must_contain_all_lines(test.stderr(), lines)
-
-
# Test that --debug=stacktrace works for UserError exceptions,
# which are handled by different code than other exceptions.
@@ -73,25 +62,14 @@ import SCons.Errors
raise SCons.Errors.UserError("explicit UserError!")
""")
-test.run(arguments = '--debug=stacktrace',
- status = 2,
- stderr = None)
-
+test.run(arguments='--debug=stacktrace', status=2, stderr=None)
user_error_lines = [
'UserError: explicit UserError!',
'scons: *** explicit UserError!',
]
-
-# The "(most recent call last)" message is used by more recent Python
-# versions than the "(innermost last)" message, so that's the one
-# we report if neither matches.
-traceback_lines = [
- "Traceback (most recent call last)",
- "Traceback (innermost last)",
-]
-
+traceback_lines = ["Traceback (most recent call last)",]
test.must_contain_all_lines(test.stderr(), user_error_lines)
-test.must_contain_any_line(test.stderr(), traceback_lines)
+test.must_contain_all_lines(test.stderr(), traceback_lines)
# Test that full path names to SConscript files show up in stack traces.
@@ -99,18 +77,10 @@ test.write('SConstruct', """\
1/0
""")
-test.run(arguments = '--debug=stacktrace',
- status = 2,
- stderr = None)
-
-lines = [
- ' File "%s", line 1:' % test.workpath('SConstruct'),
-]
-
+test.run(arguments='--debug=stacktrace', status=2, stderr=None)
+lines = [' File "%s", line 1:' % test.workpath('SConstruct'),]
test.must_contain_all_lines(test.stderr(), lines)
-
-
test.pass_test()
# Local Variables:
diff --git a/test/option/fixture/SConstruct__taskmastertrace b/test/option/fixture/SConstruct__taskmastertrace
new file mode 100644
index 0000000..91e8c91
--- /dev/null
+++ b/test/option/fixture/SConstruct__taskmastertrace
@@ -0,0 +1,9 @@
+DefaultEnvironment(tools=[])
+env = Environment(tools=[])
+
+# We name the files 'Tfile' so that they will sort after the SConstruct
+# file regardless of whether the test is being run on a case-sensitive
+# or case-insensitive system.
+
+env.Command('Tfile.out', 'Tfile.mid', Copy('$TARGET', '$SOURCE'))
+env.Command('Tfile.mid', 'Tfile.in', Copy('$TARGET', '$SOURCE'))
diff --git a/test/option/fixture/taskmaster_expected_file_1.txt b/test/option/fixture/taskmaster_expected_file_1.txt
new file mode 100644
index 0000000..3d34b2d
--- /dev/null
+++ b/test/option/fixture/taskmaster_expected_file_1.txt
@@ -0,0 +1,74 @@
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 '.'> and its children:
+Taskmaster: <no_state 0 'SConstruct'>
+Taskmaster: <no_state 0 'Tfile.in'>
+Taskmaster: <no_state 0 'Tfile.mid'>
+Taskmaster: <no_state 0 'Tfile.out'>
+Taskmaster: adjusted ref count: <pending 1 '.'>, child 'SConstruct'
+Taskmaster: adjusted ref count: <pending 2 '.'>, child 'Tfile.in'
+Taskmaster: adjusted ref count: <pending 3 '.'>, child 'Tfile.mid'
+Taskmaster: adjusted ref count: <pending 4 '.'>, child 'Tfile.out'
+Taskmaster: Considering node <no_state 0 'SConstruct'> and its children:
+Taskmaster: Evaluating <pending 0 'SConstruct'>
+
+Task.make_ready_current(): node <pending 0 'SConstruct'>
+Task.prepare(): node <up_to_date 0 'SConstruct'>
+Task.executed_with_callbacks(): node <up_to_date 0 'SConstruct'>
+Task.postprocess(): node <up_to_date 0 'SConstruct'>
+Task.postprocess(): removing <up_to_date 0 'SConstruct'>
+Task.postprocess(): adjusted parent ref count <pending 3 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending 0 'Tfile.in'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.in'>
+Task.prepare(): node <up_to_date 0 'Tfile.in'>
+Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.in'>
+Task.postprocess(): node <up_to_date 0 'Tfile.in'>
+Task.postprocess(): removing <up_to_date 0 'Tfile.in'>
+Task.postprocess(): adjusted parent ref count <pending 2 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.mid'> and its children:
+Taskmaster: <up_to_date 0 'Tfile.in'>
+Taskmaster: Evaluating <pending 0 'Tfile.mid'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.mid'>
+Task.prepare(): node <executing 0 'Tfile.mid'>
+Task.execute(): node <executing 0 'Tfile.mid'>
+Task.executed_with_callbacks(): node <executing 0 'Tfile.mid'>
+Task.postprocess(): node <executed 0 'Tfile.mid'>
+Task.postprocess(): removing <executed 0 'Tfile.mid'>
+Task.postprocess(): adjusted parent ref count <pending 1 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.out'> and its children:
+Taskmaster: <executed 0 'Tfile.mid'>
+Taskmaster: Evaluating <pending 0 'Tfile.out'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.out'>
+Task.prepare(): node <executing 0 'Tfile.out'>
+Task.execute(): node <executing 0 'Tfile.out'>
+Task.executed_with_callbacks(): node <executing 0 'Tfile.out'>
+Task.postprocess(): node <executed 0 'Tfile.out'>
+Task.postprocess(): removing <executed 0 'Tfile.out'>
+Task.postprocess(): adjusted parent ref count <pending 0 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <pending 0 '.'> and its children:
+Taskmaster: <up_to_date 0 'SConstruct'>
+Taskmaster: <up_to_date 0 'Tfile.in'>
+Taskmaster: <executed 0 'Tfile.mid'>
+Taskmaster: <executed 0 'Tfile.out'>
+Taskmaster: Evaluating <pending 0 '.'>
+
+Task.make_ready_current(): node <pending 0 '.'>
+Task.prepare(): node <executing 0 '.'>
+Task.execute(): node <executing 0 '.'>
+Task.executed_with_callbacks(): node <executing 0 '.'>
+Task.postprocess(): node <executed 0 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
diff --git a/test/option/fixture/taskmaster_expected_new_parallel.txt b/test/option/fixture/taskmaster_expected_new_parallel.txt
new file mode 100644
index 0000000..77334d9
--- /dev/null
+++ b/test/option/fixture/taskmaster_expected_new_parallel.txt
@@ -0,0 +1,90 @@
+Job.NewParallel._work(): [Thread:XXXXX] Gained exclusive access
+Job.NewParallel._work(): [Thread:XXXXX] Starting search
+Job.NewParallel._work(): [Thread:XXXXX] Found {len(results_queue)} completed tasks to process
+Job.NewParallel._work(): [Thread:XXXXX] Searching for new tasks
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 '.'> and its children:
+Taskmaster: <no_state 0 'SConstruct'>
+Taskmaster: <no_state 0 'Tfile.in'>
+Taskmaster: <no_state 0 'Tfile.mid'>
+Taskmaster: <no_state 0 'Tfile.out'>
+Taskmaster: adjusted ref count: <pending 1 '.'>, child 'SConstruct'
+Taskmaster: adjusted ref count: <pending 2 '.'>, child 'Tfile.in'
+Taskmaster: adjusted ref count: <pending 3 '.'>, child 'Tfile.mid'
+Taskmaster: adjusted ref count: <pending 4 '.'>, child 'Tfile.out'
+Taskmaster: Considering node <no_state 0 'SConstruct'> and its children:
+Taskmaster: Evaluating <pending 0 'SConstruct'>
+
+Task.make_ready_current(): node <pending 0 'SConstruct'>
+Task.prepare(): node <up_to_date 0 'SConstruct'>
+Job.NewParallel._work(): [Thread:XXXXX] Found internal task
+Task.executed_with_callbacks(): node <up_to_date 0 'SConstruct'>
+Task.postprocess(): node <up_to_date 0 'SConstruct'>
+Task.postprocess(): removing <up_to_date 0 'SConstruct'>
+Task.postprocess(): adjusted parent ref count <pending 3 '.'>
+Job.NewParallel._work(): [Thread:XXXXX] Searching for new tasks
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending 0 'Tfile.in'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.in'>
+Task.prepare(): node <up_to_date 0 'Tfile.in'>
+Job.NewParallel._work(): [Thread:XXXXX] Found internal task
+Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.in'>
+Task.postprocess(): node <up_to_date 0 'Tfile.in'>
+Task.postprocess(): removing <up_to_date 0 'Tfile.in'>
+Task.postprocess(): adjusted parent ref count <pending 2 '.'>
+Job.NewParallel._work(): [Thread:XXXXX] Searching for new tasks
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.mid'> and its children:
+Taskmaster: <up_to_date 0 'Tfile.in'>
+Taskmaster: Evaluating <pending 0 'Tfile.mid'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.mid'>
+Task.prepare(): node <up_to_date 0 'Tfile.mid'>
+Job.NewParallel._work(): [Thread:XXXXX] Found internal task
+Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.mid'>
+Task.postprocess(): node <up_to_date 0 'Tfile.mid'>
+Task.postprocess(): removing <up_to_date 0 'Tfile.mid'>
+Task.postprocess(): adjusted parent ref count <pending 1 '.'>
+Job.NewParallel._work(): [Thread:XXXXX] Searching for new tasks
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.out'> and its children:
+Taskmaster: <up_to_date 0 'Tfile.mid'>
+Taskmaster: Evaluating <pending 0 'Tfile.out'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.out'>
+Task.prepare(): node <up_to_date 0 'Tfile.out'>
+Job.NewParallel._work(): [Thread:XXXXX] Found internal task
+Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.out'>
+Task.postprocess(): node <up_to_date 0 'Tfile.out'>
+Task.postprocess(): removing <up_to_date 0 'Tfile.out'>
+Task.postprocess(): adjusted parent ref count <pending 0 '.'>
+Job.NewParallel._work(): [Thread:XXXXX] Searching for new tasks
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <pending 0 '.'> and its children:
+Taskmaster: <up_to_date 0 'SConstruct'>
+Taskmaster: <up_to_date 0 'Tfile.in'>
+Taskmaster: <up_to_date 0 'Tfile.mid'>
+Taskmaster: <up_to_date 0 'Tfile.out'>
+Taskmaster: Evaluating <pending 0 '.'>
+
+Task.make_ready_current(): node <pending 0 '.'>
+Task.prepare(): node <up_to_date 0 '.'>
+Job.NewParallel._work(): [Thread:XXXXX] Found internal task
+Task.executed_with_callbacks(): node <up_to_date 0 '.'>
+Task.postprocess(): node <up_to_date 0 '.'>
+Job.NewParallel._work(): [Thread:XXXXX] Searching for new tasks
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
+Job.NewParallel._work(): [Thread:XXXXX] Found no task requiring execution, and have no jobs: marking complete
+Job.NewParallel._work(): [Thread:XXXXX] Gained exclusive access
+Job.NewParallel._work(): [Thread:XXXXX] Completion detected, breaking from main loop
+Job.NewParallel._work(): [Thread:XXXXX] Gained exclusive access
+Job.NewParallel._work(): [Thread:XXXXX] Completion detected, breaking from main loop
diff --git a/test/option/fixture/taskmaster_expected_stdout_1.txt b/test/option/fixture/taskmaster_expected_stdout_1.txt
new file mode 100644
index 0000000..9076e68
--- /dev/null
+++ b/test/option/fixture/taskmaster_expected_stdout_1.txt
@@ -0,0 +1,76 @@
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 '.'> and its children:
+Taskmaster: <no_state 0 'SConstruct'>
+Taskmaster: <no_state 0 'Tfile.in'>
+Taskmaster: <no_state 0 'Tfile.mid'>
+Taskmaster: <no_state 0 'Tfile.out'>
+Taskmaster: adjusted ref count: <pending 1 '.'>, child 'SConstruct'
+Taskmaster: adjusted ref count: <pending 2 '.'>, child 'Tfile.in'
+Taskmaster: adjusted ref count: <pending 3 '.'>, child 'Tfile.mid'
+Taskmaster: adjusted ref count: <pending 4 '.'>, child 'Tfile.out'
+Taskmaster: Considering node <no_state 0 'SConstruct'> and its children:
+Taskmaster: Evaluating <pending 0 'SConstruct'>
+
+Task.make_ready_current(): node <pending 0 'SConstruct'>
+Task.prepare(): node <up_to_date 0 'SConstruct'>
+Task.executed_with_callbacks(): node <up_to_date 0 'SConstruct'>
+Task.postprocess(): node <up_to_date 0 'SConstruct'>
+Task.postprocess(): removing <up_to_date 0 'SConstruct'>
+Task.postprocess(): adjusted parent ref count <pending 3 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.in'> and its children:
+Taskmaster: Evaluating <pending 0 'Tfile.in'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.in'>
+Task.prepare(): node <up_to_date 0 'Tfile.in'>
+Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.in'>
+Task.postprocess(): node <up_to_date 0 'Tfile.in'>
+Task.postprocess(): removing <up_to_date 0 'Tfile.in'>
+Task.postprocess(): adjusted parent ref count <pending 2 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.mid'> and its children:
+Taskmaster: <up_to_date 0 'Tfile.in'>
+Taskmaster: Evaluating <pending 0 'Tfile.mid'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.mid'>
+Task.prepare(): node <executing 0 'Tfile.mid'>
+Task.execute(): node <executing 0 'Tfile.mid'>
+Copy("Tfile.mid", "Tfile.in")
+Task.executed_with_callbacks(): node <executing 0 'Tfile.mid'>
+Task.postprocess(): node <executed 0 'Tfile.mid'>
+Task.postprocess(): removing <executed 0 'Tfile.mid'>
+Task.postprocess(): adjusted parent ref count <pending 1 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <no_state 0 'Tfile.out'> and its children:
+Taskmaster: <executed 0 'Tfile.mid'>
+Taskmaster: Evaluating <pending 0 'Tfile.out'>
+
+Task.make_ready_current(): node <pending 0 'Tfile.out'>
+Task.prepare(): node <executing 0 'Tfile.out'>
+Task.execute(): node <executing 0 'Tfile.out'>
+Copy("Tfile.out", "Tfile.mid")
+Task.executed_with_callbacks(): node <executing 0 'Tfile.out'>
+Task.postprocess(): node <executed 0 'Tfile.out'>
+Task.postprocess(): removing <executed 0 'Tfile.out'>
+Task.postprocess(): adjusted parent ref count <pending 0 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: Considering node <pending 0 '.'> and its children:
+Taskmaster: <up_to_date 0 'SConstruct'>
+Taskmaster: <up_to_date 0 'Tfile.in'>
+Taskmaster: <executed 0 'Tfile.mid'>
+Taskmaster: <executed 0 'Tfile.out'>
+Taskmaster: Evaluating <pending 0 '.'>
+
+Task.make_ready_current(): node <pending 0 '.'>
+Task.prepare(): node <executing 0 '.'>
+Task.execute(): node <executing 0 '.'>
+Task.executed_with_callbacks(): node <executing 0 '.'>
+Task.postprocess(): node <executed 0 '.'>
+
+Taskmaster: Looking for a node to evaluate
+Taskmaster: No candidate anymore.
diff --git a/test/option/option--Y.py b/test/option/option--Y.py
index 9293f67..e8766fc 100644
--- a/test/option/option--Y.py
+++ b/test/option/option--Y.py
@@ -169,6 +169,7 @@ workpath_r_OLD = test.workpath('r.OLD')
work2_foo = test.workpath('work2', 'foo' + _exe)
SConstruct = """
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
env.Program(target = 'foo', source = 'foo.c')
"""
diff --git a/test/option/option--experimental.py b/test/option/option--experimental.py
index 51f3546..324de99 100644
--- a/test/option/option--experimental.py
+++ b/test/option/option--experimental.py
@@ -36,12 +36,13 @@ test.file_fixture('fixture/SConstruct__experimental', 'SConstruct')
tests = [
('.', []),
('--experimental=ninja', ['ninja']),
- ('--experimental=all', ['ninja', 'transporter', 'warp_speed']),
+ ('--experimental=tm_v2', ['tm_v2']),
+ ('--experimental=all', ['ninja', 'tm_v2', 'transporter', 'warp_speed']),
('--experimental=none', []),
]
for args, exper in tests:
- read_string = """All Features=ninja,transporter,warp_speed
+ read_string = """All Features=ninja,tm_v2,transporter,warp_speed
Experimental=%s
""" % (exper)
test.run(arguments=args,
@@ -50,7 +51,7 @@ Experimental=%s
test.run(arguments='--experimental=warp_drive',
stderr="""usage: scons [OPTIONS] [VARIABLES] [TARGETS]
-SCons Error: option --experimental: invalid choice: 'warp_drive' (choose from 'all','none','ninja','transporter','warp_speed')
+SCons Error: option --experimental: invalid choice: 'warp_drive' (choose from 'all','none','ninja','tm_v2','transporter','warp_speed')
""",
status=2)
diff --git a/test/option/option-n.py b/test/option/option-n.py
index eb8eb62..1170927 100644
--- a/test/option/option-n.py
+++ b/test/option/option-n.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -58,8 +60,8 @@ with open(sys.argv[1], 'w') as ofp:
""")
test.write('SConstruct', """
-DefaultEnvironment(tools=[])
MyBuild = Builder(action=r'%(_python_)s build.py $TARGETS')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS={'MyBuild': MyBuild}, tools=[])
env.Tool('install')
env.MyBuild(target='f1.out', source='f1.in')
@@ -172,7 +174,6 @@ test.subdir('configure')
test.set_match_function(TestSCons.match_re_dotall)
test.set_diff_function(TestSCons.diff_re)
test.write('configure/SConstruct', """\
-DefaultEnvironment(tools=[])
def CustomTest(context):
def userAction(target,source,env):
import shutil
@@ -185,6 +186,7 @@ def CustomTest(context):
context.Result(ok)
return ok
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools=[])
conf = Configure(env,
custom_tests={'CustomTest':CustomTest},
diff --git a/test/option/stack-size.py b/test/option/stack-size.py
index 53faa9e..d64c73b 100644
--- a/test/option/stack-size.py
+++ b/test/option/stack-size.py
@@ -1,7 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
#
+# Copyright The SCons Foundation
# 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
@@ -20,9 +21,6 @@
# 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
@@ -47,8 +45,8 @@ with open(sys.argv[1], 'wb') as f, open(sys.argv[2], 'rb') as infp:
test.write(['work1', 'SConstruct'], """
-DefaultEnvironment(tools=[])
B = Builder(action = r'%(_python_)s ../build.py $TARGETS $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(tools=[], BUILDERS = { 'B' : B })
f1 = env.B(target = 'f1.out', source = 'f1.in')
f2 = env.B(target = 'f2.out', source = 'f2.in')
@@ -62,6 +60,7 @@ test.write(['work1', 'f2.in'], "f2.in\n")
test.write(['work2', 'SConstruct'], """
SetOption('stack_size', 128)
B = Builder(action = r'%(_python_)s ../build.py $TARGETS $SOURCES')
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(BUILDERS = { 'B' : B })
f1 = env.B(target = 'f1.out', source = 'f1.in')
f2 = env.B(target = 'f2.out', source = 'f2.in')
diff --git a/test/option/taskmastertrace.py b/test/option/taskmastertrace.py
index fc8522c..99de718 100644
--- a/test/option/taskmastertrace.py
+++ b/test/option/taskmastertrace.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,111 +22,25 @@
# 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__"
"""
Simple tests of the --taskmastertrace= option.
"""
+import os
+import re
import TestSCons
test = TestSCons.TestSCons()
-test.write('SConstruct', """
-DefaultEnvironment(tools=[])
-env = Environment(tools=[])
-
-# We name the files 'Tfile' so that they will sort after the SConstruct
-# file regardless of whether the test is being run on a case-sensitive
-# or case-insensitive system.
-
-env.Command('Tfile.out', 'Tfile.mid', Copy('$TARGET', '$SOURCE'))
-env.Command('Tfile.mid', 'Tfile.in', Copy('$TARGET', '$SOURCE'))
-""")
+test.file_fixture('fixture/SConstruct__taskmastertrace', 'SConstruct')
+test.file_fixture('fixture/taskmaster_expected_stdout_1.txt', 'taskmaster_expected_stdout_1.txt')
+test.file_fixture('fixture/taskmaster_expected_file_1.txt', 'taskmaster_expected_file_1.txt')
+test.file_fixture('fixture/taskmaster_expected_new_parallel.txt', 'taskmaster_expected_new_parallel.txt')
test.write('Tfile.in', "Tfile.in\n")
-expect_stdout = test.wrap_stdout("""\
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 '.'> and its children:
-Taskmaster: <no_state 0 'SConstruct'>
-Taskmaster: <no_state 0 'Tfile.in'>
-Taskmaster: <no_state 0 'Tfile.mid'>
-Taskmaster: <no_state 0 'Tfile.out'>
-Taskmaster: adjusted ref count: <pending 1 '.'>, child 'SConstruct'
-Taskmaster: adjusted ref count: <pending 2 '.'>, child 'Tfile.in'
-Taskmaster: adjusted ref count: <pending 3 '.'>, child 'Tfile.mid'
-Taskmaster: adjusted ref count: <pending 4 '.'>, child 'Tfile.out'
-Taskmaster: Considering node <no_state 0 'SConstruct'> and its children:
-Taskmaster: Evaluating <pending 0 'SConstruct'>
-
-Task.make_ready_current(): node <pending 0 'SConstruct'>
-Task.prepare(): node <up_to_date 0 'SConstruct'>
-Task.executed_with_callbacks(): node <up_to_date 0 'SConstruct'>
-Task.postprocess(): node <up_to_date 0 'SConstruct'>
-Task.postprocess(): removing <up_to_date 0 'SConstruct'>
-Task.postprocess(): adjusted parent ref count <pending 3 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 'Tfile.in'> and its children:
-Taskmaster: Evaluating <pending 0 'Tfile.in'>
-
-Task.make_ready_current(): node <pending 0 'Tfile.in'>
-Task.prepare(): node <up_to_date 0 'Tfile.in'>
-Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.in'>
-Task.postprocess(): node <up_to_date 0 'Tfile.in'>
-Task.postprocess(): removing <up_to_date 0 'Tfile.in'>
-Task.postprocess(): adjusted parent ref count <pending 2 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 'Tfile.mid'> and its children:
-Taskmaster: <up_to_date 0 'Tfile.in'>
-Taskmaster: Evaluating <pending 0 'Tfile.mid'>
-
-Task.make_ready_current(): node <pending 0 'Tfile.mid'>
-Task.prepare(): node <executing 0 'Tfile.mid'>
-Task.execute(): node <executing 0 'Tfile.mid'>
-Copy("Tfile.mid", "Tfile.in")
-Task.executed_with_callbacks(): node <executing 0 'Tfile.mid'>
-Task.postprocess(): node <executed 0 'Tfile.mid'>
-Task.postprocess(): removing <executed 0 'Tfile.mid'>
-Task.postprocess(): adjusted parent ref count <pending 1 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 'Tfile.out'> and its children:
-Taskmaster: <executed 0 'Tfile.mid'>
-Taskmaster: Evaluating <pending 0 'Tfile.out'>
-
-Task.make_ready_current(): node <pending 0 'Tfile.out'>
-Task.prepare(): node <executing 0 'Tfile.out'>
-Task.execute(): node <executing 0 'Tfile.out'>
-Copy("Tfile.out", "Tfile.mid")
-Task.executed_with_callbacks(): node <executing 0 'Tfile.out'>
-Task.postprocess(): node <executed 0 'Tfile.out'>
-Task.postprocess(): removing <executed 0 'Tfile.out'>
-Task.postprocess(): adjusted parent ref count <pending 0 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <pending 0 '.'> and its children:
-Taskmaster: <up_to_date 0 'SConstruct'>
-Taskmaster: <up_to_date 0 'Tfile.in'>
-Taskmaster: <executed 0 'Tfile.mid'>
-Taskmaster: <executed 0 'Tfile.out'>
-Taskmaster: Evaluating <pending 0 '.'>
-
-Task.make_ready_current(): node <pending 0 '.'>
-Task.prepare(): node <executing 0 '.'>
-Task.execute(): node <executing 0 '.'>
-Task.executed_with_callbacks(): node <executing 0 '.'>
-Task.postprocess(): node <executed 0 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: No candidate anymore.
-
-""")
+expect_stdout = test.wrap_stdout(test.read('taskmaster_expected_stdout_1.txt', mode='r'))
test.run(arguments='--taskmastertrace=- .', stdout=expect_stdout)
@@ -136,86 +52,15 @@ Copy("Tfile.out", "Tfile.mid")
""")
test.run(arguments='--taskmastertrace=trace.out .', stdout=expect_stdout)
+test.must_match_file('trace.out', 'taskmaster_expected_file_1.txt', mode='r')
-expect_trace = """\
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 '.'> and its children:
-Taskmaster: <no_state 0 'SConstruct'>
-Taskmaster: <no_state 0 'Tfile.in'>
-Taskmaster: <no_state 0 'Tfile.mid'>
-Taskmaster: <no_state 0 'Tfile.out'>
-Taskmaster: adjusted ref count: <pending 1 '.'>, child 'SConstruct'
-Taskmaster: adjusted ref count: <pending 2 '.'>, child 'Tfile.in'
-Taskmaster: adjusted ref count: <pending 3 '.'>, child 'Tfile.mid'
-Taskmaster: adjusted ref count: <pending 4 '.'>, child 'Tfile.out'
-Taskmaster: Considering node <no_state 0 'SConstruct'> and its children:
-Taskmaster: Evaluating <pending 0 'SConstruct'>
-
-Task.make_ready_current(): node <pending 0 'SConstruct'>
-Task.prepare(): node <up_to_date 0 'SConstruct'>
-Task.executed_with_callbacks(): node <up_to_date 0 'SConstruct'>
-Task.postprocess(): node <up_to_date 0 'SConstruct'>
-Task.postprocess(): removing <up_to_date 0 'SConstruct'>
-Task.postprocess(): adjusted parent ref count <pending 3 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 'Tfile.in'> and its children:
-Taskmaster: Evaluating <pending 0 'Tfile.in'>
-
-Task.make_ready_current(): node <pending 0 'Tfile.in'>
-Task.prepare(): node <up_to_date 0 'Tfile.in'>
-Task.executed_with_callbacks(): node <up_to_date 0 'Tfile.in'>
-Task.postprocess(): node <up_to_date 0 'Tfile.in'>
-Task.postprocess(): removing <up_to_date 0 'Tfile.in'>
-Task.postprocess(): adjusted parent ref count <pending 2 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 'Tfile.mid'> and its children:
-Taskmaster: <up_to_date 0 'Tfile.in'>
-Taskmaster: Evaluating <pending 0 'Tfile.mid'>
-
-Task.make_ready_current(): node <pending 0 'Tfile.mid'>
-Task.prepare(): node <executing 0 'Tfile.mid'>
-Task.execute(): node <executing 0 'Tfile.mid'>
-Task.executed_with_callbacks(): node <executing 0 'Tfile.mid'>
-Task.postprocess(): node <executed 0 'Tfile.mid'>
-Task.postprocess(): removing <executed 0 'Tfile.mid'>
-Task.postprocess(): adjusted parent ref count <pending 1 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <no_state 0 'Tfile.out'> and its children:
-Taskmaster: <executed 0 'Tfile.mid'>
-Taskmaster: Evaluating <pending 0 'Tfile.out'>
-
-Task.make_ready_current(): node <pending 0 'Tfile.out'>
-Task.prepare(): node <executing 0 'Tfile.out'>
-Task.execute(): node <executing 0 'Tfile.out'>
-Task.executed_with_callbacks(): node <executing 0 'Tfile.out'>
-Task.postprocess(): node <executed 0 'Tfile.out'>
-Task.postprocess(): removing <executed 0 'Tfile.out'>
-Task.postprocess(): adjusted parent ref count <pending 0 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: Considering node <pending 0 '.'> and its children:
-Taskmaster: <up_to_date 0 'SConstruct'>
-Taskmaster: <up_to_date 0 'Tfile.in'>
-Taskmaster: <executed 0 'Tfile.mid'>
-Taskmaster: <executed 0 'Tfile.out'>
-Taskmaster: Evaluating <pending 0 '.'>
-
-Task.make_ready_current(): node <pending 0 '.'>
-Task.prepare(): node <executing 0 '.'>
-Task.execute(): node <executing 0 '.'>
-Task.executed_with_callbacks(): node <executing 0 '.'>
-Task.postprocess(): node <executed 0 '.'>
-
-Taskmaster: Looking for a node to evaluate
-Taskmaster: No candidate anymore.
-
-"""
+# Test NewParallel Job implementation
+test.run(arguments='-j 2 --experimental=tm_v2 --taskmastertrace=new_parallel_trace.out .')
-test.must_match('trace.out', expect_trace, mode='r')
+new_trace = test.read('new_parallel_trace.out', mode='r')
+thread_id = re.compile(r'\[Thread:\d+\]')
+new_trace=thread_id.sub('[Thread:XXXXX]', new_trace)
+test.must_match('taskmaster_expected_new_parallel.txt', new_trace, mode='r')
test.pass_test()
diff --git a/test/rebuild-generated.py b/test/rebuild-generated.py
index 0b3659e..91b4e1e 100644
--- a/test/rebuild-generated.py
+++ b/test/rebuild-generated.py
@@ -83,7 +83,7 @@ env = Environment()
kernelDefines = env.Command("header.hh", "header.hh.in", Copy('$TARGET', '$SOURCE'))
kernelImporterSource = env.Command("generated.cc", ["%s"], "%s")
kernelImporter = env.Program(kernelImporterSource + ["main.cc"])
-kernelImports = env.Command("KernelImport.hh", kernelImporter, ".%s$SOURCE > $TARGET")
+kernelImports = env.Command("KernelImport.hh", kernelImporter, r".%s$SOURCE > $TARGET")
osLinuxModule = env.StaticObject(["target.cc"])
""" % (generator_name, kernel_action, sep))
diff --git a/test/runtest/python.py b/test/runtest/python.py
index 499ab77..abd4f0c 100644
--- a/test/runtest/python.py
+++ b/test/runtest/python.py
@@ -50,20 +50,12 @@ pythonflags = TestRuntest.pythonflags
# getting called with "/bin/../bin/python" as first argument, e.g. Fedora 17 Desktop.
mypython = os.path.normpath(os.path.join(head, dir, os.path.pardir, dir, python))
-def escape(s):
- return s.replace('\\', '\\\\')
-
-if re.search(r'\s', mypython):
- mypythonstring = '"%s"' % escape(mypython)
-else:
- mypythonstring = escape(mypython)
-
test.subdir('test')
test.write_passing_test(['test', 'pass.py'])
expect_stdout = """\
-%(mypythonstring)s%(pythonflags)s %(test_pass_py)s
+%(mypython)s%(pythonflags)s %(test_pass_py)s
PASSING TEST STDOUT
""" % locals()
diff --git a/test/sconsign/script/Configure.py b/test/sconsign/script/Configure.py
index 7597634..2e1e9c1 100644
--- a/test/sconsign/script/Configure.py
+++ b/test/sconsign/script/Configure.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 we can print .sconsign files with Configure context
@@ -58,6 +57,9 @@ CC_file = re.escape(CC_file)
test.write('SConstruct', """
import os
+
+DefaultEnvironment(tools=[])
+
env = Environment(ENV={'PATH' : os.environ.get('PATH','')})
conf = Configure(env)
r1 = conf.CheckCHeader( 'math.h' )
diff --git a/test/sconsign/script/Signatures.py b/test/sconsign/script/Signatures.py
index ef8bac0..2225f83 100644
--- a/test/sconsign/script/Signatures.py
+++ b/test/sconsign/script/Signatures.py
@@ -44,8 +44,8 @@ if NEED_HELPER:
# in the expected output because paths in the .sconsign files are
# canonicalized to use / as the separator.
-sub1_hello_c = 'sub1/hello.c'
-sub1_hello_obj = 'sub1/hello.obj'
+sub1_hello_c = 'sub1/hello.c'
+sub1_hello_obj = 'sub1/hello.obj'
test.subdir('sub1', 'sub2')
@@ -135,7 +135,7 @@ env2.Program('sub2/hello.c')
""",
)
# TODO in the above, we would normally want to run a python program
-# using "our python" like this:
+# using "our Python" like this:
# CCCOM=[[r'{_python_}', r'{fake_cc_py}', 'sub2', '$TARGET', '$SOURCE']],
# LINKCOM=[[r'{_python_}', r'{fake_link_py}', '$TARGET', '$SOURCE']],
# however we're looking at dependencies with sconsign, so that breaks things.
@@ -160,17 +160,16 @@ test.write(['sub2', 'inc2.h'], r"""\
#define STRING2 "inc2.h"
""")
-test.sleep()
-
+test.sleep() # delay for timestamps
test.run(arguments = '. --max-drift=1')
sig_re = r'[0-9a-fA-F]{32,64}'
date_re = r'\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d'
-
database_name = test.get_sconsignname()
-test.run_sconsign(arguments = f"-e hello.exe -e hello.obj sub1/{database_name}",
- stdout = r"""hello.exe: %(sig_re)s \d+ \d+
+test.run_sconsign(
+ arguments=f"-e hello.exe -e hello.obj sub1/{database_name}",
+ stdout=r"""hello.exe: %(sig_re)s \d+ \d+
%(sub1_hello_obj)s: %(sig_re)s \d+ \d+
fake_link\.py: None \d+ \d+
%(sig_re)s \[.*\]
@@ -178,10 +177,12 @@ hello.obj: %(sig_re)s \d+ \d+
%(sub1_hello_c)s: None \d+ \d+
fake_cc\.py: None \d+ \d+
%(sig_re)s \[.*\]
-""" % locals())
+""" % locals(),
+)
-test.run_sconsign(arguments = f"-e hello.exe -e hello.obj -r sub1/{database_name}",
- stdout = r"""hello.exe: %(sig_re)s '%(date_re)s' \d+
+test.run_sconsign(
+ arguments=f"-e hello.exe -e hello.obj -r sub1/{database_name}",
+ stdout=r"""hello.exe: %(sig_re)s '%(date_re)s' \d+
%(sub1_hello_obj)s: %(sig_re)s '%(date_re)s' \d+
fake_link\.py: None '%(date_re)s' \d+
%(sig_re)s \[.*\]
@@ -189,7 +190,8 @@ hello.obj: %(sig_re)s '%(date_re)s' \d+
%(sub1_hello_c)s: None '%(date_re)s' \d+
fake_cc\.py: None '%(date_re)s' \d+
%(sig_re)s \[.*\]
-""" % locals())
+""" % locals(),
+)
test.pass_test()
diff --git a/test/sconsign/script/dblite.py b/test/sconsign/script/dblite.py
index 1fcf8c0..24d8403 100644
--- a/test/sconsign/script/dblite.py
+++ b/test/sconsign/script/dblite.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
#
-# __COPYRIGHT__
+# MIT License
+#
+# Copyright The SCons Foundation
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -20,9 +22,6 @@
# 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 various ways of getting at a an sconsign file written with
@@ -97,17 +96,11 @@ main(int argc, char *argv[])
}
""")
-test.write(['sub2', 'inc1.h'], r"""\
-#define STRING1 "inc1.h"
-""")
-
-test.write(['sub2', 'inc2.h'], r"""\
-#define STRING2 "inc2.h"
-""")
-
-test.sleep()
+test.write(['sub2', 'inc1.h'], r'#define STRING1 "inc1.h"')
+test.write(['sub2', 'inc2.h'], r'#define STRING2 "inc2.h"')
-test.run(arguments = '. --max-drift=1')
+test.sleep() # delay for timestamps
+test.run(arguments='. --max-drift=1')
sig_re = r'[0-9a-fA-F]{32,64}'
date_re = r'\S+ \S+ [ \d]\d \d\d:\d\d:\d\d \d\d\d\d'
@@ -146,23 +139,23 @@ hello%(_obj)s: %(sig_re)s '%(date_re)s' \d+
common_flags = '-e hello%(_exe)s -e hello%(_obj)s -d sub1' % locals()
-test.run_sconsign(arguments = "%s my_sconsign" % common_flags,
- stdout = expect)
+test.run_sconsign(arguments="%s my_sconsign" % common_flags, stdout=expect)
-test.run_sconsign(arguments = "%s my_sconsign.dblite" % common_flags,
- stdout = expect)
+test.run_sconsign(arguments="%s my_sconsign.dblite" % common_flags, stdout=expect)
-test.run_sconsign(arguments = "%s -f dblite my_sconsign" % common_flags,
- stdout = expect)
+test.run_sconsign(arguments="%s -f dblite my_sconsign" % common_flags, stdout=expect)
-test.run_sconsign(arguments = "%s -f dblite my_sconsign.dblite" % common_flags,
- stdout = expect)
+test.run_sconsign(
+ arguments="%s -f dblite my_sconsign.dblite" % common_flags, stdout=expect
+)
-test.run_sconsign(arguments = "%s -r -f dblite my_sconsign" % common_flags,
- stdout = expect_r)
+test.run_sconsign(
+ arguments="%s -r -f dblite my_sconsign" % common_flags, stdout=expect_r
+)
-test.run_sconsign(arguments = "%s -r -f dblite my_sconsign.dblite" % common_flags,
- stdout = expect_r)
+test.run_sconsign(
+ arguments="%s -r -f dblite my_sconsign.dblite" % common_flags, stdout=expect_r
+)
test.pass_test()
diff --git a/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool1.py b/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool1.py
index 072daf0..1c024e1 100644
--- a/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool1.py
+++ b/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool2/__init__.py b/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool2/__init__.py
index f4ccefe..225b9aa 100644
--- a/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool2/__init__.py
+++ b/test/toolpath/nested/image/Libs/tools_example/Toolpath_TestTool2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_1.py b/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_1.py
index 2a70e67..c266f89 100644
--- a/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_1.py
+++ b/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool1_1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool1_1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_2/__init__.py b/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_2/__init__.py
index 424991f..f203260 100644
--- a/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_2/__init__.py
+++ b/test/toolpath/nested/image/Libs/tools_example/subdir1/Toolpath_TestTool1_2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool1_2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool1_2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_1.py b/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_1.py
index 13d0496..b1b47a2 100644
--- a/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_1.py
+++ b/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool2_1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool2_1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py b/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py
index 3f8fd5e..407df86 100644
--- a/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py
+++ b/test/toolpath/nested/image/Libs/tools_example/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool2_2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool2_2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/SConstruct b/test/toolpath/nested/image/SConstruct
index 1187a7c..7fac870 100644
--- a/test/toolpath/nested/image/SConstruct
+++ b/test/toolpath/nested/image/SConstruct
@@ -1,69 +1,69 @@
-import sys, os
-
-toollist = ['Toolpath_TestTool1',
- 'Toolpath_TestTool2',
- 'subdir1.Toolpath_TestTool1_1',
- 'subdir1.Toolpath_TestTool1_2',
- 'subdir1.subdir2.Toolpath_TestTool2_1',
- 'subdir1.subdir2.Toolpath_TestTool2_2',
- ]
-
-print('Test where tools are located under site_scons/site_tools')
-env1 = Environment(tools=toollist)
-print("env1['Toolpath_TestTool1'] = %s"%env1.get('Toolpath_TestTool1'))
-print("env1['Toolpath_TestTool2'] = %s"%env1.get('Toolpath_TestTool2'))
-print("env1['Toolpath_TestTool1_1'] = %s"%env1.get('Toolpath_TestTool1_1'))
-print("env1['Toolpath_TestTool1_2'] = %s"%env1.get('Toolpath_TestTool1_2'))
-print("env1['Toolpath_TestTool2_1'] = %s"%env1.get('Toolpath_TestTool2_1'))
-print("env1['Toolpath_TestTool2_2'] = %s"%env1.get('Toolpath_TestTool2_2'))
-
-print('Test where toolpath is set in the env constructor')
-env2 = Environment(tools=toollist, toolpath=['Libs/tools_example'])
-print("env2['Toolpath_TestTool1'] = %s"%env2.get('Toolpath_TestTool1'))
-print("env2['Toolpath_TestTool2'] = %s"%env2.get('Toolpath_TestTool2'))
-print("env2['Toolpath_TestTool1_1'] = %s"%env2.get('Toolpath_TestTool1_1'))
-print("env2['Toolpath_TestTool1_2'] = %s"%env2.get('Toolpath_TestTool1_2'))
-print("env2['Toolpath_TestTool2_1'] = %s"%env2.get('Toolpath_TestTool2_1'))
-print("env2['Toolpath_TestTool2_2'] = %s"%env2.get('Toolpath_TestTool2_2'))
-
-print('Test a Clone')
-base = Environment(tools=[], toolpath=['Libs/tools_example'])
-derived = base.Clone(tools=['subdir1.Toolpath_TestTool1_1'])
-print("derived['Toolpath_TestTool1_1'] = %s"%derived.get('Toolpath_TestTool1_1'))
-
-
-print('Test using syspath as the toolpath')
-print('Lets pretend that tools_example within Libs is actually a module installed via pip')
-oldsyspath = sys.path
-dir_path = Dir('.').srcnode().abspath
-dir_path = os.path.join(dir_path, 'Libs')
-sys.path.append(dir_path)
-
-searchpaths = []
-for item in sys.path:
- if os.path.isdir(item): searchpaths.append(item)
-
-toollist = ['tools_example.Toolpath_TestTool1',
- 'tools_example.Toolpath_TestTool2',
- 'tools_example.subdir1.Toolpath_TestTool1_1',
- 'tools_example.subdir1.Toolpath_TestTool1_2',
- 'tools_example.subdir1.subdir2.Toolpath_TestTool2_1',
- 'tools_example.subdir1.subdir2.Toolpath_TestTool2_2',
- ]
-
-env3 = Environment(tools=toollist, toolpath=searchpaths)
-print("env3['Toolpath_TestTool1'] = %s"%env3.get('Toolpath_TestTool1'))
-print("env3['Toolpath_TestTool2'] = %s"%env3.get('Toolpath_TestTool2'))
-print("env3['Toolpath_TestTool1_1'] = %s"%env3.get('Toolpath_TestTool1_1'))
-print("env3['Toolpath_TestTool1_2'] = %s"%env3.get('Toolpath_TestTool1_2'))
-print("env3['Toolpath_TestTool2_1'] = %s"%env3.get('Toolpath_TestTool2_1'))
-print("env3['Toolpath_TestTool2_2'] = %s"%env3.get('Toolpath_TestTool2_2'))
-
-
-print('Test using PyPackageDir')
-toollist = ['Toolpath_TestTool2_1', 'Toolpath_TestTool2_2']
-env4 = Environment(tools = toollist, toolpath = [PyPackageDir('tools_example.subdir1.subdir2')])
-print("env4['Toolpath_TestTool2_1'] = %s"%env4.get('Toolpath_TestTool2_1'))
-print("env4['Toolpath_TestTool2_2'] = %s"%env4.get('Toolpath_TestTool2_2'))
-
-sys.path = oldsyspath
+import sys, os
+
+toollist = ['Toolpath_TestTool1',
+ 'Toolpath_TestTool2',
+ 'subdir1.Toolpath_TestTool1_1',
+ 'subdir1.Toolpath_TestTool1_2',
+ 'subdir1.subdir2.Toolpath_TestTool2_1',
+ 'subdir1.subdir2.Toolpath_TestTool2_2',
+ ]
+
+print('Test where tools are located under site_scons/site_tools')
+env1 = Environment(tools=toollist)
+print("env1['Toolpath_TestTool1'] = %s"%env1.get('Toolpath_TestTool1'))
+print("env1['Toolpath_TestTool2'] = %s"%env1.get('Toolpath_TestTool2'))
+print("env1['Toolpath_TestTool1_1'] = %s"%env1.get('Toolpath_TestTool1_1'))
+print("env1['Toolpath_TestTool1_2'] = %s"%env1.get('Toolpath_TestTool1_2'))
+print("env1['Toolpath_TestTool2_1'] = %s"%env1.get('Toolpath_TestTool2_1'))
+print("env1['Toolpath_TestTool2_2'] = %s"%env1.get('Toolpath_TestTool2_2'))
+
+print('Test where toolpath is set in the env constructor')
+env2 = Environment(tools=toollist, toolpath=['Libs/tools_example'])
+print("env2['Toolpath_TestTool1'] = %s"%env2.get('Toolpath_TestTool1'))
+print("env2['Toolpath_TestTool2'] = %s"%env2.get('Toolpath_TestTool2'))
+print("env2['Toolpath_TestTool1_1'] = %s"%env2.get('Toolpath_TestTool1_1'))
+print("env2['Toolpath_TestTool1_2'] = %s"%env2.get('Toolpath_TestTool1_2'))
+print("env2['Toolpath_TestTool2_1'] = %s"%env2.get('Toolpath_TestTool2_1'))
+print("env2['Toolpath_TestTool2_2'] = %s"%env2.get('Toolpath_TestTool2_2'))
+
+print('Test a Clone')
+base = Environment(tools=[], toolpath=['Libs/tools_example'])
+derived = base.Clone(tools=['subdir1.Toolpath_TestTool1_1'])
+print("derived['Toolpath_TestTool1_1'] = %s"%derived.get('Toolpath_TestTool1_1'))
+
+
+print('Test using syspath as the toolpath')
+print('Lets pretend that tools_example within Libs is actually a module installed via pip')
+oldsyspath = sys.path
+dir_path = Dir('.').srcnode().abspath
+dir_path = os.path.join(dir_path, 'Libs')
+sys.path.append(dir_path)
+
+searchpaths = []
+for item in sys.path:
+ if os.path.isdir(item): searchpaths.append(item)
+
+toollist = ['tools_example.Toolpath_TestTool1',
+ 'tools_example.Toolpath_TestTool2',
+ 'tools_example.subdir1.Toolpath_TestTool1_1',
+ 'tools_example.subdir1.Toolpath_TestTool1_2',
+ 'tools_example.subdir1.subdir2.Toolpath_TestTool2_1',
+ 'tools_example.subdir1.subdir2.Toolpath_TestTool2_2',
+ ]
+
+env3 = Environment(tools=toollist, toolpath=searchpaths)
+print("env3['Toolpath_TestTool1'] = %s"%env3.get('Toolpath_TestTool1'))
+print("env3['Toolpath_TestTool2'] = %s"%env3.get('Toolpath_TestTool2'))
+print("env3['Toolpath_TestTool1_1'] = %s"%env3.get('Toolpath_TestTool1_1'))
+print("env3['Toolpath_TestTool1_2'] = %s"%env3.get('Toolpath_TestTool1_2'))
+print("env3['Toolpath_TestTool2_1'] = %s"%env3.get('Toolpath_TestTool2_1'))
+print("env3['Toolpath_TestTool2_2'] = %s"%env3.get('Toolpath_TestTool2_2'))
+
+
+print('Test using PyPackageDir')
+toollist = ['Toolpath_TestTool2_1', 'Toolpath_TestTool2_2']
+env4 = Environment(tools = toollist, toolpath = [PyPackageDir('tools_example.subdir1.subdir2')])
+print("env4['Toolpath_TestTool2_1'] = %s"%env4.get('Toolpath_TestTool2_1'))
+print("env4['Toolpath_TestTool2_2'] = %s"%env4.get('Toolpath_TestTool2_2'))
+
+sys.path = oldsyspath
diff --git a/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool1.py b/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool1.py
index 072daf0..1c024e1 100644
--- a/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool1.py
+++ b/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool2/__init__.py b/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool2/__init__.py
index f4ccefe..225b9aa 100644
--- a/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool2/__init__.py
+++ b/test/toolpath/nested/image/site_scons/site_tools/Toolpath_TestTool2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_1.py b/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_1.py
index 2a70e67..c266f89 100644
--- a/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_1.py
+++ b/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool1_1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool1_1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_2/__init__.py b/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_2/__init__.py
index 424991f..f203260 100644
--- a/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_2/__init__.py
+++ b/test/toolpath/nested/image/site_scons/site_tools/subdir1/Toolpath_TestTool1_2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool1_2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool1_2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_1.py b/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_1.py
index 13d0496..b1b47a2 100644
--- a/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_1.py
+++ b/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool2_1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool2_1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py b/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py
index 3f8fd5e..407df86 100644
--- a/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py
+++ b/test/toolpath/nested/image/site_scons/site_tools/subdir1/subdir2/Toolpath_TestTool2_2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['Toolpath_TestTool2_2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['Toolpath_TestTool2_2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/relative_import/image/SConstruct b/test/toolpath/relative_import/image/SConstruct
index c9b28bb..cf607d1 100644
--- a/test/toolpath/relative_import/image/SConstruct
+++ b/test/toolpath/relative_import/image/SConstruct
@@ -1,10 +1,10 @@
-env = Environment(tools=['TestTool1', 'TestTool1.TestTool1_2'], toolpath=['tools'])
-
-# Test a relative import within the root of the tools directory
-print("env['TestTool1'] = %s"%env.get('TestTool1'))
-print("env['TestTool1_1'] = %s"%env.get('TestTool1_1'))
-
-# Test a relative import within a sub dir
-print("env['TestTool1_2'] = %s"%env.get('TestTool1_2'))
-print("env['TestTool1_2_1'] = %s"%env.get('TestTool1_2_1'))
-print("env['TestTool1_2_2'] = %s"%env.get('TestTool1_2_2'))
+env = Environment(tools=['TestTool1', 'TestTool1.TestTool1_2'], toolpath=['tools'])
+
+# Test a relative import within the root of the tools directory
+print("env['TestTool1'] = %s"%env.get('TestTool1'))
+print("env['TestTool1_1'] = %s"%env.get('TestTool1_1'))
+
+# Test a relative import within a sub dir
+print("env['TestTool1_2'] = %s"%env.get('TestTool1_2'))
+print("env['TestTool1_2_1'] = %s"%env.get('TestTool1_2_1'))
+print("env['TestTool1_2_2'] = %s"%env.get('TestTool1_2_2'))
diff --git a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_1.py b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_1.py
index 4c9a7bc..584d353 100644
--- a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_1.py
+++ b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['TestTool1_1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['TestTool1_1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_1.py b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_1.py
index e65f8cd..62d754b 100644
--- a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_1.py
+++ b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_1.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['TestTool1_2_1'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['TestTool1_2_1'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_2/__init__.py b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_2/__init__.py
index 463baee..12c6018 100644
--- a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_2/__init__.py
+++ b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/TestTool1_2_2/__init__.py
@@ -1,4 +1,4 @@
-def generate(env):
- env['TestTool1_2_2'] = 1
-def exists(env):
- return 1
+def generate(env):
+ env['TestTool1_2_2'] = 1
+def exists(env):
+ return 1
diff --git a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/__init__.py b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/__init__.py
index 8bd698f..58fdc93 100644
--- a/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/__init__.py
+++ b/test/toolpath/relative_import/image/tools/TestTool1/TestTool1_2/__init__.py
@@ -1,11 +1,11 @@
-from . import TestTool1_2_1
-from . import TestTool1_2_2
-
-def generate(env):
- env['TestTool1_2'] = 1
- TestTool1_2_1.generate(env)
- TestTool1_2_2.generate(env)
-def exists(env):
- TestTool1_2_1.exists(env)
- TestTool1_2_2.exists(env)
- return 1
+from . import TestTool1_2_1
+from . import TestTool1_2_2
+
+def generate(env):
+ env['TestTool1_2'] = 1
+ TestTool1_2_1.generate(env)
+ TestTool1_2_2.generate(env)
+def exists(env):
+ TestTool1_2_1.exists(env)
+ TestTool1_2_2.exists(env)
+ return 1
diff --git a/test/toolpath/relative_import/image/tools/TestTool1/__init__.py b/test/toolpath/relative_import/image/tools/TestTool1/__init__.py
index d5510d2..c479560 100644
--- a/test/toolpath/relative_import/image/tools/TestTool1/__init__.py
+++ b/test/toolpath/relative_import/image/tools/TestTool1/__init__.py
@@ -1,9 +1,9 @@
-from . import TestTool1_1
-
-def generate(env):
- env['TestTool1'] = 1
- # Include another tool within the same directory
- TestTool1_1.generate(env)
-def exists(env):
- TestTool1_1.exists(env)
- return 1
+from . import TestTool1_1
+
+def generate(env):
+ env['TestTool1'] = 1
+ # Include another tool within the same directory
+ TestTool1_1.generate(env)
+def exists(env):
+ TestTool1_1.exists(env)
+ return 1
diff --git a/test/toolpath/relative_import/relative_import.py b/test/toolpath/relative_import/relative_import.py
index f4362f7..43ae6cf 100644
--- a/test/toolpath/relative_import/relative_import.py
+++ b/test/toolpath/relative_import/relative_import.py
@@ -1,52 +1,52 @@
-#!/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
-
-test = TestSCons.TestSCons()
-
-test.dir_fixture('image')
-
-test.run(arguments = '.', stdout = """\
-scons: Reading SConscript files ...
-env['TestTool1'] = 1
-env['TestTool1_1'] = 1
-env['TestTool1_2'] = 1
-env['TestTool1_2_1'] = 1
-env['TestTool1_2_2'] = 1
-scons: done reading SConscript files.
-scons: Building targets ...
-scons: `.' is up to date.
-scons: done building targets.
-""")
-
-test.pass_test()
-
-# Local Variables:
-# tab-width:4
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=4 shiftwidth=4:
+#!/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
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+
+test.run(arguments = '.', stdout = """\
+scons: Reading SConscript files ...
+env['TestTool1'] = 1
+env['TestTool1_1'] = 1
+env['TestTool1_2'] = 1
+env['TestTool1_2_1'] = 1
+env['TestTool1_2_2'] = 1
+scons: done reading SConscript files.
+scons: Building targets ...
+scons: `.' is up to date.
+scons: done building targets.
+""")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py
index 5759121..b109843 100644
--- a/testing/framework/TestCmd.py
+++ b/testing/framework/TestCmd.py
@@ -299,9 +299,17 @@ __version__ = "1.3"
import atexit
import difflib
import errno
+import hashlib
import os
import re
+try:
+ import psutil
+except ImportError:
+ HAVE_PSUTIL = False
+else:
+ HAVE_PSUTIL = True
import shutil
+import signal
import stat
import subprocess
import sys
@@ -310,6 +318,7 @@ import threading
import time
import traceback
from collections import UserList, UserString
+from pathlib import Path
from subprocess import PIPE, STDOUT
from typing import Optional
@@ -360,7 +369,7 @@ def is_String(e):
testprefix = 'testcmd.'
if os.name in ('posix', 'nt'):
- testprefix += "%s." % str(os.getpid())
+ testprefix += f"{os.getpid()}."
re_space = re.compile(r'\s')
@@ -377,12 +386,47 @@ def _caller(tblist, skip):
if name in ("?", "<module>"):
name = ""
else:
- name = " (" + name + ")"
+ name = f" ({name})"
string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name))
atfrom = "\tfrom"
return string
+def clean_up_ninja_daemon(self, result_type) -> None:
+ """
+ Kill any running scons daemon started by ninja and clean up
+
+ Working directory and temp files are removed.
+ Skipped if this platform doesn't have psutil (e.g. msys2 on Windows)
+ """
+ if not self:
+ return
+
+ for path in Path(self.workdir).rglob('.ninja'):
+ daemon_dir = Path(tempfile.gettempdir()) / (
+ f"scons_daemon_{str(hashlib.md5(str(path.resolve()).encode()).hexdigest())}"
+ )
+ pidfiles = [daemon_dir / 'pidfile', path / 'scons_daemon_dirty']
+ for pidfile in pidfiles:
+ if pidfile.exists():
+ with open(pidfile) as f:
+ try:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGINT)
+ except OSError:
+ pass
+
+ while HAVE_PSUTIL:
+ if pid not in [proc.pid for proc in psutil.process_iter()]:
+ break
+ else:
+ time.sleep(0.1)
+
+ if not self._preserve[result_type]:
+ if daemon_dir.exists():
+ shutil.rmtree(daemon_dir)
+
+
def fail_test(self=None, condition=True, function=None, skip=0, message=None):
"""Causes a test to exit with a fail.
@@ -402,23 +446,24 @@ def fail_test(self=None, condition=True, function=None, skip=0, message=None):
return
if function is not None:
function()
+ clean_up_ninja_daemon(self, 'fail_test')
of = ""
desc = ""
sep = " "
if self is not None:
if self.program:
- of = " of " + self.program
+ of = f" of {self.program}"
sep = "\n\t"
if self.description:
- desc = " [" + self.description + "]"
+ desc = f" [{self.description}]"
sep = "\n\t"
at = _caller(traceback.extract_stack(), skip)
if message:
- msg = "\t%s\n" % message
+ msg = f"\t{message}\n"
else:
msg = ""
- sys.stderr.write("FAILED test" + of + desc + sep + at + msg)
+ sys.stderr.write(f"FAILED test{of}{desc}{sep}{at}{msg}")
sys.exit(1)
@@ -447,19 +492,20 @@ def no_result(self=None, condition=True, function=None, skip=0):
return
if function is not None:
function()
+ clean_up_ninja_daemon(self, 'no_result')
of = ""
desc = ""
sep = " "
if self is not None:
if self.program:
- of = " of " + self.program
+ of = f" of {self.program}"
sep = "\n\t"
if self.description:
- desc = " [" + self.description + "]"
+ desc = f" [{self.description}]"
sep = "\n\t"
at = _caller(traceback.extract_stack(), skip)
- sys.stderr.write("NO RESULT for test" + of + desc + sep + at)
+ sys.stderr.write(f"NO RESULT for test{of}{desc}{sep}{at}")
sys.exit(2)
@@ -483,6 +529,7 @@ def pass_test(self=None, condition=True, function=None):
return
if function is not None:
function()
+ clean_up_ninja_daemon(self, 'pass_test')
sys.stderr.write("PASSED\n")
sys.exit(0)
@@ -555,7 +602,7 @@ def match_re(lines=None, res=None):
if not is_List(res):
res = res.split("\n")
if len(lines) != len(res):
- print("match_re: expected %d lines, found %d" % (len(res), len(lines)))
+ print(f"match_re: expected {len(res)} lines, found {len(lines)}")
return None
for i, (line, regex) in enumerate(zip(lines, res)):
s = r"^{}$".format(regex)
@@ -627,24 +674,24 @@ def simple_diff(a, b, fromfile='', tofile='',
sm = difflib.SequenceMatcher(None, a, b)
def comma(x1, x2):
- return x1 + 1 == x2 and str(x2) or '%s,%s' % (x1 + 1, x2)
+ return x1 + 1 == x2 and str(x2) or f'{x1 + 1},{x2}'
for op, a1, a2, b1, b2 in sm.get_opcodes():
if op == 'delete':
- yield "{}d{}{}".format(comma(a1, a2), b1, lineterm)
+ yield f"{comma(a1, a2)}d{b1}{lineterm}"
for l in a[a1:a2]:
- yield '< ' + l
+ yield f"< {l}"
elif op == 'insert':
- yield "{}a{}{}".format(a1, comma(b1, b2), lineterm)
+ yield f"{a1}a{comma(b1, b2)}{lineterm}"
for l in b[b1:b2]:
- yield '> ' + l
+ yield f"> {l}"
elif op == 'replace':
- yield "{}c{}{}".format(comma(a1, a2), comma(b1, b2), lineterm)
+ yield f"{comma(a1, a2)}c{comma(b1, b2)}{lineterm}"
for l in a[a1:a2]:
- yield '< ' + l
- yield '---{}'.format(lineterm)
+ yield f"< {l}"
+ yield f'---{lineterm}'
for l in b[b1:b2]:
- yield '> ' + l
+ yield f"> {l}"
def diff_re(a, b, fromfile='', tofile='',
@@ -674,10 +721,10 @@ def diff_re(a, b, fromfile='', tofile='',
msg = "Regular expression error in %s: %s"
raise re.error(msg % (repr(s), e.args[0]))
if not expr.search(bline):
- result.append("%sc%s" % (i + 1, i + 1))
- result.append('< ' + repr(a[i]))
+ result.append(f"{i + 1}c{i + 1}")
+ result.append(f"< {repr(a[i])}")
result.append('---')
- result.append('> ' + repr(b[i]))
+ result.append(f"> {repr(b[i])}")
return result
@@ -690,7 +737,7 @@ if os.name == 'posix':
for c in special:
arg = arg.replace(c, slash + c)
if re_space.search(arg):
- arg = '"' + arg + '"'
+ arg = f"\"{arg}\""
return arg
else:
# Windows does not allow special characters in file names
@@ -698,7 +745,7 @@ else:
# the arg.
def escape(arg):
if re_space.search(arg):
- arg = '"' + arg + '"'
+ arg = f"\"{arg}\""
return arg
if os.name == 'java':
@@ -967,8 +1014,7 @@ def _clean():
class TestCmd:
- """Class TestCmd
- """
+ """Class TestCmd"""
def __init__(
self,
@@ -1002,7 +1048,10 @@ class TestCmd:
self.combine = combine
self.universal_newlines = universal_newlines
self.process = None
- self.set_timeout(timeout)
+ # Two layers of timeout: one at the test class instance level,
+ # one set on an individual start() call (usually via a run() call)
+ self.timeout = timeout
+ self.start_timeout = None
self.set_match_function(match, match_stdout, match_stderr)
self.set_diff_function(diff, diff_stdout, diff_stderr)
self._dirlist = []
@@ -1042,7 +1091,7 @@ class TestCmd:
self.cleanup()
def __repr__(self):
- return "%x" % id(self)
+ return f"{id(self):x}"
banner_char = '='
banner_width = 80
@@ -1050,7 +1099,7 @@ class TestCmd:
def banner(self, s, width=None):
if width is None:
width = self.banner_width
- return s + self.banner_char * (width - len(s))
+ return f"{s:{self.banner_char}<{width}}"
escape = staticmethod(escape)
@@ -1090,7 +1139,7 @@ class TestCmd:
condition = self.condition
if self._preserve[condition]:
for dir in self._dirlist:
- print("Preserved directory " + dir)
+ print(f"Preserved directory {dir}")
else:
list = self._dirlist[:]
list.reverse()
@@ -1125,6 +1174,9 @@ class TestCmd:
interpreter = [interpreter]
cmd = list(interpreter) + cmd
if arguments:
+ if isinstance(arguments, dict):
+ cmd.extend([f"{k}={v}" for k, v in arguments.items()])
+ return cmd
if isinstance(arguments, str):
arguments = arguments.split()
cmd.extend(arguments)
@@ -1324,14 +1376,6 @@ class TestCmd:
dir = self.canonicalize(dir)
os.rmdir(dir)
- def _timeout(self):
- self.process.terminate()
- self.timer.cancel()
- self.timer = None
-
- def set_timeout(self, timeout):
- self.timeout = timeout
- self.timer = None
def parse_path(self, path, suppress_current=False):
"""Return a list with the single path components of path."""
@@ -1458,7 +1502,7 @@ class TestCmd:
interpreter=None,
arguments=None,
universal_newlines=None,
- timeout=_Null,
+ timeout=None,
**kw):
""" Starts a program or script for the test environment.
@@ -1486,11 +1530,8 @@ class TestCmd:
else:
stderr_value = PIPE
- if timeout is _Null:
- timeout = self.timeout
if timeout:
- self.timer = threading.Timer(float(timeout), self._timeout)
- self.timer.start()
+ self.start_timeout = timeout
if sys.platform == 'win32':
# Set this otherwist stdout/stderr pipes default to
@@ -1542,14 +1583,32 @@ class TestCmd:
"""
if popen is None:
popen = self.process
- stdout, stderr = popen.communicate()
+ if self.start_timeout:
+ timeout = self.start_timeout
+ # we're using a timeout from start, now reset it to default
+ self.start_timeout = None
+ else:
+ timeout = self.timeout
+ try:
+ stdout, stderr = popen.communicate(timeout=timeout)
+ except subprocess.TimeoutExpired:
+ popen.terminate()
+ stdout, stderr = popen.communicate()
+
+ # this is instead of using Popen as a context manager:
+ if popen.stdout:
+ popen.stdout.close()
+ if popen.stderr:
+ popen.stderr.close()
+ try:
+ if popen.stdin:
+ popen.stdin.close()
+ finally:
+ popen.wait()
stdout = self.fix_binary_stream(stdout)
stderr = self.fix_binary_stream(stderr)
- if self.timer:
- self.timer.cancel()
- self.timer = None
self.status = popen.returncode
self.process = None
self._stdout.append(stdout or '')
@@ -1561,7 +1620,7 @@ class TestCmd:
chdir=None,
stdin=None,
universal_newlines=None,
- timeout=_Null):
+ timeout=None):
"""Runs a test of the program or script for the test environment.
Output and error output are saved for future retrieval via
@@ -1569,6 +1628,9 @@ class TestCmd:
The specified program will have the original directory
prepended unless it is enclosed in a [list].
+
+ argument: If this is a dict() then will create arguments with KEY+VALUE for
+ each entry in the dict.
"""
if self.external:
if not program:
@@ -1584,8 +1646,10 @@ class TestCmd:
if not os.path.isabs(chdir):
chdir = os.path.join(self.workpath(chdir))
if self.verbose:
- sys.stderr.write("chdir(" + chdir + ")\n")
+ sys.stderr.write(f"chdir({chdir})\n")
os.chdir(chdir)
+ if not timeout:
+ timeout = self.timeout
p = self.start(program=program,
interpreter=interpreter,
arguments=arguments,
@@ -1603,17 +1667,29 @@ class TestCmd:
# subclasses that redefine .finish(). We could abstract this
# into Yet Another common method called both here and by .finish(),
# but that seems ill-thought-out.
- stdout, stderr = p.communicate(input=stdin)
- if self.timer:
- self.timer.cancel()
- self.timer = None
+ try:
+ stdout, stderr = p.communicate(input=stdin, timeout=timeout)
+ except subprocess.TimeoutExpired:
+ p.terminate()
+ stdout, stderr = p.communicate()
+
+ # this is instead of using Popen as a context manager:
+ if p.stdout:
+ p.stdout.close()
+ if p.stderr:
+ p.stderr.close()
+ try:
+ if p.stdin:
+ p.stdin.close()
+ finally:
+ p.wait()
+
self.status = p.returncode
self.process = None
stdout = self.fix_binary_stream(stdout)
stderr = self.fix_binary_stream(stderr)
-
self._stdout.append(stdout or '')
self._stderr.append(stderr or '')
@@ -1624,12 +1700,12 @@ class TestCmd:
write('============ STATUS: %d\n' % self.status)
out = self.stdout()
if out or self.verbose >= 3:
- write('============ BEGIN STDOUT (len=%d):\n' % len(out))
+ write(f'============ BEGIN STDOUT (len={len(out)}):\n')
write(out)
write('============ END STDOUT\n')
err = self.stderr()
if err or self.verbose >= 3:
- write('============ BEGIN STDERR (len=%d)\n' % len(err))
+ write(f'============ BEGIN STDERR (len={len(err)})\n')
write(err)
write('============ END STDERR\n')
@@ -1643,12 +1719,16 @@ class TestCmd:
time.sleep(seconds)
def stderr(self, run=None) -> Optional[str]:
- """Returns the error output from the specified run number.
+ """Returns the stored standard error output from a given run.
- If there is no specified run number, then returns the error
- output of the last run. If the run number is less than zero,
- then returns the error output from that many runs back from the
- current run.
+ Args:
+ run: run number to select. If run number is omitted,
+ return the standard error of the most recent run.
+ If negative, use as a relative offset, e.g. -2
+ means the run two prior to the most recent.
+
+ Returns:
+ selected sterr string or None if there are no stored runs.
"""
if not run:
run = len(self._stderr)
@@ -1665,13 +1745,12 @@ class TestCmd:
Args:
run: run number to select. If run number is omitted,
- return the standard output of the most recent run.
- If negative, use as a relative offset, so that -2
- means the run two prior to the most recent.
+ return the standard output of the most recent run.
+ If negative, use as a relative offset, e.g. -2
+ means the run two prior to the most recent.
Returns:
- selected stdout string or None if there are no
- stored runs.
+ selected stdout string or None if there are no stored runs.
"""
if not run:
run = len(self._stdout)
diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py
index 212c59a..0a1fa26 100644
--- a/testing/framework/TestCmdTests.py
+++ b/testing/framework/TestCmdTests.py
@@ -19,8 +19,6 @@ Unit tests for the TestCmd.py module.
# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-__author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCmdTests.py 1.3.D001 2010/06/03 12:58:27 knight"
import os
import shutil
@@ -42,6 +40,7 @@ from SCons.Util import to_bytes, to_str
sys.path = sys.path[1:]
import TestCmd
+from TestCmd import _python_
def _is_readable(path):
# XXX this doesn't take into account UID, it assumes it's our file
@@ -97,10 +96,10 @@ class TestCmdTestCase(unittest.TestCase):
textx = fmt % (t.scriptx, t.scriptx)
if sys.platform == 'win32':
textx = textx.replace('%', '%%')
- textx = '@python -c "%s"' % textx + ' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'
+ textx = f"@{_python_} -c \"{textx}\" %1 %2 %3 %4 %5 %6 %7 %8 %9\n"
else:
- textx = '#! /usr/bin/env python\n' + textx + '\n'
- text1 = 'A first line to be ignored!\n' + fmt % (t.script1, t.script1)
+ textx = f"#!{_python_}\n{textx}\n"
+ text1 = f"A first line to be ignored!\n{fmt % (t.script1, t.script1)}"
textout = fmtout % t.scriptout
texterr = fmterr % t.scripterr
@@ -151,33 +150,27 @@ class TestCmdTestCase(unittest.TestCase):
python = sys.executable
_stdout, _stderr, _status = self.call_python(indata, python)
assert _status == status, (
- "status = %s, expected %s\n" % (str(_status), str(status))
- + "STDOUT ===================\n"
- + _stdout
- + "STDERR ===================\n"
- + _stderr
+ f"status = {_status}, expected {status}\n"
+ f"STDOUT ===================\n{_stdout}"
+ f"STDERR ===================\n{_stderr}"
)
assert _stdout == stdout, (
- "Expected STDOUT ==========\n"
- + stdout
- + "Actual STDOUT ============\n"
- + _stdout
- + "STDERR ===================\n"
- + _stderr
+ f"Expected STDOUT ==========\n{stdout}"
+ f"Actual STDOUT ============\n{_stdout}"
+ f"STDERR ===================\n{_stderr}"
)
assert _stderr == stderr, (
- "Expected STDERR ==========\n"
- + stderr
- + "Actual STDERR ============\n"
- + _stderr
+ f"Expected STDERR ==========\n{stderr}"
+ f"Actual STDERR ============\n{_stderr}"
)
def run_match(self, content, *args):
expect = "%s: %s: %s: %s\n" % args
content = self.translate_newlines(to_str(content))
- assert content == expect, \
- "Expected %s ==========\n" % args[1] + expect + \
- "Actual %s ============\n" % args[1] + content
+ assert content == expect, (
+ f"Expected {args[1] + expect} ==========\n"
+ f"Actual {args[1] + content} ============\n"
+ )
@@ -238,12 +231,12 @@ class cleanup_TestCase(TestCmdTestCase):
def test_atexit(self):
"""Test cleanup when atexit is used"""
- self.popen_python("""\
+ self.popen_python(f"""\
import atexit
import sys
import TestCmd
-sys.path = ['%s'] + sys.path
+sys.path = [r'{self.orig_cwd}'] + sys.path
@atexit.register
def cleanup():
@@ -251,7 +244,7 @@ def cleanup():
result = TestCmd.TestCmd(workdir='')
sys.exit(0)
-""" % self.orig_cwd, stdout='cleanup()\n')
+""", stdout='cleanup()\n')
class chmod_TestCase(TestCmdTestCase):
@@ -273,17 +266,17 @@ class chmod_TestCase(TestCmdTestCase):
test.chmod(['sub', 'file2'], stat.S_IWRITE)
file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE])
- assert file1_mode == 0o444, '0%o' % file1_mode
+ assert file1_mode == 0o444, f'0{file1_mode:o}'
file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE])
- assert file2_mode == 0o666, '0%o' % file2_mode
+ assert file2_mode == 0o666, f'0{file2_mode:o}'
test.chmod('file1', stat.S_IWRITE)
test.chmod(wdir_sub_file2, stat.S_IREAD)
file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE])
- assert file1_mode == 0o666, '0%o' % file1_mode
+ assert file1_mode == 0o666, f'0{file1_mode:o}'
file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE])
- assert file2_mode == 0o444, '0%o' % file2_mode
+ assert file2_mode == 0o444, f'0{file2_mode:o}'
else:
@@ -291,17 +284,17 @@ class chmod_TestCase(TestCmdTestCase):
test.chmod(['sub', 'file2'], 0o760)
file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE])
- assert file1_mode == 0o700, '0%o' % file1_mode
+ assert file1_mode == 0o700, f'0{file1_mode:o}'
file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE])
- assert file2_mode == 0o760, '0%o' % file2_mode
+ assert file2_mode == 0o760, f'0{file2_mode:o}'
test.chmod('file1', 0o765)
test.chmod(wdir_sub_file2, 0o567)
file1_mode = stat.S_IMODE(os.stat(wdir_file1)[stat.ST_MODE])
- assert file1_mode == 0o765, '0%o' % file1_mode
+ assert file1_mode == 0o765, f'0{file1_mode:o}'
file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE])
- assert file2_mode == 0o567, '0%o' % file2_mode
+ assert file2_mode == 0o567, f'0{file2_mode:o}'
@@ -335,7 +328,7 @@ sys.stderr.write("run2 STDERR third line\\n")
combine = 1)
output = test.stdout()
if output is not None:
- raise IndexError("got unexpected output:\n\t`%s'\n" % output)
+ raise IndexError(f"got unexpected output:\n\t`{output}'\n")
# The underlying system subprocess implementations can combine
# stdout and stderr in different orders, so we accomodate both.
@@ -414,8 +407,8 @@ class diff_TestCase(TestCmdTestCase):
def test_diff_custom_function(self):
"""Test diff() using a custom function"""
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def my_diff(a, b):
return [
@@ -428,7 +421,7 @@ def my_diff(a, b):
test = TestCmd.TestCmd(diff = my_diff)
test.diff("a\\nb1\\nc\\n", "a\\nb2\\nc\\n", "STDOUT")
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout = """\
STDOUT==========================================================================
*****
@@ -439,13 +432,13 @@ STDOUT==========================================================================
""")
def test_diff_string(self):
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff = 'diff_re')
test.diff("a\\nb1\\nc\\n", "a\\nb2\\nc\\n", 'STDOUT')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout = """\
STDOUT==========================================================================
2c2
@@ -456,12 +449,12 @@ STDOUT==========================================================================
def test_error(self):
"""Test handling a compilation error in TestCmd.diff_re()"""
- script_input = """import sys
-sys.path = ['%s'] + sys.path
+ script_input = f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
assert TestCmd.diff_re([r"a.*(e"], ["abcde"])
sys.exit(0)
-""" % self.orig_cwd
+"""
stdout, stderr, status = self.call_python(script_input)
assert status == 1, status
expect1 = "Regular expression error in '^a.*(e$': missing )"
@@ -471,8 +464,8 @@ sys.exit(0)
def test_simple_diff_static_method(self):
"""Test calling the TestCmd.TestCmd.simple_diff() static method"""
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
result = TestCmd.TestCmd.simple_diff(['a', 'b', 'c', 'e', 'f1'],
['a', 'c', 'd', 'e', 'f2'])
@@ -480,12 +473,12 @@ result = list(result)
expect = ['2d1', '< b', '3a3', '> d', '5c5', '< f1', '---', '> f2']
assert result == expect, result
sys.exit(0)
-""" % self.orig_cwd)
+""")
def test_context_diff_static_method(self):
"""Test calling the TestCmd.TestCmd.context_diff() static method"""
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
result = TestCmd.TestCmd.context_diff(['a\\n', 'b\\n', 'c\\n', 'e\\n', 'f1\\n'],
['a\\n', 'c\\n', 'd\\n', 'e\\n', 'f2\\n'])
@@ -509,12 +502,12 @@ expect = [
]
assert result == expect, result
sys.exit(0)
-""" % self.orig_cwd)
+""")
def test_unified_diff_static_method(self):
"""Test calling the TestCmd.TestCmd.unified_diff() static method"""
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
result = TestCmd.TestCmd.unified_diff(['a\\n', 'b\\n', 'c\\n', 'e\\n', 'f1\\n'],
['a\\n', 'c\\n', 'd\\n', 'e\\n', 'f2\\n'])
@@ -533,12 +526,12 @@ expect = [
]
assert result == expect, result
sys.exit(0)
-""" % self.orig_cwd)
+""")
def test_diff_re_static_method(self):
"""Test calling the TestCmd.TestCmd.diff_re() static method"""
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
result = TestCmd.TestCmd.diff_re(['a', 'b', 'c', '.', 'f1'],
['a', 'c', 'd', 'e', 'f2'])
@@ -559,20 +552,20 @@ expect = [
]
assert result == expect, result
sys.exit(0)
-""" % self.orig_cwd)
+""")
class diff_stderr_TestCase(TestCmdTestCase):
def test_diff_stderr_default(self):
"""Test diff_stderr() default behavior"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd()
test.diff_stderr('a\nb1\nc\n', 'a\nb2\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
2c2
< b1
@@ -582,9 +575,9 @@ sys.exit(0)
def test_diff_stderr_not_affecting_diff_stdout(self):
"""Test diff_stderr() not affecting diff_stdout() behavior"""
- self.popen_python(r"""
+ self.popen_python(fr"""
import sys
-sys.path = ['%s'] + sys.path
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stderr='diff_re')
print("diff_stderr:")
@@ -592,7 +585,7 @@ test.diff_stderr('a\nb.\nc\n', 'a\nbb\nc\n')
print("diff_stdout:")
test.diff_stdout('a\nb.\nc\n', 'a\nbb\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
diff_stderr:
diff_stdout:
@@ -604,15 +597,15 @@ diff_stdout:
def test_diff_stderr_custom_function(self):
"""Test diff_stderr() using a custom function"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def my_diff(a, b):
return ["a:"] + a + ["b:"] + b
test = TestCmd.TestCmd(diff_stderr=my_diff)
test.diff_stderr('abc', 'def')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
a:
abc
@@ -622,13 +615,13 @@ def
def test_diff_stderr_TestCmd_function(self):
"""Test diff_stderr() using a TestCmd function"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stderr = TestCmd.diff_re)
test.diff_stderr('a\n.\n', 'b\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
1c1
< 'a'
@@ -638,13 +631,13 @@ sys.exit(0)
def test_diff_stderr_static_method(self):
"""Test diff_stderr() using a static method"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stderr=TestCmd.TestCmd.diff_re)
test.diff_stderr('a\n.\n', 'b\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
1c1
< 'a'
@@ -654,13 +647,13 @@ sys.exit(0)
def test_diff_stderr_string(self):
"""Test diff_stderr() using a string to fetch the diff method"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stderr='diff_re')
test.diff_stderr('a\n.\n', 'b\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
1c1
< 'a'
@@ -673,13 +666,13 @@ sys.exit(0)
class diff_stdout_TestCase(TestCmdTestCase):
def test_diff_stdout_default(self):
"""Test diff_stdout() default behavior"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd()
test.diff_stdout('a\nb1\nc\n', 'a\nb2\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
2c2
< b1
@@ -689,9 +682,9 @@ sys.exit(0)
def test_diff_stdout_not_affecting_diff_stderr(self):
"""Test diff_stdout() not affecting diff_stderr() behavior"""
- self.popen_python(r"""
+ self.popen_python(fr"""
import sys
-sys.path = ['%s'] + sys.path
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stdout='diff_re')
print("diff_stdout:")
@@ -699,7 +692,7 @@ test.diff_stdout('a\nb.\nc\n', 'a\nbb\nc\n')
print("diff_stderr:")
test.diff_stderr('a\nb.\nc\n', 'a\nbb\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
diff_stdout:
diff_stderr:
@@ -711,15 +704,15 @@ diff_stderr:
def test_diff_stdout_custom_function(self):
"""Test diff_stdout() using a custom function"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def my_diff(a, b):
return ["a:"] + a + ["b:"] + b
test = TestCmd.TestCmd(diff_stdout=my_diff)
test.diff_stdout('abc', 'def')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
a:
abc
@@ -729,13 +722,13 @@ def
def test_diff_stdout_TestCmd_function(self):
"""Test diff_stdout() using a TestCmd function"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stdout = TestCmd.diff_re)
test.diff_stdout('a\n.\n', 'b\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
1c1
< 'a'
@@ -745,13 +738,13 @@ sys.exit(0)
def test_diff_stdout_static_method(self):
"""Test diff_stdout() using a static method"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stdout=TestCmd.TestCmd.diff_re)
test.diff_stdout('a\n.\n', 'b\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
1c1
< 'a'
@@ -761,13 +754,13 @@ sys.exit(0)
def test_diff_stdout_string(self):
"""Test diff_stdout() using a string to fetch the diff method"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(diff_stdout='diff_re')
test.diff_stdout('a\n.\n', 'b\nc\n')
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
1c1
< 'a'
@@ -787,12 +780,12 @@ class exit_TestCase(TestCmdTestCase):
'fail_test': "FAILED test at line 5 of <stdin>\n",
'no_result': "NO RESULT for test at line 5 of <stdin>\n"}
global ExitError
- input = """import sys
-sys.path = ['%s'] + sys.path
+ input = f"""import sys
+sys.path = [r'{cwd}'] + sys.path
import TestCmd
-test = TestCmd.TestCmd(workdir = '%s')
-test.%s()
-""" % (cwd, tempdir, condition)
+test = TestCmd.TestCmd(workdir = '{tempdir}')
+test.{condition}()
+"""
stdout, stderr, status = self.call_python(input, python="python")
if close_true[condition]:
unexpected = (status != 0)
@@ -805,7 +798,7 @@ test.%s()
msg = "Expected exit status %d, got %d\n"
raise ExitError(msg % (exit_status[condition], status))
if stderr != result_string[condition]:
- msg = "Expected error output:\n%sGot error output:\n%s"
+ msg = "Expected error output:\n%s\nGot error output:\n%s"
raise ExitError(msg % (result_string[condition], stderr))
if preserved:
if not os.path.exists(tempdir):
@@ -862,40 +855,40 @@ sys.stderr.write("run: STDERR\\n")
os.chdir(run_env.workdir)
# Everything before this prepared our "source directory."
# Now do the real test.
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
TestCmd.fail_test(condition = 1)
-""" % self.orig_cwd, status = 1, stderr = "FAILED test at line 4 of <stdin>\n")
+""", status = 1, stderr = "FAILED test at line 4 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')
test.run()
test.fail_test(condition = (test.status == 0))
-""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 6 of <stdin>\n" % run_env.workpath('run'))
+""", status = 1, stderr = f"FAILED test of {run_env.workpath('run')}\n\tat line 6 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', description = 'xyzzy', workdir = '')
test.run()
test.fail_test(condition = (test.status == 0))
-""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s [xyzzy]\n\tat line 6 of <stdin>\n" % run_env.workpath('run'))
+""", status = 1, stderr = f"FAILED test of {run_env.workpath('run')} [xyzzy]\n\tat line 6 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')
test.run()
def xxx():
sys.stderr.write("printed on failure\\n")
test.fail_test(condition = (test.status == 0), function = xxx)
-""" % self.orig_cwd, status = 1, stderr = "printed on failure\nFAILED test of %s\n\tat line 8 of <stdin>\n" % run_env.workpath('run'))
+""", status = 1, stderr = f"printed on failure\nFAILED test of {run_env.workpath('run')}\n\tat line 8 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def test1(self):
self.run()
@@ -903,10 +896,10 @@ def test1(self):
def test2(self):
test1(self)
test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = ''))
-""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 6 of <stdin> (test1)\n\tfrom line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n" % run_env.workpath('run'))
+""", status = 1, stderr = f"FAILED test of {run_env.workpath('run')}\n\tat line 6 of <stdin> (test1)\n\tfrom line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def test1(self):
self.run()
@@ -914,7 +907,7 @@ def test1(self):
def test2(self):
test1(self)
test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = ''))
-""" % self.orig_cwd, status = 1, stderr = "FAILED test of %s\n\tat line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n" % run_env.workpath('run'))
+""", status = 1, stderr = f"FAILED test of {run_env.workpath('run')}\n\tat line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n")
@@ -1062,12 +1055,12 @@ class match_re_dotall_TestCase(TestCmdTestCase):
# Everything before this prepared our "source directory."
# Now do the real test.
try:
- script_input = """import sys
-sys.path = ['%s'] + sys.path
+ script_input = f"""import sys
+sys.path = [r'{cwd}'] + sys.path
import TestCmd
assert TestCmd.match_re_dotall("abcde", r"a.*(e")
sys.exit(0)
-""" % cwd
+"""
stdout, stderr, status = self.call_python(script_input)
assert status == 1, status
expect1 = "Regular expression error in '^a.*(e$': missing )"
@@ -1135,12 +1128,14 @@ class match_re_TestCase(TestCmdTestCase):
# Everything before this prepared our "source directory."
# Now do the real test.
try:
- script_input = """import sys
-sys.path = ['%s'] + sys.path
+ script_input = f"""import sys
+sys.path = [r'{cwd}'] + sys.path
import TestCmd
-assert TestCmd.match_re("abcde\\n", "a.*(e\\n")
+assert TestCmd.match_re("abcde\
+", "a.*(e\
+")
sys.exit(0)
-""" % cwd
+"""
stdout, stderr, status = self.call_python(script_input)
assert status == 1, status
expect1 = "Regular expression error in '^a.*(e$': missing )"
@@ -1345,40 +1340,40 @@ sys.stderr.write("run: STDERR\\n")
os.chdir(run_env.workdir)
# Everything before this prepared our "source directory."
# Now do the real test.
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
TestCmd.no_result(condition = 1)
-""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test at line 4 of <stdin>\n")
+""", status = 2, stderr = "NO RESULT for test at line 4 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')
test.run()
test.no_result(condition = (test.status == 0))
-""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 6 of <stdin>\n" % run_env.workpath('run'))
+""", status = 2, stderr = f"NO RESULT for test of {run_env.workpath('run')}\n\tat line 6 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', description = 'xyzzy', workdir = '')
test.run()
test.no_result(condition = (test.status == 0))
-""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s [xyzzy]\n\tat line 6 of <stdin>\n" % run_env.workpath('run'))
+""", status = 2, stderr = f"NO RESULT for test of {run_env.workpath('run')} [xyzzy]\n\tat line 6 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')
test.run()
def xxx():
sys.stderr.write("printed on no result\\n")
test.no_result(condition = (test.status == 0), function = xxx)
-""" % self.orig_cwd, status = 2, stderr = "printed on no result\nNO RESULT for test of %s\n\tat line 8 of <stdin>\n" % run_env.workpath('run'))
+""", status = 2, stderr = f"printed on no result\nNO RESULT for test of {run_env.workpath('run')}\n\tat line 8 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def test1(self):
self.run()
@@ -1386,10 +1381,10 @@ def test1(self):
def test2(self):
test1(self)
test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = ''))
-""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 6 of <stdin> (test1)\n\tfrom line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n" % run_env.workpath('run'))
+""", status = 2, stderr = f"NO RESULT for test of {run_env.workpath('run')}\n\tat line 6 of <stdin> (test1)\n\tfrom line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
def test1(self):
self.run()
@@ -1397,7 +1392,7 @@ def test1(self):
def test2(self):
test1(self)
test2(TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = ''))
-""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test of %s\n\tat line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n" % run_env.workpath('run'))
+""", status = 2, stderr = f"NO RESULT for test of {run_env.workpath('run')}\n\tat line 8 of <stdin> (test2)\n\tfrom line 9 of <stdin>\n")
@@ -1412,29 +1407,29 @@ sys.stderr.write("run: STDERR\\n")
os.chdir(run_env.workdir)
# Everything before this prepared our "source directory."
# Now do the real test.
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
TestCmd.pass_test(condition = 1)
-""" % self.orig_cwd, stderr = "PASSED\n")
+""", stderr = "PASSED\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')
test.run()
test.pass_test(condition = (test.status == 0))
-""" % self.orig_cwd, stderr = "PASSED\n")
+""", stderr = "PASSED\n")
- self.popen_python("""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '')
test.run()
def brag():
sys.stderr.write("printed on success\\n")
test.pass_test(condition = (test.status == 0), function = brag)
-""" % self.orig_cwd, stderr = "printed on success\nPASSED\n")
+""", stderr = "printed on success\nPASSED\n")
# TODO(sgk): SHOULD ALSO TEST FAILURE CONDITIONS
@@ -1453,7 +1448,7 @@ class preserve_TestCase(TestCmdTestCase):
else:
test.cleanup()
o = io.getvalue()
- assert o == stdout, "o = `%s', stdout = `%s'" % (o, stdout)
+ assert o == stdout, f"o = `{o}', stdout = `{stdout}'"
finally:
sys.stdout = save
@@ -1474,7 +1469,7 @@ class preserve_TestCase(TestCmdTestCase):
try:
test.write('file2', "Test file #2\n")
test.preserve('pass_test')
- cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'pass_test', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
cleanup_test(test, 'fail_test')
assert not os.path.exists(wdir)
@@ -1488,7 +1483,7 @@ class preserve_TestCase(TestCmdTestCase):
try:
test.write('file3', "Test file #3\n")
test.preserve('fail_test')
- cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'fail_test', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
cleanup_test(test, 'pass_test')
assert not os.path.exists(wdir)
@@ -1502,9 +1497,9 @@ class preserve_TestCase(TestCmdTestCase):
try:
test.write('file4', "Test file #4\n")
test.preserve('fail_test', 'no_result')
- cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'fail_test', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
- cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'no_result', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
cleanup_test(test, 'pass_test')
assert not os.path.exists(wdir)
@@ -1517,11 +1512,11 @@ class preserve_TestCase(TestCmdTestCase):
wdir = test.workdir
try:
test.preserve()
- cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'pass_test', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
- cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'fail_test', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
- cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir)
+ cleanup_test(test, 'no_result', f"Preserved directory {wdir}\n")
assert os.path.isdir(wdir)
finally:
if os.path.exists(wdir):
@@ -1626,7 +1621,7 @@ class rmdir_TestCase(TestCmdTestCase):
else:
raise Exception("did not catch expected SConsEnvironmentError")
- assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o
+ assert os.path.isdir(s_d_o), f"{s_d_o} is gone?"
try:
test.rmdir(['sub'])
@@ -1635,21 +1630,21 @@ class rmdir_TestCase(TestCmdTestCase):
else:
raise Exception("did not catch expected SConsEnvironmentError")
- assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o
+ assert os.path.isdir(s_d_o), f"{s_d_o} is gone?"
test.rmdir(['sub', 'dir', 'one'])
- assert not os.path.exists(s_d_o), "%s exists?" % s_d_o
- assert os.path.isdir(s_d), "%s is gone?" % s_d
+ assert not os.path.exists(s_d_o), f"{s_d_o} exists?"
+ assert os.path.isdir(s_d), f"{s_d} is gone?"
test.rmdir(['sub', 'dir'])
- assert not os.path.exists(s_d), "%s exists?" % s_d
- assert os.path.isdir(s), "%s is gone?" % s
+ assert not os.path.exists(s_d), f"{s_d} exists?"
+ assert os.path.isdir(s), f"{s} is gone?"
test.rmdir('sub')
- assert not os.path.exists(s), "%s exists?" % s
+ assert not os.path.exists(s), f"{s} exists?"
@@ -1854,7 +1849,7 @@ class run_verbose_TestCase(TestCmdTestCase):
o = sys.stdout.getvalue()
assert o == '', o
e = sys.stderr.getvalue()
- expect = 'python "%s" "arg1 arg2"\n' % t.script_path
+ expect = f'python "{t.script_path}" "arg1 arg2\"\n'
assert expect == e, (expect, e)
testx = TestCmd.TestCmd(program = t.scriptx,
@@ -1863,7 +1858,7 @@ class run_verbose_TestCase(TestCmdTestCase):
with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr:
testx.run(arguments = ['arg1 arg2'])
- expect = '"%s" "arg1 arg2"\n' % t.scriptx_path
+ expect = f'"{t.scriptx_path}" "arg1 arg2\"\n'
o = sys.stdout.getvalue()
assert o == '', o
e = sys.stderr.getvalue()
@@ -1907,7 +1902,7 @@ class run_verbose_TestCase(TestCmdTestCase):
o = sys.stdout.getvalue()
assert expect == o, (expect, o)
- expect = 'python "%s" "arg1 arg2"\n' % t.script_path
+ expect = f'python "{t.script_path}" "arg1 arg2\"\n'
e = sys.stderr.getvalue()
assert e == expect, (e, expect)
@@ -1926,7 +1921,7 @@ class run_verbose_TestCase(TestCmdTestCase):
o = sys.stdout.getvalue()
assert expect == o, (expect, o)
- expect = '"%s" "arg1 arg2"\n' % t.scriptx_path
+ expect = f'"{t.scriptx_path}" "arg1 arg2\"\n'
e = sys.stderr.getvalue()
assert e == expect, (e, expect)
@@ -1947,7 +1942,7 @@ class run_verbose_TestCase(TestCmdTestCase):
assert expect == o, (expect, o)
e = sys.stderr.getvalue()
- expect = 'python "%s" "arg1 arg2"\n' % t.scriptout_path
+ expect = f'python "{t.scriptout_path}" "arg1 arg2\"\n'
assert e == expect, (e, expect)
test = TestCmd.TestCmd(program = t.scriptout,
@@ -1966,7 +1961,7 @@ class run_verbose_TestCase(TestCmdTestCase):
assert expect == o, (expect, o)
e = sys.stderr.getvalue()
- expect = 'python "%s" "arg1 arg2"\n' % t.scriptout_path
+ expect = f'python "{t.scriptout_path}" "arg1 arg2\"\n'
assert e == expect, (e, expect)
# Test letting TestCmd() pick up verbose = 2 from the environment.
@@ -1988,7 +1983,7 @@ class run_verbose_TestCase(TestCmdTestCase):
o = sys.stdout.getvalue()
assert expect == o, (expect, o)
- expect = 'python "%s" "arg1 arg2"\n' % t.script_path
+ expect = f'python "{t.script_path}" "arg1 arg2\"\n'
e = sys.stderr.getvalue()
assert e == expect, (e, expect)
@@ -2006,7 +2001,7 @@ class run_verbose_TestCase(TestCmdTestCase):
o = sys.stdout.getvalue()
assert expect == o, (expect, o)
- expect = '"%s" "arg1 arg2"\n' % t.scriptx_path
+ expect = f'"{t.scriptx_path}" "arg1 arg2\"\n'
e = sys.stderr.getvalue()
assert e == expect, (e, expect)
@@ -2024,7 +2019,7 @@ class run_verbose_TestCase(TestCmdTestCase):
o = sys.stdout.getvalue()
assert o == '', o
e = sys.stderr.getvalue()
- expect = 'python "%s" "arg1 arg2"\n' % t.script_path
+ expect = f'python "{t.script_path}" "arg1 arg2\"\n'
assert expect == e, (expect, e)
testx = TestCmd.TestCmd(program = t.scriptx,
@@ -2033,7 +2028,7 @@ class run_verbose_TestCase(TestCmdTestCase):
with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr:
testx.run(arguments = ['arg1 arg2'])
- expect = '"%s" "arg1 arg2"\n' % t.scriptx_path
+ expect = f'"{t.scriptx_path}" "arg1 arg2\"\n'
o = sys.stdout.getvalue()
assert o == '', o
e = sys.stderr.getvalue()
@@ -2050,21 +2045,20 @@ class run_verbose_TestCase(TestCmdTestCase):
class set_diff_function_TestCase(TestCmdTestCase):
def test_set_diff_function(self):
"""Test set_diff_function()"""
- self.popen_python(r"""import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(fr"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd()
test.diff("a\n", "a\n")
test.set_diff_function('diff_re')
test.diff(".\n", "a\n")
sys.exit(0)
-""" % self.orig_cwd)
+""")
def test_set_diff_function_stdout(self):
"""Test set_diff_function(): stdout"""
- self.popen_python("""\
-import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd()
print("diff:")
@@ -2077,7 +2071,7 @@ test.diff(".\\n", "a\\n")
print("diff_stdout:")
test.diff_stdout(".\\n", "a\\n")
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
diff:
diff_stdout:
@@ -2091,9 +2085,8 @@ diff_stdout:
def test_set_diff_function_stderr(self):
"""Test set_diff_function(): stderr """
- self.popen_python("""\
-import sys
-sys.path = ['%s'] + sys.path
+ self.popen_python(f"""import sys
+sys.path = [r'{self.orig_cwd}'] + sys.path
import TestCmd
test = TestCmd.TestCmd()
print("diff:")
@@ -2106,7 +2099,7 @@ test.diff(".\\n", "a\\n")
print("diff_stderr:")
test.diff_stderr(".\\n", "a\\n")
sys.exit(0)
-""" % self.orig_cwd,
+""",
stdout="""\
diff:
diff_stderr:
@@ -2173,13 +2166,13 @@ class sleep_TestCase(TestCmdTestCase):
test.sleep()
end = time.perf_counter()
diff = end - start
- assert diff > 0.9, "only slept %f seconds (start %f, end %f), not default" % (diff, start, end)
+ assert diff > 0.9, f"only slept {diff:f} seconds (start {start:f}, end {end:f}), not default"
start = time.perf_counter()
test.sleep(3)
end = time.perf_counter()
diff = end - start
- assert diff > 2.9, "only slept %f seconds (start %f, end %f), not 3" % (diff, start, end)
+ assert diff > 2.9, f"only slept {diff:f} seconds (start {start:f}, end {end:f}), not 3"
@@ -2208,7 +2201,8 @@ sys.stderr.write("run2 STDERR second line\\n")
except IndexError:
pass
else:
- raise IndexError("got unexpected output:\n" + output)
+ if output is not None:
+ raise IndexError(f"got unexpected output:\n{output}")
test.program_set('run1')
test.run(arguments = 'foo bar')
test.program_set('run2')
@@ -2261,6 +2255,12 @@ class command_args_TestCase(TestCmdTestCase):
expect = ['PYTHON', default_prog, 'arg3', 'arg4']
assert r == expect, (expect, r)
+ # Test arguments = dict
+ r = test.command_args(interpreter='PYTHON', arguments={'VAR1':'1'})
+ expect = ['PYTHON', default_prog, 'VAR1=1']
+ assert r == expect, (expect, r)
+
+
test.interpreter_set('default_python')
r = test.command_args()
@@ -2287,8 +2287,7 @@ class start_TestCase(TestCmdTestCase):
t.recv_script = 'script_recv'
t.recv_script_path = t.run_env.workpath(t.sub_dir, t.recv_script)
t.recv_out_path = t.run_env.workpath('script_recv.out')
- text = """\
-import os
+ text = f"""import os
import sys
class Unbuffered:
@@ -2305,19 +2304,26 @@ sys.stderr = Unbuffered(sys.stderr)
sys.stdout.write('script_recv: STDOUT\\n')
sys.stderr.write('script_recv: STDERR\\n')
-with open(r'%s', 'wb') as logfp:
- while 1:
+with open(r'{t.recv_out_path}', 'w') as logfp:
+ while True:
line = sys.stdin.readline()
if not line:
break
logfp.write('script_recv: ' + line)
sys.stdout.write('script_recv: STDOUT: ' + line)
sys.stderr.write('script_recv: STDERR: ' + line)
-""" % t.recv_out_path
+"""
t.run_env.write(t.recv_script_path, text)
os.chmod(t.recv_script_path, 0o644) # XXX UNIX-specific
return t
+ def _cleanup(self, popen):
+ """Quiet Python ResourceWarning after wait()"""
+ if popen.stdout:
+ popen.stdout.close()
+ if popen.stderr:
+ popen.stderr.close()
+
def test_start(self):
"""Test start()"""
@@ -2337,6 +2343,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir,
repr([]))
p.wait()
+ self._cleanup(p)
p = test.start(arguments='arg1 arg2 arg3')
self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir,
@@ -2344,6 +2351,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir,
repr(['arg1', 'arg2', 'arg3']))
p.wait()
+ self._cleanup(p)
p = test.start(program=t.scriptx, arguments='foo')
self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir,
@@ -2351,6 +2359,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir,
repr(['foo']))
p.wait()
+ self._cleanup(p)
p = test.start(program=t.script1, interpreter=['python', '-x'])
self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir,
@@ -2358,9 +2367,11 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir,
repr([]))
p.wait()
+ self._cleanup(p)
p = test.start(program='no_script', interpreter='python')
status = p.wait()
+ self._cleanup(p)
assert status is not None, status
try:
@@ -2372,6 +2383,7 @@ with open(r'%s', 'wb') as logfp:
pass
else:
status = p.wait()
+ self._cleanup(p)
# Python versions that use os.popen3() or the Popen3
# class run things through the shell, which just returns
# a non-zero exit status.
@@ -2387,6 +2399,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir,
repr([]))
p.wait()
+ self._cleanup(p)
p = testx.start(arguments='foo bar')
self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir,
@@ -2394,6 +2407,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir,
repr(['foo', 'bar']))
p.wait()
+ self._cleanup(p)
p = testx.start(program=t.script, interpreter='python', arguments='bar')
self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir,
@@ -2401,6 +2415,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir,
repr(['bar']))
p.wait()
+ self._cleanup(p)
p = testx.start(program=t.script1, interpreter=('python', '-x'))
self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir,
@@ -2408,6 +2423,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir,
repr([]))
p.wait()
+ self._cleanup(p)
s = os.path.join('.', t.scriptx)
p = testx.start(program=[s])
@@ -2416,6 +2432,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir,
repr([]))
p.wait()
+ self._cleanup(p)
try:
testx.start(program='no_program')
@@ -2431,6 +2448,7 @@ with open(r'%s', 'wb') as logfp:
# we can wait() for it.
try:
p = p.wait()
+ self._cleanup(p)
except OSError:
pass
@@ -2444,6 +2462,7 @@ with open(r'%s', 'wb') as logfp:
self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir,
repr([]))
p.wait()
+ self._cleanup(p)
finally:
os.chdir(t.orig_cwd)
@@ -2582,19 +2601,19 @@ script_recv: STDERR: input
p.stdin.write(to_bytes(input))
p.stdin.close()
p.wait()
- with open(t.recv_out_path, 'rb') as f:
+ with open(t.recv_out_path, 'r') as f:
result = to_str(f.read())
- expect = 'script_recv: ' + input
- assert result == expect, repr(result)
+ expect = f"script_recv: {input}"
+ assert result == expect, f"Result:[{result}] should match\nExpected:[{expect}]"
p = test.start(stdin=1)
input = 'send() input to the receive script\n'
p.send(input)
p.stdin.close()
p.wait()
- with open(t.recv_out_path, 'rb') as f:
+ with open(t.recv_out_path, 'r') as f:
result = to_str(f.read())
- expect = 'script_recv: ' + input
+ expect = f"script_recv: {input}"
assert result == expect, repr(result)
finally:
@@ -2654,7 +2673,7 @@ script_recv: STDERR: input to the receive script
assert stderr == expect_stderr, stderr
with open(t.recv_out_path, 'rb') as f:
result = f.read()
- expect = ('script_recv: ' + input) * 2
+ expect = f"script_recv: {input}" * 2
assert result == expect, (result, stdout, stderr)
finally:
@@ -2693,13 +2712,15 @@ class stdout_TestCase(TestCmdTestCase):
def test_stdout(self):
"""Test stdout()"""
run_env = TestCmd.TestCmd(workdir = '')
- run_env.write('run1', """import sys
+ run_env.write('run1', """\
+import sys
sys.stdout.write("run1 STDOUT %s\\n" % sys.argv[1:])
sys.stdout.write("run1 STDOUT second line\\n")
sys.stderr.write("run1 STDERR %s\\n" % sys.argv[1:])
sys.stderr.write("run1 STDERR second line\\n")
""")
- run_env.write('run2', """import sys
+ run_env.write('run2', """\
+import sys
sys.stdout.write("run2 STDOUT %s\\n" % sys.argv[1:])
sys.stdout.write("run2 STDOUT second line\\n")
sys.stderr.write("run2 STDERR %s\\n" % sys.argv[1:])
@@ -2711,7 +2732,7 @@ sys.stderr.write("run2 STDERR second line\\n")
test = TestCmd.TestCmd(interpreter = 'python', workdir = '')
output = test.stdout()
if output is not None:
- raise IndexError("got unexpected output:\n\t`%s'\n" % output)
+ raise IndexError(f"got unexpected output:\n\t`{output}'\n")
test.program_set('run1')
test.run(arguments = 'foo bar')
test.program_set('run2')
@@ -2766,11 +2787,9 @@ class subdir_TestCase(TestCmdTestCase):
class symlink_TestCase(TestCmdTestCase):
+ @unittest.skipIf(sys.platform == 'win32', "Skip symlink test on win32")
def test_symlink(self):
"""Test symlink()"""
- try: os.symlink
- except AttributeError: return
-
test = TestCmd.TestCmd(workdir = '', subdir = 'foo')
wdir_file1 = os.path.join(test.workdir, 'file1')
wdir_target1 = os.path.join(test.workdir, 'target1')
@@ -2841,7 +2860,7 @@ sys.exit(0)
class timeout_TestCase(TestCmdTestCase):
def test_initialization(self):
- """Test initialization timeout"""
+ """Test initializating a TestCmd with a timeout"""
test = TestCmd.TestCmd(workdir='', timeout=2)
test.write('sleep.py', timeout_script)
@@ -2875,40 +2894,39 @@ class timeout_TestCase(TestCmdTestCase):
test = TestCmd.TestCmd(workdir='', timeout=8)
test.write('sleep.py', timeout_script)
- test.run([sys.executable, test.workpath('sleep.py'), '2'],
- timeout=4)
+ test.run([sys.executable, test.workpath('sleep.py'), '2'], timeout=4)
assert test.stderr() == '', test.stderr()
assert test.stdout() == 'sleeping 2\nslept 2\n', test.stdout()
- test.run([sys.executable, test.workpath('sleep.py'), '6'],
- timeout=4)
+ test.run([sys.executable, test.workpath('sleep.py'), '6'], timeout=4)
assert test.stderr() == '', test.stderr()
assert test.stdout() == 'sleeping 6\n', test.stdout()
- def test_set_timeout(self):
- """Test set_timeout()"""
- test = TestCmd.TestCmd(workdir='', timeout=2)
- test.write('sleep.py', timeout_script)
-
- test.run([sys.executable, test.workpath('sleep.py'), '4'])
- assert test.stderr() == '', test.stderr()
- assert test.stdout() == 'sleeping 4\n', test.stdout()
-
- test.set_timeout(None)
-
- test.run([sys.executable, test.workpath('sleep.py'), '4'])
- assert test.stderr() == '', test.stderr()
- assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout()
-
- test.set_timeout(6)
-
- test.run([sys.executable, test.workpath('sleep.py'), '4'])
- assert test.stderr() == '', test.stderr()
- assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout()
-
- test.run([sys.executable, test.workpath('sleep.py'), '8'])
- assert test.stderr() == '', test.stderr()
- assert test.stdout() == 'sleeping 8\n', test.stdout()
+ # This method has been removed
+ #def test_set_timeout(self):
+ # """Test set_timeout()"""
+ # test = TestCmd.TestCmd(workdir='', timeout=2)
+ # test.write('sleep.py', timeout_script)
+ #
+ # test.run([sys.executable, test.workpath('sleep.py'), '4'])
+ # assert test.stderr() == '', test.stderr()
+ # assert test.stdout() == 'sleeping 4\n', test.stdout()
+ #
+ # test.set_timeout(None)
+ #
+ # test.run([sys.executable, test.workpath('sleep.py'), '4'])
+ # assert test.stderr() == '', test.stderr()
+ # assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout()
+ #
+ # test.set_timeout(6)
+ #
+ # test.run([sys.executable, test.workpath('sleep.py'), '4'])
+ # assert test.stderr() == '', test.stderr()
+ # assert test.stdout() == 'sleeping 4\nslept 4\n', test.stdout()
+ #
+ # test.run([sys.executable, test.workpath('sleep.py'), '8'])
+ # assert test.stderr() == '', test.stderr()
+ # assert test.stdout() == 'sleeping 8\n', test.stdout()
@@ -3103,17 +3121,14 @@ class workpath_TestCase(TestCmdTestCase):
assert wpath == os.path.join(test.workdir, 'foo', 'bar')
-
class readable_TestCase(TestCmdTestCase):
+ @unittest.skipIf(sys.platform == 'win32', "Skip permission fiddling on win32")
def test_readable(self):
"""Test readable()"""
test = TestCmd.TestCmd(workdir = '', subdir = 'foo')
test.write('file1', "Test file #1\n")
test.write(['foo', 'file2'], "Test file #2\n")
-
- try: symlink = os.symlink
- except AttributeError: pass
- else: symlink('no_such_file', test.workpath('dangling_symlink'))
+ os.symlink('no_such_file', test.workpath('dangling_symlink'))
test.readable(test.workdir, 0)
# XXX skip these tests if euid == 0?
@@ -3146,15 +3161,13 @@ class readable_TestCase(TestCmdTestCase):
class writable_TestCase(TestCmdTestCase):
+ @unittest.skipIf(sys.platform == 'win32', "Skip permission fiddling on win32")
def test_writable(self):
"""Test writable()"""
test = TestCmd.TestCmd(workdir = '', subdir = 'foo')
test.write('file1', "Test file #1\n")
test.write(['foo', 'file2'], "Test file #2\n")
-
- try: symlink = os.symlink
- except AttributeError: pass
- else: symlink('no_such_file', test.workpath('dangling_symlink'))
+ os.symlink('no_such_file', test.workpath('dangling_symlink'))
test.writable(test.workdir, 0)
# XXX skip these tests if euid == 0?
@@ -3183,17 +3196,14 @@ class writable_TestCase(TestCmdTestCase):
assert not _is_writable(test.workpath('file1'))
-
class executable_TestCase(TestCmdTestCase):
+ @unittest.skipIf(sys.platform == 'win32', "Skip permission fiddling on win32")
def test_executable(self):
"""Test executable()"""
test = TestCmd.TestCmd(workdir = '', subdir = 'foo')
test.write('file1', "Test file #1\n")
test.write(['foo', 'file2'], "Test file #2\n")
-
- try: symlink = os.symlink
- except AttributeError: pass
- else: symlink('no_such_file', test.workpath('dangling_symlink'))
+ os.symlink('no_such_file', test.workpath('dangling_symlink'))
def make_executable(fname):
st = os.stat(fname)
@@ -3331,68 +3341,8 @@ class variables_TestCase(TestCmdTestCase):
assert stderr == "", stderr
-
if __name__ == "__main__":
- tclasses = [
- __init__TestCase,
- basename_TestCase,
- cleanup_TestCase,
- chmod_TestCase,
- combine_TestCase,
- command_args_TestCase,
- description_TestCase,
- diff_TestCase,
- diff_stderr_TestCase,
- diff_stdout_TestCase,
- exit_TestCase,
- fail_test_TestCase,
- interpreter_TestCase,
- match_TestCase,
- match_exact_TestCase,
- match_re_dotall_TestCase,
- match_re_TestCase,
- match_stderr_TestCase,
- match_stdout_TestCase,
- no_result_TestCase,
- pass_test_TestCase,
- preserve_TestCase,
- program_TestCase,
- read_TestCase,
- rmdir_TestCase,
- run_TestCase,
- run_verbose_TestCase,
- set_diff_function_TestCase,
- set_match_function_TestCase,
- sleep_TestCase,
- start_TestCase,
- stderr_TestCase,
- stdin_TestCase,
- stdout_TestCase,
- subdir_TestCase,
- symlink_TestCase,
- tempdir_TestCase,
- timeout_TestCase,
- unlink_TestCase,
- touch_TestCase,
- verbose_TestCase,
- workdir_TestCase,
- workdirs_TestCase,
- workpath_TestCase,
- writable_TestCase,
- write_TestCase,
- variables_TestCase,
- ]
- if sys.platform != 'win32':
- tclasses.extend([
- executable_TestCase,
- readable_TestCase,
- ])
- suite = unittest.TestSuite()
- for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
- suite.addTests([ tclass(n) for n in names ])
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ unittest.main()
# Local Variables:
# tab-width:4
diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py
index 1999f74..91d5332 100644
--- a/testing/framework/TestCommon.py
+++ b/testing/framework/TestCommon.py
@@ -319,10 +319,10 @@ class TestCommon(TestCmd):
file_contents = self.read(file, mode)
if not contains(file_contents, required, find):
- print("File `%s' does not contain required string." % file)
+ print(f"File `{file}' does not contain required string.")
print(self.banner('Required string '))
print(required)
- print(self.banner('%s contents ' % file))
+ print(self.banner(f'{file} contents '))
print(file_contents)
self.fail_test()
@@ -343,9 +343,9 @@ class TestCommon(TestCmd):
if not contains(output, input, find):
if title is None:
title = 'output'
- print('Missing expected input from {}:'.format(title))
+ print(f'Missing expected input from {title}:')
print(input)
- print(self.banner(title + ' '))
+ print(self.banner(f"{title} "))
print(output)
self.fail_test()
@@ -367,10 +367,10 @@ class TestCommon(TestCmd):
if missing:
if title is None:
title = 'output'
- sys.stdout.write("Missing expected lines from %s:\n" % title)
+ sys.stdout.write(f"Missing expected lines from {title}:\n")
for line in missing:
- sys.stdout.write(' ' + repr(line) + '\n')
- sys.stdout.write(self.banner(title + ' ') + '\n')
+ sys.stdout.write(f" {repr(line)}\n")
+ sys.stdout.write(f"{self.banner(f'{title} ')}\n")
sys.stdout.write(output)
self.fail_test()
@@ -394,10 +394,10 @@ class TestCommon(TestCmd):
if counts:
if title is None:
title = 'output'
- sys.stdout.write("Unexpected number of lines from %s:\n" % title)
+ sys.stdout.write(f"Unexpected number of lines from {title}:\n")
for line in counts:
- sys.stdout.write(' ' + repr(line) + ": found " + str(counts[line]) + '\n')
- sys.stdout.write(self.banner(title + ' ') + '\n')
+ sys.stdout.write(f" {repr(line)}: found {str(counts[line])}\n")
+ sys.stdout.write(f"{self.banner(f'{title} ')}\n")
sys.stdout.write(output)
self.fail_test()
@@ -418,10 +418,10 @@ class TestCommon(TestCmd):
if title is None:
title = 'output'
- sys.stdout.write("Missing any expected line from %s:\n" % title)
+ sys.stdout.write(f"Missing any expected line from {title}:\n")
for line in lines:
- sys.stdout.write(' ' + repr(line) + '\n')
- sys.stdout.write(self.banner(title + ' ') + '\n')
+ sys.stdout.write(f" {repr(line)}\n")
+ sys.stdout.write(f"{self.banner(f'{title} ')}\n")
sys.stdout.write(output)
self.fail_test()
@@ -461,15 +461,15 @@ class TestCommon(TestCmd):
if title is None:
title = 'output'
if missing:
- sys.stdout.write("Missing expected lines from %s:\n" % title)
+ sys.stdout.write(f"Missing expected lines from {title}:\n")
for line in missing:
- sys.stdout.write(' ' + repr(line) + '\n')
- sys.stdout.write(self.banner('Missing %s ' % title) + '\n')
+ sys.stdout.write(f" {repr(line)}\n")
+ sys.stdout.write(f"{self.banner(f'Missing {title} ')}\n")
if out:
- sys.stdout.write("Extra unexpected lines from %s:\n" % title)
+ sys.stdout.write(f"Extra unexpected lines from {title}:\n")
for line in out:
- sys.stdout.write(' ' + repr(line) + '\n')
- sys.stdout.write(self.banner('Extra %s ' % title) + '\n')
+ sys.stdout.write(f" {repr(line)}\n")
+ sys.stdout.write(f"{self.banner(f'Extra {title} ')}\n")
sys.stdout.flush()
self.fail_test()
@@ -523,20 +523,41 @@ class TestCommon(TestCmd):
except KeyboardInterrupt:
raise
except:
- print("Unexpected contents of `%s'" % file)
+ print(f"Unexpected contents of `{file}'")
self.diff(expect, file_contents, 'contents ')
raise
+ def must_match_file(self, file, golden_file, mode='rb', match=None, message=None, newline=None):
+ """Matches the contents of the specified file (first argument)
+ against the expected contents (second argument). The expected
+ contents are a list of lines or a string which will be split
+ on newlines.
+ """
+ file_contents = self.read(file, mode, newline)
+ golden_file_contents = self.read(golden_file, mode, newline)
+
+ if not match:
+ match = self.match
+
+ try:
+ self.fail_test(not match(to_str(file_contents), to_str(golden_file_contents)), message=message)
+ except KeyboardInterrupt:
+ raise
+ except:
+ print("Unexpected contents of `%s'" % file)
+ self.diff(golden_file_contents, file_contents, 'contents ')
+ raise
+
def must_not_contain(self, file, banned, mode = 'rb', find = None):
"""Ensures that the specified file doesn't contain the banned text.
"""
file_contents = self.read(file, mode)
if contains(file_contents, banned, find):
- print("File `%s' contains banned string." % file)
+ print(f"File `{file}' contains banned string.")
print(self.banner('Banned string '))
print(banned)
- print(self.banner('%s contents ' % file))
+ print(self.banner(f'{file} contents '))
print(file_contents)
self.fail_test()
@@ -559,10 +580,10 @@ class TestCommon(TestCmd):
if unexpected:
if title is None:
title = 'output'
- sys.stdout.write("Unexpected lines in %s:\n" % title)
+ sys.stdout.write(f"Unexpected lines in {title}:\n")
for line in unexpected:
- sys.stdout.write(' ' + repr(line) + '\n')
- sys.stdout.write(self.banner(title + ' ') + '\n')
+ sys.stdout.write(f" {repr(line)}\n")
+ sys.stdout.write(f"{self.banner(f'{title} ')}\n")
sys.stdout.write(output)
self.fail_test()
@@ -606,7 +627,7 @@ class TestCommon(TestCmd):
Exits FAILED if the file doesn't exist or is empty.
"""
if not (os.path.exists(file) or os.path.islink(file)):
- print("File doesn't exist: `%s'" % file)
+ print(f"File doesn't exist: `{file}'")
self.fail_test(file)
try:
@@ -615,7 +636,7 @@ class TestCommon(TestCmd):
fsize = 0
if fsize == 0:
- print("File is empty: `%s'" % file)
+ print(f"File is empty: `{file}'")
self.fail_test(file)
def must_not_be_writable(self, *files):
@@ -643,8 +664,8 @@ class TestCommon(TestCmd):
if _failed(self, status):
expect = ''
if status != 0:
- expect = " (expected %s)" % str(status)
- print("%s returned %s%s" % (self.program, _status(self), expect))
+ expect = f" (expected {str(status)})"
+ print(f"{self.program} returned {_status(self)}{expect}")
print(self.banner('STDOUT '))
print(actual_stdout)
print(self.banner('STDERR '))
@@ -676,7 +697,7 @@ class TestCommon(TestCmd):
"""
arguments = self.options_arguments(options, arguments)
try:
- return TestCmd.start(self, program, interpreter, arguments,
+ return super().start(program, interpreter, arguments,
universal_newlines, **kw)
except KeyboardInterrupt:
raise
@@ -692,7 +713,7 @@ class TestCommon(TestCmd):
except IndexError:
pass
cmd_args = self.command_args(program, interpreter, arguments)
- sys.stderr.write('Exception trying to execute: %s\n' % cmd_args)
+ sys.stderr.write(f'Exception trying to execute: {cmd_args}\n')
raise e
def finish(self, popen, stdout = None, stderr = '', status = 0, **kw):
@@ -713,7 +734,7 @@ class TestCommon(TestCmd):
command. A value of None means don't
test exit status.
"""
- TestCmd.finish(self, popen, **kw)
+ super().finish(popen, **kw)
match = kw.get('match', self.match)
self._complete(self.stdout(), stdout,
self.stderr(), stderr, status, match)
@@ -750,7 +771,7 @@ class TestCommon(TestCmd):
del kw['match']
except KeyError:
match = self.match
- TestCmd.run(self, **kw)
+ super().run(**kw)
self._complete(self.stdout(), stdout,
self.stderr(), stderr, status, match)
@@ -782,6 +803,8 @@ class TestCommon(TestCmd):
"""
if message:
sys.stdout.write(message)
+ if not message.endswith('\n'):
+ sys.stdout.write('\n')
sys.stdout.flush()
pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS')
if pass_skips in [None, 0, '0']:
@@ -794,6 +817,22 @@ class TestCommon(TestCmd):
# so this is an Aegis invocation; pass the test (exit 0).
self.pass_test()
+ @staticmethod
+ def detailed_diff(value, expect):
+ v_split = value.split('\n')
+ e_split = expect.split('\n')
+ if len(v_split) != len(e_split):
+ print(f"different number of lines:{len(v_split)} {len(e_split)}")
+
+ # breakpoint()
+ for v, e in zip(v_split, e_split):
+ # print("%s:%s"%(v,e))
+ if v != e:
+ print(f"\n[{v}]\n[{e}]")
+
+ return f"Expected:\n{expect}\nGot:\n{value}"
+
+
# Local Variables:
# tab-width:4
# indent-tabs-mode:nil
diff --git a/testing/framework/TestCommonTests.py b/testing/framework/TestCommonTests.py
index 03a5508..6d8c27f 100644
--- a/testing/framework/TestCommonTests.py
+++ b/testing/framework/TestCommonTests.py
@@ -19,14 +19,12 @@ Unit tests for the TestCommon.py module.
# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-__author__ = "Steven Knight <knight at baldmt dot com>"
-__revision__ = "TestCommonTests.py 1.3.D001 2010/06/03 12:58:27 knight"
-
import os
import re
import signal
import sys
import unittest
+from textwrap import dedent
# Strip the current directory so we get the right TestCommon.py module.
sys.path = sys.path[1:]
@@ -34,15 +32,9 @@ sys.path = sys.path[1:]
import TestCmd
import TestCommon
+# this used to be a custom function, now use the stdlib equivalent
def lstrip(s):
- lines = [ _.expandtabs() for _ in s.split('\n') ]
- if lines[0] == '':
- lines = lines[1:]
- spaces = len(re.match('^( *).*', lines[0]).group(1))
- if spaces:
- lines = [ l[spaces:] for l in lines ]
- return '\n'.join(lines)
-
+ return dedent(s)
expected_newline = '\\n'
@@ -52,17 +44,16 @@ def assert_display(expect, result, error=None):
expect = expect.pattern
except AttributeError:
pass
- result = [
+ display = [
'\n',
- 'EXPECTED'+('*'*80) + '\n',
+ f"{'EXPECTED: ':*<80}\n",
expect,
- 'GOT'+('*'*80) + '\n',
+ f"{'GOT: ':*<80}\n",
result,
- ('*'*80) + '\n',
+ '' if error is None else error,
+ f"{'':*<80}\n",
]
- if error:
- result.append(error)
- return ''.join(result)
+ return ''.join(display)
class TestCommonTestCase(unittest.TestCase):
@@ -355,7 +346,7 @@ class must_contain_TestCase(TestCommonTestCase):
""")
run_env.run(program=sys.executable, stdin=script)
stdout = run_env.stdout()
- assert stdout == expect, "got:\n%s\nexpected:\n%s"%(stdout, expect)
+ assert stdout == expect, f"got:\n{stdout}\nexpected:\n{expect}"
stderr = run_env.stderr()
assert stderr.find("FAILED") != -1, stderr
@@ -987,6 +978,7 @@ class must_exist_TestCase(TestCommonTestCase):
stderr = run_env.stderr()
assert stderr == "PASSED\n", stderr
+ @unittest.skipIf(sys.platform == 'win32', "Skip symlink test on win32")
def test_broken_link(self) :
"""Test must_exist(): exists but it is a broken link"""
run_env = self.run_env
@@ -1334,7 +1326,7 @@ class must_not_contain_TestCase(TestCommonTestCase):
""")
run_env.run(program=sys.executable, stdin=script)
stdout = run_env.stdout()
- assert stdout == expect, "\ngot:\n%s\nexpected:\n%s" % (stdout, expect)
+ assert stdout == expect, f"\ngot:\n{stdout}\nexpected:\n{expect}"
stderr = run_env.stderr()
assert stderr.find("FAILED") != -1, stderr
@@ -1359,7 +1351,7 @@ class must_not_contain_TestCase(TestCommonTestCase):
""")
run_env.run(program=sys.executable, stdin=script)
stdout = run_env.stdout()
- assert stdout == expect, "\ngot:\n%s\nexpected:\n%s" % (stdout, expect)
+ assert stdout == expect, f"\ngot:\n{stdout}\nexpected:\n{expect}"
stderr = run_env.stderr()
assert stderr.find("FAILED") != -1, stderr
@@ -1660,6 +1652,7 @@ class must_not_exist_TestCase(TestCommonTestCase):
stderr = run_env.stderr()
assert stderr == "PASSED\n", stderr
+ @unittest.skipIf(sys.platform == 'win32', "Skip symlink test on win32")
def test_existing_broken_link(self):
"""Test must_not_exist(): exists but it is a broken link"""
run_env = self.run_env
@@ -1840,7 +1833,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
tc.run(arguments = "arg1 arg2 arg3",
@@ -1868,7 +1861,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(fail_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run()
""")
@@ -1886,7 +1879,7 @@ class run_TestCase(TestCommonTestCase):
FAILED test of .*fail
\\tat line \\d+ of .*TestCommon\\.py \\(_complete\\)
\\tfrom line \\d+ of .*TestCommon\\.py \\(run\\)
- \\tfrom line \\d+ of <stdin>( \(<module>\))?
+ \\tfrom line \\d+ of <stdin>( \\(<module>\\))?
""")
expect_stderr = re.compile(expect_stderr, re.M)
@@ -1897,7 +1890,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(stderr_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run()
""")
@@ -1929,8 +1922,8 @@ class run_TestCase(TestCommonTestCase):
def raise_exception(*args, **kw):
raise TypeError("forced TypeError")
TestCmd.TestCmd.start = raise_exception
- tc = TestCommon(program='%(pass_script)s',
- interpreter='%(python)s',
+ tc = TestCommon(program=r'%(pass_script)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run()
""")
@@ -1939,23 +1932,24 @@ class run_TestCase(TestCommonTestCase):
STDOUT =========================================================================
None
STDERR =========================================================================
+ None
""")
- expect_stderr = lstrip("""\
- Exception trying to execute: \\[%s, '[^']*pass'\\]
- Traceback \\((innermost|most recent call) last\\):
- File "<stdin>", line \\d+, in (\\?|<module>)
- File "[^"]+TestCommon.py", line \\d+, in run
- TestCmd.run\\(self, \\*\\*kw\\)
- File "[^"]+TestCmd.py", line \\d+, in run
- .*
- File "[^"]+TestCommon.py", line \\d+, in start
- raise e
- File "[^"]+TestCommon.py", line \\d+, in start
- return TestCmd.start\\(self, program, interpreter, arguments,
- File "<stdin>", line \\d+, in raise_exception
- TypeError: forced TypeError
- """ % re.escape(repr(sys.executable)))
+ expect_stderr = lstrip(
+ fr"""Exception trying to execute: \[{re.escape(repr(sys.executable))}, '[^']*pass'\]
+Traceback \(most recent call last\):
+ File "<stdin>", line \d+, in (\?|<module>)
+ File "[^"]+TestCommon.py", line \d+, in run
+ super\(\).run\(\*\*kw\)
+ File "[^"]+TestCmd.py", line \d+, in run
+ p = self.start\(program=program,
+(?:\s*\^*\s)? File \"[^\"]+TestCommon.py\", line \d+, in start
+ raise e
+ File "[^"]+TestCommon.py", line \d+, in start
+ return super\(\).start\(program, interpreter, arguments,
+(?:\s*\^*\s)? File \"<stdin>\", line \d+, in raise_exception
+TypeError: forced TypeError
+""")
expect_stderr = re.compile(expect_stderr, re.M)
self.run_execution_test(script, expect_stdout, expect_stderr)
@@ -1966,7 +1960,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(stderr_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run(stderr = None)
""")
@@ -1980,7 +1974,7 @@ class run_TestCase(TestCommonTestCase):
def my_match_exact(actual, expect): return actual == expect
from TestCommon import TestCommon, match_re_dotall
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_re_dotall)
tc.run(arguments = "arg1 arg2 arg3",
@@ -1997,7 +1991,7 @@ class run_TestCase(TestCommonTestCase):
def my_match_exact(actual, expect): return actual == expect
from TestCommon import TestCommon, match_re_dotall
tc = TestCommon(program=r'%(stderr_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_re_dotall)
tc.run(arguments = "arg1 arg2 arg3",
@@ -2013,7 +2007,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(fail_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run(status = 1)
""")
@@ -2026,7 +2020,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
tc.run(stdout = r"%(pass_script)s: STDOUT: []" + "\\n")
@@ -2040,7 +2034,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(stderr_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
tc.run(stderr = r"%(stderr_script)s: STDERR: []" + "\\n")
@@ -2054,7 +2048,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run(status = 1)
""")
@@ -2072,7 +2066,7 @@ class run_TestCase(TestCommonTestCase):
FAILED test of .*pass
\\tat line \\d+ of .*TestCommon\\.py \\(_complete\\)
\\tfrom line \\d+ of .*TestCommon\\.py \\(run\\)
- \\tfrom line \\d+ of <stdin>( \(<module>\))?
+ \\tfrom line \\d+ of <stdin>( \\(<module>\\))?
""")
expect_stderr = re.compile(expect_stderr, re.M)
@@ -2084,7 +2078,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(fail_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run(status = 2)
""")
@@ -2102,7 +2096,7 @@ class run_TestCase(TestCommonTestCase):
FAILED test of .*fail
\\tat line \\d+ of .*TestCommon\\.py \\(_complete\\)
\\tfrom line \\d+ of .*TestCommon\\.py \\(run\\)
- \\tfrom line \\d+ of <stdin>( \(<module>\))?
+ \\tfrom line \\d+ of <stdin>( \\(<module>\\))?
""")
expect_stderr = re.compile(expect_stderr, re.M)
@@ -2114,7 +2108,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run(stdout = "Not found\\n")
""")
@@ -2134,7 +2128,7 @@ class run_TestCase(TestCommonTestCase):
FAILED test of .*pass
\\tat line \\d+ of .*TestCommon\\.py \\(_complete\\)
\\tfrom line \\d+ of .*TestCommon\\.py \\(run\\)
- \\tfrom line \\d+ of <stdin>( \(<module>\))?
+ \\tfrom line \\d+ of <stdin>( \\(<module>\\))?
""")
expect_stderr = re.compile(expect_stderr, re.M)
@@ -2146,7 +2140,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(stderr_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run(stderr = "Not found\\n")
""")
@@ -2168,7 +2162,7 @@ class run_TestCase(TestCommonTestCase):
FAILED test of .*stderr
\\tat line \\d+ of .*TestCommon\\.py \\(_complete\\)
\\tfrom line \\d+ of .*TestCommon\\.py \\(run\\)
- \\tfrom line \\d+ of <stdin>( \(<module>\))?
+ \\tfrom line \\d+ of <stdin>( \\(<module>\\))?
""")
expect_stderr = re.compile(expect_stderr, re.M)
@@ -2180,7 +2174,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
tc.run(options = "opt1 opt2 opt3",
@@ -2195,7 +2189,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
tc.run(options = "opt1 opt2 opt3",
@@ -2217,16 +2211,16 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon
tc = TestCommon(program=r'%(signal_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='')
tc.run()
""")
- self.SIGTERM = int(signal.SIGTERM)
+ self.SIGTERM = f"{'' if sys.platform == 'win32' else '-'}{signal.SIGTERM}"
# Script returns the signal value as a negative number.
expect_stdout = lstrip("""\
- %(signal_script)s returned -%(SIGTERM)s
+ %(signal_script)s returned %(SIGTERM)s
STDOUT =========================================================================
STDERR =========================================================================
@@ -2249,7 +2243,7 @@ class run_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(stdin_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir='',
match=match_exact)
expect_stdout = r"%(stdin_script)s: STDOUT: 'input'" + "\\n"
@@ -2277,7 +2271,7 @@ class start_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
p = tc.start(options = "opt1 opt2 opt3")
@@ -2293,7 +2287,7 @@ class start_TestCase(TestCommonTestCase):
script = lstrip("""\
from TestCommon import TestCommon, match_exact
tc = TestCommon(program=r'%(pass_script)s',
- interpreter='%(python)s',
+ interpreter=r'%(python)s',
workdir="",
match=match_exact)
p = tc.start(options = "opt1 opt2 opt3",
@@ -2389,13 +2383,13 @@ class variables_TestCase(TestCommonTestCase):
]
script = "import TestCommon\n" + \
- '\n'.join([ "print(TestCommon.%s)\n" % v for v in variables ])
+ '\n'.join([f"print(TestCommon.{v})\n" for v in variables])
run_env.run(program=sys.executable, stdin=script)
stderr = run_env.stderr()
assert stderr == "", stderr
script = "from TestCommon import *\n" + \
- '\n'.join([ "print(%s)" % v for v in variables ])
+ '\n'.join([f"print({v})" for v in variables])
run_env.run(program=sys.executable, stdin=script)
stderr = run_env.stderr()
assert stderr == "", stderr
@@ -2403,36 +2397,8 @@ class variables_TestCase(TestCommonTestCase):
if __name__ == "__main__":
- tclasses = [
- __init__TestCase,
- banner_TestCase,
- must_be_writable_TestCase,
- must_contain_TestCase,
- must_contain_all_lines_TestCase,
- must_contain_any_line_TestCase,
- must_contain_exactly_lines_TestCase,
- must_contain_lines_TestCase,
- must_exist_TestCase,
- must_exist_one_of_TestCase,
- must_match_TestCase,
- must_not_be_writable_TestCase,
- must_not_contain_TestCase,
- must_not_contain_any_line_TestCase,
- must_not_contain_lines_TestCase,
- must_not_exist_TestCase,
- must_not_exist_any_of_TestCase,
- must_not_be_empty_TestCase,
- run_TestCase,
- start_TestCase,
- skip_test_TestCase,
- variables_TestCase,
- ]
- suite = unittest.TestSuite()
- for tclass in tclasses:
- names = unittest.getTestCaseNames(tclass, 'test_')
- suite.addTests([ tclass(n) for n in names ])
- if not unittest.TextTestRunner().run(suite).wasSuccessful():
- sys.exit(1)
+ unittest.main()
+
# Local Variables:
# tab-width:4
diff --git a/testing/framework/TestRuntest.py b/testing/framework/TestRuntest.py
index 9368953..378f441 100644
--- a/testing/framework/TestRuntest.py
+++ b/testing/framework/TestRuntest.py
@@ -51,11 +51,7 @@ __all__.extend(
]
)
-if re.search(r'\s', python):
- pythonstring = _python_
-else:
- pythonstring = python
-pythonstring = pythonstring.replace('\\', '\\\\')
+pythonstring = python
pythonflags = ''
failing_test_template = """\
diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py
index b8c729e..469f6c1 100644
--- a/testing/framework/TestSCons.py
+++ b/testing/framework/TestSCons.py
@@ -44,7 +44,7 @@ import zipfile
from collections import namedtuple
from TestCommon import *
-from TestCommon import __all__
+from TestCommon import __all__, _python_
from SCons.Util import get_hash_format, get_current_hash_algorithm_used
from TestCmd import Popen
@@ -55,20 +55,14 @@ from TestCmd import PIPE
# here provides some independent verification that what we packaged
# conforms to what we expect.
-default_version = '4.3.1ayyyymmdd'
+default_version = '4.4.1ayyyymmdd'
# TODO: these need to be hand-edited when there are changes
python_version_unsupported = (3, 6, 0)
python_version_deprecated = (3, 6, 0)
python_version_supported_str = "3.6.0" # str of lowest non-deprecated version
-# In the checked-in source, the value of SConsVersion in the following
-# line must remain "__ VERSION __" (without the spaces) so the built
-# version in build/testing/framework/TestSCons.py contains the actual version
-# string of the packages that have been built.
-SConsVersion = '__VERSION__'
-if SConsVersion == '__' + 'VERSION' + '__':
- SConsVersion = default_version
+SConsVersion = default_version
__all__.extend([
'TestSCons',
@@ -126,7 +120,7 @@ file_expr = r"""File "[^"]*", line \d+, in [^\n]+
# re.escape escapes too much.
def re_escape(str):
for c in '\\.[]()*+?': # Not an exhaustive list.
- str = str.replace(c, '\\' + c)
+ str = str.replace(c, f"\\{c}")
return str
@@ -370,7 +364,7 @@ class TestSCons(TestCommon):
"""
env = self.Environment(ENV)
if env:
- v = env.subst('$' + var)
+ v = env.subst(f"${var}")
if not v:
return None
if prog is None:
@@ -422,20 +416,35 @@ class TestSCons(TestCommon):
return None
- def wrap_stdout(self, build_str="", read_str="", error=0, cleaning=0):
- """Wraps standard output string(s) in the normal
- "Reading ... done" and "Building ... done" strings
+ def wrap_stdout(self, build_str="", read_str="", error=0, cleaning=0) -> str:
+ """Wraps "expect" strings in SCons boilerplate.
+
+ Given strings of expected output specific to a test,
+ returns a string which includes the SCons wrapping such as
+ "Reading ... done", etc.: that is, adds the text that would
+ be left out by running SCons in quiet mode;
+ Makes a complete message to match against.
+
+ Args:
+ read_str: the message for the execution part of the output.
+ If non-empty, needs to be newline-terminated.
+ read_str: the message for the reading-sconscript part of
+ the output. If non-empty, needs to be newline-terminated.
+ error: if true, expect a fail message rather than a done message.
+ cleaning (int): index into type messages, if 0 selects
+ build messages, if 1 selects clean messages.
"""
cap, lc = [('Build', 'build'),
('Clean', 'clean')][cleaning]
if error:
- term = "scons: %sing terminated because of errors.\n" % lc
+ term = f"scons: {lc}ing terminated because of errors.\n"
else:
- term = "scons: done %sing targets.\n" % lc
+ term = f"scons: done {lc}ing targets.\n"
+
return "scons: Reading SConscript files ...\n" + \
read_str + \
"scons: done reading SConscript files.\n" + \
- "scons: %sing targets ...\n" % cap + \
+ f"scons: {cap}ing targets ...\n" + \
build_str + \
term
@@ -446,7 +455,7 @@ class TestSCons(TestCommon):
"""
sconsflags = initialize_sconsflags(self.ignore_python_version)
try:
- TestCommon.run(self, *args, **kw)
+ super().run(*args, **kw)
finally:
restore_sconsflags(sconsflags)
@@ -480,12 +489,12 @@ class TestSCons(TestCommon):
"""
s = ""
for arg in arguments.split():
- s = s + "scons: `%s' is up to date.\n" % arg
+ s = f"{s}scons: `{arg}' is up to date.\n"
kw['arguments'] = arguments
stdout = self.wrap_stdout(read_str=read_str, build_str=s)
# Append '.*' so that timing output that comes after the
# up-to-date output is okay.
- kw['stdout'] = re.escape(stdout) + '.*'
+ kw['stdout'] = f"{re.escape(stdout)}.*"
kw['match'] = self.match_re_dotall
self.run(**kw)
@@ -496,8 +505,8 @@ class TestSCons(TestCommon):
"""
s = ""
for arg in arguments.split():
- s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg)
- s = '(' + s + '[^\n]*\n)*'
+ s = f"{s}(?!scons: `{re.escape(arg)}' is up to date.)"
+ s = f"({s}[^\n]*\n)*"
kw['arguments'] = arguments
stdout = re.escape(self.wrap_stdout(read_str=read_str, build_str='ARGUMENTSGOHERE'))
kw['stdout'] = stdout.replace('ARGUMENTSGOHERE', s)
@@ -509,7 +518,7 @@ class TestSCons(TestCommon):
Verifies expected behavior for options that are not yet implemented:
a warning message, and exit status 1.
"""
- msg = "Warning: the %s option is not yet implemented\n" % option
+ msg = f"Warning: the {option} option is not yet implemented\n"
kw['stderr'] = msg
if arguments:
# If it's a long option and the argument string begins with '=',
@@ -517,14 +526,14 @@ class TestSCons(TestCommon):
if option[:2] == '--' and arguments[0] == '=':
kw['arguments'] = option + arguments
else:
- kw['arguments'] = option + ' ' + arguments
+ kw['arguments'] = f"{option} {arguments}"
return self.run(**kw)
def deprecated_wrap(self, msg):
"""
Calculate the pattern that matches a deprecation warning.
"""
- return '\nscons: warning: ' + re_escape(msg) + '\n' + file_expr
+ return f"\nscons: warning: {re_escape(msg)}\n{file_expr}"
def deprecated_fatal(self, warn, msg):
"""
@@ -586,11 +595,11 @@ class TestSCons(TestCommon):
def RunPair(option, expected):
# run the same test with the option on the command line and
# then with the option passed via SetOption().
- self.run(options='--warn=' + option,
+ self.run(options=f"--warn={option}",
arguments='.',
stderr=expected,
match=match_re_dotall)
- self.run(options='WARN=' + option,
+ self.run(options=f"WARN={option}",
arguments='.',
stderr=expected,
match=match_re_dotall)
@@ -602,8 +611,8 @@ class TestSCons(TestCommon):
RunPair(warn, warning)
# warning disabled, should get either nothing or mandatory message
- expect = """()|(Can not disable mandataory warning: 'no-%s'\n\n%s)""" % (warn, warning)
- RunPair('no-' + warn, expect)
+ expect = f"""()|(Can not disable mandataory warning: 'no-{warn}'\n\n{warning})"""
+ RunPair(f"no-{warn}", expect)
return warning
@@ -643,7 +652,7 @@ class TestSCons(TestCommon):
# x = x.replace('<string>', file)
# x = x.replace('line 1,', 'line %s,' % line)
# x="\n".join(x)
- x = 'File "%s", line %s, in <module>\n' % (file, line)
+ x = f'File "{file}", line {line}, in <module>\n'
return x
def normalize_ps(self, s):
@@ -718,12 +727,12 @@ class TestSCons(TestCommon):
for p in patterns:
result.extend(sorted(glob.glob(p)))
return result
-
+
def get_sconsignname(self):
"""Get the scons database name used, and return both the prefix and full filename.
if the user left the options defaulted AND the default algorithm set by
SCons is md5, then set the database name to be the special default name
-
+
otherwise, if it defaults to something like 'sha1' or the user explicitly
set 'md5' as the hash format, set the database name to .sconsign_<algorithm>
eg .sconsign_sha1, etc.
@@ -736,7 +745,7 @@ class TestSCons(TestCommon):
if hash_format is None and current_hash_algorithm == 'md5':
return ".sconsign"
else:
- database_prefix=".sconsign_%s" % current_hash_algorithm
+ database_prefix=f".sconsign_{current_hash_algorithm}"
return database_prefix
@@ -782,13 +791,13 @@ class TestSCons(TestCommon):
if version:
if sys.platform == 'win32':
patterns = [
- 'C:/Program Files*/Java/jdk*%s*/bin' % version,
+ f'C:/Program Files*/Java/jdk*{version}*/bin',
]
else:
patterns = [
- '/usr/java/jdk%s*/bin' % version,
- '/usr/lib/jvm/*-%s*/bin' % version,
- '/usr/local/j2sdk%s*/bin' % version,
+ f'/usr/java/jdk{version}*/bin',
+ f'/usr/lib/jvm/*-{version}*/bin',
+ f'/usr/local/j2sdk{version}*/bin',
]
java_path = self.paths(patterns) + [env['ENV']['PATH']]
else:
@@ -833,10 +842,10 @@ class TestSCons(TestCommon):
'/usr/lib/jvm/default-java/include/jni.h',
'/usr/lib/jvm/java-*-oracle/include/jni.h']
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/java/jdk%s*/include/jni.h' % version])
+ jni_dirs = [f'/System/Library/Frameworks/JavaVM.framework/Versions/{version}*/Headers/jni.h']
+ jni_dirs.extend([f'/usr/lib/jvm/java-*-sun-{version}*/include/jni.h',
+ f'/usr/lib/jvm/java-{version}*-openjdk*/include/jni.h',
+ f'/usr/java/jdk{version}*/include/jni.h'])
dirs = self.paths(jni_dirs)
if not dirs:
return None
@@ -857,7 +866,7 @@ class TestSCons(TestCommon):
Args:
version: if set, match only that version
- Returns:
+ Returns:
path where JDK components live
Bails out of the entire test (skip) if not found.
"""
@@ -881,10 +890,10 @@ class TestSCons(TestCommon):
if os.path.exists(home):
return home
else:
- if java_home.find('jdk%s' % version) != -1:
+ if java_home.find(f'jdk{version}') != -1:
return java_home
for home in [
- '/System/Library/Frameworks/JavaVM.framework/Versions/%s/Home' % version,
+ f'/System/Library/Frameworks/JavaVM.framework/Versions/{version}/Home',
# osx 10.10
'/System/Library/Frameworks/JavaVM.framework/Versions/Current/'
]:
@@ -894,7 +903,7 @@ class TestSCons(TestCommon):
home = ''
else:
jar = self.java_where_jar(version)
- home = os.path.normpath('%s/..' % jar)
+ home = os.path.normpath(f'{jar}/..')
if home and os.path.isdir(home):
return home
@@ -919,7 +928,7 @@ class TestSCons(TestCommon):
or b"Unable to locate a Java Runtime" in cp.stdout
):
self.skip_test(
- "Could not find Java " + java_bin_name + ", skipping test.\n",
+ f"Could not find Java {java_bin_name}, skipping test.\n",
from_fw=True,
)
@@ -988,7 +997,7 @@ class TestSCons(TestCommon):
status=None)
# Note recent versions output version info to stdout instead of stderr
if version:
- verf = 'javac %s' % version
+ verf = f'javac {version}'
if self.stderr().find(verf) == -1 and self.stdout().find(verf) == -1:
fmt = "Could not find javac for Java version %s, skipping test(s).\n"
self.skip_test(fmt % version, from_fw=True)
@@ -1144,6 +1153,7 @@ void my_qt_symbol(const char *arg) {
self.write([dir, 'lib', 'SConstruct'], r"""
import sys
+DefaultEnvironment(tools=[]) # test speedup
env = Environment()
if sys.platform == 'win32':
env.StaticLibrary('myqt', 'my_qobject.cpp')
@@ -1158,8 +1168,8 @@ else:
self.QT = self.workpath(dir)
self.QT_LIB = 'myqt'
- self.QT_MOC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'mymoc.py'))
- self.QT_UIC = '%s %s' % (_python_, self.workpath(dir, 'bin', 'myuic.py'))
+ self.QT_MOC = f"{_python_} {self.workpath(dir, 'bin', 'mymoc.py')}"
+ self.QT_UIC = f"{_python_} {self.workpath(dir, 'bin', 'myuic.py')}"
self.QT_LIB_DIR = self.workpath(dir, 'lib')
def Qt_create_SConstruct(self, place):
@@ -1170,6 +1180,7 @@ if ARGUMENTS.get('noqtdir', 0):
QTDIR = None
else:
QTDIR = r'%s'
+DefaultEnvironment(tools=[]) # test speedup
env = Environment(
QTDIR=QTDIR, QT_LIB=r'%s', QT_MOC=r'%s', QT_UIC=r'%s', tools=['default', 'qt']
)
@@ -1224,7 +1235,7 @@ SConscript(sconscript)
"""
if check_platform:
if sys.platform != 'win32':
- msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+ msg = f"Skipping Visual C/C++ test on non-Windows platform '{sys.platform}'\n"
self.skip_test(msg, from_fw=True)
return
@@ -1278,14 +1289,14 @@ SConscript(sconscript)
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
- log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls
+ log = f"\t{re.escape(f'Configure(confdir = {sconf_dir})')}" + ls
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
rdstr = ""
for check_info in checks:
- log = re.escape("scons: Configure: " + check_info.check_string) + ls
+ log = re.escape(f"scons: Configure: {check_info.check_string}") + ls
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
@@ -1301,9 +1312,9 @@ SConscript(sconscript)
# rebuild will pass
if ext in ['.c', '.cpp']:
log = log + conf_filename + re.escape(" <-") + ls
- log = log + r"( \|" + nols + "*" + ls + ")+?"
+ log = f"{log}( \\|{nols}*{ls})+?"
else:
- log = log + "(" + nols + "*" + ls + ")*?"
+ log = f"{log}({nols}*{ls})*?"
result_cached = 0
if flag == self.CR:
# CR = cached rebuild (up to date)s
@@ -1314,10 +1325,10 @@ SConscript(sconscript)
re.escape("\" is up to date.") + ls
log = log + re.escape("scons: Configure: The original builder "
"output was:") + ls
- log = log + r"( \|.*" + ls + ")+"
+ log = f"{log}( \\|.*{ls})+"
if flag == self.NCF:
# non-cached rebuild failure
- log = log + "(" + nols + "*" + ls + ")*?"
+ log = f"{log}({nols}*{ls})*?"
result_cached = 0
if flag == self.CF:
# cached rebuild failure
@@ -1326,14 +1337,14 @@ SConscript(sconscript)
conf_filename + \
re.escape("\" failed in a previous run and all its sources are up to date.") + ls
log = log + re.escape("scons: Configure: The original builder output was:") + ls
- log = log + r"( \|.*" + ls + ")+"
+ log = f"{log}( \\|.*{ls})+"
if result_cached:
- result = "(cached) " + check_info.result
+ result = f"(cached) {check_info.result}"
else:
result = check_info.result
- rdstr = rdstr + re.escape(check_info.check_string) + re.escape(result) + "\n"
+ rdstr = f"{rdstr + re.escape(check_info.check_string) + re.escape(result)}\n"
- log = log + re.escape("scons: Configure: " + result) + ls + ls
+ log = log + re.escape(f"scons: Configure: {result}") + ls + ls
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
@@ -1416,7 +1427,7 @@ SConscript(sconscript)
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
- log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls
+ log = f"\t{re.escape(f'Configure(confdir = {sconf_dir})')}" + ls
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
@@ -1424,7 +1435,7 @@ SConscript(sconscript)
cnt = 0
for check, result, cache_desc in zip(checks, results, cached):
- log = re.escape("scons: Configure: " + check) + ls
+ log = re.escape(f"scons: Configure: {check}") + ls
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
@@ -1457,9 +1468,9 @@ SConscript(sconscript)
# rebuild will pass
if ext in ['.c', '.cpp']:
log = log + conf_filename + re.escape(" <-") + ls
- log = log + r"( \|" + nols + "*" + ls + ")+?"
+ log = f"{log}( \\|{nols}*{ls})+?"
else:
- log = log + "(" + nols + "*" + ls + ")*?"
+ log = f"{log}({nols}*{ls})*?"
result_cached = 0
if flag == self.CR:
# CR = cached rebuild (up to date)s
@@ -1470,10 +1481,10 @@ SConscript(sconscript)
re.escape("\" is up to date.") + ls
log = log + re.escape("scons: Configure: The original builder "
"output was:") + ls
- log = log + r"( \|.*" + ls + ")+"
+ log = f"{log}( \\|.*{ls})+"
if flag == self.NCF:
# non-cached rebuild failure
- log = log + "(" + nols + "*" + ls + ")*?"
+ log = f"{log}({nols}*{ls})*?"
result_cached = 0
if flag == self.CF:
# cached rebuild failure
@@ -1482,14 +1493,14 @@ SConscript(sconscript)
conf_filename + \
re.escape("\" failed in a previous run and all its sources are up to date.") + ls
log = log + re.escape("scons: Configure: The original builder output was:") + ls
- log = log + r"( \|.*" + ls + ")+"
+ log = f"{log}( \\|.*{ls})+"
# cnt = cnt + 1
if result_cached:
- result = "(cached) " + result
+ result = f"(cached) {result}"
- rdstr = rdstr + re.escape(check) + re.escape(result) + "\n"
+ rdstr = f"{rdstr + re.escape(check) + re.escape(result)}\n"
- log = log + re.escape("scons: Configure: " + result) + ls + ls
+ log = log + re.escape(f"scons: Configure: {result}") + ls + ls
if doCheckLog:
lastEnd = match_part_of_configlog(log, logfile, lastEnd)
@@ -1621,7 +1632,7 @@ else:
kw['stdin'] = True
sconsflags = initialize_sconsflags(self.ignore_python_version)
try:
- p = TestCommon.start(self, *args, **kw)
+ p = super().start(*args, **kw)
finally:
restore_sconsflags(sconsflags)
return p
@@ -1633,7 +1644,7 @@ else:
waited = 0.0
while not os.path.exists(fname):
if timeout and waited >= timeout:
- sys.stderr.write('timed out waiting for %s to exist\n' % fname)
+ sys.stderr.write(f'timed out waiting for {fname} to exist\n')
if popen:
popen.stdin.close()
popen.stdin = None
@@ -1641,11 +1652,11 @@ else:
self.finish(popen)
stdout = self.stdout()
if stdout:
- sys.stdout.write(self.banner('STDOUT ') + '\n')
+ sys.stdout.write(f"{self.banner('STDOUT ')}\n")
sys.stdout.write(stdout)
stderr = self.stderr()
if stderr:
- sys.stderr.write(self.banner('STDERR ') + '\n')
+ sys.stderr.write(f"{self.banner('STDERR ')}\n")
sys.stderr.write(stderr)
self.fail_test()
time.sleep(1.0)
@@ -1790,7 +1801,7 @@ class TimeSCons(TestSCons):
if 'options' not in kw and self.variables:
options = []
for variable, value in self.variables.items():
- options.append('%s=%s' % (variable, value))
+ options.append(f'{variable}={value}')
kw['options'] = ' '.join(options)
if self.calibrate:
self.calibration(*args, **kw)
@@ -1804,8 +1815,8 @@ class TimeSCons(TestSCons):
fmt = "TRACE: graph=%s name=%s value=%s units=%s"
line = fmt % (graph, name, value, units)
if sort is not None:
- line = line + (' sort=%s' % sort)
- line = line + '\n'
+ line = f"{line} sort={sort}"
+ line = f"{line}\n"
sys.stdout.write(line)
sys.stdout.flush()
@@ -1849,7 +1860,7 @@ class TimeSCons(TestSCons):
options = kw.get('options', '')
if additional is not None:
options += additional
- kw['options'] = options + ' --debug=memory,time'
+ kw['options'] = f"{options} --debug=memory,time"
def startup(self, *args, **kw):
"""
@@ -1895,8 +1906,8 @@ class TimeSCons(TestSCons):
self.run(*args, **kw)
for variable in self.calibrate_variables:
value = self.variables[variable]
- sys.stdout.write('VARIABLE: %s=%s\n' % (variable, value))
- sys.stdout.write('ELAPSED: %s\n' % self.elapsed_time())
+ sys.stdout.write(f'VARIABLE: {variable}={value}\n')
+ sys.stdout.write(f'ELAPSED: {self.elapsed_time()}\n')
def null(self, *args, **kw):
"""
@@ -1980,12 +1991,12 @@ class TimeSCons(TestSCons):
"""
s = ""
for arg in arguments.split():
- s = s + "scons: `%s' is up to date.\n" % arg
+ s = f"{s}scons: `{arg}' is up to date.\n"
kw['arguments'] = arguments
stdout = self.wrap_stdout(read_str="REPLACEME", build_str=s)
# Append '.*' so that timing output that comes after the
# up-to-date output is okay.
- stdout = re.escape(stdout) + '.*'
+ stdout = f"{re.escape(stdout)}.*"
stdout = stdout.replace('REPLACEME', read_str)
kw['stdout'] = stdout
kw['match'] = self.match_re_dotall
diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py
index 6e5e01b..b001d79 100644
--- a/testing/framework/TestSConsMSVS.py
+++ b/testing/framework/TestSConsMSVS.py
@@ -690,7 +690,7 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions()))
orig = 'sys.path = [ join(sys'
enginepath = repr(os.path.join(self._cwd, '..', 'engine'))
- replace = 'sys.path = [ %s, join(sys' % enginepath
+ replace = f'sys.path = [ {enginepath}, join(sys'
contents = self.read(fname, mode='r')
contents = contents.replace(orig, replace)
@@ -719,9 +719,9 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions()))
project_guid = "{B0CC4EE9-0174-51CD-A06A-41D0713E928A}"
if 'SCONS_LIB_DIR' in os.environ:
- exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % os.environ['SCONS_LIB_DIR']
+ exec_script_main = f"from os.path import join; import sys; sys.path = [ r'{os.environ['SCONS_LIB_DIR']}' ] + sys.path; import SCons.Script; SCons.Script.main()"
else:
- exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%s'), join(sys.prefix, 'scons-%s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % (self.scons_version, self.scons_version)
+ exec_script_main = f"from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-{self.scons_version}'), join(sys.prefix, 'scons-{self.scons_version}'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()"
exec_script_main_xml = exec_script_main.replace("'", "&apos;")
result = input.replace(r'<WORKPATH>', workpath)
@@ -823,7 +823,7 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions()))
elif major > 10:
return '12.00'
else:
- raise SCons.Errors.UserError('Received unexpected VC version %s' % vc_version)
+ raise SCons.Errors.UserError(f'Received unexpected VC version {vc_version}')
def _get_solution_file_vs_number(self, vc_version):
"""
@@ -846,7 +846,7 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions()))
elif major == 14 and minor == 2:
return '16'
else:
- raise SCons.Errors.UserError('Received unexpected VC version %s' % vc_version)
+ raise SCons.Errors.UserError(f'Received unexpected VC version {vc_version}')
def _get_vcxproj_file_tools_version(self, vc_version):
"""
@@ -877,7 +877,7 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions()))
# ToolsVersion='17'
return '17.0'
else:
- raise SCons.Errors.UserError('Received unexpected VC version %s' % vc_version)
+ raise SCons.Errors.UserError(f'Received unexpected VC version {vc_version}')
def _get_vcxproj_file_cpp_path(self, dirs):
"""Returns the include paths expected in the .vcxproj file"""
diff --git a/testing/framework/TestSCons_time.py b/testing/framework/TestSCons_time.py
index e647fe2..282a9a6 100644
--- a/testing/framework/TestSCons_time.py
+++ b/testing/framework/TestSCons_time.py
@@ -73,8 +73,7 @@ with open('SConstruct', 'r') as f:
exec(script)
"""
-svn_py = """\
-#!/usr/bin/env python
+svn_py = f"""#!/usr/bin/env python
import os
import sys
@@ -82,12 +81,11 @@ dir = sys.argv[-1]
script_dir = dir + '/scripts'
os.makedirs(script_dir)
with open(script_dir + '/scons.py', 'w') as f:
- f.write(r'''%s''')
-""" % scons_py
+ f.write(r'''{scons_py}''')
+"""
-git_py = """\
-#!/usr/bin/env python
+git_py = f"""#!/usr/bin/env python
import os
import sys
@@ -95,8 +93,8 @@ dir = sys.argv[-1]
script_dir = dir + '/scripts'
os.makedirs(script_dir)
with open(script_dir + '/scons.py', 'w') as f:
- f.write(r'''%s''')
-""" % scons_py
+ f.write(r'''{scons_py}''')
+"""
logfile_contents = """\
@@ -243,7 +241,7 @@ class TestSCons_time(TestCommon):
args = (tempdir, 'scons-time-',) + args
x = os.path.join(*args)
x = re.escape(x)
- x = x.replace('time\\-', 'time\\-[^%s]*' % sep)
+ x = x.replace('time\\-', f'time\\-[^{sep}]*')
return x
def write_fake_scons_py(self):