From dabd3fb05bf269d7b84b02b9bfd86571961800cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 13 Jul 2019 11:33:16 +0200 Subject: Migrate to debhelper 12 --- debian/changelog | 8 ++++++++ debian/compat | 2 +- debian/control | 2 +- debian/files | 1 - 4 files changed, 10 insertions(+), 3 deletions(-) delete mode 100644 debian/files diff --git a/debian/changelog b/debian/changelog index ad535bd..723e2af 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +scons-doc (3.0.0+repack-3) UNRELEASED; urgency=medium + + * Migrate to debhelper 12: + - Change debian/compat to 12. + - Bump minimum debhelper version in debian/control to >= 12. + + -- Jörg Frings-Fürst Sat, 13 Jul 2019 11:30:55 +0200 + scons-doc (3.0.0+repack-2) unstable; urgency=medium * Switch from openjdk-8-jre to default-jre (Closes: #894355). diff --git a/debian/compat b/debian/compat index b4de394..48082f7 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -11 +12 diff --git a/debian/control b/debian/control index 674f9a2..0d3bc16 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Jörg Frings-Fürst Uploaders: Laszlo Boszormenyi (GCS) Build-Depends: - debhelper (>= 11), + debhelper (>= 12), scons (>= 3.0) Build-Depends-Indep: default-jre, diff --git a/debian/files b/debian/files deleted file mode 100644 index 8cfe841..0000000 --- a/debian/files +++ /dev/null @@ -1 +0,0 @@ -scons-doc_3.0.0+repack-2_source.buildinfo doc optional -- cgit v1.2.3 From bdf86e9f617256c3c7d47b69bf3acd103727f5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 13 Jul 2019 11:34:10 +0200 Subject: Declare compliance with Debian Policy 4.4.0 --- debian/changelog | 1 + debian/control | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 723e2af..21bb119 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,7 @@ scons-doc (3.0.0+repack-3) UNRELEASED; urgency=medium * Migrate to debhelper 12: - Change debian/compat to 12. - Bump minimum debhelper version in debian/control to >= 12. + * Declare compliance with Debian Policy 4.4.0. (No changes needed). -- Jörg Frings-Fürst Sat, 13 Jul 2019 11:30:55 +0200 diff --git a/debian/control b/debian/control index 0d3bc16..1e4edf5 100644 --- a/debian/control +++ b/debian/control @@ -13,7 +13,7 @@ Build-Depends-Indep: fop, python-libxml2, python-libxslt1 -Standards-Version: 4.1.4 +Standards-Version: 4.4.0 Homepage: https://www.scons.org/ Vcs-Git: https://salsa.debian.org/debian/scons-doc.git Vcs-Browser: https://salsa.debian.org/debian/scons-doc -- cgit v1.2.3 From 85dbcc01ae3f6b10849aa71faef6946d8e16d55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 14 Jul 2019 08:34:42 +0200 Subject: rewrite Files-Excluded --- debian/copyright | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/debian/copyright b/debian/copyright index 6bf9a60..185d6c0 100644 --- a/debian/copyright +++ b/debian/copyright @@ -2,20 +2,19 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: scons-doc Upstream-Contact: Steven Knight Source: http://www.scons.org -Files-Excluded: HOWTO - debian/* +Files-Excluded: debian/* bench/* - examples/* gentoo/* rpm/* template/* test/* - testing/* timings/* - .hgtags *-local ReleaseConfig runtest.py + .github + .svnt + .travis Files: * Copyright: 2001-2017 The SCons Foundation -- cgit v1.2.3 From 697e33ed224b539a42ff68121f7497f5bbf941b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 14 Jul 2019 08:35:24 +0200 Subject: New upstream version 3.0.5 --- .appveyor.yml | 248 ++ .codecov.yml | 50 + .travis.yml | 188 ++ LICENSE | 2 +- QMTest/README.txt | 58 - QMTest/SConscript | 64 - QMTest/TestCmd.py | 1907 ----------- QMTest/TestCmdTests.py | 3419 -------------------- QMTest/TestCommon.py | 748 ----- QMTest/TestCommonTests.py | 2340 -------------- QMTest/TestRuntest.py | 167 - QMTest/TestSCons.py | 1617 --------- QMTest/TestSConsMSVS.py | 1177 ------- QMTest/TestSCons_time.py | 331 -- QMTest/TestSConsign.py | 90 - QMTest/classes.qmc | 12 - QMTest/configuration | 6 - QMTest/scons_tdb.py | 603 ---- QMTest/test-framework.rst | 430 --- README.rst | 87 +- SConstruct | 501 +-- bin/SConsDoc.py | 16 +- bin/SConsExamples.py | 65 +- bin/import-test.py | 4 +- bin/linecount.py | 4 +- bin/restore.sh | 28 +- bin/scons-cdist | 272 -- bin/scons_dev_master.py | 23 +- bin/time-scons.py | 2 +- bin/update-release-info.py | 43 +- bin/upload-release-files.sh | 19 +- bin/xmlagenda.py | 6 +- bootstrap.py | 108 +- doc/SConscript | 27 +- doc/design/SConstruct | 2 +- doc/design/acks.xml | 2 +- doc/design/bground.xml | 2 +- doc/design/chtml.xsl | 2 +- doc/design/copyright.xml | 2 +- doc/design/engine.xml | 2 +- doc/design/goals.xml | 2 +- doc/design/html.xsl | 2 +- doc/design/install.xml | 2 +- doc/design/intro.xml | 2 +- doc/design/issues.xml | 2 +- doc/design/main.xml | 2 +- doc/design/native.xml | 7 +- doc/design/overview.xml | 2 +- doc/design/pdf.xsl | 2 +- doc/design/scons_title.xsl | 2 +- doc/design/summary.xml | 2 +- doc/developer/SConstruct | 2 +- doc/developer/architecture.xml | 2 +- doc/developer/branches.xml | 2 +- doc/developer/copyright.xml | 2 +- doc/developer/cycle.xml | 2 +- doc/developer/main.xml | 2 +- doc/developer/packaging.xml | 2 +- doc/developer/preface.xml | 2 +- doc/developer/sourcetree.xml | 2 +- doc/developer/testing.xml | 2 +- doc/generated/builders.gen | 615 ++-- doc/generated/examples/addmethod_ex2_2.xml | 2 +- .../examples/builderswriting_MY_EMITTER_1.xml | 4 +- doc/generated/examples/caching_ex-random_1.xml | 6 +- doc/generated/examples/environments_ex3_1.xml | 22 +- doc/generated/examples/java_jar1_1.xml | 2 +- .../examples/mergeflags_MergeFlags1_1.xml | 9 +- .../examples/mergeflags_MergeFlags2_1.xml | 9 +- .../examples/mergeflags_MergeFlags3_1.xml | 10 +- doc/generated/examples/misc_FindFile1b_1.xml | 8 +- doc/generated/examples/parseflags_ex1_1.xml | 12 +- doc/generated/examples/parseflags_ex1_2.xml | 13 +- doc/generated/examples/parseflags_ex2_1.xml | 10 +- doc/generated/examples/parseflags_ex3_1.xml | 12 +- doc/generated/examples/parseflags_ex4_1.xml | 12 +- doc/generated/examples/troubleshoot_Dump_1.xml | 82 +- doc/generated/examples/troubleshoot_Dump_2.xml | 115 +- doc/generated/examples/troubleshoot_Dump_ENV_1.xml | 12 +- doc/generated/examples/troubleshoot_Dump_ENV_2.xml | 14 +- doc/generated/examples/troubleshoot_explain1_3.xml | 2 +- .../examples/troubleshoot_stacktrace_2.xml | 2 +- doc/generated/functions.gen | 23 +- doc/generated/tools.gen | 37 +- doc/generated/tools.mod | 4 +- doc/generated/variables.gen | 1132 ++++--- doc/generated/variables.mod | 24 +- doc/man/SConstruct | 2 +- doc/man/epub.xsl | 2 +- doc/man/html.xsl | 2 +- doc/man/pdf.xsl | 2 +- doc/man/scons-time.xml | 6 +- doc/man/scons.xml | 126 +- doc/man/scons_title.xsl | 2 +- doc/man/sconsign.xml | 24 +- doc/python10/SConstruct | 2 +- doc/python10/abstract.xml | 2 +- doc/python10/acks.xml | 2 +- doc/python10/copyright.xml | 2 +- doc/python10/design.xml | 2 +- doc/python10/future.xml | 2 +- doc/python10/install.xml | 2 +- doc/python10/intro.xml | 2 +- doc/python10/main.xml | 2 +- doc/python10/process.xml | 2 +- doc/python10/summary.xml | 2 +- doc/reference/Alias.xml | 2 +- doc/reference/CFile.xml | 2 +- doc/reference/CXXFile.xml | 2 +- doc/reference/Command.xml | 2 +- doc/reference/Install.xml | 2 +- doc/reference/InstallAs.xml | 2 +- doc/reference/Library.xml | 2 +- doc/reference/Object.xml | 2 +- doc/reference/PCH.xml | 2 +- doc/reference/PDF.xml | 2 +- doc/reference/PostScript.xml | 2 +- doc/reference/Program.xml | 2 +- doc/reference/RES.xml | 2 +- doc/reference/SConstruct | 2 +- doc/reference/SharedLibrary.xml | 2 +- doc/reference/SharedObject.xml | 2 +- doc/reference/StaticLibrary.xml | 2 +- doc/reference/StaticObject.xml | 2 +- doc/reference/chtml.xsl | 2 +- doc/reference/copyright.xml | 2 +- doc/reference/errors.xml | 2 +- doc/reference/html.xsl | 2 +- doc/reference/main.xml | 2 +- doc/reference/pdf.xsl | 2 +- doc/reference/preface.xml | 2 +- doc/reference/scons_title.xsl | 2 +- doc/scons.mod | 5 +- doc/user/README | 2 +- doc/user/SConstruct | 2 +- doc/user/actions.xml | 2 +- doc/user/add-method.xml | 2 +- doc/user/alias.xml | 2 +- doc/user/ant.xml | 2 +- doc/user/build-install.xml | 292 +- doc/user/builders-built-in.xml | 2 +- doc/user/builders-commands.xml | 2 +- doc/user/builders-writing.xml | 24 +- doc/user/builders.xml | 2 +- doc/user/caching.xml | 2 +- doc/user/chtml.xsl | 2 +- doc/user/command-line.xml | 37 +- doc/user/copyright.xml | 55 +- doc/user/depends.xml | 10 +- doc/user/environments.xml | 32 +- doc/user/epub.xsl | 2 +- doc/user/errors.xml | 2 +- doc/user/example.xml | 2 +- doc/user/factories.xml | 2 +- doc/user/file-removal.xml | 2 +- doc/user/functions.xml | 2 +- doc/user/gettext.xml | 2 +- doc/user/hierarchy.xml | 2 +- doc/user/html.xsl | 2 +- doc/user/install.xml | 2 +- doc/user/java.xml | 2 +- doc/user/less-simple.xml | 61 +- doc/user/libraries.xml | 2 +- doc/user/main.xml | 6 +- doc/user/make.xml | 2 +- doc/user/mergeflags.xml | 2 +- doc/user/misc.xml | 57 +- doc/user/nodes.xml | 8 +- doc/user/output.xml | 2 +- doc/user/parseconfig.xml | 2 +- doc/user/parseflags.xml | 4 +- doc/user/pdf.xsl | 2 +- doc/user/preface.xml | 2 +- doc/user/python.xml | 2 +- doc/user/repositories.xml | 2 +- doc/user/run.xml | 2 +- doc/user/scanners.xml | 57 +- doc/user/sconf.xml | 2 +- doc/user/scons_title.xsl | 2 +- doc/user/separate.xml | 2 +- doc/user/sideeffect.xml | 2 +- doc/user/simple.xml | 2 +- doc/user/sourcecode.xml | 2 +- doc/user/tasks.xml | 2 +- doc/user/tools.xml | 2 +- doc/user/troubleshoot.xml | 2 +- doc/user/variables.xml | 2 +- doc/user/variants.xml | 2 +- site_scons/SConsRevision.py | 40 + site_scons/Utilities.py | 43 + site_scons/site_init.py | 4 + site_scons/soe_utils.py | 36 + site_scons/zip_utils.py | 54 + src/Announce.txt | 60 +- src/CHANGES.txt | 337 +- src/LICENSE.txt | 2 +- src/README.txt | 21 +- src/RELEASE.txt | 72 +- src/engine/MANIFEST-xml.in | 8 - src/engine/MANIFEST.in | 6 +- src/engine/SCons/Action.py | 27 +- src/engine/SCons/Action.xml | 2 +- src/engine/SCons/ActionTests.py | 572 ++-- src/engine/SCons/Builder.py | 18 +- src/engine/SCons/BuilderTests.py | 20 +- src/engine/SCons/CacheDir.py | 17 +- src/engine/SCons/CacheDirTests.py | 16 +- src/engine/SCons/Conftest.py | 20 +- src/engine/SCons/Debug.py | 4 +- src/engine/SCons/Defaults.py | 14 +- src/engine/SCons/Defaults.xml | 32 +- src/engine/SCons/DefaultsTests.py | 13 +- src/engine/SCons/Environment.py | 15 +- src/engine/SCons/Environment.xml | 4 +- src/engine/SCons/EnvironmentTests.py | 24 +- src/engine/SCons/EnvironmentValues.py | 1 + src/engine/SCons/Errors.py | 25 +- src/engine/SCons/ErrorsTests.py | 34 +- src/engine/SCons/Executor.py | 6 +- src/engine/SCons/ExecutorTests.py | 13 +- src/engine/SCons/Job.py | 6 +- src/engine/SCons/JobTests.py | 130 +- src/engine/SCons/Memoize.py | 4 +- src/engine/SCons/MemoizeTests.py | 16 +- src/engine/SCons/Node/Alias.py | 4 +- src/engine/SCons/Node/AliasTests.py | 17 +- src/engine/SCons/Node/FS.py | 325 +- src/engine/SCons/Node/FSTests.py | 185 +- src/engine/SCons/Node/NodeTests.py | 21 +- src/engine/SCons/Node/Python.py | 17 +- src/engine/SCons/Node/PythonTests.py | 23 +- src/engine/SCons/Node/__init__.py | 81 +- src/engine/SCons/Options/BoolOption.py | 50 - src/engine/SCons/Options/EnumOption.py | 50 - src/engine/SCons/Options/ListOption.py | 50 - src/engine/SCons/Options/PackageOption.py | 50 - src/engine/SCons/Options/PathOption.py | 76 - src/engine/SCons/Options/__init__.py | 67 - src/engine/SCons/PathList.py | 4 +- src/engine/SCons/PathListTests.py | 4 +- src/engine/SCons/Platform/PlatformTests.py | 25 +- src/engine/SCons/Platform/__init__.py | 64 +- src/engine/SCons/Platform/__init__.xml | 26 +- src/engine/SCons/Platform/aix.py | 4 +- src/engine/SCons/Platform/cygwin.py | 13 +- src/engine/SCons/Platform/darwin.py | 4 +- src/engine/SCons/Platform/hpux.py | 4 +- src/engine/SCons/Platform/irix.py | 4 +- src/engine/SCons/Platform/mingw.py | 39 + src/engine/SCons/Platform/os2.py | 4 +- src/engine/SCons/Platform/posix.py | 11 +- src/engine/SCons/Platform/posix.xml | 6 +- src/engine/SCons/Platform/sunos.py | 4 +- src/engine/SCons/Platform/sunos.xml | 2 +- src/engine/SCons/Platform/virtualenv.py | 120 + src/engine/SCons/Platform/virtualenvTests.py | 243 ++ src/engine/SCons/Platform/win32.py | 35 +- src/engine/SCons/Platform/win32.xml | 2 +- src/engine/SCons/SConf.py | 84 +- src/engine/SCons/SConfTests.py | 34 +- src/engine/SCons/SConsign.py | 7 +- src/engine/SCons/SConsignTests.py | 4 +- src/engine/SCons/Scanner/C.py | 4 +- src/engine/SCons/Scanner/CTests.py | 20 +- src/engine/SCons/Scanner/D.py | 4 +- src/engine/SCons/Scanner/DTests.py | 16 +- src/engine/SCons/Scanner/Dir.py | 4 +- src/engine/SCons/Scanner/DirTests.py | 13 +- src/engine/SCons/Scanner/Fortran.py | 6 +- src/engine/SCons/Scanner/FortranTests.py | 6 +- src/engine/SCons/Scanner/IDL.py | 4 +- src/engine/SCons/Scanner/IDLTests.py | 12 +- src/engine/SCons/Scanner/LaTeX.py | 20 +- src/engine/SCons/Scanner/LaTeXTests.py | 23 +- src/engine/SCons/Scanner/Prog.py | 4 +- src/engine/SCons/Scanner/ProgTests.py | 7 +- src/engine/SCons/Scanner/RC.py | 4 +- src/engine/SCons/Scanner/RCTests.py | 9 +- src/engine/SCons/Scanner/SWIG.py | 4 +- src/engine/SCons/Scanner/ScannerTests.py | 62 +- src/engine/SCons/Scanner/__init__.py | 4 +- src/engine/SCons/Scanner/__init__.xml | 2 +- src/engine/SCons/Script/Interactive.py | 4 +- src/engine/SCons/Script/Main.py | 25 +- src/engine/SCons/Script/Main.xml | 2 +- src/engine/SCons/Script/MainTests.py | 13 +- src/engine/SCons/Script/SConsOptions.py | 18 +- src/engine/SCons/Script/SConscript.py | 83 +- src/engine/SCons/Script/SConscript.xml | 25 +- src/engine/SCons/Script/SConscriptTests.py | 4 +- src/engine/SCons/Script/__init__.py | 27 +- src/engine/SCons/Subst.py | 24 +- src/engine/SCons/Subst.xml | 2 +- src/engine/SCons/SubstTests.py | 26 +- src/engine/SCons/Taskmaster.py | 13 +- src/engine/SCons/TaskmasterTests.py | 9 +- src/engine/SCons/Tool/386asm.py | 4 +- src/engine/SCons/Tool/386asm.xml | 2 +- src/engine/SCons/Tool/DCommon.py | 4 +- src/engine/SCons/Tool/DCommon.xml | 2 +- src/engine/SCons/Tool/FortranCommon.py | 4 +- src/engine/SCons/Tool/GettextCommon.py | 12 +- src/engine/SCons/Tool/JavaCommon.py | 192 +- src/engine/SCons/Tool/JavaCommonTests.py | 56 +- src/engine/SCons/Tool/MSCommon/__init__.py | 4 +- src/engine/SCons/Tool/MSCommon/arch.py | 12 +- src/engine/SCons/Tool/MSCommon/common.py | 14 +- src/engine/SCons/Tool/MSCommon/netframework.py | 4 +- src/engine/SCons/Tool/MSCommon/sdk.py | 14 +- src/engine/SCons/Tool/MSCommon/vc.py | 328 +- src/engine/SCons/Tool/MSCommon/vs.py | 19 +- src/engine/SCons/Tool/PharLapCommon.py | 4 +- src/engine/SCons/Tool/ToolTests.py | 4 +- src/engine/SCons/Tool/__init__.py | 535 +-- src/engine/SCons/Tool/__init__.xml | 2 +- src/engine/SCons/Tool/aixc++.py | 4 +- src/engine/SCons/Tool/aixc++.xml | 2 +- src/engine/SCons/Tool/aixcc.py | 4 +- src/engine/SCons/Tool/aixcc.xml | 2 +- src/engine/SCons/Tool/aixcxx.py | 4 +- src/engine/SCons/Tool/aixf77.py | 4 +- src/engine/SCons/Tool/aixf77.xml | 2 +- src/engine/SCons/Tool/aixlink.py | 4 +- src/engine/SCons/Tool/aixlink.xml | 2 +- src/engine/SCons/Tool/applelink.py | 155 +- src/engine/SCons/Tool/applelink.xml | 379 ++- src/engine/SCons/Tool/ar.py | 4 +- src/engine/SCons/Tool/ar.xml | 2 +- src/engine/SCons/Tool/as.py | 4 +- src/engine/SCons/Tool/as.xml | 2 +- src/engine/SCons/Tool/bcc32.py | 4 +- src/engine/SCons/Tool/bcc32.xml | 2 +- src/engine/SCons/Tool/c++.py | 4 +- src/engine/SCons/Tool/c++.xml | 2 +- src/engine/SCons/Tool/cc.py | 4 +- src/engine/SCons/Tool/cc.xml | 2 +- src/engine/SCons/Tool/clang.py | 15 +- src/engine/SCons/Tool/clang.xml | 2 +- src/engine/SCons/Tool/clangCommon/__init__.py | 17 + src/engine/SCons/Tool/clangxx.py | 17 +- src/engine/SCons/Tool/clangxx.xml | 2 +- src/engine/SCons/Tool/cvf.py | 4 +- src/engine/SCons/Tool/cvf.xml | 2 +- src/engine/SCons/Tool/cxx.py | 4 +- src/engine/SCons/Tool/cyglink.xml | 2 +- src/engine/SCons/Tool/default.py | 4 +- src/engine/SCons/Tool/default.xml | 2 +- src/engine/SCons/Tool/dmd.py | 4 +- src/engine/SCons/Tool/dmd.xml | 2 +- src/engine/SCons/Tool/docbook/__init__.py | 4 +- src/engine/SCons/Tool/docbook/__init__.xml | 2 +- src/engine/SCons/Tool/docbook/docs/manual.xml | 2 +- src/engine/SCons/Tool/dvi.py | 4 +- src/engine/SCons/Tool/dvi.xml | 2 +- src/engine/SCons/Tool/dvipdf.py | 4 +- src/engine/SCons/Tool/dvipdf.xml | 2 +- src/engine/SCons/Tool/dvips.py | 4 +- src/engine/SCons/Tool/dvips.xml | 2 +- src/engine/SCons/Tool/f03.py | 4 +- src/engine/SCons/Tool/f03.xml | 2 +- src/engine/SCons/Tool/f08.py | 4 +- src/engine/SCons/Tool/f08.xml | 2 +- src/engine/SCons/Tool/f77.py | 4 +- src/engine/SCons/Tool/f77.xml | 2 +- src/engine/SCons/Tool/f90.py | 4 +- src/engine/SCons/Tool/f90.xml | 2 +- src/engine/SCons/Tool/f95.py | 4 +- src/engine/SCons/Tool/f95.xml | 2 +- src/engine/SCons/Tool/filesystem.py | 4 +- src/engine/SCons/Tool/fortran.py | 4 +- src/engine/SCons/Tool/fortran.xml | 15 +- src/engine/SCons/Tool/g++.py | 4 +- src/engine/SCons/Tool/g++.xml | 2 +- src/engine/SCons/Tool/g77.py | 4 +- src/engine/SCons/Tool/g77.xml | 2 +- src/engine/SCons/Tool/gas.py | 4 +- src/engine/SCons/Tool/gas.xml | 2 +- src/engine/SCons/Tool/gcc.py | 51 +- src/engine/SCons/Tool/gcc.xml | 2 +- src/engine/SCons/Tool/gdc.py | 4 +- src/engine/SCons/Tool/gdc.xml | 2 +- src/engine/SCons/Tool/gettext.xml | 2 +- src/engine/SCons/Tool/gettext_tool.py | 16 +- src/engine/SCons/Tool/gfortran.py | 6 +- src/engine/SCons/Tool/gfortran.xml | 2 +- src/engine/SCons/Tool/gnulink.py | 4 +- src/engine/SCons/Tool/gnulink.xml | 2 +- src/engine/SCons/Tool/gs.py | 4 +- src/engine/SCons/Tool/gs.xml | 2 +- src/engine/SCons/Tool/gxx.py | 8 +- src/engine/SCons/Tool/hpc++.py | 4 +- src/engine/SCons/Tool/hpc++.xml | 2 +- src/engine/SCons/Tool/hpcc.py | 4 +- src/engine/SCons/Tool/hpcc.xml | 2 +- src/engine/SCons/Tool/hpcxx.py | 4 +- src/engine/SCons/Tool/hplink.py | 4 +- src/engine/SCons/Tool/hplink.xml | 2 +- src/engine/SCons/Tool/icc.py | 4 +- src/engine/SCons/Tool/icc.xml | 2 +- src/engine/SCons/Tool/icl.py | 4 +- src/engine/SCons/Tool/icl.xml | 2 +- src/engine/SCons/Tool/ifl.py | 4 +- src/engine/SCons/Tool/ifl.xml | 2 +- src/engine/SCons/Tool/ifort.py | 4 +- src/engine/SCons/Tool/ifort.xml | 2 +- src/engine/SCons/Tool/ilink.py | 4 +- src/engine/SCons/Tool/ilink.xml | 2 +- src/engine/SCons/Tool/ilink32.py | 4 +- src/engine/SCons/Tool/ilink32.xml | 2 +- src/engine/SCons/Tool/install.py | 4 +- src/engine/SCons/Tool/install.xml | 2 +- src/engine/SCons/Tool/intelc.py | 6 +- src/engine/SCons/Tool/intelc.xml | 2 +- src/engine/SCons/Tool/ipkg.py | 4 +- src/engine/SCons/Tool/jar.py | 135 +- src/engine/SCons/Tool/jar.xml | 2 +- src/engine/SCons/Tool/javac.py | 24 +- src/engine/SCons/Tool/javac.xml | 515 +-- src/engine/SCons/Tool/javacTests.py | 11 +- src/engine/SCons/Tool/javah.py | 14 +- src/engine/SCons/Tool/javah.xml | 2 +- src/engine/SCons/Tool/latex.py | 4 +- src/engine/SCons/Tool/latex.xml | 2 +- src/engine/SCons/Tool/ldc.py | 4 +- src/engine/SCons/Tool/ldc.xml | 2 +- src/engine/SCons/Tool/lex.py | 48 +- src/engine/SCons/Tool/lex.xml | 11 +- src/engine/SCons/Tool/link.py | 185 +- src/engine/SCons/Tool/link.xml | 2 +- src/engine/SCons/Tool/linkloc.py | 4 +- src/engine/SCons/Tool/linkloc.xml | 2 +- src/engine/SCons/Tool/m4.py | 4 +- src/engine/SCons/Tool/m4.xml | 2 +- src/engine/SCons/Tool/masm.py | 4 +- src/engine/SCons/Tool/masm.xml | 2 +- src/engine/SCons/Tool/midl.py | 6 +- src/engine/SCons/Tool/midl.xml | 2 +- src/engine/SCons/Tool/mingw.py | 132 +- src/engine/SCons/Tool/mingw.xml | 2 +- src/engine/SCons/Tool/msgfmt.py | 18 +- src/engine/SCons/Tool/msgfmt.xml | 2 +- src/engine/SCons/Tool/msginit.py | 18 +- src/engine/SCons/Tool/msginit.xml | 2 +- src/engine/SCons/Tool/msgmerge.py | 19 +- src/engine/SCons/Tool/msgmerge.xml | 2 +- src/engine/SCons/Tool/mslib.py | 6 +- src/engine/SCons/Tool/mslib.xml | 2 +- src/engine/SCons/Tool/mslink.py | 6 +- src/engine/SCons/Tool/mslink.xml | 2 +- src/engine/SCons/Tool/mssdk.py | 4 +- src/engine/SCons/Tool/mssdk.xml | 2 +- src/engine/SCons/Tool/msvc.py | 19 +- src/engine/SCons/Tool/msvc.xml | 14 +- src/engine/SCons/Tool/msvs.py | 40 +- src/engine/SCons/Tool/msvs.xml | 968 +++--- src/engine/SCons/Tool/msvsTests.py | 13 +- src/engine/SCons/Tool/mwcc.py | 4 +- src/engine/SCons/Tool/mwcc.xml | 2 +- src/engine/SCons/Tool/mwld.py | 4 +- src/engine/SCons/Tool/mwld.xml | 2 +- src/engine/SCons/Tool/nasm.py | 4 +- src/engine/SCons/Tool/nasm.xml | 2 +- src/engine/SCons/Tool/packaging.xml | 2 +- src/engine/SCons/Tool/packaging/__init__.py | 41 +- src/engine/SCons/Tool/packaging/__init__.xml | 61 +- src/engine/SCons/Tool/packaging/ipk.py | 12 +- src/engine/SCons/Tool/packaging/msi.py | 15 +- src/engine/SCons/Tool/packaging/rpm.py | 57 +- src/engine/SCons/Tool/packaging/src_tarbz2.py | 6 +- src/engine/SCons/Tool/packaging/src_targz.py | 6 +- src/engine/SCons/Tool/packaging/src_tarxz.py | 43 + src/engine/SCons/Tool/packaging/src_zip.py | 4 +- src/engine/SCons/Tool/packaging/tarbz2.py | 8 +- src/engine/SCons/Tool/packaging/targz.py | 6 +- src/engine/SCons/Tool/packaging/tarxz.py | 44 + src/engine/SCons/Tool/packaging/zip.py | 4 +- src/engine/SCons/Tool/pdf.py | 4 +- src/engine/SCons/Tool/pdf.xml | 2 +- src/engine/SCons/Tool/pdflatex.py | 4 +- src/engine/SCons/Tool/pdflatex.xml | 2 +- src/engine/SCons/Tool/pdftex.py | 4 +- src/engine/SCons/Tool/pdftex.xml | 2 +- src/engine/SCons/Tool/qt.py | 43 +- src/engine/SCons/Tool/qt.xml | 2 +- src/engine/SCons/Tool/rmic.py | 19 +- src/engine/SCons/Tool/rmic.xml | 2 +- src/engine/SCons/Tool/rpcgen.py | 6 +- src/engine/SCons/Tool/rpcgen.xml | 2 +- src/engine/SCons/Tool/rpm.py | 4 +- src/engine/SCons/Tool/rpmutils.py | 4 +- src/engine/SCons/Tool/sgiar.py | 4 +- src/engine/SCons/Tool/sgiar.xml | 2 +- src/engine/SCons/Tool/sgic++.py | 4 +- src/engine/SCons/Tool/sgic++.xml | 2 +- src/engine/SCons/Tool/sgicc.py | 4 +- src/engine/SCons/Tool/sgicc.xml | 2 +- src/engine/SCons/Tool/sgicxx.py | 4 +- src/engine/SCons/Tool/sgilink.py | 4 +- src/engine/SCons/Tool/sgilink.xml | 2 +- src/engine/SCons/Tool/sunar.py | 4 +- src/engine/SCons/Tool/sunar.xml | 2 +- src/engine/SCons/Tool/sunc++.py | 4 +- src/engine/SCons/Tool/sunc++.xml | 2 +- src/engine/SCons/Tool/suncc.py | 4 +- src/engine/SCons/Tool/suncc.xml | 2 +- src/engine/SCons/Tool/suncxx.py | 4 +- src/engine/SCons/Tool/sunf77.py | 4 +- src/engine/SCons/Tool/sunf77.xml | 2 +- src/engine/SCons/Tool/sunf90.py | 4 +- src/engine/SCons/Tool/sunf90.xml | 2 +- src/engine/SCons/Tool/sunf95.py | 4 +- src/engine/SCons/Tool/sunf95.xml | 2 +- src/engine/SCons/Tool/sunlink.py | 4 +- src/engine/SCons/Tool/sunlink.xml | 2 +- src/engine/SCons/Tool/swig.py | 42 +- src/engine/SCons/Tool/swig.xml | 9 +- src/engine/SCons/Tool/tar.py | 4 +- src/engine/SCons/Tool/tar.xml | 2 +- src/engine/SCons/Tool/tex.py | 32 +- src/engine/SCons/Tool/tex.xml | 2 +- src/engine/SCons/Tool/textfile.py | 4 +- src/engine/SCons/Tool/textfile.xml | 6 +- src/engine/SCons/Tool/tlib.py | 4 +- src/engine/SCons/Tool/tlib.xml | 2 +- src/engine/SCons/Tool/wix.py | 4 +- src/engine/SCons/Tool/wixTests.py | 8 +- src/engine/SCons/Tool/xgettext.py | 18 +- src/engine/SCons/Tool/xgettext.xml | 2 +- src/engine/SCons/Tool/yacc.py | 45 +- src/engine/SCons/Tool/yacc.xml | 2 +- src/engine/SCons/Tool/zip.py | 4 +- src/engine/SCons/Tool/zip.xml | 2 +- src/engine/SCons/Util.py | 170 +- src/engine/SCons/UtilTests.py | 321 +- src/engine/SCons/Variables/BoolVariable.py | 4 +- src/engine/SCons/Variables/BoolVariableTests.py | 9 +- src/engine/SCons/Variables/EnumVariable.py | 4 +- src/engine/SCons/Variables/EnumVariableTests.py | 9 +- src/engine/SCons/Variables/ListVariable.py | 4 +- src/engine/SCons/Variables/ListVariableTests.py | 9 +- src/engine/SCons/Variables/PackageVariable.py | 4 +- src/engine/SCons/Variables/PackageVariableTests.py | 9 +- src/engine/SCons/Variables/PathVariable.py | 4 +- src/engine/SCons/Variables/PathVariableTests.py | 9 +- src/engine/SCons/Variables/VariablesTests.py | 44 +- src/engine/SCons/Variables/__init__.py | 11 +- src/engine/SCons/Warnings.py | 17 +- src/engine/SCons/WarningsTests.py | 9 +- src/engine/SCons/__init__.py | 12 +- src/engine/SCons/compat/__init__.py | 6 +- src/engine/SCons/compat/_scons_dbm.py | 4 +- src/engine/SCons/cpp.py | 41 +- src/engine/SCons/cppTests.py | 145 +- src/engine/SCons/dblite.py | 16 +- src/engine/SCons/exitfuncs.py | 4 +- src/script/scons-configure-cache.py | 141 +- src/script/scons-time.py | 26 +- src/script/scons.bat | 12 +- src/script/scons.py | 113 +- src/script/sconsign.py | 227 +- src/setup.py | 61 +- src/test_files.py | 4 +- src/test_interrupts.py | 4 +- src/test_pychecker.py | 4 +- src/test_setup.py | 4 +- src/test_strings.py | 18 +- testing/README.md | 7 + testing/buildbot.hosts | 1 + testing/buildbot.yml | 80 + testing/framework/README.txt | 50 + testing/framework/SConscript | 61 + testing/framework/TestCmd.py | 1926 +++++++++++ testing/framework/TestCmdTests.py | 3419 ++++++++++++++++++++ testing/framework/TestCommon.py | 749 +++++ testing/framework/TestCommonTests.py | 2392 ++++++++++++++ testing/framework/TestRuntest.py | 173 + testing/framework/TestSCons.py | 1701 ++++++++++ testing/framework/TestSConsMSVS.py | 1285 ++++++++ testing/framework/TestSCons_time.py | 360 +++ testing/framework/TestSConsign.py | 90 + testing/framework/TestUnit/__init__.py | 5 + testing/framework/TestUnit/cli.py | 35 + testing/framework/TestUnit/taprunner.py | 130 + testing/framework/test-framework.rst | 523 +++ 584 files changed, 22556 insertions(+), 18894 deletions(-) create mode 100644 .appveyor.yml create mode 100644 .codecov.yml create mode 100644 .travis.yml delete mode 100644 QMTest/README.txt delete mode 100644 QMTest/SConscript delete mode 100644 QMTest/TestCmd.py delete mode 100644 QMTest/TestCmdTests.py delete mode 100644 QMTest/TestCommon.py delete mode 100644 QMTest/TestCommonTests.py delete mode 100644 QMTest/TestRuntest.py delete mode 100644 QMTest/TestSCons.py delete mode 100644 QMTest/TestSConsMSVS.py delete mode 100644 QMTest/TestSCons_time.py delete mode 100644 QMTest/TestSConsign.py delete mode 100644 QMTest/classes.qmc delete mode 100644 QMTest/configuration delete mode 100644 QMTest/scons_tdb.py delete mode 100644 QMTest/test-framework.rst mode change 100644 => 100755 README.rst delete mode 100644 bin/scons-cdist create mode 100644 site_scons/SConsRevision.py create mode 100644 site_scons/Utilities.py create mode 100644 site_scons/site_init.py create mode 100644 site_scons/soe_utils.py create mode 100644 site_scons/zip_utils.py mode change 100644 => 100755 src/Announce.txt mode change 100644 => 100755 src/CHANGES.txt mode change 100644 => 100755 src/RELEASE.txt delete mode 100644 src/engine/MANIFEST-xml.in delete mode 100644 src/engine/SCons/Options/BoolOption.py delete mode 100644 src/engine/SCons/Options/EnumOption.py delete mode 100644 src/engine/SCons/Options/ListOption.py delete mode 100644 src/engine/SCons/Options/PackageOption.py delete mode 100644 src/engine/SCons/Options/PathOption.py delete mode 100644 src/engine/SCons/Options/__init__.py create mode 100644 src/engine/SCons/Platform/mingw.py create mode 100644 src/engine/SCons/Platform/virtualenv.py create mode 100644 src/engine/SCons/Platform/virtualenvTests.py create mode 100644 src/engine/SCons/Tool/clangCommon/__init__.py create mode 100644 src/engine/SCons/Tool/packaging/src_tarxz.py create mode 100644 src/engine/SCons/Tool/packaging/tarxz.py mode change 100644 => 100755 src/script/scons.py mode change 100644 => 100755 src/setup.py create mode 100644 testing/README.md create mode 100644 testing/buildbot.hosts create mode 100644 testing/buildbot.yml create mode 100644 testing/framework/README.txt create mode 100644 testing/framework/SConscript create mode 100644 testing/framework/TestCmd.py create mode 100644 testing/framework/TestCmdTests.py create mode 100644 testing/framework/TestCommon.py create mode 100644 testing/framework/TestCommonTests.py create mode 100644 testing/framework/TestRuntest.py create mode 100644 testing/framework/TestSCons.py create mode 100644 testing/framework/TestSConsMSVS.py create mode 100644 testing/framework/TestSCons_time.py create mode 100644 testing/framework/TestSConsign.py create mode 100644 testing/framework/TestUnit/__init__.py create mode 100644 testing/framework/TestUnit/cli.py create mode 100644 testing/framework/TestUnit/taprunner.py create mode 100644 testing/framework/test-framework.rst diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..cbf7233 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,248 @@ +#version: '3.0.1.{build}' + +image: + # linux builds disabled for now + # - Ubuntu + - Visual Studio 2015 + - Visual Studio 2017 + +cache: + - downloads -> appveyor.yml + - '%LOCALAPPDATA%\pip\Cache' + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml + +install: + ### WINDOWS ### + # add python and python user-base to path for pip installs + - cmd: "C:\\%WINPYTHON%\\python.exe --version" + - cmd: for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -m site --user-site') do (set PYSITEDIR=%%g) + # use mingw 32 bit until #3291 is resolved + - cmd: "set PATH=C:\\%WINPYTHON%;C:\\%WINPYTHON%\\Scripts;C:\\ProgramData\\chocolatey\\bin;C:\\MinGW\\bin;C:\\MinGW\\msys\\1.0\\bin;C:\\cygwin\\bin;%PATH%" + - cmd: "C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pip setuptools wheel " + - cmd: "C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pypiwin32 coverage codecov" + - cmd: set STATIC_DEPS=true & C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off lxml + # install 3rd party tools to test with + - cmd: choco install --allow-empty-checksums dmd ldc swig vswhere xsltproc winflexbison + - cmd: set + + ### LINUX ### + - sh: sudo add-apt-repository -y ppa:deadsnakes/ppa + # allow the CI to continue even if some pkg in the pkglist may not be up to date + - sh: sudo apt-get update || true + - sh: sudo apt-get -y install python$PYTHON + - sh: wget https://bootstrap.pypa.io/get-pip.py + - sh: sudo -H python$PYTHON get-pip.py + - sh: which python$PYTHON + - sh: python$PYTHON --version + - sh: export PYSITEDIR=$(python$PYTHON -m site --user-site) + - sh: python$PYTHON -m pip install --user -U --progress-bar off pip setuptools wheel + - sh: python$PYTHON -m pip install --user -U --progress-bar off coverage codecov + - sh: STATIC_DEPS=true python$PYTHON -m pip install --user -U --progress-bar off lxml + - sh: ./.travis/install.sh + - sh: printenv + +# build matrix will be number of images multiplied +# by each '-' below, split jobs into groups of 4 +# to avoid timeing out +environment: + matrix: + - WINPYTHON: "Python27" + PYTHON: "2.7" + PYVER: 27 + BUILD_JOB_NUM: 1 + COVERAGE: 1 + - WINPYTHON: "Python27" + PYTHON: "2.7" + PYVER: 27 + BUILD_JOB_NUM: 2 + COVERAGE: 1 + - WINPYTHON: "Python27" + PYTHON: "2.7" + PYVER: 27 + BUILD_JOB_NUM: 3 + COVERAGE: 1 + - WINPYTHON: "Python27" + PYTHON: "2.7" + PYVER: 27 + BUILD_JOB_NUM: 4 + COVERAGE: 1 + + - WINPYTHON: "Python36" + PYTHON: "3.6" + PYVER: 36 + BUILD_JOB_NUM: 1 + COVERAGE: 1 + - WINPYTHON: "Python36" + PYTHON: "3.6" + PYVER: 36 + BUILD_JOB_NUM: 2 + COVERAGE: 1 + - WINPYTHON: "Python36" + PYTHON: "3.6" + PYVER: 36 + BUILD_JOB_NUM: 3 + COVERAGE: 1 + - WINPYTHON: "Python36" + PYTHON: "3.6" + PYVER: 36 + BUILD_JOB_NUM: 4 + COVERAGE: 1 + + - WINPYTHON: "Python35" + PYTHON: "3.5" + PYVER: 35 + BUILD_JOB_NUM: 1 + COVERAGE: 0 + - WINPYTHON: "Python35" + PYTHON: "3.5" + PYVER: 35 + BUILD_JOB_NUM: 2 + COVERAGE: 0 + - WINPYTHON: "Python35" + PYTHON: "3.5" + PYVER: 35 + BUILD_JOB_NUM: 3 + COVERAGE: 0 + - WINPYTHON: "Python35" + PYTHON: "3.5" + PYVER: 35 + BUILD_JOB_NUM: 4 + COVERAGE: 0 + + - WINPYTHON: "Python37" + PYTHON: "3.7" + PYVER: 37 + BUILD_JOB_NUM: 1 + COVERAGE: 0 + - WINPYTHON: "Python37" + PYTHON: "3.7" + PYVER: 37 + BUILD_JOB_NUM: 2 + COVERAGE: 0 + - WINPYTHON: "Python37" + PYTHON: "3.7" + PYVER: 37 + BUILD_JOB_NUM: 3 + COVERAGE: 0 + - WINPYTHON: "Python37" + PYTHON: "3.7" + PYVER: 37 + BUILD_JOB_NUM: 4 + COVERAGE: 0 + +# remove sets of build jobs based on critia below +# to fine tune the number and platforms tested +matrix: + exclude: + # test python 3.5, 3.6 on Visual Studio 2015 image + - image: Visual Studio 2015 + WINPYTHON: "Python37" + - image: Visual Studio 2015 + WINPYTHON: "Python27" + + # test python 2.7, 3.7 on Visual Studio 2015 image + - image: Visual Studio 2017 + WINPYTHON: "Python35" + - image: Visual Studio 2017 + WINPYTHON: "Python36" + + # test python 3.7 on Ubuntu + - image: Ubuntu + WINPYTHON: "Python27" + - image: Ubuntu + WINPYTHON: "Python35" + - image: Ubuntu + WINPYTHON: "Python36" + +# remove some binaries we dont to be found +before_build: + - ps: | + if ($isWindows) + { + dir "C:\Program Files\Git\usr\bin\x*.exe" + if (Test-Path "C:\Program Files\Git\usr\bin\xsltproc.EXE" ) { + Remove-Item "C:\Program Files\Git\usr\bin\xsltproc.EXE" -ErrorAction Ignore + } + dir "C:\Program Files\Git\usr\bin\x*.exe" + } + +build: off + +build_script: + # get all tests into a list + - cmd: "C:\\%WINPYTHON%\\python.exe runtest.py -l -a > all_tests.txt" + - sh: python$PYTHON runtest.py -l -a > all_tests.txt + + # setup coverage by creating the coverage config file, and adding coverage + # to the usercustomize so that all python processes start with coverage + - ps: | + if ($env:COVERAGE -eq 1){ + $env:COVERAGE_PROCESS_START = "$($env:APPVEYOR_BUILD_FOLDER)/.coveragerc"; + $env:PYTHONNOUSERSITE = ""; + New-Item -ItemType Directory -Force -Path "$($env:PYSITEDIR)"; + $env:COVERAGE_FILE = "$($env:APPVEYOR_BUILD_FOLDER)/.coverage"; + $usercustomizeText = "import os`r`nos.environ['COVERAGE_PROCESS_START'] = '$($env:COVERAGE_PROCESS_START)'`r`nimport coverage`r`ncoverage.process_startup()"; + $usercustomizeText|Set-Content "$($env:PYSITEDIR)/usercustomize.py"; + if ($isWindows) + { + $coveragercFile = "[run]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)/src`r`nparallel = True`r`ndisable_warnings = trace-changed`r`nomit =`r`n`t*Tests.py`r`n`tsrc\test_*`r`n`tsrc\setup.py`r`n`r`n[path]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)`r`n[report]`r`nomit =`r`n`t*Tests.py`r`n`tsrc\test_*`r`n`tsrc\setup.py`r`n`r`n" + } + else + { + $coveragercFile = "[run]`nsource = $($env:APPVEYOR_BUILD_FOLDER)/src`nparallel = True`ndisable_warnings = trace-changed`nomit =`n`t*Tests.py`n`tsrc/test_*`n`tsrc/setup.py`n`n[path]`nsource = $($env:APPVEYOR_BUILD_FOLDER)`n[report]`nomit =`n`t*Tests.py`n`tsrc/test_*`n`tsrc/setup.py`n`n" + } + $coveragercFile|Set-Content "$($env:COVERAGE_PROCESS_START)"; + } + + # setup portion of tests for this build job (1-4) + - ps: | + $TOTAL_BUILD_JOBS = 4; + $Lines = (Get-Content all_tests.txt | Measure-Object -line).Lines; + $start = ($Lines / $TOTAL_BUILD_JOBS) * ($Env:BUILD_JOB_NUM - 1); + $end = ($Lines / $TOTAL_BUILD_JOBS) * $Env:BUILD_JOB_NUM; + if ( $Env:BUILD_JOB_NUM -eq $TOTAL_BUILD_JOBS){ $end = $Lines }; + if ( $start -eq 0 ){ $start = 1 }; + get-content all_tests.txt | select -first ($end - $start) -skip ($start - 1) | Out-File -Encoding ASCII build_tests.txt; + + # 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: | + New-Item -Name exclude_list.txt -ItemType File + $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' + } + + # Windows run the tests + # NOTE: running powershell from cmd on purpose because it formats the output + # correctly + - cmd: powershell -Command "& { if($env:COVERAGE -eq 1) { coverage run -p --rcfile=$($env:COVERAGE_PROCESS_START) runtest.py --exclude-list exclude_list.txt -f build_tests.txt } else { C:\\%WINPYTHON%\\python.exe runtest.py -j 2 --exclude-list exclude_list.txt -f build_tests.txt }; if($LastExitCode -eq 2 -Or $LastExitCode -eq 0) { $host.SetShouldExit(0 )} else {$host.SetShouldExit(1)}}" + + # linux run the tests + # unset JAVA_TOOL_OPTIONS because newer java prints this to stderr + - sh: | + unset JAVA_TOOL_OPTIONS + if [ "$COVERAGE" -eq "1" ]; then + coverage run -p --rcfile="$COVERAGE_PROCESS_START" runtest.py --exclude-list exclude_list.txt -f build_tests.txt || if [[ $? == 2 ]]; then true; else false; fi; + else + python$PYTHON runtest.py -j 2 --exclude-list exclude_list.txt -f build_tests.txt || if [[ $? == 2 ]]; then true; else false; fi; + fi + +# run converage even if there was a test failure +on_finish: + - ps: | + if ($env:COVERAGE -eq 1) + { + & coverage combine + & coverage report + & coverage xml -o coverage_xml.xml + } + # running codecov in powershell causes an error so running in platform + # shells + - cmd: if %COVERAGE% equ 1 codecov -X gcov --file coverage_xml.xml + - sh: if [ $COVERAGE -eq 1 ]; then codecov -X gcov --file coverage_xml.xml; fi + # not using coveralls, so leaving it commented out in case we switch back + #- cmd: "C:\\%WINPYTHON%\\python.exe -m pip install --user -U coveralls" + #- sh: python$PYTHON -m pip install --user -U coveralls + #- ps: coveralls --rcfile="$($env:COVERAGE_PROCESS_START)" diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..375b10f --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,50 @@ +codecov: + notify: + # calculate coverge even when we fail + require_ci_to_pass: no + +ignore: + # ignore test files in the source + # this is redundant and should not be in the report anyways + # because the coveragerc file ignores them + - "*Test.py" + - "setup.py" + - "test_*" + +coverage: + precision: 2 + round: down + range: "70...100" + + notify: + irc: + default: + server: "chat.freenode.net#scons" + branches: master + threshold: null + message: "Coverage {{changed}} for {{owner}}/{{repo}}" # customize the message + flags: null + paths: null + + status: + project: + default: + # compare against the current coverage + # that PR is attempt to merge to + # don't consider a drop in coverage success + target: auto + threshold: null + base: pr + + patch: + default: + # considering only the lines changed + # make sure all new lines in the PR are covered + # to consider a success + target: 100 + threshold: null + base: pr + + changes: no + +comment: off diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a8cb940 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,188 @@ +dist: trusty +language: python + +notifications: + irc: "chat.freenode.net#scons" + +addons: + apt: + update: true + +os: + - linux + +install: + - ./.travis/install.sh + +# pypy is not passing atm, but still report build success for now +# allow coverage to fail, so we can still do testing for all platforms +matrix: + allow_failures: + - python: pypy + - python: pypy3 + - stage: Coverage + +# run coverage first as its still useful to collect +stages: + - Coverage + - Test + +jobs: + include: + - &test_job + stage: Test + script: python runtest.py -a -t || if [[ $? == 2 ]]; then true; else false; fi + before_script: skip + after_success: skip + python: 2.7 + env: + - PYVER=27 + - PYTHON=2.7 + sudo: required + + - <<: *test_job + python: 3.5 + env: + - PYVER=35 + - PYTHON=3.5 + sudo: required + + - <<: *test_job + python: 3.6 + env: + - PYVER=36 + - PYTHON=3.6 + sudo: required + + - <<: *test_job + python: 3.7 + env: + - PYVER=37 + - PYTHON=3.7 + sudo: required + dist: xenial # required for Python 3.7 (travis-ci/travis-ci#9069) + + - &slow_test_job + stage: Test + script: python runtest.py -a -j 4 -t || if [[ $? == 2 ]]; then true; else false; fi + before_script: skip + after_success: skip + python: pypy + env: + - PYVER=pypy + - PYTHON=pypy + sudo: required + + - <<: *slow_test_job + python: pypy3 + env: + - PYVER=pypy3 + - PYTHON=pypy3 + sudo: required + + + - &coverage_jobs + stage: Coverage + python: 2.7 + before_script: + # install our own python so we can modify usercustomize.py + - deactivate + - sudo add-apt-repository -y ppa:deadsnakes/ppa + - sudo apt-get update || true + - sudo apt-get -y install python$PYTHON + - wget https://bootstrap.pypa.io/get-pip.py + - sudo -H python$PYTHON get-pip.py + - which python$PYTHON + - python$PYTHON --version + - python$PYTHON -m pip install --user -U coverage codecov + # set this ensure user sites are available + - export PYTHONNOUSERSITE= + # attempt to get a location where we can store the usercustomize.py file + - python$PYTHON -m site + - export PYSITEDIR=$(python$PYTHON -m site --user-site) + - sudo mkdir -p $PYSITEDIR + - sudo touch ${PYSITEDIR}/usercustomize.py + - export COVERAGE_FILE=$PWD/.coverage + # write the usercustomize.py file so all python processes use coverage and know where the config file is + - echo "import os" | sudo tee --append ${PYSITEDIR}/usercustomize.py + - echo "os.environ['COVERAGE_PROCESS_START'] = '$PWD/.coveragerc'" | sudo tee --append ${PYSITEDIR}/usercustomize.py + - echo "import coverage" | sudo tee --append ${PYSITEDIR}/usercustomize.py + - echo "coverage.process_startup()" | sudo tee --append ${PYSITEDIR}/usercustomize.py + + script: + - export TOTAL_BUILD_JOBS=4 + # write the coverage config file + - export COVERAGE_PROCESS_START=$PWD/.coveragerc + - echo "[run]" >> .coveragerc + - echo "source = $PWD/src" >> .coveragerc + - echo "parallel = True" >> .coveragerc + - printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsrc/setup.py\n\n" >> .coveragerc + - echo "[path]" >> .coveragerc + - echo "source = $PWD" >> .coveragerc + - echo "[report]" >> .coveragerc + - printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsrc/setup.py\n\n" >> .coveragerc + # get a list of all the tests to split them up + - python$PYTHON runtest.py -l -a > all_tests + - let "start = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * (${BUILD_JOB_NUM} - 1)"; true; + - let "end = ($(wc -l < all_tests) / ${TOTAL_BUILD_JOBS}) * ${BUILD_JOB_NUM}" + - if (( ${BUILD_JOB_NUM} == ${TOTAL_BUILD_JOBS} )); then end=$(wc -l < all_tests); fi + - if (( ${start} == 0 )); then start=1; fi + - sed -n ${start},${end}p all_tests > build_tests + - coverage run -p --rcfile=$PWD/.coveragerc runtest.py -f build_tests || if [[ $? == 2 ]]; then true; else false; fi + + after_script: + - coverage combine + - coverage report + - coverage xml -o coverage_xml.xml + - codecov -X gcov --file coverage_xml.xml + # not using coveralls but leaving it commented to + # make it easy to re-enable + #- python$PYTHON -m pip install --user -U coveralls + #- coveralls --rcfile=$PWD/.coveragerc + + env: + - PYVER=27 + - PYTHON=2.7 + - BUILD_JOB_NUM=1 + + - <<: *coverage_jobs + env: + - PYVER=27 + - PYTHON=2.7 + - BUILD_JOB_NUM=2 + - <<: *coverage_jobs + env: + - PYVER=27 + - PYTHON=2.7 + - BUILD_JOB_NUM=3 + - <<: *coverage_jobs + env: + - PYVER=27 + - PYTHON=2.7 + - BUILD_JOB_NUM=4 + + - <<: *coverage_jobs + python: 3.6 + env: + - PYVER=36 + - PYTHON=3.6 + - BUILD_JOB_NUM=1 + - <<: *coverage_jobs + python: 3.6 + env: + - PYVER=36 + - PYTHON=3.6 + - BUILD_JOB_NUM=2 + - <<: *coverage_jobs + python: 3.6 + env: + - PYVER=36 + - PYTHON=3.6 + - BUILD_JOB_NUM=3 + - <<: *coverage_jobs + python: 3.6 + env: + - PYVER=36 + - PYTHON=3.6 + - BUILD_JOB_NUM=4 + diff --git a/LICENSE b/LICENSE index ff04f2e..6b6cd6c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2001 - 2017 The SCons Foundation +Copyright (c) 2001 - 2019 The SCons Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/QMTest/README.txt b/QMTest/README.txt deleted file mode 100644 index c644565..0000000 --- a/QMTest/README.txt +++ /dev/null @@ -1,58 +0,0 @@ -This directory contains testing infrastructure. Note that not all of -the pieces here are local to SCons. - - README.txt - - What you're looking at right now. - - SConscript - - Configuration for our packaging build, to copy the necessary - parts of the infrastructure into a build directory. - - TestCmd.py - TestCmdTests.py - TestCommon.py - TestCommonTests.py - - The TestCmd infrastructure for testing external commands. - These are for generic command testing, are used by some - other projects, and are developed separately from SCons. - (They're developed by SK, but still...) - - We've captured the unit tests (Test*Tests.py) for these files - along with the actual modules themselves to make it a little - easier to hack on them for our purposes. Note, however, - that any SCons-specific functionality should be implemented - in one of the - - TestRuntest.py - - Test infrastructure for our runtest.py script. - - TestSCons.py - - Test infrastructure for SCons itself. - - TestSConsMSVS.py - - Test infrastructure for SCons' Visual Studio support. - - TestSCons_time.py - - Test infrastructure for the scons-time.py script. - - TestSConsign.py - - Test infrastructure for the sconsign.py script. - - classes.qmc - configuration - scons-tdb.py - - Pieces for the use of QMTest to test SCons. We're moving away - from this infrastructure, in no small part because we're not - really using it as originally envisioned. - -Copyright (c) 2001 - 2017 The SCons Foundation -QMTest/README.txt rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog diff --git a/QMTest/SConscript b/QMTest/SConscript deleted file mode 100644 index 4107862..0000000 --- a/QMTest/SConscript +++ /dev/null @@ -1,64 +0,0 @@ -# -# SConscript file for external packages we need. -# - -# -# Copyright (c) 2001 - 2017 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.path - -Import('build_dir', 'env') - -files = [ - 'classes.qmc', - 'configuration', - 'scons_tdb.py', - 'TestCmd.py', - 'TestCommon.py', - 'TestRuntest.py', - 'TestSCons.py', - 'TestSConsign.py', - 'TestSCons_time.py', -] - -def copy(target, source, env): - t = str(target[0]) - s = str(source[0]) - c = open(s, 'r').read() - # Note: We construct the __ VERSION __ substitution string at - # run-time so it doesn't get replaced when this file gets copied - # into the tree for packaging. - c = c.replace('__' + 'VERSION' + '__', env['VERSION']) - open(t, 'w').write(c) - -for file in files: - # Guarantee that real copies of these files always exist in - # build/QMTest. If there's a symlink there, then this is an Aegis - # build and we blow them away now so that they'll get "built" later. - p = os.path.join(build_dir, 'QMTest', file) - if os.path.islink(p): - os.unlink(p) - if not os.path.isabs(p): - p = '#' + p - env.Command(p, file, copy) - Local(p) diff --git a/QMTest/TestCmd.py b/QMTest/TestCmd.py deleted file mode 100644 index 0aab9a8..0000000 --- a/QMTest/TestCmd.py +++ /dev/null @@ -1,1907 +0,0 @@ -""" -TestCmd.py: a testing framework for commands and scripts. - -The TestCmd module provides a framework for portable automated testing -of executable commands and scripts (in any language, not just Python), -especially commands and scripts that require file system interaction. - -In addition to running tests and evaluating conditions, the TestCmd -module manages and cleans up one or more temporary workspace -directories, and provides methods for creating files and directories in -those workspace directories from in-line data, here-documents), allowing -tests to be completely self-contained. - -A TestCmd environment object is created via the usual invocation: - - import TestCmd - test = TestCmd.TestCmd() - -There are a bunch of keyword arguments available at instantiation: - - test = TestCmd.TestCmd(description = 'string', - program = 'program_or_script_to_test', - interpreter = 'script_interpreter', - workdir = 'prefix', - subdir = 'subdir', - verbose = Boolean, - match = default_match_function, - match_stdout = default_match_stdout_function, - match_stderr = default_match_stderr_function, - diff = default_diff_stderr_function, - diff_stdout = default_diff_stdout_function, - diff_stderr = default_diff_stderr_function, - combine = Boolean) - -There are a bunch of methods that let you do different things: - - test.verbose_set(1) - - test.description_set('string') - - test.program_set('program_or_script_to_test') - - test.interpreter_set('script_interpreter') - test.interpreter_set(['script_interpreter', 'arg']) - - test.workdir_set('prefix') - test.workdir_set('') - - test.workpath('file') - test.workpath('subdir', 'file') - - test.subdir('subdir', ...) - - test.rmdir('subdir', ...) - - test.write('file', "contents\n") - test.write(['subdir', 'file'], "contents\n") - - test.read('file') - test.read(['subdir', 'file']) - test.read('file', mode) - test.read(['subdir', 'file'], mode) - - test.writable('dir', 1) - test.writable('dir', None) - - test.preserve(condition, ...) - - test.cleanup(condition) - - test.command_args(program = 'program_or_script_to_run', - interpreter = 'script_interpreter', - arguments = 'arguments to pass to program') - - test.run(program = 'program_or_script_to_run', - interpreter = 'script_interpreter', - arguments = 'arguments to pass to program', - chdir = 'directory_to_chdir_to', - stdin = 'input to feed to the program\n') - universal_newlines = True) - - p = test.start(program = 'program_or_script_to_run', - interpreter = 'script_interpreter', - arguments = 'arguments to pass to program', - universal_newlines = None) - - test.finish(self, p) - - test.pass_test() - test.pass_test(condition) - test.pass_test(condition, function) - - test.fail_test() - test.fail_test(condition) - test.fail_test(condition, function) - test.fail_test(condition, function, skip) - test.fail_test(condition, function, skip, message) - - test.no_result() - test.no_result(condition) - test.no_result(condition, function) - test.no_result(condition, function, skip) - - test.stdout() - test.stdout(run) - - test.stderr() - test.stderr(run) - - test.symlink(target, link) - - test.banner(string) - test.banner(string, width) - - test.diff(actual, expected) - - test.diff_stderr(actual, expected) - - test.diff_stdout(actual, expected) - - test.match(actual, expected) - - test.match_stderr(actual, expected) - - test.match_stdout(actual, expected) - - test.set_match_function(match, stdout, stderr) - - test.match_exact("actual 1\nactual 2\n", "expected 1\nexpected 2\n") - test.match_exact(["actual 1\n", "actual 2\n"], - ["expected 1\n", "expected 2\n"]) - test.match_caseinsensitive("Actual 1\nACTUAL 2\n", "expected 1\nEXPECTED 2\n") - - test.match_re("actual 1\nactual 2\n", regex_string) - test.match_re(["actual 1\n", "actual 2\n"], list_of_regexes) - - test.match_re_dotall("actual 1\nactual 2\n", regex_string) - test.match_re_dotall(["actual 1\n", "actual 2\n"], list_of_regexes) - - test.tempdir() - test.tempdir('temporary-directory') - - test.sleep() - test.sleep(seconds) - - test.where_is('foo') - test.where_is('foo', 'PATH1:PATH2') - test.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') - - test.unlink('file') - test.unlink('subdir', 'file') - -The TestCmd module provides pass_test(), fail_test(), and no_result() -unbound functions that report test results for use with the Aegis change -management system. These methods terminate the test immediately, -reporting PASSED, FAILED, or NO RESULT respectively, and exiting with -status 0 (success), 1 or 2 respectively. This allows for a distinction -between an actual failed test and a test that could not be properly -evaluated because of an external condition (such as a full file system -or incorrect permissions). - - import TestCmd - - TestCmd.pass_test() - TestCmd.pass_test(condition) - TestCmd.pass_test(condition, function) - - TestCmd.fail_test() - TestCmd.fail_test(condition) - TestCmd.fail_test(condition, function) - TestCmd.fail_test(condition, function, skip) - TestCmd.fail_test(condition, function, skip, message) - - TestCmd.no_result() - TestCmd.no_result(condition) - TestCmd.no_result(condition, function) - TestCmd.no_result(condition, function, skip) - -The TestCmd module also provides unbound global functions that handle -matching in the same way as the match_*() methods described above. - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.match_exact) - - test = TestCmd.TestCmd(match = TestCmd.match_caseinsensitive) - - test = TestCmd.TestCmd(match = TestCmd.match_re) - - test = TestCmd.TestCmd(match = TestCmd.match_re_dotall) - -These functions are also available as static methods: - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_exact) - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_caseinsensitive) - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re) - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re_dotall) - -These static methods can be accessed by a string naming the method: - - import TestCmd - - test = TestCmd.TestCmd(match = 'match_exact') - - test = TestCmd.TestCmd(match = 'match_caseinsensitive') - - test = TestCmd.TestCmd(match = 'match_re') - - test = TestCmd.TestCmd(match = 'match_re_dotall') - -The TestCmd module provides unbound global functions that can be used -for the "diff" argument to TestCmd.TestCmd instantiation: - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.match_re, - diff = TestCmd.diff_re) - - test = TestCmd.TestCmd(diff = TestCmd.simple_diff) - - test = TestCmd.TestCmd(diff = TestCmd.context_diff) - - test = TestCmd.TestCmd(diff = TestCmd.unified_diff) - -These functions are also available as static methods: - - import TestCmd - - test = TestCmd.TestCmd(match = TestCmd.TestCmd.match_re, - diff = TestCmd.TestCmd.diff_re) - - test = TestCmd.TestCmd(diff = TestCmd.TestCmd.simple_diff) - - test = TestCmd.TestCmd(diff = TestCmd.TestCmd.context_diff) - - test = TestCmd.TestCmd(diff = TestCmd.TestCmd.unified_diff) - -These static methods can be accessed by a string naming the method: - - import TestCmd - - test = TestCmd.TestCmd(match = 'match_re', diff = 'diff_re') - - test = TestCmd.TestCmd(diff = 'simple_diff') - - test = TestCmd.TestCmd(diff = 'context_diff') - - test = TestCmd.TestCmd(diff = 'unified_diff') - -The "diff" argument can also be used with standard difflib functions: - - import difflib - - test = TestCmd.TestCmd(diff = difflib.context_diff) - - test = TestCmd.TestCmd(diff = difflib.unified_diff) - -Lastly, the where_is() method also exists in an unbound function -version. - - import TestCmd - - TestCmd.where_is('foo') - TestCmd.where_is('foo', 'PATH1:PATH2') - TestCmd.where_is('foo', 'PATH1;PATH2', '.suffix3;.suffix4') -""" - -# Copyright 2000-2010 Steven Knight -# This module is free software, and you may redistribute it and/or modify -# it under the same terms as Python itself, so long as this copyright message -# and disclaimer are retained in their original form. -# -# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -from __future__ import division, print_function - -__author__ = "Steven Knight " -__revision__ = "TestCmd.py 1.3.D001 2010/06/03 12:58:27 knight" -__version__ = "1.3" - -import atexit -import difflib -import errno -import os -import re -import shutil -import signal -import stat -import sys -import tempfile -import threading -import time -import traceback -import types - - -IS_PY3 = sys.version_info[0] == 3 -IS_WINDOWS = sys.platform == 'win32' - - -class null(object): - pass - - -_Null = null() - -try: - from collections import UserList, UserString -except ImportError: - # no 'collections' module or no UserFoo in collections - exec('from UserList import UserList') - exec('from UserString import UserString') - -__all__ = [ - 'diff_re', - 'fail_test', - 'no_result', - 'pass_test', - 'match_exact', - 'match_caseinsensitive', - 'match_re', - 'match_re_dotall', - 'python', - '_python_', - 'TestCmd', - 'to_bytes', - 'to_str', -] - - -def is_List(e): - return isinstance(e, (list, UserList)) - - -def to_bytes(s): - if isinstance(s, bytes) or bytes is str: - return s - return bytes(s, 'utf-8') - - -def to_str(s): - if bytes is str or is_String(s): - return s - return str(s, 'utf-8') - - -try: - eval('unicode') -except NameError: - def is_String(e): - return isinstance(e, (str, UserString)) -else: - def is_String(e): - return isinstance(e, (str, unicode, UserString)) - -tempfile.template = 'testcmd.' -if os.name in ('posix', 'nt'): - tempfile.template = 'testcmd.' + str(os.getpid()) + '.' -else: - tempfile.template = 'testcmd.' - -re_space = re.compile('\s') - - -def _caller(tblist, skip): - string = "" - arr = [] - for file, line, name, text in tblist: - if file[-10:] == "TestCmd.py": - break - arr = [(file, line, name, text)] + arr - atfrom = "at" - for file, line, name, text in arr[skip:]: - if name in ("?", ""): - name = "" - else: - name = " (" + name + ")" - string = string + ("%s line %d of %s%s\n" % (atfrom, line, file, name)) - atfrom = "\tfrom" - return string - - -def fail_test(self=None, condition=1, function=None, skip=0, message=None): - """Cause the test to fail. - - By default, the fail_test() method reports that the test FAILED - and exits with a status of 1. If a condition argument is supplied, - the test fails only if the condition is true. - """ - if not condition: - return - if not function is None: - function() - of = "" - desc = "" - sep = " " - if not self is None: - if self.program: - of = " of " + self.program - sep = "\n\t" - if self.description: - desc = " [" + self.description + "]" - sep = "\n\t" - - at = _caller(traceback.extract_stack(), skip) - if message: - msg = "\t%s\n" % message - else: - msg = "" - sys.stderr.write("FAILED test" + of + desc + sep + at + msg) - - sys.exit(1) - - -def no_result(self=None, condition=1, function=None, skip=0): - """Causes a test to exit with no valid result. - - By default, the no_result() method reports NO RESULT for the test - and exits with a status of 2. If a condition argument is supplied, - the test fails only if the condition is true. - """ - if not condition: - return - if not function is None: - function() - of = "" - desc = "" - sep = " " - if not self is None: - if self.program: - of = " of " + self.program - sep = "\n\t" - if self.description: - desc = " [" + self.description + "]" - sep = "\n\t" - - at = _caller(traceback.extract_stack(), skip) - sys.stderr.write("NO RESULT for test" + of + desc + sep + at) - - sys.exit(2) - - -def pass_test(self=None, condition=1, function=None): - """Causes a test to pass. - - By default, the pass_test() method reports PASSED for the test - and exits with a status of 0. If a condition argument is supplied, - the test passes only if the condition is true. - """ - if not condition: - return - if not function is None: - function() - sys.stderr.write("PASSED\n") - sys.exit(0) - - -def match_exact(lines=None, matches=None, newline=os.sep): - """ - """ - - if isinstance(lines, bytes) or bytes is str: - newline = to_bytes(newline) - - if not is_List(lines): - lines = lines.split(newline) - if not is_List(matches): - matches = matches.split(newline) - if len(lines) != len(matches): - return - for i in range(len(lines)): - if lines[i] != matches[i]: - return - return 1 - - -def match_caseinsensitive(lines=None, matches=None): - """ - """ - if not is_List(lines): - lines = lines.split("\n") - if not is_List(matches): - matches = matches.split("\n") - if len(lines) != len(matches): - return - for i in range(len(lines)): - if lines[i].lower() != matches[i].lower(): - return - return 1 - - -def match_re(lines=None, res=None): - """ - """ - if not is_List(lines): - # CRs mess up matching (Windows) so split carefully - lines = re.split('\r?\n', lines) - if not is_List(res): - res = res.split("\n") - if len(lines) != len(res): - print("match_re: expected %d lines, found %d" % (len(res), len(lines))) - return - for i in range(len(lines)): - s = "^" + res[i] + "$" - try: - expr = re.compile(s) - except re.error as e: - msg = "Regular expression error in %s: %s" - raise re.error(msg % (repr(s), e.args[0])) - if not expr.search(lines[i]): - print("match_re: mismatch at line %d:\n search re='%s'\n line='%s'" % ( - i, s, lines[i])) - return - return 1 - - -def match_re_dotall(lines=None, res=None): - """ - """ - if not isinstance(lines, str): - lines = "\n".join(lines) - if not isinstance(res, str): - res = "\n".join(res) - s = "^" + res + "$" - try: - expr = re.compile(s, re.DOTALL) - except re.error as e: - msg = "Regular expression error in %s: %s" - raise re.error(msg % (repr(s), e.args[0])) - return expr.match(lines) - - -def simple_diff(a, b, fromfile='', tofile='', - fromfiledate='', tofiledate='', n=3, lineterm='\n'): - """ - A function with the same calling signature as difflib.context_diff - (diff -c) and difflib.unified_diff (diff -u) but which prints - output like the simple, unadorned 'diff" command. - """ - a = [to_str(q) for q in a] - b = [to_str(q) for q in b] - sm = difflib.SequenceMatcher(None, a, b) - - def comma(x1, x2): - return x1 + 1 == x2 and str(x2) or '%s,%s' % (x1 + 1, x2) - result = [] - for op, a1, a2, b1, b2 in sm.get_opcodes(): - if op == 'delete': - result.append("%sd%d" % (comma(a1, a2), b1)) - result.extend(['< ' + l for l in a[a1:a2]]) - elif op == 'insert': - result.append("%da%s" % (a1, comma(b1, b2))) - result.extend(['> ' + l for l in b[b1:b2]]) - elif op == 'replace': - result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) - result.extend(['< ' + l for l in a[a1:a2]]) - result.append('---') - result.extend(['> ' + l for l in b[b1:b2]]) - return result - - -def diff_re(a, b, fromfile='', tofile='', - fromfiledate='', tofiledate='', n=3, lineterm='\n'): - """ - A simple "diff" of two sets of lines when the expected lines - are regular expressions. This is a really dumb thing that - just compares each line in turn, so it doesn't look for - chunks of matching lines and the like--but at least it lets - you know exactly which line first didn't compare correctl... - """ - result = [] - diff = len(a) - len(b) - if diff < 0: - a = a + [''] * (-diff) - elif diff > 0: - b = b + [''] * diff - i = 0 - for aline, bline in zip(a, b): - s = "^" + aline + "$" - try: - expr = re.compile(s) - except re.error as e: - 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('---') - result.append('> ' + repr(b[i])) - i = i + 1 - return result - - -if os.name == 'posix': - def escape(arg): - "escape shell special characters" - slash = '\\' - special = '"$' - arg = arg.replace(slash, slash + slash) - for c in special: - arg = arg.replace(c, slash + c) - if re_space.search(arg): - arg = '"' + arg + '"' - return arg -else: - # Windows does not allow special characters in file names - # anyway, so no need for an escape function, we will just quote - # the arg. - def escape(arg): - if re_space.search(arg): - arg = '"' + arg + '"' - return arg - -if os.name == 'java': - python = os.path.join(sys.prefix, 'jython') -else: - python = os.environ.get('python_executable', sys.executable) -_python_ = escape(python) - -if sys.platform == 'win32': - - default_sleep_seconds = 2 - - def where_is(file, path=None, pathext=None): - if path is None: - path = os.environ['PATH'] - if is_String(path): - path = path.split(os.pathsep) - if pathext is None: - pathext = os.environ['PATHEXT'] - if is_String(pathext): - pathext = pathext.split(os.pathsep) - for ext in pathext: - if ext.lower() == file[-len(ext):].lower(): - pathext = [''] - break - for dir in path: - f = os.path.join(dir, file) - for ext in pathext: - fext = f + ext - if os.path.isfile(fext): - return fext - return None - -else: - - def where_is(file, path=None, pathext=None): - if path is None: - path = os.environ['PATH'] - if is_String(path): - path = path.split(os.pathsep) - for dir in path: - f = os.path.join(dir, file) - if os.path.isfile(f): - try: - st = os.stat(f) - except OSError: - continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: - return f - return None - - default_sleep_seconds = 1 - - -import subprocess - -try: - subprocess.Popen.terminate -except AttributeError: - if sys.platform == 'win32': - import win32process - - def terminate(self): - win32process.TerminateProcess(self._handle, 1) - else: - def terminate(self): - os.kill(self.pid, signal.SIGTERM) - method = types.MethodType(terminate, None, subprocess.Popen) - setattr(subprocess.Popen, 'terminate', method) - - -# From Josiah Carlson, -# ASPN : Python Cookbook : Module to allow Asynchronous subprocess use on Windows and Posix platforms -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 - -PIPE = subprocess.PIPE - -if sys.platform == 'win32': # and subprocess.mswindows: - try: - from win32file import ReadFile, WriteFile - from win32pipe import PeekNamedPipe - except ImportError: - # If PyWin32 is not available, try ctypes instead - # XXX These replicate _just_enough_ PyWin32 behaviour for our purposes - import ctypes - from ctypes.wintypes import DWORD - - def ReadFile(hFile, bufSize, ol=None): - assert ol is None - lpBuffer = ctypes.create_string_buffer(bufSize) - bytesRead = DWORD() - bErr = ctypes.windll.kernel32.ReadFile( - hFile, lpBuffer, bufSize, ctypes.byref(bytesRead), ol) - if not bErr: - raise ctypes.WinError() - return (0, ctypes.string_at(lpBuffer, bytesRead.value)) - - def WriteFile(hFile, data, ol=None): - assert ol is None - bytesWritten = DWORD() - bErr = ctypes.windll.kernel32.WriteFile( - hFile, data, len(data), ctypes.byref(bytesWritten), ol) - if not bErr: - raise ctypes.WinError() - return (0, bytesWritten.value) - - def PeekNamedPipe(hPipe, size): - assert size == 0 - bytesAvail = DWORD() - bErr = ctypes.windll.kernel32.PeekNamedPipe( - hPipe, None, size, None, ctypes.byref(bytesAvail), None) - if not bErr: - raise ctypes.WinError() - return ("", bytesAvail.value, None) - import msvcrt -else: - import select - import fcntl - - try: - fcntl.F_GETFL - except AttributeError: - fcntl.F_GETFL = 3 - - try: - fcntl.F_SETFL - except AttributeError: - fcntl.F_SETFL = 4 - - -class Popen(subprocess.Popen): - def recv(self, maxsize=None): - return self._recv('stdout', maxsize) - - def recv_err(self, maxsize=None): - return self._recv('stderr', maxsize) - - def send_recv(self, input='', maxsize=None): - return self.send(input), self.recv(maxsize), self.recv_err(maxsize) - - def get_conn_maxsize(self, which, maxsize): - if maxsize is None: - maxsize = 1024 - elif maxsize < 1: - maxsize = 1 - return getattr(self, which), maxsize - - def _close(self, which): - getattr(self, which).close() - setattr(self, which, None) - - if sys.platform == 'win32': # and subprocess.mswindows: - def send(self, input): - input = to_bytes(input) - if not self.stdin: - return None - - try: - x = msvcrt.get_osfhandle(self.stdin.fileno()) - (errCode, written) = WriteFile(x, input) - except ValueError: - return self._close('stdin') - except (subprocess.pywintypes.error, Exception) as why: - if why.args[0] in (109, errno.ESHUTDOWN): - return self._close('stdin') - raise - - return written - - def _recv(self, which, maxsize): - conn, maxsize = self.get_conn_maxsize(which, maxsize) - if conn is None: - return None - - try: - x = msvcrt.get_osfhandle(conn.fileno()) - (read, nAvail, nMessage) = PeekNamedPipe(x, 0) - if maxsize < nAvail: - nAvail = maxsize - if nAvail > 0: - (errCode, read) = ReadFile(x, nAvail, None) - except ValueError: - return self._close(which) - except (subprocess.pywintypes.error, Exception) as why: - if why.args[0] in (109, errno.ESHUTDOWN): - return self._close(which) - raise - - # if self.universal_newlines: - # read = self._translate_newlines(read) - return read - - else: - def send(self, input): - if not self.stdin: - return None - - if not select.select([], [self.stdin], [], 0)[1]: - return 0 - - try: - written = os.write(self.stdin.fileno(), - bytearray(input, 'utf-8')) - except OSError as why: - if why.args[0] == errno.EPIPE: # broken pipe - return self._close('stdin') - raise - - return written - - def _recv(self, which, maxsize): - conn, maxsize = self.get_conn_maxsize(which, maxsize) - if conn is None: - return None - - try: - flags = fcntl.fcntl(conn, fcntl.F_GETFL) - except TypeError: - flags = None - else: - if not conn.closed: - fcntl.fcntl(conn, fcntl.F_SETFL, flags | os.O_NONBLOCK) - - try: - if not select.select([conn], [], [], 0)[0]: - return '' - - r = conn.read(maxsize) - if not r: - return self._close(which) - - # if self.universal_newlines: - # r = self._translate_newlines(r) - return r - finally: - if not conn.closed and not flags is None: - fcntl.fcntl(conn, fcntl.F_SETFL, flags) - - -disconnect_message = "Other end disconnected!" - - -def recv_some(p, t=.1, e=1, tr=5, stderr=0): - if tr < 1: - tr = 1 - x = time.time() + t - y = [] - r = '' - pr = p.recv - if stderr: - pr = p.recv_err - while time.time() < x or r: - r = pr() - if r is None: - if e: - raise Exception(disconnect_message) - else: - break - elif r: - y.append(r) - else: - time.sleep(max((x - time.time()) / tr, 0)) - return ''.join(y) - - -def send_all(p, data): - while len(data): - sent = p.send(data) - if sent is None: - raise Exception(disconnect_message) - data = memoryview(data)[sent:] - - -_Cleanup = [] - - -def _clean(): - global _Cleanup - cleanlist = [c for c in _Cleanup if c] - del _Cleanup[:] - cleanlist.reverse() - for test in cleanlist: - test.cleanup() - - -atexit.register(_clean) - - -class TestCmd(object): - """Class TestCmd - """ - - def __init__(self, description=None, - program=None, - interpreter=None, - workdir=None, - subdir=None, - verbose=None, - match=None, - match_stdout=None, - match_stderr=None, - diff=None, - diff_stdout=None, - diff_stderr=None, - combine=0, - universal_newlines=True, - timeout=None): - self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) - self._cwd = os.getcwd() - self.description_set(description) - self.program_set(program) - self.interpreter_set(interpreter) - if verbose is None: - try: - verbose = max(0, int(os.environ.get('TESTCMD_VERBOSE', 0))) - except ValueError: - verbose = 0 - self.verbose_set(verbose) - self.combine = combine - self.universal_newlines = universal_newlines - self.process = None - self.set_timeout(timeout) - self.set_match_function(match, match_stdout, match_stderr) - self.set_diff_function(diff, diff_stdout, diff_stderr) - self._dirlist = [] - self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0} - preserve_value = os.environ.get('PRESERVE', False) - if preserve_value not in [0, '0', 'False']: - self._preserve['pass_test'] = os.environ['PRESERVE'] - self._preserve['fail_test'] = os.environ['PRESERVE'] - self._preserve['no_result'] = os.environ['PRESERVE'] - else: - try: - self._preserve['pass_test'] = os.environ['PRESERVE_PASS'] - except KeyError: - pass - try: - self._preserve['fail_test'] = os.environ['PRESERVE_FAIL'] - except KeyError: - pass - try: - self._preserve['no_result'] = os.environ['PRESERVE_NO_RESULT'] - except KeyError: - pass - self._stdout = [] - self._stderr = [] - self.status = None - self.condition = 'no_result' - self.workdir_set(workdir) - self.subdir(subdir) - self.fixture_dirs = [] - - def __del__(self): - self.cleanup() - - def __repr__(self): - return "%x" % id(self) - - banner_char = '=' - banner_width = 80 - - def banner(self, s, width=None): - if width is None: - width = self.banner_width - return s + self.banner_char * (width - len(s)) - - escape = staticmethod(escape) - - def canonicalize(self, path): - if is_List(path): - path = os.path.join(*tuple(path)) - if not os.path.isabs(path): - path = os.path.join(self.workdir, path) - return path - - def chmod(self, path, mode): - """Changes permissions on the specified file or directory - path name.""" - path = self.canonicalize(path) - os.chmod(path, mode) - - def cleanup(self, condition=None): - """Removes any temporary working directories for the specified - TestCmd environment. If the environment variable PRESERVE was - set when the TestCmd environment was created, temporary working - directories are not removed. If any of the environment variables - PRESERVE_PASS, PRESERVE_FAIL, or PRESERVE_NO_RESULT were set - when the TestCmd environment was created, then temporary working - directories are not removed if the test passed, failed, or had - no result, respectively. Temporary working directories are also - preserved for conditions specified via the preserve method. - - Typically, this method is not called directly, but is used when - the script exits to clean up temporary working directories as - appropriate for the exit status. - """ - if not self._dirlist: - return - os.chdir(self._cwd) - self.workdir = None - if condition is None: - condition = self.condition - if self._preserve[condition]: - for dir in self._dirlist: - print(u"Preserved directory " + dir + "\n") - else: - list = self._dirlist[:] - list.reverse() - for dir in list: - self.writable(dir, 1) - shutil.rmtree(dir, ignore_errors=1) - self._dirlist = [] - - global _Cleanup - if self in _Cleanup: - _Cleanup.remove(self) - - def command_args(self, program=None, - interpreter=None, - arguments=None): - if not self.external: - if program: - if isinstance(program, str) and not os.path.isabs(program): - program = os.path.join(self._cwd, program) - else: - program = self.program - if not interpreter: - interpreter = self.interpreter - else: - if not program: - program = self.program - if not interpreter: - interpreter = self.interpreter - if not isinstance(program, (list, tuple)): - program = [program] - cmd = list(program) - if interpreter: - if not isinstance(interpreter, (list, tuple)): - interpreter = [interpreter] - cmd = list(interpreter) + cmd - if arguments: - if isinstance(arguments, str): - arguments = arguments.split() - cmd.extend(arguments) - return cmd - - def description_set(self, description): - """Set the description of the functionality being tested. - """ - self.description = description - - def set_diff_function(self, diff=_Null, stdout=_Null, stderr=_Null): - """Sets the specified diff functions. - """ - if diff is not _Null: - self._diff_function = diff - if stdout is not _Null: - self._diff_stdout_function = stdout - if stderr is not _Null: - self._diff_stderr_function = stderr - - def diff(self, a, b, name=None, diff_function=None, *args, **kw): - if diff_function is None: - try: - diff_function = getattr(self, self._diff_function) - except TypeError: - diff_function = self._diff_function - if diff_function is None: - diff_function = self.simple_diff - if name is not None: - print(self.banner(name)) - args = (a.splitlines(), b.splitlines()) + args - for line in diff_function(*args, **kw): - print(line) - - def diff_stderr(self, a, b, *args, **kw): - """Compare actual and expected file contents. - """ - try: - diff_stderr_function = getattr(self, self._diff_stderr_function) - except TypeError: - diff_stderr_function = self._diff_stderr_function - return self.diff(a, b, diff_function=diff_stderr_function, *args, **kw) - - def diff_stdout(self, a, b, *args, **kw): - """Compare actual and expected file contents. - """ - try: - diff_stdout_function = getattr(self, self._diff_stdout_function) - except TypeError: - diff_stdout_function = self._diff_stdout_function - return self.diff(a, b, diff_function=diff_stdout_function, *args, **kw) - - simple_diff = staticmethod(simple_diff) - - diff_re = staticmethod(diff_re) - - context_diff = staticmethod(difflib.context_diff) - - unified_diff = staticmethod(difflib.unified_diff) - - def fail_test(self, condition=1, function=None, skip=0, message=None): - """Cause the test to fail. - """ - if not condition: - return - self.condition = 'fail_test' - fail_test(self=self, - condition=condition, - function=function, - skip=skip, - message=message) - - def interpreter_set(self, interpreter): - """Set the program to be used to interpret the program - under test as a script. - """ - self.interpreter = interpreter - - def set_match_function(self, match=_Null, stdout=_Null, stderr=_Null): - """Sets the specified match functions. - """ - if match is not _Null: - self._match_function = match - if stdout is not _Null: - self._match_stdout_function = stdout - if stderr is not _Null: - self._match_stderr_function = stderr - - def match(self, lines, matches): - """Compare actual and expected file contents. - """ - try: - match_function = getattr(self, self._match_function) - except TypeError: - match_function = self._match_function - if match_function is None: - # Default is regular expression matches. - match_function = self.match_re - return match_function(lines, matches) - - def match_stderr(self, lines, matches): - """Compare actual and expected file contents. - """ - try: - match_stderr_function = getattr(self, self._match_stderr_function) - except TypeError: - match_stderr_function = self._match_stderr_function - if match_stderr_function is None: - # Default is to use whatever match= is set to. - match_stderr_function = self.match - return match_stderr_function(lines, matches) - - def match_stdout(self, lines, matches): - """Compare actual and expected file contents. - """ - try: - match_stdout_function = getattr(self, self._match_stdout_function) - except TypeError: - match_stdout_function = self._match_stdout_function - if match_stdout_function is None: - # Default is to use whatever match= is set to. - match_stdout_function = self.match - return match_stdout_function(lines, matches) - - match_exact = staticmethod(match_exact) - - match_caseinsensitive = staticmethod(match_caseinsensitive) - - match_re = staticmethod(match_re) - - match_re_dotall = staticmethod(match_re_dotall) - - def no_result(self, condition=1, function=None, skip=0): - """Report that the test could not be run. - """ - if not condition: - return - self.condition = 'no_result' - no_result(self=self, - condition=condition, - function=function, - skip=skip) - - def pass_test(self, condition=1, function=None): - """Cause the test to pass. - """ - if not condition: - return - self.condition = 'pass_test' - pass_test(self=self, condition=condition, function=function) - - def preserve(self, *conditions): - """Arrange for the temporary working directories for the - specified TestCmd environment to be preserved for one or more - conditions. If no conditions are specified, arranges for - the temporary working directories to be preserved for all - conditions. - """ - if conditions is (): - conditions = ('pass_test', 'fail_test', 'no_result') - for cond in conditions: - self._preserve[cond] = 1 - - def program_set(self, program): - """Set the executable program or script to be tested. - """ - if not self.external: - if program and not os.path.isabs(program): - program = os.path.join(self._cwd, program) - self.program = program - - def read(self, file, mode='rb', newline=None): - """Reads and returns the contents of the specified file name. - The file name may be a list, in which case the elements are - concatenated with the os.path.join() method. The file is - assumed to be under the temporary working directory unless it - is an absolute path name. The I/O mode for the file may - be specified; it must begin with an 'r'. The default is - 'rb' (binary read). - """ - file = self.canonicalize(file) - if mode[0] != 'r': - raise ValueError("mode must begin with 'r'") - if IS_PY3 and 'b' not in mode: - return open(file, mode, newline=newline).read() - else: - return open(file, mode).read() - - def rmdir(self, dir): - """Removes the specified dir name. - The dir name may be a list, in which case the elements are - concatenated with the os.path.join() method. The dir is - assumed to be under the temporary working directory unless it - is an absolute path name. - The dir must be empty. - """ - 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. - """ - head, tail = os.path.split(path) - result = [] - if not tail: - if head == path: - return [head] - else: - result.append(tail) - head, tail = os.path.split(head) - while head and tail: - result.append(tail) - head, tail = os.path.split(head) - result.append(head or tail) - result.reverse() - - return result - - def dir_fixture(self, srcdir, dstdir=None): - """Copies the contents of the specified folder srcdir from - the directory of the called script, to the current - working directory. - The srcdir name may be a list, in which case the elements are - concatenated with the os.path.join() method. The dstdir is - assumed to be under the temporary working directory, it gets - created automatically, if it does not already exist. - """ - - if srcdir and self.fixture_dirs and not os.path.isabs(srcdir): - for dir in self.fixture_dirs: - spath = os.path.join(dir, srcdir) - if os.path.isdir(spath): - break - else: - spath = srcdir - - if dstdir: - dstdir = self.canonicalize(dstdir) - else: - dstdir = '.' - - if dstdir != '.' and not os.path.exists(dstdir): - dstlist = self.parse_path(dstdir) - if len(dstlist) > 0 and dstlist[0] == ".": - dstlist = dstlist[1:] - for idx in range(len(dstlist)): - self.subdir(dstlist[:idx + 1]) - - if dstdir and self.workdir: - dstdir = os.path.join(self.workdir, dstdir) - - for entry in os.listdir(spath): - epath = os.path.join(spath, entry) - dpath = os.path.join(dstdir, entry) - if os.path.isdir(epath): - # Copy the subfolder - shutil.copytree(epath, dpath) - else: - shutil.copy(epath, dpath) - - def file_fixture(self, srcfile, dstfile=None): - """Copies the file srcfile from the directory of - the called script, to the current working directory. - The dstfile is assumed to be under the temporary working - directory unless it is an absolute path name. - If dstfile is specified its target directory gets created - automatically, if it does not already exist. - """ - srcpath, srctail = os.path.split(srcfile) - - if srcpath and (not self.fixture_dirs or os.path.isabs(srcpath)): - spath = srcfile - else: - for dir in self.fixture_dirs: - spath = os.path.join(dir, srcfile) - if os.path.isfile(spath): - break - - if not dstfile: - if srctail: - dpath = os.path.join(self.workdir, srctail) - else: - return - else: - dstpath, dsttail = os.path.split(dstfile) - if dstpath: - if not os.path.exists(os.path.join(self.workdir, dstpath)): - dstlist = self.parse_path(dstpath) - if len(dstlist) > 0 and dstlist[0] == ".": - dstlist = dstlist[1:] - for idx in range(len(dstlist)): - self.subdir(dstlist[:idx + 1]) - - dpath = os.path.join(self.workdir, dstfile) - shutil.copy(spath, dpath) - - def start(self, program=None, - interpreter=None, - arguments=None, - universal_newlines=None, - timeout=_Null, - **kw): - """ - Starts a program or script for the test environment. - - The specified program will have the original directory - prepended unless it is enclosed in a [list]. - """ - cmd = self.command_args(program, interpreter, arguments) - if self.verbose: - cmd_string = ' '.join([self.escape(c) for c in cmd]) - sys.stderr.write(cmd_string + "\n") - if universal_newlines is None: - universal_newlines = self.universal_newlines - - # On Windows, if we make stdin a pipe when we plan to send - # no input, and the test program exits before - # Popen calls msvcrt.open_osfhandle, that call will fail. - # So don't use a pipe for stdin if we don't need one. - stdin = kw.get('stdin', None) - if stdin is not None: - stdin = subprocess.PIPE - - combine = kw.get('combine', self.combine) - if combine: - stderr_value = subprocess.STDOUT - else: - stderr_value = subprocess.PIPE - - if timeout is _Null: - timeout = self.timeout - if timeout: - self.timer = threading.Timer(float(timeout), self._timeout) - self.timer.start() - - if IS_PY3 and sys.platform == 'win32': - # Set this otherwist stdout/stderr pipes default to - # windows default locale cp1252 which will throw exception - # if using non-ascii characters. - # For example test/Install/non-ascii-name.py - os.environ['PYTHONIOENCODING'] = 'utf-8' - - # It seems that all pythons up to py3.6 still set text mode if you set encoding. - # TODO: File enhancement request on python to propagate universal_newlines even - # if encoding is set.hg c - p = Popen(cmd, - stdin=stdin, - stdout=subprocess.PIPE, - stderr=stderr_value, - env=os.environ, - universal_newlines=False) - - self.process = p - return p - - @staticmethod - def fix_binary_stream(stream): - """ - Handle stdout/stderr from popen when we specify universal_newlines = False. - This will read from the pipes in binary mode, not decode the output, - and not convert line endings to \n. - We do this because in py3 (3.5) with universal_newlines=True, it will - choose the default system locale to decode the output, and this breaks unicode - output. Specifically breaking test/option--tree.py which outputs a unicode char. - - py 3.6 allows us to pass an encoding param to popen thus not requiring the decode - nor end of line handling, because we propagate universal_newlines as specified. - - TODO: Do we need to pass universal newlines into this function? - """ - - if not stream: - return stream - # TODO: Run full tests on both platforms and see if this fixes failures - # It seems that py3.6 still sets text mode if you set encoding. - elif sys.version_info[0] == 3:# TODO and sys.version_info[1] < 6: - stream = stream.decode('utf-8') - stream = stream.replace('\r\n', '\n') - elif sys.version_info[0] == 2: - stream = stream.replace('\r\n', '\n') - - return stream - - - def finish(self, popen=None, **kw): - """ - Finishes and waits for the process being run under control of - the specified popen argument, recording the exit status, - standard output and error output. - """ - if popen is None: - popen = self.process - stdout, stderr = popen.communicate() - - 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 '') - self._stderr.append(stderr or '') - - def run(self, program=None, - interpreter=None, - arguments=None, - chdir=None, - stdin=None, - universal_newlines=None, - timeout=_Null): - """Runs a test of the program or script for the test - environment. Standard output and error output are saved for - future retrieval via the stdout() and stderr() methods. - - The specified program will have the original directory - prepended unless it is enclosed in a [list]. - """ - if self.external: - if not program: - program = self.program - if not interpreter: - interpreter = self.interpreter - - if universal_newlines is None: - universal_newlines = self.universal_newlines - - if chdir: - oldcwd = os.getcwd() - if not os.path.isabs(chdir): - chdir = os.path.join(self.workpath(chdir)) - if self.verbose: - sys.stderr.write("chdir(" + chdir + ")\n") - os.chdir(chdir) - p = self.start(program=program, - interpreter=interpreter, - arguments=arguments, - universal_newlines=universal_newlines, - timeout=timeout, - stdin=stdin) - if is_List(stdin): - stdin = ''.join(stdin) - - if stdin and IS_PY3:# and sys.version_info[1] < 6: - stdin = to_bytes(stdin) - - # TODO(sgk): figure out how to re-use the logic in the .finish() - # method above. Just calling it from here causes problems with - # 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 - 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 '') - - if chdir: - os.chdir(oldcwd) - if self.verbose >= 2: - write = sys.stdout.write - write('============ STATUS: %d\n' % self.status) - out = self.stdout() - if out or self.verbose >= 3: - write('============ BEGIN STDOUT (len=%d):\n' % len(out)) - write(out) - write('============ END STDOUT\n') - err = self.stderr() - if err or self.verbose >= 3: - write('============ BEGIN STDERR (len=%d)\n' % len(err)) - write(err) - write('============ END STDERR\n') - - def sleep(self, seconds=default_sleep_seconds): - """Sleeps at least the specified number of seconds. If no - number is specified, sleeps at least the minimum number of - seconds necessary to advance file time stamps on the current - system. Sleeping more seconds is all right. - """ - time.sleep(seconds) - - def stderr(self, run=None): - """Returns the error output from the specified run number. - 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. - """ - if not run: - run = len(self._stderr) - elif run < 0: - run = len(self._stderr) + run - run = run - 1 - return self._stderr[run] - - def stdout(self, run=None): - """Returns the standard output from the specified run number. - If there is no specified run number, then returns the standard - output of the last run. If the run number is less than zero, - then returns the standard output from that many runs back from - the current run. - """ - if not run: - run = len(self._stdout) - elif run < 0: - run = len(self._stdout) + run - run = run - 1 - return self._stdout[run] - - def subdir(self, *subdirs): - """Create new subdirectories under the temporary working - directory, one for each argument. An argument may be a list, - in which case the list elements are concatenated using the - os.path.join() method. Subdirectories multiple levels deep - must be created using a separate argument for each level: - - test.subdir('sub', ['sub', 'dir'], ['sub', 'dir', 'ectory']) - - Returns the number of subdirectories actually created. - """ - count = 0 - for sub in subdirs: - if sub is None: - continue - if is_List(sub): - sub = os.path.join(*tuple(sub)) - new = os.path.join(self.workdir, sub) - try: - os.mkdir(new) - except OSError: - pass - else: - count = count + 1 - return count - - def symlink(self, target, link): - """Creates a symlink to the specified target. - The link name may be a list, in which case the elements are - concatenated with the os.path.join() method. The link is - assumed to be under the temporary working directory unless it - is an absolute path name. The target is *not* assumed to be - under the temporary working directory. - """ - if sys.platform == 'win32': - # Skip this on windows as we're not enabling it due to - # it requiring user permissions which aren't always present - # and we don't have a good way to detect those permissions yet. - return - link = self.canonicalize(link) - try: - os.symlink(target, link) - except AttributeError: - pass # Windows has no symlink - - def tempdir(self, path=None): - """Creates a temporary directory. - A unique directory name is generated if no path name is specified. - The directory is created, and will be removed when the TestCmd - object is destroyed. - """ - if path is None: - try: - path = tempfile.mktemp(prefix=tempfile.template) - except TypeError: - path = tempfile.mktemp() - os.mkdir(path) - - # Symlinks in the path will report things - # differently from os.getcwd(), so chdir there - # and back to fetch the canonical path. - cwd = os.getcwd() - try: - os.chdir(path) - path = os.getcwd() - finally: - os.chdir(cwd) - - # Uppercase the drive letter since the case of drive - # letters is pretty much random on win32: - drive, rest = os.path.splitdrive(path) - if drive: - path = drive.upper() + rest - - # - self._dirlist.append(path) - - global _Cleanup - if self not in _Cleanup: - _Cleanup.append(self) - - return path - - def touch(self, path, mtime=None): - """Updates the modification time on the specified file or - directory path name. The default is to update to the - current time if no explicit modification time is specified. - """ - path = self.canonicalize(path) - atime = os.path.getatime(path) - if mtime is None: - mtime = time.time() - os.utime(path, (atime, mtime)) - - def unlink(self, file): - """Unlinks the specified file name. - The file name may be a list, in which case the elements are - concatenated with the os.path.join() method. The file is - assumed to be under the temporary working directory unless it - is an absolute path name. - """ - file = self.canonicalize(file) - os.unlink(file) - - def verbose_set(self, verbose): - """Set the verbose level. - """ - self.verbose = verbose - - def where_is(self, file, path=None, pathext=None): - """Find an executable file. - """ - if is_List(file): - file = os.path.join(*tuple(file)) - if not os.path.isabs(file): - file = where_is(file, path, pathext) - return file - - def workdir_set(self, path): - """Creates a temporary working directory with the specified - path name. If the path is a null string (''), a unique - directory name is created. - """ - if (path != None): - if path == '': - path = None - path = self.tempdir(path) - self.workdir = path - - def workpath(self, *args): - """Returns the absolute path name to a subdirectory or file - within the current temporary working directory. Concatenates - the temporary working directory name with the specified - arguments using the os.path.join() method. - """ - return os.path.join(self.workdir, *tuple(args)) - - def readable(self, top, read=1): - """Make the specified directory tree readable (read == 1) - or not (read == None). - - This method has no effect on Windows systems, which use a - completely different mechanism to control file readability. - """ - - if sys.platform == 'win32': - return - - if read: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] | stat.S_IREAD)) - else: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IREAD)) - - if os.path.isfile(top): - # If it's a file, that's easy, just chmod it. - do_chmod(top) - elif read: - # It's a directory and we're trying to turn on read - # permission, so it's also pretty easy, just chmod the - # directory and then chmod every entry on our walk down the - # tree. - do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - else: - # It's a directory and we're trying to turn off read - # permission, which means we have to chmod the directories - # in the tree bottom-up, lest disabling read permission from - # the top down get in the way of being able to get at lower - # parts of the tree. - for dirpath, dirnames, filenames in os.walk(top, topdown=0): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - do_chmod(top) - - def writable(self, top, write=1): - """Make the specified directory tree writable (write == 1) - or not (write == None). - """ - - if sys.platform == 'win32': - - if write: - def do_chmod(fname): - try: - os.chmod(fname, stat.S_IWRITE) - except OSError: - pass - else: - def do_chmod(fname): - try: - os.chmod(fname, stat.S_IREAD) - except OSError: - pass - - else: - - if write: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE] | 0o200)) - else: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~0o200)) - - if os.path.isfile(top): - do_chmod(top) - else: - do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top, topdown=0): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - - def executable(self, top, execute=1): - """Make the specified directory tree executable (execute == 1) - or not (execute == None). - - This method has no effect on Windows systems, which use a - completely different mechanism to control file executability. - """ - - if sys.platform == 'win32': - return - - if execute: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] | stat.S_IEXEC)) - else: - def do_chmod(fname): - try: - st = os.stat(fname) - except OSError: - pass - else: - os.chmod(fname, stat.S_IMODE( - st[stat.ST_MODE] & ~stat.S_IEXEC)) - - if os.path.isfile(top): - # If it's a file, that's easy, just chmod it. - do_chmod(top) - elif execute: - # It's a directory and we're trying to turn on execute - # permission, so it's also pretty easy, just chmod the - # directory and then chmod every entry on our walk down the - # tree. - do_chmod(top) - for dirpath, dirnames, filenames in os.walk(top): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - else: - # It's a directory and we're trying to turn off execute - # permission, which means we have to chmod the directories - # in the tree bottom-up, lest disabling execute permission from - # the top down get in the way of being able to get at lower - # parts of the tree. - for dirpath, dirnames, filenames in os.walk(top, topdown=0): - for name in dirnames + filenames: - do_chmod(os.path.join(dirpath, name)) - do_chmod(top) - - def write(self, file, content, mode='wb'): - """Writes the specified content text (second argument) to the - specified file name (first argument). The file name may be - a list, in which case the elements are concatenated with the - os.path.join() method. The file is created under the temporary - working directory. Any subdirectories in the path must already - exist. The I/O mode for the file may be specified; it must - begin with a 'w'. The default is 'wb' (binary write). - """ - file = self.canonicalize(file) - if mode[0] != 'w': - raise ValueError("mode must begin with 'w'") - with open(file, mode) as f: - try: - f.write(content) - except TypeError as e: - # python 3 default strings are not bytes, but unicode - f.write(bytes(content, 'utf-8')) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestCmdTests.py b/QMTest/TestCmdTests.py deleted file mode 100644 index b9226fd..0000000 --- a/QMTest/TestCmdTests.py +++ /dev/null @@ -1,3419 +0,0 @@ -#!/usr/bin/env python -""" -TestCmdTests.py: Unit tests for the TestCmd.py module. - -Copyright 2000-2010 Steven Knight -This module is free software, and you may redistribute it and/or modify -it under the same terms as Python itself, so long as this copyright message -and disclaimer are retained in their original form. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -""" - -__author__ = "Steven Knight " -__revision__ = "TestCmdTests.py 1.3.D001 2010/06/03 12:58:27 knight" - -import os -import shutil -import signal -import stat -from StringIO import StringIO -import sys -import tempfile -import time -import types -import unittest -from UserList import UserList - - -# Strip the current directory so we get the right TestCmd.py module. -sys.path = sys.path[1:] - -import TestCmd - -def _is_readable(path): - # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IREAD - -def _is_writable(path): - # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IWRITE - -def _is_executable(path): - # XXX this doesn't take into account UID, it assumes it's our file - return os.stat(path)[stat.ST_MODE] & stat.S_IEXEC - -def _clear_dict(dict, *keys): - for key in keys: - try: - dict[key] = '' # del dict[key] - except KeyError: - pass - -import subprocess - -try: - subprocess.Popen.terminate -except AttributeError: - if sys.platform == 'win32': - import win32process - def terminate(self): - win32process.TerminateProcess(self._handle, 1) - else: - def terminate(self): - os.kill(self.pid, signal.SIGTERM) - method = types.MethodType(terminate, None, subprocess.Popen) - setattr(subprocess.Popen, 'terminate', method) - -class ExitError(Exception): - pass - -class TestCmdTestCase(unittest.TestCase): - """Base class for TestCmd test cases, with fixture and utility methods.""" - - def setUp(self): - self.orig_cwd = os.getcwd() - - def tearDown(self): - os.chdir(self.orig_cwd) - - def setup_run_scripts(self): - class T: - pass - - t = T() - - t.script = 'script' - t.scriptx = 'scriptx.bat' - t.script1 = 'script_1.txt' - t.scriptout = 'scriptout' - t.scripterr = 'scripterr' - fmt = "import os, sys; cwd = os.getcwd(); " + \ - "sys.stdout.write('%s: STDOUT: %%s: %%s\\n' %% (cwd, sys.argv[1:])); " + \ - "sys.stderr.write('%s: STDERR: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" - fmtout = "import os, sys; cwd = os.getcwd(); " + \ - "sys.stdout.write('%s: STDOUT: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" - fmterr = "import os, sys; cwd = os.getcwd(); " + \ - "sys.stderr.write('%s: STDERR: %%s: %%s\\n' %% (cwd, sys.argv[1:]))" - text = fmt % (t.script, t.script) - 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' - else: - textx = '#! /usr/bin/env python\n' + textx + '\n' - text1 = 'A first line to be ignored!\n' + fmt % (t.script1, t.script1) - textout = fmtout % (t.scriptout) - texterr = fmterr % (t.scripterr) - - run_env = TestCmd.TestCmd(workdir = '') - run_env.subdir('sub dir') - t.run_env = run_env - - t.sub_dir = run_env.workpath('sub dir') - t.script_path = run_env.workpath('sub dir', t.script) - t.scriptx_path = run_env.workpath('sub dir', t.scriptx) - t.script1_path = run_env.workpath('sub dir', t.script1) - t.scriptout_path = run_env.workpath('sub dir', t.scriptout) - t.scripterr_path = run_env.workpath('sub dir', t.scripterr) - - run_env.write(t.script_path, text) - run_env.write(t.scriptx_path, textx) - run_env.write(t.script1_path, text1) - run_env.write(t.scriptout_path, textout) - run_env.write(t.scripterr_path, texterr) - - os.chmod(t.script_path, 0o644) # XXX UNIX-specific - os.chmod(t.scriptx_path, 0o755) # XXX UNIX-specific - os.chmod(t.script1_path, 0o644) # XXX UNIX-specific - os.chmod(t.scriptout_path, 0o644) # XXX UNIX-specific - os.chmod(t.scripterr_path, 0o644) # XXX UNIX-specific - - t.orig_cwd = os.getcwd() - - t.workdir = run_env.workpath('sub dir') - os.chdir(t.workdir) - - return t - - def translate_newlines(self, data): - data = data.replace("\r\n", "\n") - return data - - def call_python(self, input, python=None): - if python is None: - python = sys.executable - p = subprocess.Popen(python, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE, - stdout=subprocess.PIPE) - stdout, stderr = p.communicate(input) - stdout = self.translate_newlines(stdout) - stderr = self.translate_newlines(stderr) - return stdout, stderr, p.returncode - - def popen_python(self, input, status=0, stdout="", stderr="", python=None): - if python is None: - python = sys.executable - _stdout, _stderr, _status = self.call_python(input, python) - _stdout = self.translate_newlines(_stdout) - _stderr = self.translate_newlines(_stderr) - assert _status == status, \ - "status = %s, expected %s\n" % (str(_status), str(status)) + \ - "STDOUT ===================\n" + _stdout + \ - "STDERR ===================\n" + _stderr - assert _stdout == stdout, \ - "Expected STDOUT ==========\n" + stdout + \ - "Actual STDOUT ============\n" + _stdout + \ - "STDERR ===================\n" + _stderr - assert _stderr == stderr, \ - "Expected STDERR ==========\n" + stderr + \ - "Actual STDERR ============\n" + _stderr - - def run_match(self, content, *args): - expect = "%s: %s: %s: %s\n" % args - content = self.translate_newlines(content) - assert content == expect, \ - "Expected %s ==========\n" % args[1] + expect + \ - "Actual %s ============\n" % args[1] + content - - - -class __init__TestCase(TestCmdTestCase): - def test_init(self): - """Test init()""" - test = TestCmd.TestCmd() - test = TestCmd.TestCmd(description = 'test') - test = TestCmd.TestCmd(description = 'test', program = 'foo') - test = TestCmd.TestCmd(description = 'test', - program = 'foo', - universal_newlines=None) - - - -class basename_TestCase(TestCmdTestCase): - def test_basename(self): - """Test basename() [XXX TO BE WRITTEN]""" - assert 1 == 1 - - - -class cleanup_TestCase(TestCmdTestCase): - def test_cleanup(self): - """Test cleanup()""" - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - test.write('file1', "Test file #1\n") - test.cleanup() - assert not os.path.exists(wdir) - - def test_writable(self): - """Test cleanup() when the directory isn't writable""" - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - test.write('file2', "Test file #2\n") - os.chmod(test.workpath('file2'), 0o400) - os.chmod(wdir, 0o500) - test.cleanup() - assert not os.path.exists(wdir) - - def test_shutil(self): - """Test cleanup() when used with shutil""" - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - os.chdir(wdir) - - import shutil - save_rmtree = shutil.rmtree - def my_rmtree(dir, ignore_errors=0, wdir=wdir, _rmtree=save_rmtree): - assert os.getcwd() != wdir - return _rmtree(dir, ignore_errors=ignore_errors) - try: - shutil.rmtree = my_rmtree - test.cleanup() - finally: - shutil.rmtree = save_rmtree - - def test_atexit(self): - """Test cleanup() when atexit is used""" - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import atexit -def my_exitfunc(): - print("my_exitfunc()") -atexit.register(my_exitfunc) -import TestCmd -result = TestCmd.TestCmd(workdir = '') -sys.exit(0) -""" % self.orig_cwd, stdout='my_exitfunc()\n') - - def test_exitfunc(self): - """Test cleanup() when sys.exitfunc is set""" - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -def my_exitfunc(): - print("my_exitfunc()") -sys.exitfunc = my_exitfunc -import TestCmd -result = TestCmd.TestCmd(workdir = '') -sys.exit(0) -""" % self.orig_cwd, stdout='my_exitfunc()\n') - - - -class chmod_TestCase(TestCmdTestCase): - def test_chmod(self): - """Test chmod()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'sub') - - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') - - open(wdir_file1, 'w').write("") - open(wdir_sub_file2, 'w').write("") - - if sys.platform == 'win32': - - test.chmod(wdir_file1, stat.S_IREAD) - 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 - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o666, '0%o' % file2_mode - - 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 - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o444, '0%o' % file2_mode - - else: - - test.chmod(wdir_file1, 0o700) - 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 - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o760, '0%o' % file2_mode - - 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 - file2_mode = stat.S_IMODE(os.stat(wdir_sub_file2)[stat.ST_MODE]) - assert file2_mode == 0o567, '0%o' % file2_mode - - - -class combine_TestCase(TestCmdTestCase): - def test_combine(self): - """Test combining stdout and stderr""" - run_env = TestCmd.TestCmd(workdir = '') - 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") -sys.stdout.write("run1 STDOUT third line\\n") -sys.stderr.write("run1 STDERR third line\\n") -""") - 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:]) -sys.stderr.write("run2 STDERR second line\\n") -sys.stdout.write("run2 STDOUT third line\\n") -sys.stderr.write("run2 STDERR third line\\n") -""") - cwd = os.getcwd() - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(interpreter = 'python', - workdir = '', - combine = 1) - try: - output = test.stdout() - except IndexError: - pass - else: - raise IndexError("got unexpected output:\n\t`%s'\n" % output) - - # The underlying system subprocess implementations can combine - # stdout and stderr in different orders, so we accomodate both. - - test.program_set('run1') - test.run(arguments = 'foo bar') - stdout_lines = """\ -run1 STDOUT ['foo', 'bar'] -run1 STDOUT second line -run1 STDOUT third line -""" - stderr_lines = """\ -run1 STDERR ['foo', 'bar'] -run1 STDERR second line -run1 STDERR third line -""" - foo_bar_expect = (stdout_lines + stderr_lines, - stderr_lines + stdout_lines) - - test.program_set('run2') - test.run(arguments = 'snafu') - stdout_lines = """\ -run2 STDOUT ['snafu'] -run2 STDOUT second line -run2 STDOUT third line -""" - stderr_lines = """\ -run2 STDERR ['snafu'] -run2 STDERR second line -run2 STDERR third line -""" - snafu_expect = (stdout_lines + stderr_lines, - stderr_lines + stdout_lines) - - # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL - output = test.stdout() - output = self.translate_newlines(output) - assert output in snafu_expect, output - error = test.stderr() - assert error == '', error - - output = test.stdout(run = -1) - output = self.translate_newlines(output) - assert output in foo_bar_expect, output - error = test.stderr(-1) - assert error == '', error - finally: - os.chdir(cwd) - - - -class description_TestCase(TestCmdTestCase): - def test_description(self): - """Test description()""" - test = TestCmd.TestCmd() - assert test.description is None, 'initialized description?' - test = TestCmd.TestCmd(description = 'test') - assert test.description == 'test', 'uninitialized description' - test.description_set('foo') - assert test.description == 'foo', 'did not set description' - - - -class diff_TestCase(TestCmdTestCase): - def test_diff_re(self): - """Test diff_re()""" - result = TestCmd.diff_re(["abcde"], ["abcde"]) - assert result == [], result - result = TestCmd.diff_re(["a.*e"], ["abcde"]) - assert result == [], result - result = TestCmd.diff_re(["a.*e"], ["xxx"]) - assert result == ['1c1', "< 'a.*e'", '---', "> 'xxx'"], result - - def test_diff_custom_function(self): - """Test diff() using a custom function""" - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def my_diff(a, b): - return [ - '*****', - 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========================================================================== -***** -['a', 'b1', 'c'] -***** -['a', 'b2', 'c'] -***** -""") - - def test_diff_string(self): - self.popen_python("""import sys -sys.path = ['%s'] + 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 -< 'b1' ---- -> 'b2' -""") - - def test_error(self): - """Test handling a compilation error in TestCmd.diff_re()""" - script_input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -assert TestCmd.diff_re(["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 )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" - assert (stderr.find(expect1) != -1 or - stderr.find(expect2) != -1), repr(stderr) - - 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 -import TestCmd -result = TestCmd.TestCmd.simple_diff(['a', 'b', 'c', 'e', 'f1'], - ['a', 'c', 'd', 'e', 'f2']) -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 -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']) -result = list(result) -expect = [ - '*** \\n', - '--- \\n', - '***************\\n', - '*** 1,5 ****\\n', - ' a\\n', - '- b\\n', - ' c\\n', - ' e\\n', - '! f1\\n', - '--- 1,5 ----\\n', - ' a\\n', - ' c\\n', - '+ d\\n', - ' e\\n', - '! f2\\n', -] -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 -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']) -result = list(result) -expect = [ - '--- \\n', - '+++ \\n', - '@@ -1,5 +1,5 @@\\n', - ' a\\n', - '-b\\n', - ' c\\n', - '+d\\n', - ' e\\n', - '-f1\\n', - '+f2\\n' -] -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 -import TestCmd -result = TestCmd.TestCmd.diff_re(['a', 'b', 'c', '.', 'f1'], - ['a', 'c', 'd', 'e', 'f2']) -expect = [ - '2c2', - "< 'b'", - '---', - "> 'c'", - '3c3', - "< 'c'", - '---', - "> 'd'", - '5c5', - "< 'f1'", - '---', - "> 'f2'" -] -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 -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 ---- -> b2 -""") - - def test_diff_stderr_not_affecting_diff_stdout(self): - """Test diff_stderr() not affecting diff_stdout() behavior""" - self.popen_python(r"""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stderr='diff_re') -print("diff_stderr:") -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: -2c2 -< b. ---- -> bb -""") - - 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 -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 -b: -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 -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' ---- -> 'b' -""") - - 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 -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' ---- -> 'b' -""") - - 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 -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' ---- -> 'b' -""") - - - -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 -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 ---- -> b2 -""") - - def test_diff_stdout_not_affecting_diff_stderr(self): - """Test diff_stdout() not affecting diff_stderr() behavior""" - self.popen_python(r"""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(diff_stdout='diff_re') -print("diff_stdout:") -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: -2c2 -< b. ---- -> bb -""") - - 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 -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 -b: -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 -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' ---- -> 'b' -""") - - 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 -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' ---- -> 'b' -""") - - 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 -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' ---- -> 'b' -""") - - - -class exit_TestCase(TestCmdTestCase): - def test_exit(self): - """Test exit()""" - def _test_it(cwd, tempdir, condition, preserved): - close_true = {'pass_test': 1, 'fail_test': 0, 'no_result': 0} - exit_status = {'pass_test': 0, 'fail_test': 1, 'no_result': 2} - result_string = {'pass_test': "PASSED\n", - 'fail_test': "FAILED test at line 5 of \n", - 'no_result': "NO RESULT for test at line 5 of \n"} - global ExitError - input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd(workdir = '%s') -test.%s() -""" % (cwd, tempdir, condition) - stdout, stderr, status = self.call_python(input, python="python") - if close_true[condition]: - unexpected = (status != 0) - else: - unexpected = (status == 0) - if unexpected: - msg = "Unexpected exit status from python: %s\n" - raise ExitError(msg % status + stdout + stderr) - if status != exit_status[condition]: - 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" - raise ExitError(msg % (result_string[condition], stderr)) - if preserved: - if not os.path.exists(tempdir): - msg = "Working directory %s was mistakenly removed\n" - raise ExitError(msg % tempdir + stdout) - else: - if os.path.exists(tempdir): - msg = "Working directory %s was mistakenly preserved\n" - raise ExitError(msg % tempdir + stdout) - - run_env = TestCmd.TestCmd(workdir = '') - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - cwd = self.orig_cwd - _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') - _test_it(cwd, 'dir01', 'pass_test', 0) - _test_it(cwd, 'dir02', 'fail_test', 0) - _test_it(cwd, 'dir03', 'no_result', 0) - os.environ['PRESERVE'] = '1' - _test_it(cwd, 'dir04', 'pass_test', 1) - _test_it(cwd, 'dir05', 'fail_test', 1) - _test_it(cwd, 'dir06', 'no_result', 1) - os.environ['PRESERVE'] = '' # del os.environ['PRESERVE'] - os.environ['PRESERVE_PASS'] = '1' - _test_it(cwd, 'dir07', 'pass_test', 1) - _test_it(cwd, 'dir08', 'fail_test', 0) - _test_it(cwd, 'dir09', 'no_result', 0) - os.environ['PRESERVE_PASS'] = '' # del os.environ['PRESERVE_PASS'] - os.environ['PRESERVE_FAIL'] = '1' - _test_it(cwd, 'dir10', 'pass_test', 0) - _test_it(cwd, 'dir11', 'fail_test', 1) - _test_it(cwd, 'dir12', 'no_result', 0) - os.environ['PRESERVE_FAIL'] = '' # del os.environ['PRESERVE_FAIL'] - os.environ['PRESERVE_NO_RESULT'] = '1' - _test_it(cwd, 'dir13', 'pass_test', 0) - _test_it(cwd, 'dir14', 'fail_test', 0) - _test_it(cwd, 'dir15', 'no_result', 1) - os.environ['PRESERVE_NO_RESULT'] = '' # del os.environ['PRESERVE_NO_RESULT'] - finally: - _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') - - - -class fail_test_TestCase(TestCmdTestCase): - def test_fail_test(self): - """Test fail_test()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -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 -import TestCmd -TestCmd.fail_test(condition = 1) -""" % self.orig_cwd, status = 1, stderr = "FAILED test at line 4 of \n") - - self.popen_python("""import sys -sys.path = ['%s'] + 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 \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + 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 \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + 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 \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.fail_test(condition = (self.status == 0)) -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 (test1)\n\tfrom line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.fail_test(condition = (self.status == 0), skip = 1) -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 (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - - -class interpreter_TestCase(TestCmdTestCase): - def test_interpreter(self): - """Test interpreter()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -sys.stderr.write("run: STDERR\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(program = 'run', workdir = '') - test.interpreter_set('foo') - assert test.interpreter == 'foo', 'did not set interpreter' - test.interpreter_set('python') - assert test.interpreter == 'python', 'did not set interpreter' - test.run() - - - -class match_TestCase(TestCmdTestCase): - def test_match_default(self): - """Test match() default behavior""" - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match(lines, regexes) - - def test_match_custom_function(self): - """Test match() using a custom function""" - def match_length(lines, matches): - return len(lines) == len(matches) - test = TestCmd.TestCmd(match=match_length) - assert not test.match("123\n", "1\n") - assert test.match("123\n", "111\n") - assert not test.match("123\n123\n", "1\n1\n") - assert test.match("123\n123\n", "111\n111\n") - lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] - assert test.match(lines, regexes) # due to equal numbers of lines - - def test_match_TestCmd_function(self): - """Test match() using a TestCmd function""" - test = TestCmd.TestCmd(match = TestCmd.match_exact) - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match(lines, regexes) - assert test.match(lines, lines) - - def test_match_static_method(self): - """Test match() using a static method""" - test = TestCmd.TestCmd(match=TestCmd.TestCmd.match_exact) - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match(lines, regexes) - assert test.match(lines, lines) - - def test_match_string(self): - """Test match() using a string to fetch the match method""" - test = TestCmd.TestCmd(match='match_exact') - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match(lines, regexes) - assert test.match(lines, lines) - - - -class match_exact_TestCase(TestCmdTestCase): - def test_match_exact_function(self): - """Test calling the TestCmd.match_exact() function""" - assert not TestCmd.match_exact("abcde\\n", "a.*e\\n") - assert TestCmd.match_exact("abcde\\n", "abcde\\n") - - def test_match_exact_instance_method(self): - """Test calling the TestCmd.TestCmd().match_exact() instance method""" - test = TestCmd.TestCmd() - assert not test.match_exact("abcde\\n", "a.*e\\n") - assert test.match_exact("abcde\\n", "abcde\\n") - - def test_match_exact_static_method(self): - """Test calling the TestCmd.TestCmd.match_exact() static method""" - assert not TestCmd.TestCmd.match_exact("abcde\\n", "a.*e\\n") - assert TestCmd.TestCmd.match_exact("abcde\\n", "abcde\\n") - - def test_evaluation(self): - """Test match_exact() evaluation""" - test = TestCmd.TestCmd() - assert not test.match_exact("abcde\n", "a.*e\n") - assert test.match_exact("abcde\n", "abcde\n") - assert not test.match_exact(["12345\n", "abcde\n"], ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_exact(["12345\n", "abcde\n"], ["12345\n", "abcde\n"]) - assert not test.match_exact(UserList(["12345\n", "abcde\n"]), - ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_exact(UserList(["12345\n", "abcde\n"]), - ["12345\n", "abcde\n"]) - assert not test.match_exact(["12345\n", "abcde\n"], - UserList(["1[0-9]*5\n", "a.*e\n"])) - assert test.match_exact(["12345\n", "abcde\n"], - UserList(["12345\n", "abcde\n"])) - assert not test.match_exact("12345\nabcde\n", "1[0-9]*5\na.*e\n") - assert test.match_exact("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_exact(lines, regexes) - assert test.match_exact(lines, lines) - - - -class match_re_dotall_TestCase(TestCmdTestCase): - def test_match_re_dotall_function(self): - """Test calling the TestCmd.match_re_dotall() function""" - assert TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") - - def test_match_re_dotall_instance_method(self): - """Test calling the TestCmd.TestCmd().match_re_dotall() instance method""" - test = TestCmd.TestCmd() - test.match_re_dotall("abcde\\nfghij\\n", "a.*j\\n") - - def test_match_re_dotall_static_method(self): - """Test calling the TestCmd.TestCmd.match_re_dotall() static method""" - assert TestCmd.TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") - - def test_error(self): - """Test handling a compilation error in TestCmd.match_re_dotall()""" - run_env = TestCmd.TestCmd(workdir = '') - cwd = os.getcwd() - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - script_input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -assert TestCmd.match_re_dotall("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 )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" - assert (stderr.find(expect1) != -1 or - stderr.find(expect2) != -1), repr(stderr) - finally: - os.chdir(cwd) - - def test_evaluation(self): - """Test match_re_dotall() evaluation""" - test = TestCmd.TestCmd() - assert test.match_re_dotall("abcde\nfghij\n", "a.*e\nf.*j\n") - assert test.match_re_dotall("abcde\nfghij\n", "a[^j]*j\n") - assert test.match_re_dotall("abcde\nfghij\n", "abcde\nfghij\n") - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["1.*j\n"]) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["12345\n", "abcde\n", "fghij\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["1.*j\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["12345\n", "abcde\n", "fghij\n"]) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["1[0-9]*5\n", - "a.*e\n", - "f.*j\n"])) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["1.*j\n"])) - assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["12345\n", - "abcde\n", - "fghij\n"])) - assert test.match_re_dotall("12345\nabcde\nfghij\n", - "1[0-9]*5\na.*e\nf.*j\n") - assert test.match_re_dotall("12345\nabcde\nfghij\n", "1.*j\n") - assert test.match_re_dotall("12345\nabcde\nfghij\n", - "12345\nabcde\nfghij\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_re_dotall(lines, regexes) - assert test.match_re_dotall(lines, lines) - - - -class match_re_TestCase(TestCmdTestCase): - def test_match_re_function(self): - """Test calling the TestCmd.match_re() function""" - assert TestCmd.match_re("abcde\n", "a.*e\n") - - def test_match_re_instance_method(self): - """Test calling the TestCmd.TestCmd().match_re() instance method""" - test = TestCmd.TestCmd() - assert test.match_re("abcde\n", "a.*e\n") - - def test_match_re_static_method(self): - """Test calling the TestCmd.TestCmd.match_re() static method""" - assert TestCmd.TestCmd.match_re("abcde\n", "a.*e\n") - - def test_error(self): - """Test handling a compilation error in TestCmd.match_re()""" - run_env = TestCmd.TestCmd(workdir = '') - cwd = os.getcwd() - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - try: - script_input = """import sys -sys.path = ['%s'] + sys.path -import TestCmd -assert TestCmd.match_re("abcde\\n", "a.*(e\\n") -sys.exit(0) -""" % cwd - stdout, stderr, status = self.call_python(script_input) - assert status == 1, status - expect1 = "Regular expression error in '^a.*(e$': missing )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" - assert (stderr.find(expect1) != -1 or - stderr.find(expect2) != -1), repr(stderr) - finally: - os.chdir(cwd) - - def test_evaluation(self): - """Test match_re() evaluation""" - test = TestCmd.TestCmd() - assert test.match_re("abcde\n", "a.*e\n") - assert test.match_re("abcde\n", "abcde\n") - assert test.match_re(["12345\n", "abcde\n"], ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_re(["12345\n", "abcde\n"], ["12345\n", "abcde\n"]) - assert test.match_re(UserList(["12345\n", "abcde\n"]), - ["1[0-9]*5\n", "a.*e\n"]) - assert test.match_re(UserList(["12345\n", "abcde\n"]), - ["12345\n", "abcde\n"]) - assert test.match_re(["12345\n", "abcde\n"], - UserList(["1[0-9]*5\n", "a.*e\n"])) - assert test.match_re(["12345\n", "abcde\n"], - UserList(["12345\n", "abcde\n"])) - assert test.match_re("12345\nabcde\n", "1[0-9]*5\na.*e\n") - assert test.match_re("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_re(lines, regexes) - assert test.match_re(lines, lines) - - - -class match_stderr_TestCase(TestCmdTestCase): - def test_match_stderr_default(self): - """Test match_stderr() default behavior""" - test = TestCmd.TestCmd() - assert test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stderr(lines, regexes) - - def test_match_stderr_not_affecting_match_stdout(self): - """Test match_stderr() not affecting match_stdout() behavior""" - test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) - - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - assert test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stdout(lines, regexes) - - def test_match_stderr_custom_function(self): - """Test match_stderr() using a custom function""" - def match_length(lines, matches): - return len(lines) == len(matches) - test = TestCmd.TestCmd(match_stderr=match_length) - assert not test.match_stderr("123\n", "1\n") - assert test.match_stderr("123\n", "111\n") - assert not test.match_stderr("123\n123\n", "1\n1\n") - assert test.match_stderr("123\n123\n", "111\n111\n") - lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] - assert test.match_stderr(lines, regexes) # equal numbers of lines - - def test_match_stderr_TestCmd_function(self): - """Test match_stderr() using a TestCmd function""" - test = TestCmd.TestCmd(match_stderr = TestCmd.match_exact) - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - def test_match_stderr_static_method(self): - """Test match_stderr() using a static method""" - test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - def test_match_stderr_string(self): - """Test match_stderr() using a string to fetch the match method""" - test = TestCmd.TestCmd(match_stderr='match_exact') - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stderr(lines, regexes) - assert test.match_stderr(lines, lines) - - - -class match_stdout_TestCase(TestCmdTestCase): - def test_match_stdout_default(self): - """Test match_stdout() default behavior""" - test = TestCmd.TestCmd() - assert test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stdout(lines, regexes) - - def test_match_stdout_not_affecting_match_stderr(self): - """Test match_stdout() not affecting match_stderr() behavior""" - test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) - - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - assert test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert test.match_stderr(lines, regexes) - - def test_match_stdout_custom_function(self): - """Test match_stdout() using a custom function""" - def match_length(lines, matches): - return len(lines) == len(matches) - test = TestCmd.TestCmd(match_stdout=match_length) - assert not test.match_stdout("123\n", "1\n") - assert test.match_stdout("123\n", "111\n") - assert not test.match_stdout("123\n123\n", "1\n1\n") - assert test.match_stdout("123\n123\n", "111\n111\n") - lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] - assert test.match_stdout(lines, regexes) # equal numbers of lines - - def test_match_stdout_TestCmd_function(self): - """Test match_stdout() using a TestCmd function""" - test = TestCmd.TestCmd(match_stdout = TestCmd.match_exact) - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - def test_match_stdout_static_method(self): - """Test match_stdout() using a static method""" - test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - def test_match_stdout_string(self): - """Test match_stdout() using a string to fetch the match method""" - test = TestCmd.TestCmd(match_stdout='match_exact') - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") - assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") - lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] - assert not test.match_stdout(lines, regexes) - assert test.match_stdout(lines, lines) - - - -class no_result_TestCase(TestCmdTestCase): - def test_no_result(self): - """Test no_result()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -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 -import TestCmd -TestCmd.no_result(condition = 1) -""" % self.orig_cwd, status = 2, stderr = "NO RESULT for test at line 4 of \n") - - self.popen_python("""import sys -sys.path = ['%s'] + 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 \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + 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 \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + 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 \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.no_result(condition = (self.status == 0)) -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 (test1)\n\tfrom line 8 of (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - self.popen_python("""import sys -sys.path = ['%s'] + sys.path -import TestCmd -def test1(self): - self.run() - self.no_result(condition = (self.status == 0), skip = 1) -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 (test2)\n\tfrom line 9 of \n" % run_env.workpath('run')) - - - -class pass_test_TestCase(TestCmdTestCase): - def test_pass_test(self): - """Test pass_test()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """import sys -sys.stdout.write("run: STDOUT\\n") -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 -import TestCmd -TestCmd.pass_test(condition = 1) -""" % self.orig_cwd, stderr = "PASSED\n") - - self.popen_python("""import sys -sys.path = ['%s'] + 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") - - self.popen_python("""import sys -sys.path = ['%s'] + 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") - - # TODO(sgk): SHOULD ALSO TEST FAILURE CONDITIONS - - - -class preserve_TestCase(TestCmdTestCase): - def test_preserve(self): - """Test preserve()""" - def cleanup_test(test, cond=None, stdout=""): - io = StringIO() - save = sys.stdout - sys.stdout = io - try: - if cond: - test.cleanup(cond) - else: - test.cleanup() - o = io.getvalue() - assert o == stdout, "o = `%s', stdout = `%s'" % (o, stdout) - finally: - sys.stdout = save - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file1', "Test file #1\n") - #test.cleanup() - cleanup_test(test, ) - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file2', "Test file #2\n") - test.preserve('pass_test') - cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'fail_test') - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file3', "Test file #3\n") - test.preserve('fail_test') - cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'pass_test') - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.write('file4', "Test file #4\n") - test.preserve('fail_test', 'no_result') - cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'pass_test') - assert not os.path.exists(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - test = TestCmd.TestCmd(workdir = '') - wdir = test.workdir - try: - test.preserve() - cleanup_test(test, 'pass_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'fail_test', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - cleanup_test(test, 'no_result', "Preserved directory %s\n" % wdir) - assert os.path.isdir(wdir) - finally: - if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) - test._dirlist.remove(wdir) - - - -class program_TestCase(TestCmdTestCase): - def test_program(self): - """Test program()""" - test = TestCmd.TestCmd() - assert test.program is None, 'initialized program?' - test = TestCmd.TestCmd(program = 'test') - assert test.program == os.path.join(os.getcwd(), 'test'), 'uninitialized program' - test.program_set('foo') - assert test.program == os.path.join(os.getcwd(), 'foo'), 'did not set program' - - - -class read_TestCase(TestCmdTestCase): - def test_read(self): - """Test read()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_file2 = os.path.join(test.workdir, 'file2') - wdir_foo_file3 = os.path.join(test.workdir, 'foo', 'file3') - wdir_file4 = os.path.join(test.workdir, 'file4') - wdir_file5 = os.path.join(test.workdir, 'file5') - - open(wdir_file1, 'wb').write("") - open(wdir_file2, 'wb').write("Test\nfile\n#2.\n") - open(wdir_foo_file3, 'wb').write("Test\nfile\n#3.\n") - open(wdir_file4, 'wb').write("Test\nfile\n#4.\n") - open(wdir_file5, 'wb').write("Test\r\nfile\r\n#5.\r\n") - - try: - contents = test.read('no_file') - except IOError: # expect "No such file or directory" - pass - except: - raise - - try: - test.read(test.workpath('file_x'), mode = 'w') - except ValueError: # expect "mode must begin with 'r' - pass - except: - raise - - def _file_matches(file, contents, expected): - assert contents == expected, \ - "Expected contents of " + str(file) + "==========\n" + \ - expected + \ - "Actual contents of " + str(file) + "============\n" + \ - contents - - _file_matches(wdir_file1, test.read('file1'), "") - _file_matches(wdir_file2, test.read('file2'), "Test\nfile\n#2.\n") - _file_matches(wdir_foo_file3, test.read(['foo', 'file3']), - "Test\nfile\n#3.\n") - _file_matches(wdir_foo_file3, - test.read(UserList(['foo', 'file3'])), - "Test\nfile\n#3.\n") - _file_matches(wdir_file4, test.read('file4', mode = 'r'), - "Test\nfile\n#4.\n") - _file_matches(wdir_file5, test.read('file5', mode = 'rb'), - "Test\r\nfile\r\n#5.\r\n") - - - -class rmdir_TestCase(TestCmdTestCase): - def test_rmdir(self): - """Test rmdir()""" - test = TestCmd.TestCmd(workdir = '') - - try: - test.rmdir(['no', 'such', 'dir']) - except EnvironmentError: - pass - else: - raise Exception("did not catch expected EnvironmentError") - - test.subdir(['sub'], - ['sub', 'dir'], - ['sub', 'dir', 'one']) - - s = test.workpath('sub') - s_d = test.workpath('sub', 'dir') - s_d_o = test.workpath('sub', 'dir', 'one') - - try: - test.rmdir(['sub']) - except EnvironmentError: - pass - else: - raise Exception("did not catch expected EnvironmentError") - - assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o - - try: - test.rmdir(['sub']) - except EnvironmentError: - pass - else: - raise Exception("did not catch expected EnvironmentError") - - assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o - - 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 - - test.rmdir(['sub', 'dir']) - - assert not os.path.exists(s_d), "%s exists?" % s_d - assert os.path.isdir(s), "%s is gone?" % s - - test.rmdir('sub') - - assert not os.path.exists(s), "%s exists?" % s - - - -class run_TestCase(TestCmdTestCase): - def test_run(self): - """Test run()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - test.run() - self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, - repr([])) - self.run_match(test.stderr(), t.script, "STDERR", t.workdir, - repr([])) - - test.run(arguments = 'arg1 arg2 arg3') - self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - self.run_match(test.stderr(), t.script, "STDERR", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - - test.run(program = t.scriptx, arguments = 'foo') - self.run_match(test.stdout(), t.scriptx, "STDOUT", t.workdir, - repr(['foo'])) - self.run_match(test.stderr(), t.scriptx, "STDERR", t.workdir, - repr(['foo'])) - - test.run(chdir = os.curdir, arguments = 'x y z') - self.run_match(test.stdout(), t.script, "STDOUT", test.workdir, - repr(['x', 'y', 'z'])) - self.run_match(test.stderr(), t.script, "STDERR", test.workdir, - repr(['x', 'y', 'z'])) - - test.run(chdir = 'script_subdir') - script_subdir = test.workpath('script_subdir') - self.run_match(test.stdout(), t.script, "STDOUT", script_subdir, - repr([])) - self.run_match(test.stderr(), t.script, "STDERR", script_subdir, - repr([])) - - test.run(program = t.script1, interpreter = ['python', '-x']) - self.run_match(test.stdout(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(test.stderr(), t.script1, "STDERR", t.workdir, - repr([])) - - try: - test.run(chdir = 'no_subdir') - except OSError: - pass - - test.run(program = 'no_script', interpreter = 'python') - assert test.status != None, test.status - - try: - test.run(program = 'no_script', interpreter = 'no_interpreter') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just returns - # a non-zero exit status. - assert test.status != None, test.status - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - subdir = 't.scriptx_subdir') - - testx.run() - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, - repr([])) - - testx.run(arguments = 'foo bar') - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, - repr(['foo', 'bar'])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, - repr(['foo', 'bar'])) - - testx.run(program = t.script, interpreter = 'python', arguments = 'bar') - self.run_match(testx.stdout(), t.script, "STDOUT", t.workdir, - repr(['bar'])) - self.run_match(testx.stderr(), t.script, "STDERR", t.workdir, - repr(['bar'])) - - testx.run(chdir = os.curdir, arguments = 'baz') - self.run_match(testx.stdout(), t.scriptx, "STDOUT", testx.workdir, - repr(['baz'])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", testx.workdir, - repr(['baz'])) - - testx.run(chdir = 't.scriptx_subdir') - t.scriptx_subdir = testx.workpath('t.scriptx_subdir') - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.scriptx_subdir, - repr([])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.scriptx_subdir, - repr([])) - - testx.run(program = t.script1, interpreter = ('python', '-x')) - self.run_match(testx.stdout(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(testx.stderr(), t.script1, "STDERR", t.workdir, - repr([])) - - s = os.path.join('.', t.scriptx) - testx.run(program = [s]) - self.run_match(testx.stdout(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(testx.stderr(), t.scriptx, "STDERR", t.workdir, - repr([])) - - try: - testx.run(chdir = 'no_subdir') - except OSError: - pass - - try: - testx.run(program = 'no_program') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just returns - # a non-zero exit status. - assert test.status != None - - test1 = TestCmd.TestCmd(program = t.script1, - interpreter = ['python', '-x'], - workdir = '') - - test1.run() - self.run_match(test1.stdout(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(test1.stderr(), t.script1, "STDERR", t.workdir, - repr([])) - - finally: - os.chdir(t.orig_cwd) - - def test_run_subclass(self): - """Test run() through a subclass with different signatures""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - - class MyTestCmdSubclass(TestCmd.TestCmd): - def start(self, additional_argument=None, **kw): - return TestCmd.TestCmd.start(self, **kw) - - try: - test = MyTestCmdSubclass(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - test.run() - self.run_match(test.stdout(), t.script, "STDOUT", t.workdir, - repr([])) - self.run_match(test.stderr(), t.script, "STDERR", t.workdir, - repr([])) - finally: - os.chdir(t.orig_cwd) - - -class run_verbose_TestCase(TestCmdTestCase): - def test_run_verbose(self): - """Test the run() method's verbose attribute""" - - # Prepare our "source directory." - t = self.setup_run_scripts() - - save_stdout = sys.stderr - save_stderr = sys.stderr - - try: - # Test calling TestCmd() with an explicit verbose = 1. - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - assert expect == e, (expect, e) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - assert expect == e, (expect, e) - - # Test calling TestCmd() with an explicit verbose = 2. - - outerr_fmt = """\ -============ STATUS: 0 -============ BEGIN STDOUT (len=%s): -%s============ END STDOUT -============ BEGIN STDERR (len=%s) -%s============ END STDERR -""" - - out_fmt = """\ -============ STATUS: 0 -============ BEGIN STDOUT (len=%s): -%s============ END STDOUT -""" - - err_fmt = """\ -============ STATUS: 0 -============ BEGIN STDERR (len=%s) -%s============ END STDERR -""" - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - verbose = 2) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "script: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - verbose = 2) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - # Test calling TestCmd() with an explicit verbose = 3. - - test = TestCmd.TestCmd(program = t.scriptout, - interpreter = 'python', - workdir = '', - verbose = 2) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - expect = out_fmt % (len(stdout_line), stdout_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) - assert e == expect, (e, expect) - - test = TestCmd.TestCmd(program = t.scriptout, - interpreter = 'python', - workdir = '', - verbose = 3) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - '0', '') - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) - assert e == expect, (e, expect) - - # Test letting TestCmd() pick up verbose = 2 from the environment. - - os.environ['TESTCMD_VERBOSE'] = '2' - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '') - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - - line_fmt = "script: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '') - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - - line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) - - # Test letting TestCmd() pick up verbose = 1 from the environment. - - os.environ['TESTCMD_VERBOSE'] = '1' - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - assert expect == e, (expect, e) - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - verbose = 1) - - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - assert expect == e, (expect, e) - - finally: - sys.stdout = save_stdout - sys.stderr = save_stderr - os.chdir(t.orig_cwd) - os.environ['TESTCMD_VERBOSE'] = '' - - - -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 -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("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -print("diff:") -test.diff("a\\n", "a\\n") -print("diff_stdout:") -test.diff_stdout("a\\n", "a\\n") -test.set_diff_function(stdout='diff_re') -print("diff:") -test.diff(".\\n", "a\\n") -print("diff_stdout:") -test.diff_stdout(".\\n", "a\\n") -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -diff: -diff_stdout: -diff: -1c1 -< . ---- -> a -diff_stdout: -""") - - def test_set_diff_function_stderr(self): - """Test set_diff_function(): stderr """ - self.popen_python("""from __future__ import print_function -import sys -sys.path = ['%s'] + sys.path -import TestCmd -test = TestCmd.TestCmd() -print("diff:") -test.diff("a\\n", "a\\n") -print("diff_stderr:") -test.diff_stderr("a\\n", "a\\n") -test.set_diff_function(stderr='diff_re') -print("diff:") -test.diff(".\\n", "a\\n") -print("diff_stderr:") -test.diff_stderr(".\\n", "a\\n") -sys.exit(0) -""" % self.orig_cwd, - stdout="""\ -diff: -diff_stderr: -diff: -1c1 -< . ---- -> a -diff_stderr: -""") - - - -class set_match_function_TestCase(TestCmdTestCase): - def test_set_match_function(self): - """Test set_match_function()""" - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - - test.set_match_function('match_exact') - - assert not test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - - def test_set_match_function_stdout(self): - """Test set_match_function(): stdout """ - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - - test.set_match_function(stdout='match_exact') - - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match_stdout("abcde\n", "a.*e\n") - assert test.match_stdout("abcde\n", "abcde\n") - - def test_set_match_function_stderr(self): - """Test set_match_function(): stderr """ - test = TestCmd.TestCmd() - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - - test.set_match_function(stderr='match_exact') - - assert test.match("abcde\n", "a.*e\n") - assert test.match("abcde\n", "abcde\n") - assert not test.match_stderr("abcde\n", "a.*e\n") - assert test.match_stderr("abcde\n", "abcde\n") - - - -class sleep_TestCase(TestCmdTestCase): - def test_sleep(self): - """Test sleep()""" - test = TestCmd.TestCmd() - - start = time.time() - test.sleep() - end = time.time() - diff = end - start - assert diff > 0.9, "only slept %f seconds (start %f, end %f), not default" % (diff, start, end) - - start = time.time() - test.sleep(3) - end = time.time() - diff = end - start - assert diff > 2.9, "only slept %f seconds (start %f, end %f), not 3" % (diff, start, end) - - - -class stderr_TestCase(TestCmdTestCase): - def test_stderr(self): - """Test stderr()""" - run_env = TestCmd.TestCmd(workdir = '') - 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 -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:]) -sys.stderr.write("run2 STDERR second line\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(interpreter = 'python', workdir = '') - try: - output = test.stderr() - except IndexError: - pass - else: - raise IndexError("got unexpected output:\n" + output) - test.program_set('run1') - test.run(arguments = 'foo bar') - test.program_set('run2') - test.run(arguments = 'snafu') - # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL - output = test.stderr() - assert output == "run2 STDERR ['snafu']\nrun2 STDERR second line\n", output - output = test.stderr(run = -1) - assert output == "run1 STDERR ['foo', 'bar']\nrun1 STDERR second line\n", output - - - -class command_args_TestCase(TestCmdTestCase): - def test_command_args(self): - """Test command_args()""" - run_env = TestCmd.TestCmd(workdir = '') - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(workdir = '') - - r = test.command_args('prog') - expect = [run_env.workpath('prog')] - assert r == expect, (expect, r) - - r = test.command_args(test.workpath('new_prog')) - expect = [test.workpath('new_prog')] - assert r == expect, (expect, r) - - r = test.command_args('prog', 'python') - expect = ['python', run_env.workpath('prog')] - assert r == expect, (expect, r) - - r = test.command_args('prog', 'python', 'arg1 arg2') - expect = ['python', run_env.workpath('prog'), 'arg1', 'arg2'] - assert r == expect, (expect, r) - - test.program_set('default_prog') - default_prog = run_env.workpath('default_prog') - - r = test.command_args() - expect = [default_prog] - assert r == expect, (expect, r) - - r = test.command_args(interpreter='PYTHON') - expect = ['PYTHON', default_prog] - assert r == expect, (expect, r) - - r = test.command_args(interpreter='PYTHON', arguments='arg3 arg4') - expect = ['PYTHON', default_prog, 'arg3', 'arg4'] - assert r == expect, (expect, r) - - test.interpreter_set('default_python') - - r = test.command_args() - expect = ['default_python', default_prog] - assert r == expect, (expect, r) - - r = test.command_args(arguments='arg5 arg6') - expect = ['default_python', default_prog, 'arg5', 'arg6'] - assert r == expect, (expect, r) - - r = test.command_args('new_prog_1') - expect = [run_env.workpath('new_prog_1')] - assert r == expect, (expect, r) - - r = test.command_args(program='new_prog_2') - expect = [run_env.workpath('new_prog_2')] - assert r == expect, (expect, r) - - - -class start_TestCase(TestCmdTestCase): - def setup_run_scripts(self): - t = TestCmdTestCase.setup_run_scripts(self) - 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 -import sys - -class Unbuffered: - def __init__(self, file): - self.file = file - def write(self, arg): - self.file.write(arg) - self.file.flush() - def __getattr__(self, attr): - return getattr(self.file, attr) - -sys.stdout = Unbuffered(sys.stdout) -sys.stderr = Unbuffered(sys.stderr) - -sys.stdout.write('script_recv: STDOUT\\n') -sys.stderr.write('script_recv: STDERR\\n') -logfp = open(r'%s', 'wb') -while 1: - 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) -logfp.close() - """ % 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 test_start(self): - """Test start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start() - self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, - repr([])) - p.wait() - - p = test.start(arguments='arg1 arg2 arg3') - self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, - repr(['arg1', 'arg2', 'arg3'])) - p.wait() - - p = test.start(program=t.scriptx, arguments='foo') - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr(['foo'])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr(['foo'])) - p.wait() - - p = test.start(program=t.script1, interpreter=['python', '-x']) - self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, - repr([])) - p.wait() - - p = test.start(program='no_script', interpreter='python') - status = p.wait() - assert status != None, status - - try: - p = test.start(program='no_script', interpreter='no_interpreter') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - status = p.wait() - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just returns - # a non-zero exit status. - assert status != None, status - - testx = TestCmd.TestCmd(program = t.scriptx, - workdir = '', - subdir = 't.scriptx_subdir') - - p = testx.start() - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr([])) - p.wait() - - p = testx.start(arguments='foo bar') - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr(['foo', 'bar'])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr(['foo', 'bar'])) - p.wait() - - p = testx.start(program=t.script, interpreter='python', arguments='bar') - self.run_match(p.stdout.read(), t.script, "STDOUT", t.workdir, - repr(['bar'])) - self.run_match(p.stderr.read(), t.script, "STDERR", t.workdir, - repr(['bar'])) - p.wait() - - p = testx.start(program=t.script1, interpreter=('python', '-x')) - self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, - repr([])) - p.wait() - - s = os.path.join('.', t.scriptx) - p = testx.start(program=[s]) - self.run_match(p.stdout.read(), t.scriptx, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.scriptx, "STDERR", t.workdir, - repr([])) - p.wait() - - try: - testx.start(program='no_program') - except OSError: - # Python versions that use subprocess throw an OSError - # exception when they try to execute something that - # isn't there. - pass - else: - # Python versions that use os.popen3() or the Popen3 - # class run things through the shell, which just dies - # trying to execute the non-existent program before - # we can wait() for it. - try: - p = p.wait() - except OSError: - pass - - test1 = TestCmd.TestCmd(program = t.script1, - interpreter = ['python', '-x'], - workdir = '') - - p = test1.start() - self.run_match(p.stdout.read(), t.script1, "STDOUT", t.workdir, - repr([])) - self.run_match(p.stderr.read(), t.script1, "STDERR", t.workdir, - repr([])) - p.wait() - - finally: - os.chdir(t.orig_cwd) - - def test_finish(self): - """Test finish()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.recv_script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - test.start(stdin=1) - test.finish() - expect_stdout = """\ -script_recv: STDOUT -""" - expect_stderr = """\ -script_recv: STDERR -""" - stdout = test.stdout() - assert stdout == expect_stdout, stdout - stderr = test.stderr() - assert stderr == expect_stderr, stderr - - p = test.start(stdin=1) - p.send('input\n') - test.finish(p) - expect_stdout = """\ -script_recv: STDOUT -script_recv: STDOUT: input -""" - expect_stderr = """\ -script_recv: STDERR -script_recv: STDERR: input -""" - stdout = test.stdout() - assert stdout == expect_stdout, stdout - stderr = test.stderr() - assert stderr == expect_stderr, stderr - - p = test.start(combine=1, stdin=1) - p.send('input\n') - test.finish(p) - expect_stdout = """\ -script_recv: STDOUT -script_recv: STDERR -script_recv: STDOUT: input -script_recv: STDERR: input -""" - expect_stderr = "" - stdout = test.stdout() - assert stdout == expect_stdout, stdout - stderr = test.stderr() - assert stderr == expect_stderr, stderr - - finally: - os.chdir(t.orig_cwd) - - def test_recv(self): - """Test the recv() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start() - stdout = p.recv() - while stdout == '': - import time - time.sleep(1) - stdout = p.recv() - self.run_match(stdout, t.script, "STDOUT", t.workdir, - repr([])) - p.wait() - - finally: - os.chdir(t.orig_cwd) - - def test_recv_err(self): - """Test the recv_err() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start() - stderr = p.recv_err() - while stderr == '': - import time - time.sleep(1) - stderr = p.recv_err() - self.run_match(stderr, t.script, "STDERR", t.workdir, - repr([])) - p.wait() - - - finally: - os.chdir(t.orig_cwd) - - def test_send(self): - """Test the send() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.recv_script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - p = test.start(stdin=1) - input = 'stdin.write() input to the receive script\n' - p.stdin.write(input) - p.stdin.close() - p.wait() - result = open(t.recv_out_path, 'rb').read() - expect = 'script_recv: ' + input - assert result == expect, repr(result) - - p = test.start(stdin=1) - input = 'send() input to the receive script\n' - p.send(input) - p.stdin.close() - p.wait() - result = open(t.recv_out_path, 'rb').read() - expect = 'script_recv: ' + input - assert result == expect, repr(result) - - finally: - os.chdir(t.orig_cwd) - - # TODO(sgk): figure out how to eliminate the race conditions here. - def __FLAKY__test_send_recv(self): - """Test the send_recv() method of objects returned by start()""" - - t = self.setup_run_scripts() - - # Everything before this prepared our "source directory." - # Now do the real test. - try: - - test = TestCmd.TestCmd(program = t.recv_script, - interpreter = 'python', - workdir = '', - subdir = 'script_subdir') - - def do_send_recv(p, input): - send, stdout, stderr = p.send_recv(input) - stdout = self.translate_newlines(stdout) - stderr = self.translate_newlines(stderr) - return send, stdout, stderr - - p = test.start(stdin=1) - input = 'input to the receive script\n' - send, stdout, stderr = do_send_recv(p, input) - # Buffering issues and a race condition prevent this from - # being completely deterministic, so check for both null - # output and the first write() on each stream. - assert stdout in ("", "script_recv: STDOUT\n"), stdout - assert stderr in ("", "script_recv: STDERR\n"), stderr - send, stdout, stderr = do_send_recv(p, input) - assert stdout in ("", "script_recv: STDOUT\n"), stdout - assert stderr in ("", "script_recv: STDERR\n"), stderr - p.stdin.close() - stdout = self.translate_newlines(p.recv()) - stderr = self.translate_newlines(p.recv_err()) - assert stdout in ("", "script_recv: STDOUT\n"), stdout - assert stderr in ("", "script_recv: STDERR\n"), stderr - p.wait() - stdout = self.translate_newlines(p.recv()) - stderr = self.translate_newlines(p.recv_err()) - expect_stdout = """\ -script_recv: STDOUT -script_recv: STDOUT: input to the receive script -script_recv: STDOUT: input to the receive script -""" - expect_stderr = """\ -script_recv: STDERR -script_recv: STDERR: input to the receive script -script_recv: STDERR: input to the receive script -""" - assert stdout == expect_stdout, stdout - assert stderr == expect_stderr, stderr - result = open(t.recv_out_path, 'rb').read() - expect = ('script_recv: ' + input) * 2 - assert result == expect, (result, stdout, stderr) - - finally: - os.chdir(t.orig_cwd) - - - -class stdin_TestCase(TestCmdTestCase): - def test_stdin(self): - """Test stdin()""" - run_env = TestCmd.TestCmd(workdir = '') - run_env.write('run', """from __future__ import print_function -import fileinput -for line in fileinput.input(): - print('Y'.join(line[:-1].split('X'))) -""") - run_env.write('input', "X on X this X line X\n") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(program = 'run', interpreter = 'python', workdir = '') - test.run(arguments = 'input') - assert test.stdout() == "Y on Y this Y line Y\n" - test.run(stdin = "X is X here X tooX\n") - assert test.stdout() == "Y is Y here Y tooY\n" - test.run(stdin = """X here X -X there X -""") - assert test.stdout() == "Y here Y\nY there Y\n" - test.run(stdin = ["X line X\n", "X another X\n"]) - assert test.stdout() == "Y line Y\nY another Y\n" - - - -class stdout_TestCase(TestCmdTestCase): - def test_stdout(self): - """Test stdout()""" - run_env = TestCmd.TestCmd(workdir = '') - 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 -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:]) -sys.stderr.write("run2 STDERR second line\\n") -""") - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd(interpreter = 'python', workdir = '') - try: - output = test.stdout() - except IndexError: - pass - else: - raise IndexError("got unexpected output:\n\t`%s'\n" % output) - test.program_set('run1') - test.run(arguments = 'foo bar') - test.program_set('run2') - test.run(arguments = 'snafu') - # XXX SHOULD TEST ABSOLUTE NUMBER AS WELL - output = test.stdout() - assert output == "run2 STDOUT ['snafu']\nrun2 STDOUT second line\n", output - output = test.stdout(run = -1) - assert output == "run1 STDOUT ['foo', 'bar']\nrun1 STDOUT second line\n", output - - - -class subdir_TestCase(TestCmdTestCase): - def test_subdir(self): - """Test subdir()""" - test = TestCmd.TestCmd(workdir = '', subdir = ['no', 'such', 'subdir']) - assert not os.path.exists(test.workpath('no')) - - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - assert test.subdir('bar') == 1 - assert test.subdir(['foo', 'succeed']) == 1 - if os.name != "nt": - os.chmod(test.workpath('foo'), 0o500) - assert test.subdir(['foo', 'fail']) == 0 - assert test.subdir(['sub', 'dir', 'ectory'], 'sub') == 1 - assert test.subdir('one', - UserList(['one', 'two']), - ['one', 'two', 'three']) == 3 - assert os.path.isdir(test.workpath('foo')) - assert os.path.isdir(test.workpath('bar')) - assert os.path.isdir(test.workpath('foo', 'succeed')) - if os.name != "nt": - assert not os.path.exists(test.workpath('foo', 'fail')) - assert os.path.isdir(test.workpath('sub')) - assert not os.path.exists(test.workpath('sub', 'dir')) - assert not os.path.exists(test.workpath('sub', 'dir', 'ectory')) - assert os.path.isdir(test.workpath('one', 'two', 'three')) - - - -class symlink_TestCase(TestCmdTestCase): - 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') - wdir_foo_file2 = os.path.join(test.workdir, 'foo', 'file2') - wdir_target2 = os.path.join(test.workdir, 'target2') - wdir_foo_target2 = os.path.join(test.workdir, 'foo', 'target2') - - test.symlink('target1', 'file1') - assert os.path.islink(wdir_file1) - assert not os.path.exists(wdir_file1) - open(wdir_target1, 'w').write("") - assert os.path.exists(wdir_file1) - - test.symlink('target2', ['foo', 'file2']) - assert os.path.islink(wdir_foo_file2) - assert not os.path.exists(wdir_foo_file2) - open(wdir_target2, 'w').write("") - assert not os.path.exists(wdir_foo_file2) - open(wdir_foo_target2, 'w').write("") - assert os.path.exists(wdir_foo_file2) - - - -class tempdir_TestCase(TestCmdTestCase): - def setUp(self): - TestCmdTestCase.setUp(self) - self._tempdir = tempfile.mktemp() - os.mkdir(self._tempdir) - os.chdir(self._tempdir) - - def tearDown(self): - TestCmdTestCase.tearDown(self) - os.rmdir(self._tempdir) - - def test_tempdir(self): - """Test tempdir()""" - test = TestCmd.TestCmd() - tdir1 = test.tempdir() - assert os.path.isdir(tdir1) - test.workdir_set(None) - test.cleanup() - assert not os.path.exists(tdir1) - - test = TestCmd.TestCmd() - tdir2 = test.tempdir('temp') - assert os.path.isdir(tdir2) - tdir3 = test.tempdir() - assert os.path.isdir(tdir3) - test.workdir_set(None) - test.cleanup() - assert not os.path.exists(tdir2) - assert not os.path.exists(tdir3) - - -timeout_script = """\ -import sys -import time -seconds = int(sys.argv[1]) -sys.stdout.write('sleeping %s\\n' % seconds) -sys.stdout.flush() -time.sleep(seconds) -sys.stdout.write('slept %s\\n' % seconds) -sys.stdout.flush() -sys.exit(0) -""" - -class timeout_TestCase(TestCmdTestCase): - def test_initialization(self): - """Test initialization 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.run([sys.executable, test.workpath('sleep.py'), '4']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 4\n', test.stdout() - - def test_cancellation(self): - """Test timer cancellation after firing""" - test = TestCmd.TestCmd(workdir='', timeout=4) - test.write('sleep.py', timeout_script) - - test.run([sys.executable, test.workpath('sleep.py'), '6']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 6\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '2']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 2\nslept 2\n', test.stdout() - - test.run([sys.executable, test.workpath('sleep.py'), '6']) - assert test.stderr() == '', test.stderr() - assert test.stdout() == 'sleeping 6\n', test.stdout() - - def test_run(self): - """Test run() timeout""" - test = TestCmd.TestCmd(workdir='', timeout=8) - test.write('sleep.py', timeout_script) - - 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) - 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() - - - -class unlink_TestCase(TestCmdTestCase): - def test_unlink(self): - """Test unlink()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_file2 = os.path.join(test.workdir, 'file2') - wdir_foo_file3a = os.path.join(test.workdir, 'foo', 'file3a') - wdir_foo_file3b = os.path.join(test.workdir, 'foo', 'file3b') - wdir_foo_file4 = os.path.join(test.workdir, 'foo', 'file4') - wdir_file5 = os.path.join(test.workdir, 'file5') - - open(wdir_file1, 'w').write("") - open(wdir_file2, 'w').write("") - open(wdir_foo_file3a, 'w').write("") - open(wdir_foo_file3b, 'w').write("") - open(wdir_foo_file4, 'w').write("") - open(wdir_file5, 'w').write("") - - try: - contents = test.unlink('no_file') - except OSError: # expect "No such file or directory" - pass - except: - raise - - test.unlink("file1") - assert not os.path.exists(wdir_file1) - - test.unlink(wdir_file2) - assert not os.path.exists(wdir_file2) - - test.unlink(['foo', 'file3a']) - assert not os.path.exists(wdir_foo_file3a) - - test.unlink(UserList(['foo', 'file3b'])) - assert not os.path.exists(wdir_foo_file3b) - - test.unlink([test.workdir, 'foo', 'file4']) - assert not os.path.exists(wdir_foo_file4) - - # Make it so we can't unlink file5. - # For UNIX, remove write permission from the dir and the file. - # For Windows, open the file. - os.chmod(test.workdir, 0o500) - os.chmod(wdir_file5, 0o400) - f = open(wdir_file5, 'r') - - try: - try: - test.unlink('file5') - except OSError: # expect "Permission denied" - pass - except: - raise - finally: - os.chmod(test.workdir, 0o700) - os.chmod(wdir_file5, 0o600) - f.close() - - - -class touch_TestCase(TestCmdTestCase): - def test_touch(self): - """Test touch()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'sub') - - wdir_file1 = os.path.join(test.workdir, 'file1') - wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') - - open(wdir_file1, 'w').write("") - open(wdir_sub_file2, 'w').write("") - - file1_old_time = os.path.getmtime(wdir_file1) - file2_old_time = os.path.getmtime(wdir_sub_file2) - - test.sleep() - - test.touch(wdir_file1) - - file1_new_time = os.path.getmtime(wdir_file1) - assert file1_new_time > file1_old_time - - test.touch('file1', file1_old_time) - - result = os.path.getmtime(wdir_file1) - # Sub-second granularity of file systems may still vary. - # On Windows, the two times may be off by a microsecond. - assert int(result) == int(file1_old_time), (result, file1_old_time) - - test.touch(['sub', 'file2']) - - file2_new_time = os.path.getmtime(wdir_sub_file2) - assert file2_new_time > file2_old_time - - - -class verbose_TestCase(TestCmdTestCase): - def test_verbose(self): - """Test verbose()""" - test = TestCmd.TestCmd() - assert test.verbose == 0, 'verbose already initialized?' - test = TestCmd.TestCmd(verbose = 1) - assert test.verbose == 1, 'did not initialize verbose' - test.verbose = 2 - assert test.verbose == 2, 'did not set verbose' - - - -class workdir_TestCase(TestCmdTestCase): - def test_workdir(self): - """Test workdir()""" - run_env = TestCmd.TestCmd(workdir = '') - os.chdir(run_env.workdir) - # Everything before this prepared our "source directory." - # Now do the real test. - test = TestCmd.TestCmd() - assert test.workdir is None - - test = TestCmd.TestCmd(workdir = None) - assert test.workdir is None - - test = TestCmd.TestCmd(workdir = '') - assert test.workdir != None - assert os.path.isdir(test.workdir) - - test = TestCmd.TestCmd(workdir = 'dir') - assert test.workdir != None - assert os.path.isdir(test.workdir) - - no_such_subdir = os.path.join('no', 'such', 'subdir') - try: - test = TestCmd.TestCmd(workdir = no_such_subdir) - except OSError: # expect "No such file or directory" - pass - except: - raise - - test = TestCmd.TestCmd(workdir = 'foo') - workdir_foo = test.workdir - assert workdir_foo != None - - test.workdir_set('bar') - workdir_bar = test.workdir - assert workdir_bar != None - - try: - test.workdir_set(no_such_subdir) - except OSError: - pass # expect "No such file or directory" - except: - raise - assert workdir_bar == test.workdir - - assert os.path.isdir(workdir_foo) - assert os.path.isdir(workdir_bar) - - - -class workdirs_TestCase(TestCmdTestCase): - def test_workdirs(self): - """Test workdirs()""" - test = TestCmd.TestCmd() - assert test.workdir is None - test.workdir_set('') - wdir1 = test.workdir - test.workdir_set('') - wdir2 = test.workdir - assert os.path.isdir(wdir1) - assert os.path.isdir(wdir2) - test.cleanup() - assert not os.path.exists(wdir1) - assert not os.path.exists(wdir2) - - - -class workpath_TestCase(TestCmdTestCase): - def test_workpath(self): - """Test workpath()""" - test = TestCmd.TestCmd() - assert test.workdir is None - - test = TestCmd.TestCmd(workdir = '') - wpath = test.workpath('foo', 'bar') - assert wpath == os.path.join(test.workdir, 'foo', 'bar') - - - -class readable_TestCase(TestCmdTestCase): - 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')) - - test.readable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_readable(test.workdir) - assert not _is_readable(test.workpath('file1')) - assert not _is_readable(test.workpath('foo')) - assert not _is_readable(test.workpath('foo', 'file2')) - - test.readable(test.workdir, 1) - assert _is_readable(test.workdir) - assert _is_readable(test.workpath('file1')) - assert _is_readable(test.workpath('foo')) - assert _is_readable(test.workpath('foo', 'file2')) - - test.readable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_readable(test.workdir) - assert not _is_readable(test.workpath('file1')) - assert not _is_readable(test.workpath('foo')) - assert not _is_readable(test.workpath('foo', 'file2')) - - test.readable(test.workpath('file1'), 1) - assert _is_readable(test.workpath('file1')) - - test.readable(test.workpath('file1'), 0) - assert not _is_readable(test.workpath('file1')) - - test.readable(test.workdir, 1) - - - -class writable_TestCase(TestCmdTestCase): - 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')) - - test.writable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_writable(test.workdir) - assert not _is_writable(test.workpath('file1')) - assert not _is_writable(test.workpath('foo')) - assert not _is_writable(test.workpath('foo', 'file2')) - - test.writable(test.workdir, 1) - assert _is_writable(test.workdir) - assert _is_writable(test.workpath('file1')) - assert _is_writable(test.workpath('foo')) - assert _is_writable(test.workpath('foo', 'file2')) - - test.writable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_writable(test.workdir) - assert not _is_writable(test.workpath('file1')) - assert not _is_writable(test.workpath('foo')) - assert not _is_writable(test.workpath('foo', 'file2')) - - test.writable(test.workpath('file1'), 1) - assert _is_writable(test.workpath('file1')) - - test.writable(test.workpath('file1'), 0) - assert not _is_writable(test.workpath('file1')) - - - -class executable_TestCase(TestCmdTestCase): - 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')) - - def make_executable(fname): - st = os.stat(fname) - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]|0o100)) - - def make_non_executable(fname): - st = os.stat(fname) - os.chmod(fname, stat.S_IMODE(st[stat.ST_MODE]&~0o100)) - - test.executable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_executable(test.workdir) - make_executable(test.workdir) - assert not _is_executable(test.workpath('file1')) - assert not _is_executable(test.workpath('foo')) - make_executable(test.workpath('foo')) - assert not _is_executable(test.workpath('foo', 'file2')) - make_non_executable(test.workpath('foo')) - make_non_executable(test.workdir) - - test.executable(test.workdir, 1) - assert _is_executable(test.workdir) - assert _is_executable(test.workpath('file1')) - assert _is_executable(test.workpath('foo')) - assert _is_executable(test.workpath('foo', 'file2')) - - test.executable(test.workdir, 0) - # XXX skip these tests if euid == 0? - assert not _is_executable(test.workdir) - make_executable(test.workdir) - assert not _is_executable(test.workpath('file1')) - assert not _is_executable(test.workpath('foo')) - make_executable(test.workpath('foo')) - assert not _is_executable(test.workpath('foo', 'file2')) - - test.executable(test.workpath('file1'), 1) - assert _is_executable(test.workpath('file1')) - - test.executable(test.workpath('file1'), 0) - assert not _is_executable(test.workpath('file1')) - - test.executable(test.workdir, 1) - - - -class write_TestCase(TestCmdTestCase): - def test_write(self): - """Test write()""" - test = TestCmd.TestCmd(workdir = '', subdir = 'foo') - test.write('file1', "Test file #1\n") - test.write(['foo', 'file2'], "Test file #2\n") - try: - test.write(['bar', 'file3'], "Test file #3 (should not get created)\n") - except IOError: # expect "No such file or directory" - pass - except: - raise - test.write(test.workpath('file4'), "Test file #4.\n") - test.write(test.workpath('foo', 'file5'), "Test file #5.\n") - try: - test.write(test.workpath('bar', 'file6'), "Test file #6 (should not get created)\n") - except IOError: # expect "No such file or directory" - pass - except: - raise - - try: - test.write('file7', "Test file #8.\n", mode = 'r') - except ValueError: # expect "mode must begin with 'w' - pass - except: - raise - - test.write('file8', "Test file #8.\n", mode = 'w') - test.write('file9', "Test file #9.\r\n", mode = 'wb') - - if os.name != "nt": - os.chmod(test.workdir, 0o500) - try: - test.write('file10', "Test file #10 (should not get created).\n") - except IOError: # expect "Permission denied" - pass - except: - raise - - assert os.path.isdir(test.workpath('foo')) - assert not os.path.exists(test.workpath('bar')) - assert os.path.isfile(test.workpath('file1')) - assert os.path.isfile(test.workpath('foo', 'file2')) - assert not os.path.exists(test.workpath('bar', 'file3')) - assert os.path.isfile(test.workpath('file4')) - assert os.path.isfile(test.workpath('foo', 'file5')) - assert not os.path.exists(test.workpath('bar', 'file6')) - assert not os.path.exists(test.workpath('file7')) - assert os.path.isfile(test.workpath('file8')) - assert os.path.isfile(test.workpath('file9')) - if os.name != "nt": - assert not os.path.exists(test.workpath('file10')) - - assert open(test.workpath('file8'), 'r').read() == "Test file #8.\n" - assert open(test.workpath('file9'), 'rb').read() == "Test file #9.\r\n" - - - -class variables_TestCase(TestCmdTestCase): - def test_variables(self): - """Test global variables""" - run_env = TestCmd.TestCmd(workdir = '') - - variables = [ - 'fail_test', - 'no_result', - 'pass_test', - 'match_exact', - 'match_re', - 'match_re_dotall', - 'python', - '_python_', - 'TestCmd', - ] - - script = "from __future__ import print_function\n" + \ - "import TestCmd\n" + \ - '\n'.join([ "print(TestCmd.%s\n)" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - script = "from __future__ import print_function\n" + \ - "from TestCmd import *\n" + \ - '\n'.join([ "print(%s)" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - 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) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestCommon.py b/QMTest/TestCommon.py deleted file mode 100644 index a475ddc..0000000 --- a/QMTest/TestCommon.py +++ /dev/null @@ -1,748 +0,0 @@ -""" -TestCommon.py: a testing framework for commands and scripts - with commonly useful error handling - -The TestCommon module provides a simple, high-level interface for writing -tests of executable commands and scripts, especially commands and scripts -that interact with the file system. All methods throw exceptions and -exit on failure, with useful error messages. This makes a number of -explicit checks unnecessary, making the test scripts themselves simpler -to write and easier to read. - -The TestCommon class is a subclass of the TestCmd class. In essence, -TestCommon is a wrapper that handles common TestCmd error conditions in -useful ways. You can use TestCommon directly, or subclass it for your -program and add additional (or override) methods to tailor it to your -program's specific needs. Alternatively, the TestCommon class serves -as a useful example of how to define your own TestCmd subclass. - -As a subclass of TestCmd, TestCommon provides access to all of the -variables and methods from the TestCmd module. Consequently, you can -use any variable or method documented in the TestCmd module without -having to explicitly import TestCmd. - -A TestCommon environment object is created via the usual invocation: - - import TestCommon - test = TestCommon.TestCommon() - -You can use all of the TestCmd keyword arguments when instantiating a -TestCommon object; see the TestCmd documentation for details. - -Here is an overview of the methods and keyword arguments that are -provided by the TestCommon class: - - test.must_be_writable('file1', ['file2', ...]) - - test.must_contain('file', 'required text\n') - - test.must_contain_all(output, input, ['title', find]) - - test.must_contain_all_lines(output, lines, ['title', find]) - - test.must_contain_any_line(output, lines, ['title', find]) - - test.must_contain_exactly_lines(output, lines, ['title', find]) - - test.must_exist('file1', ['file2', ...]) - - test.must_match('file', "expected contents\n") - - test.must_not_be_writable('file1', ['file2', ...]) - - test.must_not_contain('file', 'banned text\n') - - test.must_not_contain_any_line(output, lines, ['title', find]) - - test.must_not_exist('file1', ['file2', ...]) - - test.run(options = "options to be prepended to arguments", - stdout = "expected standard output from the program", - stderr = "expected error output from the program", - status = expected_status, - match = match_function) - -The TestCommon module also provides the following variables - - TestCommon.python - TestCommon._python_ - TestCommon.exe_suffix - TestCommon.obj_suffix - TestCommon.shobj_prefix - TestCommon.shobj_suffix - TestCommon.lib_prefix - TestCommon.lib_suffix - TestCommon.dll_prefix - TestCommon.dll_suffix - -""" - -# Copyright 2000-2010 Steven Knight -# This module is free software, and you may redistribute it and/or modify -# it under the same terms as Python itself, so long as this copyright message -# and disclaimer are retained in their original form. -# -# IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -# SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -# THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -# DAMAGE. -# -# THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -# AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - -from __future__ import print_function - -__author__ = "Steven Knight " -__revision__ = "TestCommon.py 1.3.D001 2010/06/03 12:58:27 knight" -__version__ = "1.3" - -import copy -import os -import stat -import sys -import glob - -try: - from collections import UserList -except ImportError: - # no 'collections' module or no UserList in collections - exec('from UserList import UserList') - -from TestCmd import * -from TestCmd import __all__ - -__all__.extend([ 'TestCommon', - 'exe_suffix', - 'obj_suffix', - 'shobj_prefix', - 'shobj_suffix', - 'lib_prefix', - 'lib_suffix', - 'dll_prefix', - 'dll_suffix', - ]) - -# Variables that describe the prefixes and suffixes on this system. -if sys.platform == 'win32': - exe_suffix = '.exe' - obj_suffix = '.obj' - shobj_suffix = '.obj' - shobj_prefix = '' - lib_prefix = '' - lib_suffix = '.lib' - dll_prefix = '' - dll_suffix = '.dll' -elif sys.platform == 'cygwin': - exe_suffix = '.exe' - obj_suffix = '.o' - shobj_suffix = '.os' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'cyg' - dll_suffix = '.dll' -elif sys.platform.find('irix') != -1: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.o' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.so' -elif sys.platform.find('darwin') != -1: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.os' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.dylib' -elif sys.platform.find('sunos') != -1: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.o' - shobj_prefix = 'so_' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.so' -else: - exe_suffix = '' - obj_suffix = '.o' - shobj_suffix = '.os' - shobj_prefix = '' - lib_prefix = 'lib' - lib_suffix = '.a' - dll_prefix = 'lib' - dll_suffix = '.so' - -def is_List(e): - return isinstance(e, (list, UserList)) - -def is_Tuple(e): - return isinstance(e, tuple) - -def is_Sequence(e): - return (not hasattr(e, "strip") and - hasattr(e, "__getitem__") or - hasattr(e, "__iter__")) - -def is_writable(f): - mode = os.stat(f)[stat.ST_MODE] - return mode & stat.S_IWUSR - -def separate_files(flist): - existing = [] - missing = [] - for f in flist: - if os.path.exists(f): - existing.append(f) - else: - missing.append(f) - return existing, missing - -if os.name == 'posix': - def _failed(self, status = 0): - if self.status is None or status is None: - return None - return _status(self) != status - def _status(self): - return self.status -elif os.name == 'nt': - def _failed(self, status = 0): - return not (self.status is None or status is None) and \ - self.status != status - def _status(self): - return self.status - -class TestCommon(TestCmd): - - # Additional methods from the Perl Test::Cmd::Common module - # that we may wish to add in the future: - # - # $test->subdir('subdir', ...); - # - # $test->copy('src_file', 'dst_file'); - - def __init__(self, **kw): - """Initialize a new TestCommon instance. This involves just - calling the base class initialization, and then changing directory - to the workdir. - """ - TestCmd.__init__(self, **kw) - os.chdir(self.workdir) - - def options_arguments(self, options, arguments): - """Merges the "options" keyword argument with the arguments.""" - if options: - if arguments is None: - return options - if isinstance(options, str): - options = [options] - if isinstance(arguments, str): - arguments = [arguments] - arguments = ' '.join(options + arguments) - return arguments - - def must_be_writable(self, *files): - """Ensures that the specified file(s) exist and are writable. - An individual file can be specified as a list of directory names, - in which case the pathname will be constructed by concatenating - them. Exits FAILED if any of the files does not exist or is - not writable. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing, missing = separate_files(files) - unwritable = [x for x in existing if not is_writable(x)] - if missing: - print("Missing files: `%s'" % "', `".join(missing)) - if unwritable: - print("Unwritable files: `%s'" % "', `".join(unwritable)) - self.fail_test(missing + unwritable) - - def must_contain(self, file, required, mode = 'rb', find = None): - """Ensures that the specified file contains the required text. - """ - file_contents = self.read(file, mode) - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - contains = find(file_contents, required) - if not contains: - print("File `%s' does not contain required string." % file) - print(self.banner('Required string ')) - print(required) - print(self.banner('%s contents ' % file)) - print(file_contents) - self.fail_test(not contains) - - def must_contain_all(self, output, input, title=None, find=None): - """Ensures that the specified output string (first argument) - contains all of the specified input as a block (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, i): - try: - return o.index(i) - except ValueError: - return None - - if is_List(output): - output = os.newline.join(output) - - if find(output, input) is None: - if title is None: - title = 'output' - print('Missing expected input from {}:'.format(title)) - print(input) - print(self.banner(title + ' ')) - print(output) - self.fail_test() - - def must_contain_all_lines(self, output, lines, title=None, find=None): - """Ensures that the specified output string (first argument) - contains all of the specified lines (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - missing = [] - if is_List(output): - output = '\n'.join(output) - - for line in lines: - if find(output, line) is None: - missing.append(line) - - if missing: - if title is None: - title = 'output' - sys.stdout.write("Missing expected lines from %s:\n" % title) - for line in missing: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner(title + ' ') + '\n') - sys.stdout.write(output) - self.fail_test() - - def must_contain_any_line(self, output, lines, title=None, find=None): - """Ensures that the specified output string (first argument) - contains at least one of the specified lines (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - for line in lines: - if find(output, line) is not None: - return - - if title is None: - title = 'output' - sys.stdout.write("Missing any expected line from %s:\n" % title) - for line in lines: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner(title + ' ') + '\n') - sys.stdout.write(output) - self.fail_test() - - def must_contain_exactly_lines(self, output, expect, title=None, find=None): - """Ensures that the specified output string (first argument) - contains all of the lines in the expected string (second argument) - with none left over. - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. The function must return the index - of the found line in the output, or None if the line is not found. - """ - out = output.splitlines() - if is_List(expect): - exp = [ e.rstrip('\n') for e in expect ] - else: - exp = expect.splitlines() - if sorted(out) == sorted(exp): - # early out for exact match - return - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - missing = [] - for line in exp: - found = find(out, line) - if found is None: - missing.append(line) - else: - out.pop(found) - - if not missing and not out: - # all lines were matched - return - - if title is None: - title = 'output' - if missing: - sys.stdout.write("Missing expected lines from %s:\n" % title) - for line in missing: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner('Missing %s ' % title) + '\n') - if out: - sys.stdout.write("Extra unexpected lines from %s:\n" % title) - for line in out: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner('Extra %s ' % title) + '\n') - sys.stdout.flush() - self.fail_test() - - def must_contain_lines(self, lines, output, title=None, find = None): - # Deprecated; retain for backwards compatibility. - return self.must_contain_all_lines(output, lines, title, find) - - def must_exist(self, *files): - """Ensures that the specified file(s) must exist. An individual - file be specified as a list of directory names, in which case the - pathname will be constructed by concatenating them. Exits FAILED - if any of the files does not exist. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - missing = [x for x in files if not os.path.exists(x) and not os.path.islink(x) ] - if missing: - print("Missing files: `%s'" % "', `".join(missing)) - self.fail_test(missing) - - def must_exist_one_of(self, files): - """Ensures that at least one of the specified file(s) exists. - The filenames can be given as a list, where each entry may be - a single path string, or a tuple of folder names and the final - filename that get concatenated. - Supports wildcard names like 'foo-1.2.3-*.rpm'. - Exits FAILED if none of the files exists. - """ - missing = [] - for x in files: - if is_List(x) or is_Tuple(x): - xpath = os.path.join(*x) - else: - xpath = is_Sequence(x) and os.path.join(x) or x - if glob.glob(xpath): - return - missing.append(xpath) - print("Missing one of: `%s'" % "', `".join(missing)) - self.fail_test(missing) - - def must_match(self, file, expect, 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) - if not match: - match = self.match - try: - self.fail_test(not match(to_str(file_contents), to_str(expect)), message=message) - except KeyboardInterrupt: - raise - except: - print("Unexpected contents of `%s'" % file) - self.diff(expect, 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 find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - contains = find(file_contents, banned) - if contains: - print("File `%s' contains banned string." % file) - print(self.banner('Banned string ')) - print(banned) - print(self.banner('%s contents ' % file)) - print(file_contents) - self.fail_test(contains) - - def must_not_contain_any_line(self, output, lines, title=None, find=None): - """Ensures that the specified output string (first argument) - does not contain any of the specified lines (second argument). - - An optional third argument can be used to describe the type - of output being searched, and only shows up in failure output. - - An optional fourth argument can be used to supply a different - function, of the form "find(line, output), to use when searching - for lines in the output. - """ - if find is None: - def find(o, l): - try: - return o.index(l) - except ValueError: - return None - unexpected = [] - for line in lines: - if find(output, line) is not None: - unexpected.append(line) - - if unexpected: - if title is None: - title = 'output' - sys.stdout.write("Unexpected lines in %s:\n" % title) - for line in unexpected: - sys.stdout.write(' ' + repr(line) + '\n') - sys.stdout.write(self.banner(title + ' ') + '\n') - sys.stdout.write(output) - self.fail_test() - - def must_not_contain_lines(self, lines, output, title=None, find=None): - return self.must_not_contain_any_line(output, lines, title, find) - - def must_not_exist(self, *files): - """Ensures that the specified file(s) must not exist. - An individual file be specified as a list of directory names, in - which case the pathname will be constructed by concatenating them. - Exits FAILED if any of the files exists. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing = [x for x in files if os.path.exists(x) or os.path.islink(x)] - if existing: - print("Unexpected files exist: `%s'" % "', `".join(existing)) - self.fail_test(existing) - - def must_not_exist_any_of(self, files): - """Ensures that none of the specified file(s) exists. - The filenames can be given as a list, where each entry may be - a single path string, or a tuple of folder names and the final - filename that get concatenated. - Supports wildcard names like 'foo-1.2.3-*.rpm'. - Exits FAILED if any of the files exists. - """ - existing = [] - for x in files: - if is_List(x) or is_Tuple(x): - xpath = os.path.join(*x) - else: - xpath = is_Sequence(x) and os.path.join(x) or x - if glob.glob(xpath): - existing.append(xpath) - if existing: - print("Unexpected files exist: `%s'" % "', `".join(existing)) - self.fail_test(existing) - - def must_not_be_writable(self, *files): - """Ensures that the specified file(s) exist and are not writable. - An individual file can be specified as a list of directory names, - in which case the pathname will be constructed by concatenating - them. Exits FAILED if any of the files does not exist or is - writable. - """ - files = [is_List(x) and os.path.join(*x) or x for x in files] - existing, missing = separate_files(files) - writable = [file for file in existing if is_writable(file)] - if missing: - print("Missing files: `%s'" % "', `".join(missing)) - if writable: - print("Writable files: `%s'" % "', `".join(writable)) - self.fail_test(missing + writable) - - def _complete(self, actual_stdout, expected_stdout, - actual_stderr, expected_stderr, status, match): - """ - Post-processes running a subcommand, checking for failure - status and displaying output appropriately. - """ - if _failed(self, status): - expect = '' - if status != 0: - expect = " (expected %s)" % str(status) - print("%s returned %s%s" % (self.program, _status(self), expect)) - print(self.banner('STDOUT ')) - print(actual_stdout) - print(self.banner('STDERR ')) - print(actual_stderr) - self.fail_test() - if (expected_stdout is not None - and not match(actual_stdout, expected_stdout)): - self.diff(expected_stdout, actual_stdout, 'STDOUT ') - if actual_stderr: - print(self.banner('STDERR ')) - print(actual_stderr) - self.fail_test() - if (expected_stderr is not None - and not match(actual_stderr, expected_stderr)): - print(self.banner('STDOUT ')) - print(actual_stdout) - self.diff(expected_stderr, actual_stderr, 'STDERR ') - self.fail_test() - - def start(self, program = None, - interpreter = None, - options = None, - arguments = None, - universal_newlines = None, - **kw): - """ - Starts a program or script for the test environment, handling - any exceptions. - """ - arguments = self.options_arguments(options, arguments) - try: - return TestCmd.start(self, program, interpreter, arguments, - universal_newlines, **kw) - except KeyboardInterrupt: - raise - except Exception as e: - print(self.banner('STDOUT ')) - try: - print(self.stdout()) - except IndexError: - pass - print(self.banner('STDERR ')) - try: - print(self.stderr()) - except IndexError: - pass - cmd_args = self.command_args(program, interpreter, arguments) - sys.stderr.write('Exception trying to execute: %s\n' % cmd_args) - raise e - - def finish(self, popen, stdout = None, stderr = '', status = 0, **kw): - """ - Finishes and waits for the process being run under control of - the specified popen argument. Additional arguments are similar - to those of the run() method: - - stdout The expected standard output from - the command. A value of None means - don't test standard output. - - stderr The expected error output from - the command. A value of None means - don't test error output. - - status The expected exit status from the - command. A value of None means don't - test exit status. - """ - TestCmd.finish(self, popen, **kw) - match = kw.get('match', self.match) - self._complete(self.stdout(), stdout, - self.stderr(), stderr, status, match) - - def run(self, options = None, arguments = None, - stdout = None, stderr = '', status = 0, **kw): - """Runs the program under test, checking that the test succeeded. - - The parameters are the same as the base TestCmd.run() method, - with the addition of: - - options Extra options that get appended to the beginning - of the arguments. - - stdout The expected standard output from - the command. A value of None means - don't test standard output. - - stderr The expected error output from - the command. A value of None means - don't test error output. - - status The expected exit status from the - command. A value of None means don't - test exit status. - - By default, this expects a successful exit (status = 0), does - not test standard output (stdout = None), and expects that error - output is empty (stderr = ""). - """ - kw['arguments'] = self.options_arguments(options, arguments) - try: - match = kw['match'] - del kw['match'] - except KeyError: - match = self.match - TestCmd.run(self, **kw) - self._complete(self.stdout(), stdout, - self.stderr(), stderr, status, match) - - def skip_test(self, message="Skipping test.\n"): - """Skips a test. - - Proper test-skipping behavior is dependent on the external - TESTCOMMON_PASS_SKIPS environment variable. If set, we treat - the skip as a PASS (exit 0), and otherwise treat it as NO RESULT. - In either case, we print the specified message as an indication - that the substance of the test was skipped. - - (This was originally added to support development under Aegis. - Technically, skipping a test is a NO RESULT, but Aegis would - treat that as a test failure and prevent the change from going to - the next step. Since we ddn't want to force anyone using Aegis - to have to install absolutely every tool used by the tests, we - would actually report to Aegis that a skipped test has PASSED - so that the workflow isn't held up.) - """ - if message: - sys.stdout.write(message) - sys.stdout.flush() - pass_skips = os.environ.get('TESTCOMMON_PASS_SKIPS') - if pass_skips in [None, 0, '0']: - # skip=1 means skip this function when showing where this - # result came from. They only care about the line where the - # script called test.skip_test(), not the line number where - # we call test.no_result(). - self.no_result(skip=1) - else: - # We're under the development directory for this change, - # so this is an Aegis invocation; pass the test (exit 0). - self.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestCommonTests.py b/QMTest/TestCommonTests.py deleted file mode 100644 index 7949cb8..0000000 --- a/QMTest/TestCommonTests.py +++ /dev/null @@ -1,2340 +0,0 @@ -#!/usr/bin/env python -""" -TestCommonTests.py: Unit tests for the TestCommon.py module. - -Copyright 2000-2010 Steven Knight -This module is free software, and you may redistribute it and/or modify -it under the same terms as Python itself, so long as this copyright message -and disclaimer are retained in their original form. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -""" - -__author__ = "Steven Knight " -__revision__ = "TestCommonTests.py 1.3.D001 2010/06/03 12:58:27 knight" - -import difflib -import os -import re -import signal -import stat -import sys -import unittest - -# Strip the current directory so we get the right TestCommon.py module. -sys.path = sys.path[1:] - -import TestCmd -import TestCommon - -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) - -if sys.version[:3] == '1.5': - expected_newline = '\\012' -else: - expected_newline = '\\n' - -def assert_display(expect, result, error=None): - try: - expect = expect.pattern - except AttributeError: - pass - result = [ - '\n', - ('*'*80) + '\n', - expect, - ('*'*80) + '\n', - result, - ('*'*80) + '\n', - ] - if error: - result.append(error) - return ''.join(result) - - -class TestCommonTestCase(unittest.TestCase): - """Base class for TestCommon test cases, fixture and utility methods.""" - create_run_env = True - - def setUp(self): - self.orig_cwd = os.getcwd() - if self.create_run_env: - self.run_env = TestCmd.TestCmd(workdir = '') - - def tearDown(self): - os.chdir(self.orig_cwd) - - def set_up_execution_scripts(self): - run_env = self.run_env - - run_env.subdir('sub dir') - - self.python = sys.executable - - self.pass_script = run_env.workpath('sub dir', 'pass') - self.fail_script = run_env.workpath('sub dir', 'fail') - self.stdout_script = run_env.workpath('sub dir', 'stdout') - self.stderr_script = run_env.workpath('sub dir', 'stderr') - self.signal_script = run_env.workpath('sub dir', 'signal') - self.stdin_script = run_env.workpath('sub dir', 'stdin') - - preamble = "import sys" - stdout = "; sys.stdout.write(r'%s: STDOUT: ' + repr(sys.argv[1:]) + '\\n')" - stderr = "; sys.stderr.write(r'%s: STDERR: ' + repr(sys.argv[1:]) + '\\n')" - exit0 = "; sys.exit(0)" - exit1 = "; sys.exit(1)" - if sys.platform == 'win32': - wrapper = '@python -c "%s" %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\n' - else: - wrapper = '#! /usr/bin/env python\n%s\n' - wrapper = '#! /usr/bin/env python\n%s\n' - - pass_body = preamble + stdout % self.pass_script + exit0 - fail_body = preamble + stdout % self.fail_script + exit1 - stderr_body = preamble + stderr % self.stderr_script + exit0 - - run_env.write(self.pass_script, wrapper % pass_body) - run_env.write(self.fail_script, wrapper % fail_body) - run_env.write(self.stderr_script, wrapper % stderr_body) - - signal_body = lstrip("""\ - import os - import signal - os.kill(os.getpid(), signal.SIGTERM) - """) - - run_env.write(self.signal_script, wrapper % signal_body) - - stdin_body = lstrip("""\ - import sys - input = sys.stdin.read()[:-1] - sys.stdout.write(r'%s: STDOUT: ' + repr(input) + '\\n') - sys.stderr.write(r'%s: STDERR: ' + repr(input) + '\\n') - """ % (self.stdin_script, self.stdin_script)) - - run_env.write(self.stdin_script, wrapper % stdin_body) - - def run_execution_test(self, script, expect_stdout, expect_stderr): - self.set_up_execution_scripts() - - run_env = self.run_env - - os.chdir(run_env.workpath('sub dir')) - - # Everything before this prepared our "source directory." - # Now do the real test. - script = script % self.__dict__ - run_env.run(program=sys.executable, stdin=script) - - stdout = run_env.stdout() - stderr = run_env.stderr() - - expect_stdout = expect_stdout % self.__dict__ - assert stdout == expect_stdout, assert_display(expect_stdout, - stdout, - stderr) - - try: - match = expect_stderr.match - except AttributeError: - expect_stderr = expect_stderr % self.__dict__ - assert stderr == expect_stderr, assert_display(expect_stderr, - stderr) - else: - assert expect_stderr.match(stderr), assert_display(expect_stderr, - stderr) - - -class __init__TestCase(TestCommonTestCase): - def test___init__(self): - """Test initialization""" - run_env = self.run_env - - os.chdir(run_env.workdir) - script = lstrip("""\ - from __future__ import print_function - from TestCommon import TestCommon - tc = TestCommon(workdir='') - import os - print(os.getcwd()) - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout()[:-1] - assert stdout != run_env.workdir, stdout - stderr = run_env.stderr() - assert stderr == "", stderr - - -class banner_TestCase(TestCommonTestCase): - create_run_env = False - def test_banner(self): - """Test banner()""" - tc = TestCommon.TestCommon(workdir='') - - b = tc.banner('xyzzy ') - assert b == "xyzzy ==========================================================================", b - - tc.banner_width = 10 - - b = tc.banner('xyzzy ') - assert b == "xyzzy ====", b - - b = tc.banner('xyzzy ', 20) - assert b == "xyzzy ==============", b - - tc.banner_char = '-' - - b = tc.banner('xyzzy ') - assert b == "xyzzy ----", b - -class must_be_writable_TestCase(TestCommonTestCase): - def test_file_does_not_exists(self): - """Test must_be_writable(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_writable_file_exists(self): - """Test must_be_writable(): writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode | stat.S_IWUSR) - tc.must_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_non_writable_file_exists(self): - """Test must_be_writable(): non-writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode & ~stat.S_IWUSR) - tc.must_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unwritable files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_file_specified_as_list(self): - """Test must_be_writable(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - f1 = tc.workpath('sub', 'file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode | stat.S_IWUSR) - tc.must_be_writable(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - -class must_contain_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n") - tc.must_contain('file1', "1 c") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_missing(self): - """Test must_contain(): file missing""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_contain('file1', "1 c\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr.find("No such file or directory:") != -1, stderr - - def test_failure(self): - """Test must_contain(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 does not match\\n") - tc.must_contain('file1', "1 c") - tc.run() - """) - expect = lstrip("""\ - File `file1' does not contain required string. - Required string ================================================================ - 1 c - file1 contents ================================================================= - file1 does not match - - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == expect, repr(stdout) - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_mode(self): - """Test must_contain(): mode""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n", mode='w') - tc.must_contain('file1', "1 c", mode='r') - tc.write('file2', "file2 contents\\n", mode='wb') - tc.must_contain('file2', "2 c", mode='rb') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_contain_all_lines_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain_all_lines(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_all_lines(output, lines) - - test.must_contain_all_lines(output, ['www\\n']) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_all_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_all_lines(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_contain_all_lines(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'x.*', - '.*y', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - def re_search(output, line): - return re.compile(line, re.S).search(output) - test.must_contain_all_lines(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_contain_all_lines(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_all_lines(output, lines, title='STDERR') - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from STDERR: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - STDERR ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_contain_any_line_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain_any_line(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'aaa\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_any_line(output, lines) - - test.must_contain_any_line(output, ['www\\n']) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_any_line(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_any_line(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing any expected line from output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_contain_any_line(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'aaa', - '.*y', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - def re_search(output, line): - return re.compile(line, re.S).search(output) - test.must_contain_any_line(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_contain_any_line(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_any_line(output, lines, title='STDOUT') - - test.pass_test() - """) - - expect = lstrip("""\ - Missing any expected line from STDOUT: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - STDOUT ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_contain_exactly_lines_TestCase(TestCommonTestCase): - def test_success_list(self): - """Test must_contain_exactly_lines(): success (input list)""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'yyy\\n', - 'xxx\\n', - 'zzz', - 'www\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_exactly_lines(output, lines) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_success_string(self): - """Test must_contain_exactly_lines(): success (input string)""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = '''\\ - yyy - xxx - zzz - www - ''' - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_exactly_lines(output, lines) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_exactly_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_exactly_lines(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from output: - 'xxx' - 'yyy' - Missing output ================================================================= - Extra unexpected lines from output: - 'www' - 'zzz' - Extra output =================================================================== - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_contain_exactly_lines(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'zzz', - '.*y', - 'xxx', - 'www', - ] - - output = '''\\\ - www - xxx - yyy - zzz - ''' - - def re_search(output, line): - pattern = re.compile(line, re.S) - index = 0 - for o in output: - if pattern.search(o): - return index - index +=1 - return None - test.must_contain_exactly_lines(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_contain_exactly_lines(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_exactly_lines(output, lines, title='STDOUT') - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from STDOUT: - 'xxx' - 'yyy' - Missing STDOUT ================================================================= - Extra unexpected lines from STDOUT: - 'www' - 'zzz' - Extra STDOUT =================================================================== - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_contain_lines_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_contain_lines(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_contain_lines(lines, output) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_contain_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - zzz - ''' - - test.must_contain_lines(lines, output) - - test.pass_test() - """) - - expect = lstrip("""\ - Missing expected lines from output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_exist_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_exist(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_exist(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_file_specified_as_list(self): - """Test must_exist(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_broken_link(self) : - """Test must_exist(): exists but it is a broken link""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.symlink('badtarget', "brokenlink") - tc.must_exist('brokenlink') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - -class must_exist_one_of_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_exist_one_of(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_exist_one_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_exist_one_of(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_exist_one_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing one of: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_files_specified_as_list(self): - """Test must_exist_one_of(): files specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_exist_one_of(['file2', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_files_specified_with_wildcards(self): - """Test must_exist_one_of(): files specified with wildcards""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file7', "file7\\n") - tc.must_exist_one_of(['file?']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_list(self): - """Test must_exist_one_of(): file given as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist_one_of(['file2', - ['sub', 'file1']]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_sequence(self): - """Test must_exist_one_of(): file given as sequence""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_exist_one_of(['file2', - ('sub', 'file1')]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - -class must_match_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_match(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_match('file1', "file1\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_does_not_exists(self): - """Test must_match(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_match('file1', "file1\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr.find("No such file or directory:") != -1, stderr - - def test_failure(self): - """Test must_match(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 does not match\\n") - tc.must_match('file1', "file1\\n") - tc.run() - """) - - expect = lstrip("""\ - Unexpected contents of `file1' - contents ======================================================================= - 1c1 - < file1 - --- - > file1 does not match - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == expect, stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_mode(self): - """Test must_match(): mode""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n", mode='w') - tc.must_match('file1', "file1\\n", mode='r') - tc.write('file2', "file2\\n", mode='wb') - tc.must_match('file2', "file2\\n", mode='rb') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_be_writable_TestCase(TestCommonTestCase): - def test_file_does_not_exists(self): - """Test must_not_be_writable(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Missing files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_writable_file_exists(self): - """Test must_not_be_writable(): writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode | stat.S_IWUSR) - tc.must_not_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Writable files: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_non_writable_file_exists(self): - """Test must_not_be_writable(): non-writable file exists""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - f1 = tc.workpath('file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode & ~stat.S_IWUSR) - tc.must_not_be_writable('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_specified_as_list(self): - """Test must_not_be_writable(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - import os - import stat - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - f1 = tc.workpath('sub', 'file1') - mode = os.stat(f1)[stat.ST_MODE] - os.chmod(f1, mode & ~stat.S_IWUSR) - tc.must_not_be_writable(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_contain_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_not_contain(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n") - tc.must_not_contain('file1', "1 does not contain c") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_does_not_exist(self): - """Test must_not_contain(): file does not exist""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_contain('file1', "1 c\\n") - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr.find("No such file or directory:") != -1, stderr - - def test_failure(self): - """Test must_not_contain(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 does contain contents\\n") - tc.must_not_contain('file1', "1 does contain c") - tc.run() - """) - expect = lstrip("""\ - File `file1' contains banned string. - Banned string ================================================================== - 1 does contain c - file1 contents ================================================================= - file1 does contain contents - - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == expect, repr(stdout) - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_mode(self): - """Test must_not_contain(): mode""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1 contents\\n", mode='w') - tc.must_not_contain('file1', "1 does not contain c", mode='r') - tc.write('file2', "file2 contents\\n", mode='wb') - tc.must_not_contain('file2', "2 does not contain c", mode='rb') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_contain_any_line_TestCase(TestCommonTestCase): - def test_failure(self): - """Test must_not_contain_any_line(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - 'www\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_not_contain_any_line(output, lines) - - test.pass_test() - """) - - expect = lstrip("""\ - Unexpected lines in output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - 'www%(expected_newline)s' - output ========================================================================= - www - xxx - yyy - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_find(self): - """Test must_not_contain_any_line(): find""" - run_env = self.run_env - - script = lstrip(""" - import re - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'x.*' - '.*y' - ] - - output = '''\\ - www - zzz - ''' - - def re_search(output, line): - return re.compile(line, re.S).search(output) - test.must_not_contain_any_line(output, lines, find=re_search) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_success(self): - """Test must_not_contain_any_line(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n' - 'yyy\\n' - ] - - output = '''\\ - www - zzz - ''' - - test.must_not_contain_any_line(output, lines) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_title(self): - """Test must_not_contain_any_line(): title""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_not_contain_any_line(output, lines, title='XYZZY') - - test.pass_test() - """) - - expect = lstrip("""\ - Unexpected lines in XYZZY: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - XYZZY ========================================================================== - www - xxx - yyy - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - - -class must_not_contain_lines_TestCase(TestCommonTestCase): - def test_failure(self): - """Test must_not_contain_lines(): failure""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n', - 'yyy\\n', - ] - - output = '''\\ - www - xxx - yyy - zzz - ''' - - test.must_not_contain_lines(lines, output) - - test.pass_test() - """) - - expect = lstrip("""\ - Unexpected lines in output: - 'xxx%(expected_newline)s' - 'yyy%(expected_newline)s' - output ========================================================================= - www - xxx - yyy - zzz - """ % globals()) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - stderr = run_env.stderr() - assert stdout == expect, assert_display(expect, stdout, stderr) - assert stderr.find("FAILED") != -1, stderr - - def test_success(self): - """Test must_not_contain_lines(): success""" - run_env = self.run_env - - script = lstrip(""" - import TestCommon - test = TestCommon.TestCommon(workdir='') - - lines = [ - 'xxx\\n' - 'yyy\\n' - ] - - output = '''\\ - www - zzz - ''' - - test.must_not_contain_lines(lines, output) - - test.pass_test() - """) - - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - - -class must_not_exist_TestCase(TestCommonTestCase): - def test_failure(self): - """Test must_not_exist(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_not_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unexpected files exist: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_success(self): - """Test must_not_exist(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_exist('file1') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_specified_as_list(self): - """Test must_not_exist(): file specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.must_not_exist(['sub', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_existing_broken_link(self): - """Test must_not_exist(): exists but it is a broken link""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.symlink('badtarget', 'brokenlink') - tc.must_not_exist('brokenlink') - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unexpected files exist: `brokenlink'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - -class must_not_exist_any_of_TestCase(TestCommonTestCase): - def test_success(self): - """Test must_not_exist_any_of(): success""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_exist_any_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_failure(self): - """Test must_not_exist_any_of(): failure""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file1', "file1\\n") - tc.must_not_exist_any_of(['file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Unexpected files exist: `file1'\n", stdout - stderr = run_env.stderr() - assert stderr.find("FAILED") != -1, stderr - - def test_files_specified_as_list(self): - """Test must_not_exist_any_of(): files specified as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.must_not_exist_any_of(['file2', 'file1']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_files_specified_with_wildcards(self): - """Test must_not_exist_any_of(): files specified with wildcards""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.write('file7', "file7\\n") - tc.must_not_exist_any_of(['files?']) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_list(self): - """Test must_not_exist_any_of(): file given as list""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_not_exist_any_of(['file2', - ['sub', 'files*']]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - def test_file_given_as_sequence(self): - """Test must_not_exist_any_of(): file given as sequence""" - run_env = self.run_env - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(workdir='') - tc.subdir('sub') - tc.write(['sub', 'file1'], "sub/file1\\n") - tc.must_not_exist_any_of(['file2', - ('sub', 'files?')]) - tc.pass_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - -class run_TestCase(TestCommonTestCase): - def test_argument_handling(self): - """Test run(): argument handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(arguments = "arg1 arg2 arg3", - stdout = r"%(pass_script)s: STDOUT: ['arg1', 'arg2', 'arg3']" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_default_pass(self): - """Test run(): default arguments, script passes""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(pass_script)s', - interpreter=r'%(python)s', - workdir='') - tc.run() - """) - - self.run_execution_test(script, "", "") - - def test_default_fail(self): - """Test run(): default arguments, script fails""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(fail_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - expect_stdout = lstrip("""\ - %(fail_script)s returned 1 - STDOUT ========================================================================= - %(fail_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*fail - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_default_stderr(self): - """Test run(): default arguments, error output""" - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - - STDERR ========================================================================= - 0a1 - > %(stderr_script)s: STDERR: [] - """) - - expect_stderr = lstrip("""\ - FAILED test of .*stderr - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_exception_handling(self): - """Test run(): exception handling""" - script = lstrip("""\ - import TestCmd - from TestCommon import TestCommon - def raise_exception(*args, **kw): - raise TypeError("forced TypeError") - TestCmd.TestCmd.start = raise_exception - tc = TestCommon(program='%(pass_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - STDERR ========================================================================= - """) - - expect_stderr = lstrip("""\ - Exception trying to execute: \\[%s, '[^']*pass'\\] - Traceback \\((innermost|most recent call) last\\): - File "", line \\d+, in (\\?|) - 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 - TypeError: forced TypeError - """ % re.escape(repr(sys.executable))) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_ignore_stderr(self): - """Test run(): ignore stderr""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir='') - tc.run(stderr = None) - """) - - self.run_execution_test(script, "", "") - - def test_match_function_stdout(self): - """Test run(): explicit match function, stdout""" - - script = lstrip("""\ - 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', - workdir="", - match=match_re_dotall) - tc.run(arguments = "arg1 arg2 arg3", - stdout = r"%(pass_script)s: STDOUT: ['arg1', 'arg2', 'arg3']" + "\\n", - match = my_match_exact) - """) - - self.run_execution_test(script, "", "") - - def test_match_function_stderr(self): - """Test run(): explicit match function, stderr""" - - script = lstrip("""\ - 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', - workdir="", - match=match_re_dotall) - tc.run(arguments = "arg1 arg2 arg3", - stderr = r"%(stderr_script)s: STDERR: ['arg1', 'arg2', 'arg3']" + "\\n", - match = my_match_exact) - """) - - self.run_execution_test(script, "", "") - - def test_matched_status_fails(self): - """Test run(): matched status, script fails""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(fail_script)s', - interpreter='%(python)s', - workdir='') - tc.run(status = 1) - """) - - self.run_execution_test(script, "", "") - - def test_matched_stdout(self): - """Test run(): matched stdout""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(stdout = r"%(pass_script)s: STDOUT: []" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_matched_stderr(self): - """Test run(): matched stderr""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(stderr = r"%(stderr_script)s: STDERR: []" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_mismatched_status_pass(self): - """Test run(): mismatched status, script passes""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir='') - tc.run(status = 1) - """) - - expect_stdout = lstrip("""\ - %(pass_script)s returned 0 (expected 1) - STDOUT ========================================================================= - %(pass_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*pass - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_mismatched_status_fail(self): - """Test run(): mismatched status, script fails""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(fail_script)s', - interpreter='%(python)s', - workdir='') - tc.run(status = 2) - """) - - expect_stdout = lstrip("""\ - %(fail_script)s returned 1 (expected 2) - STDOUT ========================================================================= - %(fail_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*fail - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_mismatched_stdout(self): - """Test run(): mismatched stdout""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir='') - tc.run(stdout = "Not found\\n") - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - 1c1 - < Not found - --- - > %(pass_script)s: STDOUT: [] - """) - - expect_stderr = lstrip("""\ - FAILED test of .*pass - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_mismatched_stderr(self): - """Test run(): mismatched stderr""" - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(stderr_script)s', - interpreter='%(python)s', - workdir='') - tc.run(stderr = "Not found\\n") - """) - - expect_stdout = lstrip("""\ - STDOUT ========================================================================= - - STDERR ========================================================================= - 1c1 - < Not found - --- - > %(stderr_script)s: STDERR: [] - """) - - expect_stderr = lstrip("""\ - FAILED test of .*stderr - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of ( \(\))? - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_option_handling(self): - """Test run(): option handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(options = "opt1 opt2 opt3", - stdout = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3']" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_options_plus_arguments(self): - """Test run(): option handling with arguments""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - tc.run(options = "opt1 opt2 opt3", - arguments = "arg1 arg2 arg3", - stdout = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3', 'arg1', 'arg2', 'arg3']" + "\\n") - """) - - self.run_execution_test(script, "", "") - - def test_signal_handling(self): - """Test run(): signal handling""" - - try: - os.kill - except AttributeError: - sys.stderr.write('can not test, no os.kill ... ') - return - - script = lstrip("""\ - from TestCommon import TestCommon - tc = TestCommon(program=r'%(signal_script)s', - interpreter='%(python)s', - workdir='') - tc.run() - """) - - self.SIGTERM = signal.SIGTERM - - # Script returns the signal value as a negative number. - expect_stdout = lstrip("""\ - %(signal_script)s returned -%(SIGTERM)s - STDOUT ========================================================================= - - STDERR ========================================================================= - - """) - - expect_stderr = lstrip("""\ - FAILED test of .*signal - \\tat line \\d+ of .*TestCommon\\.py \\(_complete\\) - \\tfrom line \\d+ of .*TestCommon\\.py \\(run\\) - \\tfrom line \\d+ of - """) - expect_stderr = re.compile(expect_stderr, re.M) - - self.run_execution_test(script, expect_stdout, expect_stderr) - - def test_stdin(self): - """Test run(): stdin handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(stdin_script)s', - interpreter='%(python)s', - workdir='', - match=match_exact) - expect_stdout = r"%(stdin_script)s: STDOUT: 'input'" + "\\n" - expect_stderr = r"%(stdin_script)s: STDERR: 'input'" + "\\n" - tc.run(stdin="input\\n", stdout = expect_stdout, stderr = expect_stderr) - """) - - expect_stdout = lstrip("""\ - %(pass_script)s returned 0 (expected 1) - STDOUT ========================================================================= - %(pass_script)s: STDOUT: [] - - STDERR ========================================================================= - - """) - - self.run_execution_test(script, "", "") - - - -class start_TestCase(TestCommonTestCase): - def test_option_handling(self): - """Test start(): option handling""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - p = tc.start(options = "opt1 opt2 opt3") - expect = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3']" + "\\n" - tc.finish(p, stdout = expect) - """) - - self.run_execution_test(script, "", "") - - def test_options_plus_arguments(self): - """Test start(): option handling with arguments""" - - script = lstrip("""\ - from TestCommon import TestCommon, match_exact - tc = TestCommon(program=r'%(pass_script)s', - interpreter='%(python)s', - workdir="", - match=match_exact) - p = tc.start(options = "opt1 opt2 opt3", - arguments = "arg1 arg2 arg3") - expect = r"%(pass_script)s: STDOUT: ['opt1', 'opt2', 'opt3', 'arg1', 'arg2', 'arg3']" + "\\n" - tc.finish(p, stdout = expect) - """) - - self.run_execution_test(script, "", "") - - - -class skip_test_TestCase(TestCommonTestCase): - def test_skip_test(self): - """Test skip_test()""" - run_env = self.run_env - - script = lstrip("""\ - import TestCommon - test = TestCommon.TestCommon(workdir='') - test.skip_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Skipping test.\n", stdout - stderr = run_env.stderr() - expect = [ - "NO RESULT for test at line 3 of \n", - "NO RESULT for test at line 3 of ()\n", - ] - assert stderr in expect, repr(stderr) - - script = lstrip("""\ - import TestCommon - test = TestCommon.TestCommon(workdir='') - test.skip_test("skipping test because I said so\\n") - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "skipping test because I said so\n", stdout - stderr = run_env.stderr() - expect = [ - "NO RESULT for test at line 3 of \n", - "NO RESULT for test at line 3 of ()\n", - ] - assert stderr in expect, repr(stderr) - - import os - os.environ['TESTCOMMON_PASS_SKIPS'] = '1' - - try: - script = lstrip("""\ - import TestCommon - test = TestCommon.TestCommon(workdir='') - test.skip_test() - """) - run_env.run(program=sys.executable, stdin=script) - stdout = run_env.stdout() - assert stdout == "Skipping test.\n", stdout - stderr = run_env.stderr() - assert stderr == "PASSED\n", stderr - - finally: - del os.environ['TESTCOMMON_PASS_SKIPS'] - - - -class variables_TestCase(TestCommonTestCase): - def test_variables(self): - """Test global variables""" - run_env = self.run_env - - variables = [ - 'fail_test', - 'no_result', - 'pass_test', - 'match_exact', - 'match_re', - 'match_re_dotall', - 'python', - '_python_', - 'TestCmd', - - 'TestCommon', - 'exe_suffix', - 'obj_suffix', - 'shobj_prefix', - 'shobj_suffix', - 'lib_prefix', - 'lib_suffix', - 'dll_prefix', - 'dll_suffix', - ] - - script = "from __future__ import print_function" + \ - "import TestCommon\n" + \ - '\n'.join([ "print(TestCommon.%s)\n" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - script = "from __future__ import print_function" + \ - "from TestCommon import *\n" + \ - '\n'.join([ "print(%s)" % v for v in variables ]) - run_env.run(program=sys.executable, stdin=script) - stderr = run_env.stderr() - assert stderr == "", stderr - - - -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, - 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) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestRuntest.py b/QMTest/TestRuntest.py deleted file mode 100644 index f2256de..0000000 --- a/QMTest/TestRuntest.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -TestRuntest.py: a testing framework for the runtest.py command used to -invoke SCons tests. - -A TestRuntest environment object is created via the usual invocation: - - test = TestRuntest() - -TestRuntest is a subclass of TestCommon, which is in turn is a subclass -of TestCmd), and hence has available all of the methods and attributes -from those classes, as well as any overridden or additional methods or -attributes defined in this subclass. -""" - -# Copyright (c) 2001 - 2017 The SCons Foundation - -__revision__ = "QMTest/TestRuntest.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" - -import os -import os.path -import re -import shutil -import sys - -from TestCommon import * -from TestCommon import __all__ - -__all__.extend([ 'TestRuntest', - 'pythonstring', - ]) - -if re.search('\s', python): - pythonstring = _python_ -else: - pythonstring = python -pythonstring = pythonstring.replace('\\', '\\\\') - - -failing_test_template = """\ -import sys -sys.stdout.write('FAILING TEST STDOUT\\n') -sys.stderr.write('FAILING TEST STDERR\\n') -sys.exit(1) -""" - -no_result_test_template = """\ -import sys -sys.stdout.write('NO RESULT TEST STDOUT\\n') -sys.stderr.write('NO RESULT TEST STDERR\\n') -sys.exit(2) -""" - -passing_test_template = """\ -import sys -sys.stdout.write('PASSING TEST STDOUT\\n') -sys.stderr.write('PASSING TEST STDERR\\n') -sys.exit(0) -""" - -fake_scons_py = """ -__version__ = '1.2.3' -__build__ = 'D123' -__buildsys__ = 'fake_system' -__date__ = 'Jan 1 1970' -__developer__ = 'Anonymous' -""" - -fake___init___py = """ -__version__ = '4.5.6' -__build__ = 'D456' -__buildsys__ = 'another_fake_system' -__date__ = 'Dec 31 1999' -__developer__ = 'John Doe' -""" - -class TestRuntest(TestCommon): - """Class for testing the runtest.py script. - - This provides a common place for initializing Runtest tests, - eliminating the need to begin every test with the same repeated - initializations. - """ - - def __init__(self, **kw): - """Initialize a Runtest testing object. - - If they're not overridden by keyword arguments, this - initializes the object with the following default values: - - program = 'runtest.py' - interpreter = ['python', '-tt'] - match = match_exact - workdir = '' - - The workdir value means that, by default, a temporary - workspace directory is created for a TestRuntest environment. - The superclass TestCommon.__init__() will change directory (chdir) - to the workspace directory, so an explicit "chdir = '.'" on all - of the run() method calls is not necessary. This initialization - also copies the runtest.py and QMTest/ subdirectory tree to the - temporary directory, duplicating how this test infrastructure - appears in a normal workspace. - """ - if 'program' not in kw: - kw['program'] = 'runtest.py' - if 'interpreter' not in kw: - kw['interpreter'] = [python, '-tt'] - if 'match' not in kw: - kw['match'] = match_exact - if 'workdir' not in kw: - kw['workdir'] = '' - - try: - things_to_copy = kw['things_to_copy'] - except KeyError: - things_to_copy = [ - 'runtest.py', - 'QMTest', - ] - else: - del kw['things_to_copy'] - - orig_cwd = os.getcwd() - TestCommon.__init__(self, **kw) - - dirs = [os.environ.get('SCONS_RUNTEST_DIR', orig_cwd)] - - for thing in things_to_copy: - for dir in dirs: - t = os.path.join(dir, thing) - if os.path.exists(t): - if os.path.isdir(t): - copy_func = shutil.copytree - else: - copy_func = shutil.copyfile - copy_func(t, self.workpath(thing)) - break - - self.program_set(self.workpath(kw['program'])) - - os.environ['PYTHONPATH'] = '' - - def write_fake_scons_source_tree(self): - os.mkdir('src') - os.mkdir('src/script') - self.write('src/script/scons.py', fake_scons_py) - - os.mkdir('src/engine') - os.mkdir('src/engine/SCons') - self.write('src/engine/SCons/__init__.py', fake___init___py) - os.mkdir('src/engine/SCons/Script') - self.write('src/engine/SCons/Script/__init__.py', fake___init___py) - - def write_failing_test(self, name): - self.write(name, failing_test_template) - - def write_no_result_test(self, name): - self.write(name, no_result_test_template) - - def write_passing_test(self, name): - self.write(name, passing_test_template) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py deleted file mode 100644 index c0b4ebc..0000000 --- a/QMTest/TestSCons.py +++ /dev/null @@ -1,1617 +0,0 @@ -""" -TestSCons.py: a testing framework for the SCons software construction -tool. - -A TestSCons environment object is created via the usual invocation: - - test = TestSCons() - -TestScons is a subclass of TestCommon, which in turn is a subclass -of TestCmd), and hence has available all of the methods and attributes -from those classes, as well as any overridden or additional methods or -attributes defined in this subclass. -""" - -# Copyright (c) 2001 - 2017 The SCons Foundation -from __future__ import division, print_function - -__revision__ = "QMTest/TestSCons.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" - -import os -import re -import shutil -import sys -import time -import subprocess - -from TestCommon import * -from TestCommon import __all__ - -from TestCmd import Popen -from TestCmd import PIPE - -# Some tests which verify that SCons has been packaged properly need to -# look for specific version file names. Replicating the version number -# here provides some independent verification that what we packaged -# conforms to what we expect. - -default_version = '3.0.0' - -python_version_unsupported = (2, 6, 0) -python_version_deprecated = (2, 7, 0) - -# 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/QMTest/TestSCons.py contains the actual version -# string of the packages that have been built. -SConsVersion = '3.0.0' -if SConsVersion == '__' + 'VERSION' + '__': - SConsVersion = default_version - -__all__.extend([ - 'TestSCons', - 'machine', - 'python', - '_exe', - '_obj', - '_shobj', - 'shobj_', - 'lib_', - '_lib', - 'dll_', - '_dll' - ]) - -machine_map = { - 'i686' : 'i386', - 'i586' : 'i386', - 'i486' : 'i386', -} - -try: - uname = os.uname -except AttributeError: - # Windows doesn't have a uname() function. We could use something like - # sys.platform as a fallback, but that's not really a "machine," so - # just leave it as None. - machine = None -else: - machine = uname()[4] - machine = machine_map.get(machine, machine) - -_exe = exe_suffix -_obj = obj_suffix -_shobj = shobj_suffix -shobj_ = shobj_prefix -_lib = lib_suffix -lib_ = lib_prefix -_dll = dll_suffix -dll_ = dll_prefix - - -if sys.platform == 'cygwin': - # On Cygwin, os.path.normcase() lies, so just report back the - # fact that the underlying Win32 OS is case-insensitive. - def case_sensitive_suffixes(s1, s2): - return 0 -else: - def case_sensitive_suffixes(s1, s2): - return (os.path.normcase(s1) != os.path.normcase(s2)) - - -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) - return str - -# -# Helper functions that we use as a replacement to the default re.match -# when searching for special strings in stdout/stderr. -# -def search_re(out, l): - """ Search the regular expression 'l' in the output 'out' - and return the start index when successful. - """ - m = re.search(l, out) - if m: - return m.start() - - return None - -def search_re_in_list(out, l): - """ Search the regular expression 'l' in each line of - the given string list 'out' and return the line's index - when successful. - """ - for idx, o in enumerate(out): - m = re.search(l, o) - if m: - return idx - - return None - -# -# Helpers for handling Python version numbers -# -def python_version_string(): - return sys.version.split()[0] - -def python_minor_version_string(): - return sys.version[:3] - -def unsupported_python_version(version=sys.version_info): - return version < python_version_unsupported - -def deprecated_python_version(version=sys.version_info): - return version < python_version_deprecated - -if deprecated_python_version(): - msg = r""" -scons: warning: Support for pre-2.7.0 Python version (%s) is deprecated. - If this will cause hardship, contact scons-dev@scons.org -""" - - deprecated_python_expr = re_escape(msg % python_version_string()) + file_expr - del msg -else: - deprecated_python_expr = "" - - -def initialize_sconsflags(ignore_python_version): - """ - Add the --warn=no-python-version option to SCONSFLAGS for every - command so test scripts don't have to filter out Python version - deprecation warnings. - Same for --warn=no-visual-c-missing. - """ - save_sconsflags = os.environ.get('SCONSFLAGS') - if save_sconsflags: - sconsflags = [save_sconsflags] - else: - sconsflags = [] - if ignore_python_version and deprecated_python_version(): - sconsflags.append('--warn=no-python-version') - # Provide a way to suppress or provide alternate flags for - # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. - # (The intended use case is to set it to null when running - # timing tests of earlier versions of SCons which don't - # support the --warn=no-visual-c-missing warning.) - visual_c = os.environ.get('TESTSCONS_SCONSFLAGS', - '--warn=no-visual-c-missing') - if visual_c: - sconsflags.append(visual_c) - os.environ['SCONSFLAGS'] = ' '.join(sconsflags) - return save_sconsflags - -def restore_sconsflags(sconsflags): - if sconsflags is None: - del os.environ['SCONSFLAGS'] - else: - os.environ['SCONSFLAGS'] = sconsflags - - -class TestSCons(TestCommon): - """Class for testing SCons. - - This provides a common place for initializing SCons tests, - eliminating the need to begin every test with the same repeated - initializations. - """ - - scons_version = SConsVersion - javac_is_gcj = False - - def __init__(self, **kw): - """Initialize an SCons testing object. - - If they're not overridden by keyword arguments, this - initializes the object with the following default values: - - program = 'scons' if it exists, - else 'scons.py' - interpreter = 'python' - match = match_exact - workdir = '' - - The workdir value means that, by default, a temporary workspace - directory is created for a TestSCons environment. In addition, - this method changes directory (chdir) to the workspace directory, - so an explicit "chdir = '.'" on all of the run() method calls - is not necessary. - """ - self.orig_cwd = os.getcwd() - self.external = os.environ.get('SCONS_EXTERNAL_TEST', 0) - - if not self.external: - try: - script_dir = os.environ['SCONS_SCRIPT_DIR'] - except KeyError: - pass - else: - os.chdir(script_dir) - if 'program' not in kw: - kw['program'] = os.environ.get('SCONS') - if not kw['program']: - if not self.external: - if os.path.exists('scons'): - kw['program'] = 'scons' - else: - kw['program'] = 'scons.py' - else: - kw['program'] = 'scons' - kw['interpreter'] = '' - elif not self.external and not os.path.isabs(kw['program']): - kw['program'] = os.path.join(self.orig_cwd, kw['program']) - if 'interpreter' not in kw and not os.environ.get('SCONS_EXEC'): - kw['interpreter'] = [python, '-tt'] - if 'match' not in kw: - kw['match'] = match_exact - if 'workdir' not in kw: - kw['workdir'] = '' - - # Term causing test failures due to bogus readline init - # control character output on FC8 - # TERM can cause test failures due to control chars in prompts etc. - os.environ['TERM'] = 'dumb' - - self.ignore_python_version = kw.get('ignore_python_version', 1) - if kw.get('ignore_python_version', -1) != -1: - del kw['ignore_python_version'] - - TestCommon.__init__(self, **kw) - - if not self.external: - import SCons.Node.FS - if SCons.Node.FS.default_fs is None: - SCons.Node.FS.default_fs = SCons.Node.FS.FS() - - try: - self.fixture_dirs = (os.environ['FIXTURE_DIRS']).split(os.pathsep) - except KeyError: - pass - - def Environment(self, ENV=None, *args, **kw): - """ - Return a construction Environment that optionally overrides - the default external environment with the specified ENV. - """ - if not self.external: - import SCons.Environment - import SCons.Errors - if not ENV is None: - kw['ENV'] = ENV - try: - return SCons.Environment.Environment(*args, **kw) - except (SCons.Errors.UserError, SCons.Errors.InternalError): - return None - - return None - - def detect(self, var, prog=None, ENV=None, norm=None): - """ - Detect a program named 'prog' by first checking the construction - variable named 'var' and finally searching the path used by - SCons. If either method fails to detect the program, then false - is returned, otherwise the full path to prog is returned. If - prog is None, then the value of the environment variable will be - used as prog. - """ - env = self.Environment(ENV) - if env: - v = env.subst('$'+var) - if not v: - return None - if prog is None: - prog = v - if v != prog: - return None - result = env.WhereIs(prog) - if norm and os.sep != '/': - result = result.replace(os.sep, '/') - return result - - return self.where_is(prog) - - def detect_tool(self, tool, prog=None, ENV=None): - """ - Given a tool (i.e., tool specification that would be passed - to the "tools=" parameter of Environment()) and a program that - corresponds to that tool, return true if and only if we can find - that tool using Environment.Detect(). - - By default, prog is set to the value passed into the tools parameter. - """ - - if not prog: - prog = tool - env = self.Environment(ENV, tools=[tool]) - if env is None: - return None - return env.Detect([prog]) - - def where_is(self, prog, path=None): - """ - Given a program, search for it in the specified external PATH, - or in the actual external PATH if none is specified. - """ - if path is None: - path = os.environ['PATH'] - if self.external: - if isinstance(prog, str): - prog = [prog] - import stat - paths = path.split(os.pathsep) - for p in prog: - for d in paths: - f = os.path.join(d, p) - if os.path.isfile(f): - try: - st = os.stat(f) - except OSError: - # os.stat() raises OSError, not IOError if the file - # doesn't exist, so in this case we let IOError get - # raised so as to not mask possibly serious disk or - # network issues. - continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: - return os.path.normpath(f) - else: - import SCons.Environment - env = SCons.Environment.Environment() - return env.WhereIs(prog, path) - - 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 - """ - cap,lc = [ ('Build','build'), - ('Clean','clean') ][cleaning] - if error: - term = "scons: %sing terminated because of errors.\n" % lc - else: - term = "scons: done %sing targets.\n" % lc - return "scons: Reading SConscript files ...\n" + \ - read_str + \ - "scons: done reading SConscript files.\n" + \ - "scons: %sing targets ...\n" % cap + \ - build_str + \ - term - - def run(self, *args, **kw): - """ - Set up SCONSFLAGS for every command so test scripts don't need - to worry about unexpected warnings in their output. - """ - sconsflags = initialize_sconsflags(self.ignore_python_version) - try: - TestCommon.run(self, *args, **kw) - finally: - restore_sconsflags(sconsflags) - -# Modifying the options should work and ought to be simpler, but this -# class is used for more than just running 'scons' itself. If there's -# an automated way of determining whether it's running 'scons' or -# something else, this code should be resurected. -# options = kw.get('options') -# if options: -# options = [options] -# else: -# options = [] -# if self.ignore_python_version and deprecated_python_version(): -# options.append('--warn=no-python-version') -# # Provide a way to suppress or provide alternate flags for -# # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. -# # (The intended use case is to set it to null when running -# # timing tests of earlier versions of SCons which don't -# # support the --warn=no-visual-c-missing warning.) -# visual_c = os.environ.get('TESTSCONS_SCONSFLAGS', -# '--warn=no-visual-c-missing') -# if visual_c: -# options.append(visual_c) -# kw['options'] = ' '.join(options) -# TestCommon.run(self, *args, **kw) - - def up_to_date(self, arguments = '.', read_str = "", **kw): - s = "" - for arg in arguments.split(): - s = s + "scons: `%s' is up to date.\n" % arg - 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['match'] = self.match_re_dotall - self.run(**kw) - - def not_up_to_date(self, arguments = '.', **kw): - """Asserts that none of the targets listed in arguments is - up to date, but does not make any assumptions on other targets. - This function is most useful in conjunction with the -n option. - """ - s = "" - for arg in arguments.split(): - s = s + "(?!scons: `%s' is up to date.)" % re.escape(arg) - s = '('+s+'[^\n]*\n)*' - kw['arguments'] = arguments - stdout = re.escape(self.wrap_stdout(build_str='ARGUMENTSGOHERE')) - kw['stdout'] = stdout.replace('ARGUMENTSGOHERE', s) - kw['match'] = self.match_re_dotall - self.run(**kw) - - def option_not_yet_implemented(self, option, arguments=None, **kw): - """ - 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 - kw['stderr'] = msg - if arguments: - # If it's a long option and the argument string begins with '=', - # it's of the form --foo=bar and needs no separating space. - if option[:2] == '--' and arguments[0] == '=': - kw['arguments'] = option + arguments - else: - kw['arguments'] = 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 - - def deprecated_fatal(self, warn, msg): - """ - Determines if the warning has turned into a fatal error. If so, - passes the test, as any remaining runs are now moot. - - This method expects a SConscript to be present that will causes - the warning. The method writes a SConstruct that calls the - SConsscript and looks to see what type of result occurs. - - The pattern that matches the warning is returned. - - TODO: Actually detect that it's now an error. We don't have any - cases yet, so there's no way to test it. - """ - self.write('SConstruct', """if True: - WARN = ARGUMENTS.get('WARN') - if WARN: SetOption('warn', WARN) - SConscript('SConscript') - """) - - def err_out(): - # TODO calculate stderr for fatal error - return re_escape('put something here') - - # no option, should get one of nothing, warning, or error - warning = self.deprecated_wrap(msg) - self.run(arguments = '.', stderr = None) - stderr = self.stderr() - if stderr: - # most common case done first - if match_re_dotall(stderr, warning): - # expected output - pass - elif match_re_dotall(stderr, err_out()): - # now a fatal error; skip the rest of the tests - self.pass_test() - else: - # test failed; have to do this by hand... - print(self.banner('STDOUT ')) - print(self.stdout()) - print(self.diff(warning, stderr, 'STDERR ')) - self.fail_test() - - return warning - - def deprecated_warning(self, warn, msg): - """ - Verifies the expected behavior occurs for deprecation warnings. - This method expects a SConscript to be present that will causes - the warning. The method writes a SConstruct and exercises various - combinations of command-line options and SetOption parameters to - validate that it performs correctly. - - The pattern that matches the warning is returned. - """ - warning = self.deprecated_fatal(warn, msg) - - 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, - arguments = '.', - stderr = expected, - match = match_re_dotall) - self.run(options = 'WARN=' + option, - arguments = '.', - stderr = expected, - match = match_re_dotall) - - # all warnings off, should get no output - RunPair('no-deprecated', '') - - # warning enabled, should get expected output - 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) - - return warning - - def diff_substr(self, expect, actual, prelen=20, postlen=40): - i = 0 - for x, y in zip(expect, actual): - if x != y: - return "Actual did not match expect at char %d:\n" \ - " Expect: %s\n" \ - " Actual: %s\n" \ - % (i, repr(expect[i-prelen:i+postlen]), - repr(actual[i-prelen:i+postlen])) - i = i + 1 - return "Actual matched the expected output???" - - def python_file_line(self, file, line): - """ - Returns a Python error line for output comparisons. - - The exec of the traceback line gives us the correct format for - this version of Python. - - File "", line 1, - - We stick the requested file name and line number in the right - places, abstracting out the version difference. - """ - # This routine used to use traceback to get the proper format - # that doesn't work well with py3. And the format of the - # traceback seems to be stable, so let's just format - # an appropriate string - # - #exec('import traceback; x = traceback.format_stack()[-1]') - # import traceback - # x = traceback.format_stack() - # x = # XXX: .lstrip() - # x = x.replace('', file) - # x = x.replace('line 1,', 'line %s,' % line) - # x="\n".join(x) - x='File "%s", line %s, in \n'%(file,line) - return x - - def normalize_ps(self, s): - s = re.sub(r'(Creation|Mod)Date: .*', - r'\1Date XXXX', s) - s = re.sub(r'%DVIPSSource:\s+TeX output\s.*', - r'%DVIPSSource: TeX output XXXX', s) - s = re.sub(r'/(BaseFont|FontName) /[A-Z0-9]{6}', - r'/\1 /XXXXXX', s) - s = re.sub(r'BeginFont: [A-Z0-9]{6}', - r'BeginFont: XXXXXX', s) - - return s - - @staticmethod - def to_bytes_re_sub(pattern, repl, str, count=0, flags=0): - """ - Wrapper around re.sub to change pattern and repl to bytes to work with - both python 2 & 3 - """ - pattern = to_bytes(pattern) - repl = to_bytes(repl) - return re.sub(pattern, repl, str, count, flags) - - def normalize_pdf(self, s): - s = self.to_bytes_re_sub(r'/(Creation|Mod)Date \(D:[^)]*\)', - r'/\1Date (D:XXXX)', s) - s = self.to_bytes_re_sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]', - r'/ID [ ]', s) - s = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', - r'/\1 /XXXXXX', s) - s = self.to_bytes_re_sub(r'/Length \d+ *\n/Filter /FlateDecode\n', - r'/Length XXXX\n/Filter /FlateDecode\n', s) - - try: - import zlib - except ImportError: - pass - else: - begin_marker = to_bytes('/FlateDecode\n>>\nstream\n') - end_marker = to_bytes('endstream\nendobj') - - encoded = [] - b = s.find(begin_marker, 0) - while b != -1: - b = b + len(begin_marker) - e = s.find(end_marker, b) - encoded.append((b, e)) - b = s.find(begin_marker, e + len(end_marker)) - - x = 0 - r = [] - for b, e in encoded: - r.append(s[x:b]) - d = zlib.decompress(s[b:e]) - d = self.to_bytes_re_sub(r'%%CreationDate: [^\n]*\n', - r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) - d = self.to_bytes_re_sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', - r'%DVIPSSource: TeX output 1970.01.01:0000', d) - d = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', - r'/\1 /XXXXXX', d) - r.append(d) - x = e - r.append(s[x:]) - s = to_bytes('').join(r) - - return s - - def paths(self,patterns): - import glob - result = [] - for p in patterns: - result.extend(sorted(glob.glob(p))) - return result - - def unlink_sconsignfile(self,name='.sconsign.dblite'): - """ - Delete sconsign file. - Note on python it seems to append .p3 to the file name so we take care of that - Parameters - ---------- - name - expected name of sconsign file - - Returns - ------- - None - """ - if sys.version_info[0] == 3: - name += '.p3' - self.unlink(name) - - def java_ENV(self, version=None): - """ - Initialize with a default external environment that uses a local - Java SDK in preference to whatever's found in the default PATH. - """ - if not self.external: - try: - return self._java_env[version]['ENV'] - except AttributeError: - self._java_env = {} - except KeyError: - pass - - import SCons.Environment - env = SCons.Environment.Environment() - self._java_env[version] = env - - - if version: - patterns = [ - '/usr/java/jdk%s*/bin' % version, - '/usr/lib/jvm/*-%s*/bin' % version, - '/usr/local/j2sdk%s*/bin' % version, - ] - java_path = self.paths(patterns) + [env['ENV']['PATH']] - else: - patterns = [ - '/usr/java/latest/bin', - '/usr/lib/jvm/*/bin', - '/usr/local/j2sdk*/bin', - ] - java_path = self.paths(patterns) + [env['ENV']['PATH']] - - env['ENV']['PATH'] = os.pathsep.join(java_path) - return env['ENV'] - - return None - - def java_where_includes(self,version=None): - """ - Return java include paths compiling java jni code - """ - import sys - - result = [] - if sys.platform[:6] == 'darwin': - java_home = self.java_where_java_home(version) - jni_path = os.path.join(java_home,'include','jni.h') - if os.path.exists(jni_path): - result.append(os.path.dirname(jni_path)) - - if not version: - version='' - jni_dirs = ['/System/Library/Frameworks/JavaVM.framework/Headers/jni.h', - '/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]) - dirs = self.paths(jni_dirs) - if not dirs: - return None - d=os.path.dirname(self.paths(jni_dirs)[0]) - result.append(d) - - if sys.platform == 'win32': - result.append(os.path.join(d,'win32')) - elif sys.platform.startswith('linux'): - result.append(os.path.join(d,'linux')) - return result - - def java_where_java_home(self, version=None): - if sys.platform[:6] == 'darwin': - # osx 10.11, 10.12 - home_tool = '/usr/libexec/java_home' - java_home = False - if os.path.exists(home_tool): - java_home = subprocess.check_output(home_tool).strip() - java_home = java_home.decode() - - if version is None: - if java_home: - return java_home - else: - homes = ['/System/Library/Frameworks/JavaVM.framework/Home', - # osx 10.10 - '/System/Library/Frameworks/JavaVM.framework/Versions/Current/Home'] - for home in homes: - if os.path.exists(home): - return home - - else: - if java_home.find('jdk%s'%version) != -1: - return java_home - else: - home = '/System/Library/Frameworks/JavaVM.framework/Versions/%s/Home' % version - if not os.path.exists(home): - # This works on OSX 10.10 - home = '/System/Library/Frameworks/JavaVM.framework/Versions/Current/' - else: - jar = self.java_where_jar(version) - home = os.path.normpath('%s/..'%jar) - if os.path.isdir(home): - return home - print("Could not determine JAVA_HOME: %s is not a directory" % home) - self.fail_test() - - def java_where_jar(self, version=None): - ENV = self.java_ENV(version) - if self.detect_tool('jar', ENV=ENV): - where_jar = self.detect('JAR', 'jar', ENV=ENV) - else: - where_jar = self.where_is('jar', ENV['PATH']) - if not where_jar: - self.skip_test("Could not find Java jar, skipping test(s).\n") - return where_jar - - def java_where_java(self, version=None): - """ - Return a path to the java executable. - """ - ENV = self.java_ENV(version) - where_java = self.where_is('java', ENV['PATH']) - if not where_java: - self.skip_test("Could not find Java java, skipping test(s).\n") - return where_java - - def java_where_javac(self, version=None): - """ - Return a path to the javac compiler. - """ - ENV = self.java_ENV(version) - if self.detect_tool('javac'): - where_javac = self.detect('JAVAC', 'javac', ENV=ENV) - else: - where_javac = self.where_is('javac', ENV['PATH']) - if not where_javac: - self.skip_test("Could not find Java javac, skipping test(s).\n") - self.run(program = where_javac, - arguments = '-version', - stderr=None, - status=None) - if version: - if self.stderr().find('javac %s' % version) == -1: - fmt = "Could not find javac for Java version %s, skipping test(s).\n" - self.skip_test(fmt % version) - else: - m = re.search(r'javac (\d\.\d)', self.stderr()) - if m: - version = m.group(1) - self.javac_is_gcj = False - elif self.stderr().find('gcj'): - version='1.2' - self.javac_is_gcj = True - else: - version = None - self.javac_is_gcj = False - return where_javac, version - - def java_where_javah(self, version=None): - ENV = self.java_ENV(version) - if self.detect_tool('javah'): - where_javah = self.detect('JAVAH', 'javah', ENV=ENV) - else: - where_javah = self.where_is('javah', ENV['PATH']) - if not where_javah: - self.skip_test("Could not find Java javah, skipping test(s).\n") - return where_javah - - def java_where_rmic(self, version=None): - ENV = self.java_ENV(version) - if self.detect_tool('rmic'): - where_rmic = self.detect('RMIC', 'rmic', ENV=ENV) - else: - where_rmic = self.where_is('rmic', ENV['PATH']) - if not where_rmic: - self.skip_test("Could not find Java rmic, skipping non-simulated test(s).\n") - return where_rmic - - - def java_get_class_files(self, dir): - result = [] - for dirpath, dirnames, filenames in os.walk(dir): - for fname in filenames: - if fname.endswith('.class'): - result.append(os.path.join(dirpath, fname)) - return sorted(result) - - - def Qt_dummy_installation(self, dir='qt'): - # create a dummy qt installation - - self.subdir( dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib'] ) - - self.write([dir, 'bin', 'mymoc.py'], """\ -import getopt -import sys -import re -# -w and -z are fake options used in test/QT/QTFLAGS.py -cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:wz', []) -output = None -impl = 0 -opt_string = '' -for opt, arg in cmd_opts: - if opt == '-o': output = open(arg, 'w') - elif opt == '-i': impl = 1 - else: opt_string = opt_string + ' ' + opt -output.write("/* mymoc.py%s */\\n" % opt_string) -for a in args: - contents = open(a, 'r').read() - a = a.replace('\\\\', '\\\\\\\\') - subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }' - if impl: - contents = re.sub( r'#include.*', '', contents ) - output.write(contents.replace('Q_OBJECT', subst)) -output.close() -sys.exit(0) -""") - - self.write([dir, 'bin', 'myuic.py'], """\ -import os.path -import re -import sys -output_arg = 0 -impl_arg = 0 -impl = None -source = None -opt_string = '' -for arg in sys.argv[1:]: - if output_arg: - output = open(arg, 'w') - output_arg = 0 - elif impl_arg: - impl = arg - impl_arg = 0 - elif arg == "-o": - output_arg = 1 - elif arg == "-impl": - impl_arg = 1 - elif arg[0:1] == "-": - opt_string = opt_string + ' ' + arg - else: - if source: - sys.exit(1) - source = open(arg, 'r') - sourceFile = arg -output.write("/* myuic.py%s */\\n" % opt_string) -if impl: - output.write( '#include "' + impl + '"\\n' ) - includes = re.findall('(.*?)', source.read()) - for incFile in includes: - # this is valid for ui.h files, at least - if os.path.exists(incFile): - output.write('#include "' + incFile + '"\\n') -else: - output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" ) -output.close() -sys.exit(0) -""" ) - - self.write([dir, 'include', 'my_qobject.h'], r""" -#define Q_OBJECT ; -void my_qt_symbol(const char *arg); -""") - - self.write([dir, 'lib', 'my_qobject.cpp'], r""" -#include "../include/my_qobject.h" -#include -void my_qt_symbol(const char *arg) { - fputs( arg, stdout ); -} -""") - - self.write([dir, 'lib', 'SConstruct'], r""" -env = Environment() -import sys -if sys.platform == 'win32': - env.StaticLibrary( 'myqt', 'my_qobject.cpp' ) -else: - env.SharedLibrary( 'myqt', 'my_qobject.cpp' ) -""") - - self.run(chdir = self.workpath(dir, 'lib'), - arguments = '.', - stderr = noisy_ar, - match = self.match_re_dotall) - - 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_LIB_DIR = self.workpath(dir, 'lib') - - def Qt_create_SConstruct(self, place): - if isinstance(place, list): - place = test.workpath(*place) - self.write(place, """\ -if ARGUMENTS.get('noqtdir', 0): QTDIR=None -else: QTDIR=r'%s' -env = Environment(QTDIR = QTDIR, - QT_LIB = r'%s', - QT_MOC = r'%s', - QT_UIC = r'%s', - tools=['default','qt']) -dup = 1 -if ARGUMENTS.get('variant_dir', 0): - if ARGUMENTS.get('chdir', 0): - SConscriptChdir(1) - else: - SConscriptChdir(0) - dup=int(ARGUMENTS.get('dup', 1)) - if dup == 0: - builddir = 'build_dup0' - env['QT_DEBUG'] = 1 - else: - builddir = 'build' - VariantDir(builddir, '.', duplicate=dup) - print(builddir, dup) - sconscript = Dir(builddir).File('SConscript') -else: - sconscript = File('SConscript') -Export("env dup") -SConscript( sconscript ) -""" % (self.QT, self.QT_LIB, self.QT_MOC, self.QT_UIC)) - - - NCR = 0 # non-cached rebuild - CR = 1 # cached rebuild (up to date) - NCF = 2 # non-cached build failure - CF = 3 # cached build failure - - if sys.platform == 'win32': - Configure_lib = 'msvcrt' - else: - Configure_lib = 'm' - - # to use cygwin compilers on cmd.exe -> uncomment following line - #Configure_lib = 'm' - - def skip_if_not_msvc(self, check_platform=True): - """ Check whether we are on a Windows platform and skip the - test if not. This check can be omitted by setting - check_platform to False. - Then, for a win32 platform, additionally check - whether we have a MSVC toolchain installed - in the system, and skip the test if none can be - found (=MinGW is the only compiler available). - """ - if check_platform: - if sys.platform != 'win32': - msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform - self.skip_test(msg) - return - - try: - import SCons.Tool.MSCommon as msc - if not msc.msvc_exists(): - msg = "No MSVC toolchain found...skipping test\n" - self.skip_test(msg) - except: - pass - - def checkLogAndStdout(self, checks, results, cached, - logfile, sconf_dir, sconstruct, - doCheckLog=True, doCheckStdout=True): - """ - Used to verify the expected output from using Configure() - via the contents of one or both of stdout or config.log file. - The checks, results, cached parameters all are zipped together - for use in comparing results. - - TODO: Perhaps a better API makes sense? - - Parameters - ---------- - checks : The Configure checks being run - - results : The expected results for each check - - cached : If the corresponding check is expected to be cached - - logfile : Name of the config log - - sconf_dir : Name of the sconf dir - - sconstruct : SConstruct file name - - doCheckLog : check specified log file, defaults to true - - doCheckStdout : Check stdout, defaults to true - - Returns - ------- - - """ - - class NoMatch(Exception): - def __init__(self, p): - self.pos = p - - def matchPart(log, logfile, lastEnd, NoMatch=NoMatch): - """ - Match part of the logfile - """ - m = re.match(log, logfile[lastEnd:]) - if not m: - raise NoMatch(lastEnd) - return m.end() + lastEnd - - try: - - # Build regexp for a character which is not - # a linesep, and in the case of CR/LF - # build it with both CR and CR/LF - # TODO: Not sure why this is a good idea. A static string - # could do the same since we only have two variations - # to do with? - # ls = os.linesep - # nols = "(" - # for i in range(len(ls)): - # nols = nols + "(" - # for j in range(i): - # nols = nols + ls[j] - # nols = nols + "[^" + ls[i] + "])" - # if i < len(ls)-1: - # nols = nols + "|" - # nols = nols + ")" - # - # Replaced above logic with \n as we're reading the file - # using non-binary read. Python will translate \r\n -> \n - # For us. - ls = '\n' - nols = '([^\n])' - lastEnd = 0 - - # Read the whole logfile - logfile = self.read(self.workpath(logfile), mode='r') - - # Some debug code to keep around.. - # sys.stderr.write("LOGFILE[%s]:%s"%(type(logfile),logfile)) - - if (doCheckLog and - logfile.find("scons: warning: The stored build information has an unexpected class.") >= 0): - self.fail_test() - - sconf_dir = sconf_dir - sconstruct = sconstruct - - log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - rdstr = "" - cnt = 0 - for check,result,cache_desc in zip(checks, results, cached): - log = re.escape("scons: Configure: " + check) + ls - - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - log = "" - result_cached = 1 - for bld_desc in cache_desc: # each TryXXX - for ext, flag in bld_desc: # each file in TryBuild - file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext)) - if flag == self.NCR: - # NCR = Non Cached Rebuild - # rebuild will pass - if ext in ['.c', '.cpp']: - log=log + re.escape(file + " <-") + ls - log=log + r"( \|" + nols + "*" + ls + ")+?" - else: - log=log + "(" + nols + "*" + ls +")*?" - result_cached = 0 - if flag == self.CR: - # CR = cached rebuild (up to date)s - # up to date - log=log + \ - re.escape("scons: Configure: \"%s\" is up to date." - % file) + ls - log=log+re.escape("scons: Configure: The original builder " - "output was:") + ls - log=log+r"( \|.*"+ls+")+" - if flag == self.NCF: - # non-cached rebuild failure - log=log + "(" + nols + "*" + ls + ")*?" - result_cached = 0 - if flag == self.CF: - # cached rebuild failure - log=log + \ - re.escape("scons: Configure: Building \"%s\" failed " - "in a previous run and all its sources are" - " up to date." % file) + ls - log=log+re.escape("scons: Configure: The original builder " - "output was:") + ls - log=log+r"( \|.*"+ls+")+" - cnt = cnt + 1 - if result_cached: - result = "(cached) " + result - rdstr = rdstr + re.escape(check) + re.escape(result) + "\n" - log=log + re.escape("scons: Configure: " + result) + ls + ls - - if doCheckLog: - lastEnd = matchPart(log, logfile, lastEnd) - - log = "" - if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd) - if doCheckLog and lastEnd != len(logfile): - raise NoMatch(lastEnd) - - except NoMatch as m: - print("Cannot match log file against log regexp.") - print("log file: ") - print("------------------------------------------------------") - print(logfile[m.pos:]) - print("------------------------------------------------------") - print("log regexp: ") - print("------------------------------------------------------") - print(log) - print("------------------------------------------------------") - self.fail_test() - - if doCheckStdout: - exp_stdout = self.wrap_stdout(".*", rdstr) - if not self.match_re_dotall(self.stdout(), exp_stdout): - print("Unexpected stdout: ") - print("-----------------------------------------------------") - print(repr(self.stdout())) - print("-----------------------------------------------------") - print(repr(exp_stdout)) - print("-----------------------------------------------------") - self.fail_test() - - def get_python_version(self): - """ - Returns the Python version (just so everyone doesn't have to - hand-code slicing the right number of characters). - """ - # see also sys.prefix documentation - return python_minor_version_string() - - def get_platform_python_info(self): - """ - Returns a path to a Python executable suitable for testing on - this platform and its associated include path, library path, - and library name. - """ - python = self.where_is('python') - if not python: - self.skip_test('Can not find installed "python", skipping test.\n') - - self.run(program = python, stdin = """\ -import os, sys -try: - if sys.platform == 'win32': - py_ver = 'python%d%d' % sys.version_info[:2] - else: - py_ver = 'python%d.%d' % sys.version_info[:2] -except AttributeError: - py_ver = 'python' + sys.version[:3] -# print include and lib path -try: - import distutils.sysconfig - exec_prefix = distutils.sysconfig.EXEC_PREFIX - print(distutils.sysconfig.get_python_inc()) - lib_path = os.path.join(exec_prefix, 'libs') - if not os.path.exists(lib_path): - lib_path = os.path.join(exec_prefix, 'lib') - print(lib_path) -except: - print(os.path.join(sys.prefix, 'include', py_ver)) - print(os.path.join(sys.prefix, 'lib', py_ver, 'config')) -print(py_ver) -""") - - return [python] + self.stdout().strip().split('\n') - - def start(self, *args, **kw): - """ - Starts SCons in the test environment. - - This method exists to tell Test{Cmd,Common} that we're going to - use standard input without forcing every .start() call in the - individual tests to do so explicitly. - """ - if 'stdin' not in kw: - kw['stdin'] = True - sconsflags = initialize_sconsflags(self.ignore_python_version) - try: - p = TestCommon.start(self, *args, **kw) - finally: - restore_sconsflags(sconsflags) - return p - - def wait_for(self, fname, timeout=20.0, popen=None): - """ - Waits for the specified file name to exist. - """ - 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) - if popen: - popen.stdin.close() - popen.stdin = None - self.status = 1 - self.finish(popen) - stdout = self.stdout() - if stdout: - sys.stdout.write(self.banner('STDOUT ') + '\n') - sys.stdout.write(stdout) - stderr = self.stderr() - if stderr: - sys.stderr.write(self.banner('STDERR ') + '\n') - sys.stderr.write(stderr) - self.fail_test() - time.sleep(1.0) - waited = waited + 1.0 - - def get_alt_cpp_suffix(self): - """ - Many CXX tests have this same logic. - They all needed to determine if the current os supports - files with .C and .c as different files or not - in which case they are instructed to use .cpp instead of .C - """ - if not case_sensitive_suffixes('.c','.C'): - alt_cpp_suffix = '.cpp' - else: - alt_cpp_suffix = '.C' - return alt_cpp_suffix - - def platform_has_symlink(self): - if not hasattr(os, 'symlink') or sys.platform == 'win32': - return False - else: - return True - - -class Stat: - def __init__(self, name, units, expression, convert=None): - if convert is None: - convert = lambda x: x - self.name = name - self.units = units - self.expression = re.compile(expression) - self.convert = convert - -StatList = [ - Stat('memory-initial', 'kbytes', - r'Memory before reading SConscript files:\s+(\d+)', - convert=lambda s: int(s) // 1024), - Stat('memory-prebuild', 'kbytes', - r'Memory before building targets:\s+(\d+)', - convert=lambda s: int(s) // 1024), - Stat('memory-final', 'kbytes', - r'Memory after building targets:\s+(\d+)', - convert=lambda s: int(s) // 1024), - - Stat('time-sconscript', 'seconds', - r'Total SConscript file execution time:\s+([\d.]+) seconds'), - Stat('time-scons', 'seconds', - r'Total SCons execution time:\s+([\d.]+) seconds'), - Stat('time-commands', 'seconds', - r'Total command execution time:\s+([\d.]+) seconds'), - Stat('time-total', 'seconds', - r'Total build time:\s+([\d.]+) seconds'), -] - - -class TimeSCons(TestSCons): - """Class for timing SCons.""" - def __init__(self, *args, **kw): - """ - In addition to normal TestSCons.TestSCons intialization, - this enables verbose mode (which causes the command lines to - be displayed in the output) and copies the contents of the - directory containing the executing script to the temporary - working directory. - """ - self.variables = kw.get('variables') - default_calibrate_variables = [] - if self.variables is not None: - for variable, value in self.variables.items(): - value = os.environ.get(variable, value) - try: - value = int(value) - except ValueError: - try: - value = float(value) - except ValueError: - pass - else: - default_calibrate_variables.append(variable) - else: - default_calibrate_variables.append(variable) - self.variables[variable] = value - del kw['variables'] - calibrate_keyword_arg = kw.get('calibrate') - if calibrate_keyword_arg is None: - self.calibrate_variables = default_calibrate_variables - else: - self.calibrate_variables = calibrate_keyword_arg - del kw['calibrate'] - - self.calibrate = os.environ.get('TIMESCONS_CALIBRATE', '0') != '0' - - if 'verbose' not in kw and not self.calibrate: - kw['verbose'] = True - - TestSCons.__init__(self, *args, **kw) - - # TODO(sgk): better way to get the script dir than sys.argv[0] - self.test_dir = os.path.dirname(sys.argv[0]) - test_name = os.path.basename(self.test_dir) - - if not os.path.isabs(self.test_dir): - self.test_dir = os.path.join(self.orig_cwd, self.test_dir) - self.copy_timing_configuration(self.test_dir, self.workpath()) - - def main(self, *args, **kw): - """ - The main entry point for standard execution of timings. - - This method run SCons three times: - - Once with the --help option, to have it exit after just reading - the configuration. - - Once as a full build of all targets. - - Once again as a (presumably) null or up-to-date build of - all targets. - - The elapsed time to execute each build is printed after - it has finished. - """ - if 'options' not in kw and self.variables: - options = [] - for variable, value in self.variables.items(): - options.append('%s=%s' % (variable, value)) - kw['options'] = ' '.join(options) - if self.calibrate: - self.calibration(*args, **kw) - else: - self.uptime() - self.startup(*args, **kw) - self.full(*args, **kw) - self.null(*args, **kw) - - def trace(self, graph, name, value, units, sort=None): - 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' - sys.stdout.write(line) - sys.stdout.flush() - - def report_traces(self, trace, stats): - self.trace('TimeSCons-elapsed', - trace, - self.elapsed_time(), - "seconds", - sort=0) - for name, args in stats.items(): - self.trace(name, trace, **args) - - def uptime(self): - try: - fp = open('/proc/loadavg') - except EnvironmentError: - pass - else: - avg1, avg5, avg15 = fp.readline().split(" ")[:3] - fp.close() - self.trace('load-average', 'average1', avg1, 'processes') - self.trace('load-average', 'average5', avg5, 'processes') - self.trace('load-average', 'average15', avg15, 'processes') - - def collect_stats(self, input): - result = {} - for stat in StatList: - m = stat.expression.search(input) - if m: - value = stat.convert(m.group(1)) - # The dict keys match the keyword= arguments - # of the trace() method above so they can be - # applied directly to that call. - result[stat.name] = {'value':value, 'units':stat.units} - return result - - def add_timing_options(self, kw, additional=None): - """ - Add the necessary timings options to the kw['options'] value. - """ - options = kw.get('options', '') - if additional is not None: - options += additional - kw['options'] = options + ' --debug=memory,time' - - def startup(self, *args, **kw): - """ - Runs scons with the --help option. - - This serves as a way to isolate just the amount of startup time - spent reading up the configuration, since --help exits before any - "real work" is done. - """ - self.add_timing_options(kw, ' --help') - # Ignore the exit status. If the --help run dies, we just - # won't report any statistics for it, but we can still execute - # the full and null builds. - kw['status'] = None - self.run(*args, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) - # Delete the time-commands, since no commands are ever - # executed on the help run and it is (or should be) always 0.0. - del stats['time-commands'] - self.report_traces('startup', stats) - - def full(self, *args, **kw): - """ - Runs a full build of SCons. - """ - self.add_timing_options(kw) - self.run(*args, **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) - self.report_traces('full', stats) - self.trace('full-memory', 'initial', **stats['memory-initial']) - self.trace('full-memory', 'prebuild', **stats['memory-prebuild']) - self.trace('full-memory', 'final', **stats['memory-final']) - - def calibration(self, *args, **kw): - """ - Runs a full build of SCons, but only reports calibration - information (the variable(s) that were set for this configuration, - and the elapsed time to run. - """ - self.add_timing_options(kw) - 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()) - - def null(self, *args, **kw): - """ - Runs an up-to-date null build of SCons. - """ - # TODO(sgk): allow the caller to specify the target (argument) - # that must be up-to-date. - self.add_timing_options(kw) - self.up_to_date(arguments='.', **kw) - sys.stdout.write(self.stdout()) - stats = self.collect_stats(self.stdout()) - # time-commands should always be 0.0 on a null build, because - # no commands should be executed. Remove it from the stats - # so we don't trace it, but only if it *is* 0 so that we'll - # get some indication if a supposedly-null build actually does - # build something. - if float(stats['time-commands']['value']) == 0.0: - del stats['time-commands'] - self.report_traces('null', stats) - self.trace('null-memory', 'initial', **stats['memory-initial']) - self.trace('null-memory', 'prebuild', **stats['memory-prebuild']) - self.trace('null-memory', 'final', **stats['memory-final']) - - def elapsed_time(self): - """ - Returns the elapsed time of the most recent command execution. - """ - return self.endTime - self.startTime - - def run(self, *args, **kw): - """ - Runs a single build command, capturing output in the specified file. - - Because this class is about timing SCons, we record the start - and end times of the elapsed execution, and also add the - --debug=memory and --debug=time options to have SCons report - its own memory and timing statistics. - """ - self.startTime = time.time() - try: - result = TestSCons.run(self, *args, **kw) - finally: - self.endTime = time.time() - return result - - def copy_timing_configuration(self, source_dir, dest_dir): - """ - Copies the timing configuration from the specified source_dir (the - directory in which the controlling script lives) to the specified - dest_dir (a temporary working directory). - - This ignores all files and directories that begin with the string - 'TimeSCons-', and all '.svn' subdirectories. - """ - for root, dirs, files in os.walk(source_dir): - if '.svn' in dirs: - dirs.remove('.svn') - dirs = [ d for d in dirs if not d.startswith('TimeSCons-') ] - files = [ f for f in files if not f.startswith('TimeSCons-') ] - for dirname in dirs: - source = os.path.join(root, dirname) - destination = source.replace(source_dir, dest_dir) - os.mkdir(destination) - if sys.platform != 'win32': - shutil.copystat(source, destination) - for filename in files: - source = os.path.join(root, filename) - destination = source.replace(source_dir, dest_dir) - shutil.copy2(source, destination) - - -# In some environments, $AR will generate a warning message to stderr -# if the library doesn't previously exist and is being created. One -# way to fix this is to tell AR to be quiet (sometimes the 'c' flag), -# but this is difficult to do in a platform-/implementation-specific -# method. Instead, we will use the following as a stderr match for -# tests that use AR so that we will view zero or more "ar: creating -# " messages to be successful executions of the test (see -# test/AR.py for sample usage). - -noisy_ar=r'(ar: creating( archive)? \S+\n?)*' - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSConsMSVS.py b/QMTest/TestSConsMSVS.py deleted file mode 100644 index 30eea76..0000000 --- a/QMTest/TestSConsMSVS.py +++ /dev/null @@ -1,1177 +0,0 @@ -""" -TestSConsMSVS.py: a testing framework for the SCons software construction -tool. - -A TestSConsMSVS environment object is created via the usual invocation: - - test = TestSConsMSVS() - -TestSConsMSVS is a subsclass of TestSCons, which is in turn a subclass -of TestCommon, which is in turn is a subclass of TestCmd), and hence -has available all of the methods and attributes from those classes, -as well as any overridden or additional methods or attributes defined -in this subclass. -""" - -# Copyright (c) 2001 - 2017 The SCons Foundation - -__revision__ = "QMTest/TestSConsMSVS.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" - -import os -import sys -import platform -import traceback -from xml.etree import ElementTree - -from TestSCons import * -from TestSCons import __all__ - - - -expected_dspfile_6_0 = '''\ -# Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) External Target" 0x0106 - -CFG=Test - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "Test.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "Test - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" - -!IF "$(CFG)" == "Test - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "" -# PROP BASE Intermediate_Dir "" -# PROP BASE Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP BASE Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP BASE Target_File "Test.exe" -# PROP BASE Bsc_Name "" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "" -# PROP Intermediate_Dir "" -# PROP Cmd_Line "echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP Rebuild_Opt "-c && echo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe"" -# PROP Target_File "Test.exe" -# PROP Bsc_Name "" -# PROP Target_Dir "" - -!ENDIF - -# Begin Target - -# Name "Test - Win32 Release" - -!IF "$(CFG)" == "Test - Win32 Release" - -!ENDIF - -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="sdk.h" -# End Source File -# End Group -# Begin Group "Local Headers" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE="test.h" -# End Source File -# End Group -# Begin Group "Other Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE="readme.txt" -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE="test.rc" -# End Source File -# End Group -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;l;y;def;odl;idl;hpj;bat" -# Begin Source File - -SOURCE="test.c" -# End Source File -# End Group -# Begin Source File - -SOURCE="" -# End Source File -# End Target -# End Project -''' - -expected_dswfile_6_0 = '''\ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "Test"="Test.dsp" - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### -''' - -SConscript_contents_6_0 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='6.0',HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test.c'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.dsp', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - - - -expected_slnfile_7_0 = """\ -Microsoft Visual Studio Solution File, Format Version 7.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfiguration) = preSolution -\t\tConfigName.0 = Release -\tEndGlobalSection -\tGlobalSection(ProjectDependencies) = postSolution -\tEndGlobalSection -\tGlobalSection(ProjectConfiguration) = postSolution -\t\t.Release.ActiveCfg = Release|Win32 -\t\t.Release.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ExtensibilityGlobals) = postSolution -\tEndGlobalSection -\tGlobalSection(ExtensibilityAddIns) = postSolution -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_7_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -SConscript_contents_7_0 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.0',HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - - - -expected_slnfile_7_1 = """\ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -\tProjectSection(ProjectDependencies) = postProject -\tEndProjectSection -EndProject -Global - -\tGlobalSection(SolutionConfiguration) = preSolution -\t\tConfigName.0 = Release -\tEndGlobalSection -\tGlobalSection(ProjectDependencies) = postSolution -\tEndGlobalSection -\tGlobalSection(ProjectConfiguration) = postSolution -\t\t.Release.ActiveCfg = Release|Win32 -\t\t.Release.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ExtensibilityGlobals) = postSolution -\tEndGlobalSection -\tGlobalSection(ExtensibilityAddIns) = postSolution -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_7_1 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -SConscript_contents_7_1 = """\ -env=Environment(platform='win32', tools=['msvs'], - MSVS_VERSION='7.1',HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - - - -expected_slnfile_8_0 = """\ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t.Release|Win32.ActiveCfg = Release|Win32 -\t\t.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_9_0 = """\ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t.Release|Win32.ActiveCfg = Release|Win32 -\t\t.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_10_0 = """\ -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_11_0 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 11 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_14_0 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_8_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -expected_vcprojfile_9_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -expected_vcprojfile_10_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv100 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -expected_vcprojfile_11_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv110 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -expected_vcprojfile_14_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv140 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -SConscript_contents_8_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_9_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='9.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_10_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='10.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_11_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='11.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_14_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -class TestSConsMSVS(TestSCons): - """Subclass for testing MSVS-specific portions of SCons.""" - - def msvs_versions(self): - if not hasattr(self, '_msvs_versions'): - - # Determine the SCons version and the versions of the MSVS - # environments installed on the test machine. - # - # We do this by executing SCons with an SConstruct file - # (piped on stdin) that spits out Python assignments that - # we can just exec(). We construct the SCons.__"version"__ - # string in the input here so that the SCons build itself - # doesn't fill it in when packaging SCons. - input = """\ -import SCons -import SCons.Tool.MSCommon -print("self.scons_version =", repr(SCons.__%s__)) -print("self._msvs_versions =", str(SCons.Tool.MSCommon.query_versions())) -""" % 'version' - - self.run(arguments = '-n -q -Q -f -', stdin = input) - exec(self.stdout()) - - return self._msvs_versions - - def vcproj_sys_path(self, fname): - """ - """ - orig = 'sys.path = [ join(sys' - - enginepath = repr(os.path.join(self._cwd, '..', 'engine')) - replace = 'sys.path = [ %s, join(sys' % enginepath - - contents = self.read(fname, mode='r') - contents = contents.replace(orig, replace) - self.write(fname, contents) - - def msvs_substitute(self, input, msvs_ver, - subdir=None, sconscript=None, - python=None, - project_guid=None, - vcproj_sccinfo='', sln_sccinfo=''): - if not hasattr(self, '_msvs_versions'): - self.msvs_versions() - - if subdir: - workpath = self.workpath(subdir) - else: - workpath = self.workpath() - - if sconscript is None: - sconscript = self.workpath('SConstruct') - - if python is None: - python = sys.executable - - if project_guid is None: - project_guid = "{E5466E26-0003-F18B-8F8A-BCD76C86388D}" - - 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'] - 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_xml = exec_script_main.replace("'", "'") - - result = input.replace(r'', workpath) - result = result.replace(r'', python) - result = result.replace(r'', sconscript) - result = result.replace(r'', exec_script_main) - result = result.replace(r'', exec_script_main_xml) - result = result.replace(r'', project_guid) - result = result.replace('\n', vcproj_sccinfo) - result = result.replace('\n', sln_sccinfo) - return result - - def get_msvs_executable(self, version): - """Returns a full path to the executable (MSDEV or devenv) - for the specified version of Visual Studio. - """ - from SCons.Tool.MSCommon import get_vs_by_version - - msvs = get_vs_by_version(version) - if not msvs: - return None - return msvs.get_executable() - - def run(self, *args, **kw): - """ - Suppress MSVS deprecation warnings. - """ - save_sconsflags = os.environ.get('SCONSFLAGS') - if save_sconsflags: - sconsflags = [save_sconsflags] - else: - sconsflags = [] - sconsflags = sconsflags + ['--warn=no-deprecated'] - os.environ['SCONSFLAGS'] = ' '.join(sconsflags) - try: - result = TestSCons.run(self, *args, **kw) - finally: - os.environ['SCONSFLAGS'] = save_sconsflags or '' - return result - - def get_vs_host_arch(self): - """ Get an MSVS, SDK , and/or MSVS acceptable platform arch - """ - - # Dict to 'canonalize' the arch - _ARCH_TO_CANONICAL = { - "x86": "x86", - "amd64": "amd64", - "i386": "x86", - "emt64": "amd64", - "x86_64": "amd64", - "itanium": "ia64", - "ia64": "ia64", - } - - host_platform = platform.machine() - # TODO(2.5): the native Python platform.machine() function returns - # '' on all Python versions before 2.6, after which it also uses - # PROCESSOR_ARCHITECTURE. - if not host_platform: - host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') - - - try: - host = _ARCH_TO_CANONICAL[host_platform] - except KeyError as e: - # Default to x86 for all other platforms - host = 'x86' - - - return host - - def validate_msvs_file(self, file): - try: - x = ElementTree.parse(file) - except: - print("--------------------------------------------------------------") - print("--------------------------------------------------------------") - print(traceback.format_exc()) - print("Failed to validate xml in MSVS file: ") - print(file) - print("--------------------------------------------------------------") - print("--------------------------------------------------------------") - self.fail_test() -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSCons_time.py b/QMTest/TestSCons_time.py deleted file mode 100644 index f74906a..0000000 --- a/QMTest/TestSCons_time.py +++ /dev/null @@ -1,331 +0,0 @@ -""" -TestSCons_time.py: a testing framework for the scons-test.py script - -A TestSCons_time environment object is created via the usual invocation: - - test = TestSCons_time() - -TestSCons_time is a subclass of TestCommon, which is in turn is a subclass -of TestCmd), and hence has available all of the methods and attributes -from those classes, as well as any overridden or additional methods or -attributes defined in this subclass. -""" - -# Copyright (c) 2001 - 2017 The SCons Foundation - -__revision__ = "QMTest/TestSCons_time.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" - -import os -import os.path -import sys - -from TestCommon import * -from TestCommon import __all__ -# some of the scons_time tests may need regex-based matching: -from TestSCons import search_re, search_re_in_list - -__all__.extend([ 'TestSCons_time', - ]) - -SConstruct = """\ -from __future__ import print_function -import os -print("SConstruct file directory:", os.getcwd()) -""" - -scons_py = """\ -#!/usr/bin/env python -import os -import sys -def write_args(fp, args): - fp.write(args[0] + '\\n') - for arg in args[1:]: - fp.write(' ' + arg + '\\n') -write_args(sys.stdout, sys.argv) -for arg in sys.argv[1:]: - if arg[:10] == '--profile=': - profile = open(arg[10:], 'w') - profile.write('--profile\\n') - write_args(profile, sys.argv) - break -sys.stdout.write('SCONS_LIB_DIR = ' + os.environ['SCONS_LIB_DIR'] + '\\n') -exec(open('SConstruct', 'r').read()) -""" - -aegis_py = """\ -#!/usr/bin/env python -import os -import sys -script_dir = 'src/script' -if not os.path.exists(script_dir): - os.makedirs(script_dir) -open(script_dir + '/scons.py', 'w').write( -r'''%s''') -""" % scons_py - - -svn_py = """\ -#!/usr/bin/env python -import os -import sys -dir = sys.argv[-1] -script_dir = dir + '/src/script' -os.makedirs(script_dir) -open(script_dir + '/scons.py', 'w').write( -r'''%s''') -""" % scons_py - - -logfile_contents = """\ -Memory before reading SConscript files: 100%(index)s -Memory after reading SConscript files: 200%(index)s -Memory before building targets: 300%(index)s -Memory after building targets: 400%(index)s -Object counts: - pre- post- pre- post- - read read build build Class - 101%(index)s 102%(index)s 103%(index)s 104%(index)s Action.CommandAction - 201%(index)s 202%(index)s 203%(index)s 204%(index)s Action.CommandGeneratorAction - 301%(index)s 302%(index)s 303%(index)s 304%(index)s Action.FunctionAction - 401%(index)s 402%(index)s 403%(index)s 404%(index)s Action.LazyAction - 501%(index)s 502%(index)s 503%(index)s 504%(index)s Action.ListAction - 601%(index)s 602%(index)s 603%(index)s 604%(index)s Builder.BuilderBase - 701%(index)s 702%(index)s 703%(index)s 704%(index)s Builder.CompositeBuilder - 801%(index)s 802%(index)s 803%(index)s 804%(index)s Builder.ListBuilder - 901%(index)s 902%(index)s 903%(index)s 904%(index)s Builder.MultiStepBuilder - 1001%(index)s 1002%(index)s 1003%(index)s 1004%(index)s Builder.OverrideWarner - 1101%(index)s 1102%(index)s 1103%(index)s 1104%(index)s Environment.Base - 1201%(index)s 1202%(index)s 1203%(index)s 1204%(index)s Environment.EnvironmentClone - 1301%(index)s 1302%(index)s 1303%(index)s 1304%(index)s Environment.OverrideEnvironment - 1401%(index)s 1402%(index)s 1403%(index)s 1404%(index)s Executor.Executor - 1501%(index)s 1502%(index)s 1503%(index)s 1504%(index)s Node.FS - 1601%(index)s 1602%(index)s 1603%(index)s 1604%(index)s Node.FS.Base - 1701%(index)s 1702%(index)s 1703%(index)s 1704%(index)s Node.FS.Dir - 1801%(index)s 1802%(index)s 1803%(index)s 1804%(index)s Node.FS.File - 1901%(index)s 1902%(index)s 1904%(index)s 1904%(index)s Node.FS.RootDir - 2001%(index)s 2002%(index)s 2003%(index)s 2004%(index)s Node.Node -Total build time: 11.123456 seconds -Total SConscript file execution time: 22.234567 seconds -Total SCons execution time: 33.345678 seconds -Total command execution time: 44.456789 seconds -""" - - -profile_py = """\ -%(body)s - -import profile - -try: dispatch = profile.Profile.dispatch -except AttributeError: pass -else: dispatch['c_exception'] = profile.Profile.trace_dispatch_return - -prof = profile.Profile() -prof.runcall(%(call)s) -prof.dump_stats(r'%(profile_name)s') -""" - - -class TestSCons_time(TestCommon): - """Class for testing the scons-time script. - - This provides a common place for initializing scons-time tests, - eliminating the need to begin every test with the same repeated - initializations. - """ - - def __init__(self, **kw): - """Initialize an SCons_time testing object. - - If they're not overridden by keyword arguments, this - initializes the object with the following default values: - - program = 'scons-time' - interpreter = ['python', '-tt'] - match = match_exact - workdir = '' - - The workdir value means that, by default, a temporary workspace - directory is created for a TestSCons_time environment. - In addition, this method changes directory (chdir) to the - workspace directory, so an explicit "chdir = '.'" on all of the - run() method calls is not necessary. - """ - - self.orig_cwd = os.getcwd() - try: - script_dir = os.environ['SCONS_SCRIPT_DIR'] - except KeyError: - pass - else: - os.chdir(script_dir) - if 'program' not in kw: - p = os.environ.get('SCONS_TIME') - if not p: - p = 'scons-time' - if not os.path.exists(p): - p = 'scons-time.py' - kw['program'] = p - - if 'interpreter' not in kw: - kw['interpreter'] = [python, '-tt'] - - if 'match' not in kw: - kw['match'] = match_exact - - if 'workdir' not in kw: - kw['workdir'] = '' - - TestCommon.__init__(self, **kw) - - def archive_split(self, path): - if path[-7:] == '.tar.gz': - return path[:-7], path[-7:] - else: - return os.path.splitext(path) - - def fake_logfile(self, logfile_name, index=0): - self.write(self.workpath(logfile_name), logfile_contents % locals()) - - def profile_data(self, profile_name, python_name, call, body): - profile_name = self.workpath(profile_name) - python_name = self.workpath(python_name) - d = { - 'profile_name' : profile_name, - 'python_name' : python_name, - 'call' : call, - 'body' : body, - } - self.write(python_name, profile_py % d) - self.run(program = python_name, interpreter = sys.executable) - - def tempdir_re(self, *args): - """ - Returns a regular expression to match a scons-time - temporary directory. - """ - import re - import tempfile - - sep = re.escape(os.sep) - tempdir = tempfile.gettempdir() - - try: - realpath = os.path.realpath - except AttributeError: - pass - else: - tempdir = realpath(tempdir) - - args = (tempdir, 'scons-time-',) + args - x = os.path.join(*args) - x = re.escape(x) - x = x.replace('time\\-', 'time\\-[^%s]*' % sep) - return x - - def write_fake_aegis_py(self, name): - name = self.workpath(name) - self.write(name, aegis_py) - os.chmod(name, 0o755) - return name - - def write_fake_scons_py(self): - self.subdir('src', ['src', 'script']) - self.write('src/script/scons.py', scons_py) - - def write_fake_svn_py(self, name): - name = self.workpath(name) - self.write(name, svn_py) - os.chmod(name, 0o755) - return name - - def write_sample_directory(self, archive, dir, files): - dir = self.workpath(dir) - for name, content in files: - path = os.path.join(dir, name) - d, f = os.path.split(path) - if not os.path.isdir(d): - os.makedirs(d) - open(path, 'w').write(content) - return dir - - def write_sample_tarfile(self, archive, dir, files): - import shutil - try: - import tarfile - - except ImportError: - - self.skip_test('no tarfile module\n') - - else: - - base, suffix = self.archive_split(archive) - - mode = { - '.tar' : 'w', - '.tar.gz' : 'w:gz', - '.tgz' : 'w:gz', - } - - tar = tarfile.open(archive, mode[suffix]) - for name, content in files: - path = os.path.join(dir, name) - open(path, 'wb').write(bytearray(content,'utf-8')) - tarinfo = tar.gettarinfo(path, path) - tarinfo.uid = 111 - tarinfo.gid = 111 - tarinfo.uname = 'fake_user' - tarinfo.gname = 'fake_group' - tar.addfile(tarinfo, open(path, 'rb')) - tar.close() - shutil.rmtree(dir) - return self.workpath(archive) - - def write_sample_zipfile(self, archive, dir, files): - import shutil - try: - import zipfile - except ImportError: - - sys.stderr.write('no zipfile module\n') - self.no_result() - - else: - - zip = zipfile.ZipFile(archive, 'w') - for name, content in files: - path = os.path.join(dir, name) - open(path, 'w').write(content) - zip.write(path) - zip.close() - shutil.rmtree(dir) - return self.workpath(archive) - - sample_project_files = [ - ('SConstruct', SConstruct), - ] - - def write_sample_project(self, archive, dir=None): - base, suffix = self.archive_split(archive) - - write_sample = { - '.tar' : self.write_sample_tarfile, - '.tar.gz' : self.write_sample_tarfile, - '.tgz' : self.write_sample_tarfile, - '.zip' : self.write_sample_zipfile, - }.get(suffix, self.write_sample_directory) - - if not dir: - dir = base - - os.mkdir(dir) - path = write_sample(archive, dir, self.sample_project_files) - - return path - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/TestSConsign.py b/QMTest/TestSConsign.py deleted file mode 100644 index df34aa8..0000000 --- a/QMTest/TestSConsign.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2001 - 2017 The SCons Foundation -from __future__ import print_function - -__revision__ = "QMTest/TestSConsign.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" - -__doc__ = """ -TestSConsign.py: a testing framework for the "sconsign" script -tool. - -A TestSConsign environment object is created via the usual invocation: - - test = TestSConsign() - -TestSconsign is a subclass of TestSCons, which is a subclass of -TestCommon, which is in turn is a subclass of TestCmd), and hence -has available all of the methods and attributes from those classes, -as well as any overridden or additional methods or attributes defined -in this subclass. -""" - -import os -import os.path -import sys - -from TestSCons import * -from TestSCons import __all__ - -__all__.extend([ 'TestSConsign', ]) - -class TestSConsign(TestSCons): - """Class for testing the sconsign.py script. - - This provides a common place for initializing sconsign tests, - eliminating the need to begin every test with the same repeated - initializations. - - This adds additional methods for running the sconsign script - without changing the basic ability of the run() method to run - "scons" itself, since we need to run scons to generate the - .sconsign files that we want the sconsign script to read. - """ - def __init__(self, *args, **kw): - try: - script_dir = os.environ['SCONS_SCRIPT_DIR'] - except KeyError: - pass - else: - os.chdir(script_dir) - self.script_dir = os.getcwd() - - TestSCons.__init__(self, *args, **kw) - - self.my_kw = { - 'interpreter' : python, # imported from TestSCons - } - - if 'program' not in kw: - kw['program'] = os.environ.get('SCONS') - if not kw['program']: - if os.path.exists('scons'): - kw['program'] = 'scons' - else: - kw['program'] = 'scons.py' - - sconsign = os.environ.get('SCONSIGN') - if not sconsign: - if os.path.exists(self.script_path('sconsign.py')): - sconsign = 'sconsign.py' - elif os.path.exists(self.script_path('sconsign')): - sconsign = 'sconsign' - else: - print("Can find neither 'sconsign.py' nor 'sconsign' scripts.") - self.no_result() - self.set_sconsign(sconsign) - - def script_path(self, script): - return os.path.join(self.script_dir, script) - - def set_sconsign(self, sconsign): - self.my_kw['program'] = sconsign - - def run_sconsign(self, *args, **kw): - kw.update(self.my_kw) - return self.run(*args, **kw) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/classes.qmc b/QMTest/classes.qmc deleted file mode 100644 index 88de061..0000000 --- a/QMTest/classes.qmc +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/QMTest/configuration b/QMTest/configuration deleted file mode 100644 index db648ae..0000000 --- a/QMTest/configuration +++ /dev/null @@ -1,6 +0,0 @@ - - - - . - - diff --git a/QMTest/scons_tdb.py b/QMTest/scons_tdb.py deleted file mode 100644 index c3b082f..0000000 --- a/QMTest/scons_tdb.py +++ /dev/null @@ -1,603 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2001 - 2017 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. -from __future__ import division, print_function - -""" -QMTest classes to support SCons' testing and Aegis-inspired workflow. - -Thanks to Stefan Seefeld for the initial code. -""" - -__revision__ = "QMTest/scons_tdb.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" - -######################################################################## -# Imports -######################################################################## - -import qm -import qm.common -import qm.test.base -from qm.fields import * -from qm.executable import * -from qm.test import database -from qm.test import test -from qm.test import resource -from qm.test import suite -from qm.test.result import Result -from qm.test.file_result_stream import FileResultStream -from qm.test.classes.text_result_stream import TextResultStream -from qm.test.classes.xml_result_stream import XMLResultStream -from qm.test.directory_suite import DirectorySuite -from qm.extension import get_extension_class_name, get_class_arguments_as_dictionary - -import dircache -import os -import imp - -if sys.platform == 'win32': - console = 'con' -else: - console = '/dev/tty' - -def Trace(msg): - open(console, 'w').write(msg) - -# QMTest 2.3 hard-codes how it captures the beginning and end time by -# calling the qm.common.format_time_iso() function, which canonicalizes -# the time stamp in one-second granularity ISO format. In order to get -# sub-second granularity, as well as to use the more precise time.clock() -# function on Windows, we must replace that function with our own. - -orig_format_time_iso = qm.common.format_time_iso - -if sys.platform == 'win32': - time_func = time.clock -else: - time_func = time.time - -def my_format_time(time_secs=None): - return str(time_func()) - -qm.common.format_time_iso = my_format_time - -######################################################################## -# Classes -######################################################################## - -def get_explicit_arguments(e): - """This function can be removed once QMTest 2.4 is out.""" - - # Get all of the arguments. - arguments = get_class_arguments_as_dictionary(e.__class__) - # Determine which subset of the 'arguments' have been set - # explicitly. - explicit_arguments = {} - for name, field in arguments.items(): - # Do not record computed fields. - if field.IsComputed(): - continue - if name in e.__dict__: - explicit_arguments[name] = e.__dict__[name] - - return explicit_arguments - - -def check_exit_status(result, prefix, desc, status): - """This function can be removed once QMTest 2.4 is out.""" - - if sys.platform == "win32" or os.WIFEXITED(status): - # Obtain the exit code. - if sys.platform == "win32": - exit_code = status - else: - exit_code = os.WEXITSTATUS(status) - # If the exit code is non-zero, the test fails. - if exit_code != 0: - result.Fail("%s failed with exit code %d." % (desc, exit_code)) - # Record the exit code in the result. - result[prefix + "exit_code"] = str(exit_code) - return False - - elif os.WIFSIGNALED(status): - # Obtain the signal number. - signal = os.WTERMSIG(status) - # If the program gets a fatal signal, the test fails . - result.Fail("%s received fatal signal %d." % (desc, signal)) - result[prefix + "signal"] = str(signal) - return False - else: - # A process should only be able to stop by exiting, or - # by being terminated with a signal. - assert None - - return True - - - -class Null: - pass - -_null = Null() - -sys_attributes = [ - 'byteorder', - 'exec_prefix', - 'executable', - 'maxint', - 'maxunicode', - 'platform', - 'prefix', - 'version', - 'version_info', -] - -def get_sys_values(): - sys_attributes.sort() - result = [(k, getattr(sys, k, _null)) for k in sys_attributes] - result = [t for t in result if not t[1] is _null] - result = [t[0] + '=' + repr(t[1]) for t in result] - return '\n '.join(result) - -module_attributes = [ - '__version__', - '__build__', - '__buildsys__', - '__date__', - '__developer__', -] - -def get_module_info(module): - module_attributes.sort() - result = [(k, getattr(module, k, _null)) for k in module_attributes] - result = [t for t in result if not t[1] is _null] - result = [t[0] + '=' + repr(t[1]) for t in result] - return '\n '.join(result) - -environ_keys = [ - 'PATH', - 'SCONS', - 'SCONSFLAGS', - 'SCONS_LIB_DIR', - 'PYTHON_ROOT', - 'QTDIR', - - 'COMSPEC', - 'INTEL_LICENSE_FILE', - 'INCLUDE', - 'LIB', - 'MSDEVDIR', - 'OS', - 'PATHEXT', - 'SystemRoot', - 'TEMP', - 'TMP', - 'USERNAME', - 'VXDOMNTOOLS', - 'WINDIR', - 'XYZZY' - - 'ENV', - 'HOME', - 'LANG', - 'LANGUAGE', - 'LC_ALL', - 'LC_MESSAGES', - 'LOGNAME', - 'MACHINE', - 'OLDPWD', - 'PWD', - 'OPSYS', - 'SHELL', - 'TMPDIR', - 'USER', -] - -def get_environment(): - environ_keys.sort() - result = [(k, os.environ.get(k, _null)) for k in environ_keys] - result = [t for t in result if not t[1] is _null] - result = [t[0] + '-' + t[1] for t in result] - return '\n '.join(result) - -class SConsXMLResultStream(XMLResultStream): - def __init__(self, *args, **kw): - super(SConsXMLResultStream, self).__init__(*args, **kw) - def WriteAllAnnotations(self, context): - # Load (by hand) the SCons modules we just unwrapped so we can - # extract their version information. Note that we have to override - # SCons.Script.main() with a do_nothing() function, because loading up - # the 'scons' script will actually try to execute SCons... - - src_engine = os.environ.get('SCONS_LIB_DIR') - if not src_engine: - src_engine = os.path.join('src', 'engine') - fp, pname, desc = imp.find_module('SCons', [src_engine]) - SCons = imp.load_module('SCons', fp, pname, desc) - - # Override SCons.Script.main() with a do-nothing function, because - # loading the 'scons' script will actually try to execute SCons... - - src_engine_SCons = os.path.join(src_engine, 'SCons') - fp, pname, desc = imp.find_module('Script', [src_engine_SCons]) - SCons.Script = imp.load_module('Script', fp, pname, desc) - def do_nothing(): - pass - SCons.Script.main = do_nothing - - scons_file = os.environ.get('SCONS') - if scons_file: - src_script, scons_py = os.path.split(scons_file) - scons = os.path.splitext(scons_py)[0] - else: - src_script = os.path.join('src', 'script') - scons = 'scons' - fp, pname, desc = imp.find_module(scons, [src_script]) - scons = imp.load_module('scons', fp, pname, desc) - fp.close() - - self.WriteAnnotation("scons_test.engine", get_module_info(SCons)) - self.WriteAnnotation("scons_test.script", get_module_info(scons)) - - self.WriteAnnotation("scons_test.sys", get_sys_values()) - self.WriteAnnotation("scons_test.os.environ", get_environment()) - -class AegisStream(TextResultStream): - arguments = [ - qm.fields.IntegerField( - name = "print_time", - title = "print individual test times", - description = """ - """, - default_value = 0, - ), - ] - def __init__(self, *args, **kw): - super(AegisStream, self).__init__(*args, **kw) - self._num_tests = 0 - self._outcomes = {} - self._outcome_counts = {} - for outcome in AegisTest.aegis_outcomes: - self._outcome_counts[outcome] = 0 - self.format = "full" - def _percent(self, outcome): - return 100. * self._outcome_counts[outcome] / self._num_tests - def _aegis_no_result(self, result): - outcome = result.GetOutcome() - return (outcome == Result.FAIL and result.get('Test.exit_code') == '2') - def _DisplayText(self, text): - # qm.common.html_to_text() uses htmllib, which sticks an extra - # '\n' on the front of the text. Strip it and only display - # the text if there's anything to display. - text = qm.common.html_to_text(text) - if text[0] == '\n': - text = text[1:] - if text: - lines = text.splitlines() - if lines[-1] == '': - lines = lines[:-1] - self.file.write(' ' + '\n '.join(lines) + '\n\n') - def _DisplayResult(self, result, format): - test_id = result.GetId() - kind = result.GetKind() - if self._aegis_no_result(result): - outcome = "NO_RESULT" - else: - outcome = result.GetOutcome() - self._WriteOutcome(test_id, kind, outcome) - self.file.write('\n') - def _DisplayAnnotations(self, result): - try: - self._DisplayText(result["Test.stdout"]) - except KeyError: - pass - try: - self._DisplayText(result["Test.stderr"]) - except KeyError: - pass - if self.print_time: - start = float(result['qmtest.start_time']) - end = float(result['qmtest.end_time']) - fmt = " Total execution time: %.1f seconds\n\n" - self.file.write(fmt % (end - start)) - -class AegisChangeStream(AegisStream): - def WriteResult(self, result): - test_id = result.GetId() - if self._aegis_no_result(result): - outcome = AegisTest.NO_RESULT - else: - outcome = result.GetOutcome() - self._num_tests += 1 - self._outcome_counts[outcome] += 1 - super(AegisStream, self).WriteResult(result) - def _SummarizeTestStats(self): - self.file.write("\n") - self._DisplayHeading("STATISTICS") - if self._num_tests != 0: - # We'd like to use the _FormatStatistics() method to do - # this, but it's wrapped around the list in Result.outcomes, - # so it's simpler to just do it ourselves. - print(" %6d tests total\n" % self._num_tests) - for outcome in AegisTest.aegis_outcomes: - if self._outcome_counts[outcome] != 0: - print(" %6d (%3.0f%%) tests %s" % ( - self._outcome_counts[outcome], - self._percent(outcome), - outcome - )) - -class AegisBaselineStream(AegisStream): - def WriteResult(self, result): - test_id = result.GetId() - if self._aegis_no_result(result): - outcome = AegisTest.NO_RESULT - self.expected_outcomes[test_id] = Result.PASS - self._outcome_counts[outcome] += 1 - else: - self.expected_outcomes[test_id] = Result.FAIL - outcome = result.GetOutcome() - if outcome != Result.Fail: - self._outcome_counts[outcome] += 1 - self._num_tests += 1 - super(AegisStream, self).WriteResult(result) - def _SummarizeRelativeTestStats(self): - self.file.write("\n") - self._DisplayHeading("STATISTICS") - if self._num_tests != 0: - # We'd like to use the _FormatStatistics() method to do - # this, but it's wrapped around the list in Result.outcomes, - # so it's simpler to just do it ourselves. - if self._outcome_counts[AegisTest.FAIL]: - print(" %6d (%3.0f%%) tests as expected" % ( - self._outcome_counts[AegisTest.FAIL], - self._percent(AegisTest.FAIL), - )) - non_fail_outcomes = list(AegisTest.aegis_outcomes[:]) - non_fail_outcomes.remove(AegisTest.FAIL) - for outcome in non_fail_outcomes: - if self._outcome_counts[outcome] != 0: - print(" %6d (%3.0f%%) tests unexpected %s" % ( - self._outcome_counts[outcome], - self._percent(outcome), - outcome, - )) - -class AegisBatchStream(FileResultStream): - def __init__(self, arguments): - super(AegisBatchStream, self).__init__(arguments) - self._outcomes = {} - def WriteResult(self, result): - test_id = result.GetId() - kind = result.GetKind() - outcome = result.GetOutcome() - exit_status = '0' - if outcome == Result.FAIL: - exit_status = result.get('Test.exit_code') - self._outcomes[test_id] = exit_status - def Summarize(self): - self.file.write('test_result = [\n') - for file_name in sorted(self._outcomes.keys()): - exit_status = self._outcomes[file_name] - file_name = file_name.replace('\\', '/') - self.file.write(' { file_name = "%s";\n' % file_name) - self.file.write(' exit_status = %s; },\n' % exit_status) - self.file.write('];\n') - -class AegisTest(test.Test): - PASS = "PASS" - FAIL = "FAIL" - NO_RESULT = "NO_RESULT" - ERROR = "ERROR" - UNTESTED = "UNTESTED" - - aegis_outcomes = ( - PASS, FAIL, NO_RESULT, ERROR, UNTESTED, - ) - """Aegis test outcomes.""" - -class Test(AegisTest): - """Simple test that runs a python script and checks the status - to determine whether the test passes.""" - - script = TextField(title="Script to test") - topdir = TextField(title="Top source directory") - - def Run(self, context, result): - """Run the test. The test passes if the command exits with status=0, - and fails otherwise. The program output is logged, but not validated.""" - - command = RedirectedExecutable() - args = [context.get('python', sys.executable), '-tt', self.script] - status = command.Run(args, os.environ) - if not check_exit_status(result, 'Test.', self.script, status): - # In case of failure record exit code, stdout, and stderr. - result.Fail("Non-zero exit_code.") - result["Test.stdout"] = result.Quote(command.stdout) - result["Test.stderr"] = result.Quote(command.stderr) - - -class Database(database.Database): - """Scons test database. - * The 'src' and 'test' directories are explicit suites. - * Their subdirectories are implicit suites. - * All files under 'src/' ending with 'Tests.py' contain tests. - * All files under 'test/' with extension '.py' contain tests. - * Right now there is only a single test class, which simply runs - the specified python interpreter on the given script. To be refined...""" - - srcdir = TextField(title = "Source Directory", - description = "The root of the test suite's source tree.") - _is_generic_database = True - - def is_a_test_under_test(path, t): - return os.path.splitext(t)[1] == '.py' \ - and os.path.isfile(os.path.join(path, t)) - - def is_a_test_under_src(path, t): - return t[-8:] == 'Tests.py' \ - and os.path.isfile(os.path.join(path, t)) - - is_a_test = { - 'src' : is_a_test_under_src, - 'test' : is_a_test_under_test, - } - - exclude_subdirs = { - '.svn' : 1, - 'CVS' : 1, - } - - def is_a_test_subdir(path, subdir): - if exclude_subdirs.get(subdir): - return None - return os.path.isdir(os.path.join(path, subdir)) - - def __init__(self, path, arguments): - - self.label_class = "file_label.FileLabel" - self.modifiable = "false" - # Initialize the base class. - super(Database, self).__init__(path, arguments) - - - def GetRoot(self): - - return self.srcdir - - - def GetSubdirectories(self, directory): - - components = self.GetLabelComponents(directory) - path = os.path.join(self.GetRoot(), *components) - if directory: - dirs = [d for d in dircache.listdir(path) - if os.path.isdir(os.path.join(path, d))] - else: - dirs = list(self.is_a_test.keys()) - - dirs.sort() - return dirs - - - def GetIds(self, kind, directory = "", scan_subdirs = 1): - - components = self.GetLabelComponents(directory) - path = os.path.join(self.GetRoot(), *components) - - if kind == database.Database.TEST: - - if not components: - return [] - - ids = [self.JoinLabels(directory, t) - for t in dircache.listdir(path) - if self.is_a_test[components[0]](path, t)] - - elif kind == Database.RESOURCE: - return [] # no resources yet - - else: # SUITE - - if directory: - ids = [self.JoinLabels(directory, d) - for d in dircache.listdir(path) - if os.path.isdir(os.path.join(path, d))] - else: - ids = list(self.is_a_test.keys()) - - if scan_subdirs: - for d in dircache.listdir(path): - if (os.path.isdir(d)): - ids.extend(self.GetIds(kind, - self.JoinLabels(directory, d), - True)) - - return ids - - - def GetExtension(self, id): - - if not id: - return DirectorySuite(self, id) - - components = self.GetLabelComponents(id) - path = os.path.join(self.GetRoot(), *components) - - if os.path.isdir(path): # a directory - return DirectorySuite(self, id) - - elif os.path.isfile(path): # a test - - arguments = {} - arguments['script'] = path - arguments['topdir'] = self.GetRoot() - - return Test(arguments, qmtest_id = id, qmtest_database = self) - - else: # nothing else to offer - - return None - - - def GetTest(self, test_id): - """This method can be removed once QMTest 2.4 is out.""" - - t = self.GetExtension(test_id) - if isinstance(t, test.Test): - return database.TestDescriptor(self, - test_id, - get_extension_class_name(t.__class__), - get_explicit_arguments(t)) - - raise database.NoSuchTestError(test_id) - - def GetSuite(self, suite_id): - """This method can be removed once QMTest 2.4 is out.""" - - if suite_id == "": - return DirectorySuite(self, "") - - s = self.GetExtension(suite_id) - if isinstance(s, suite.Suite): - return s - - raise database.NoSuchSuiteError(suite_id) - - - def GetResource(self, resource_id): - """This method can be removed once QMTest 2.4 is out.""" - - r = self.GetExtension(resource_id) - if isinstance(r, resource.Resource): - return ResourceDescriptor(self, - resource_id, - get_extension_class_name(r.__class__), - get_explicit_arguments(r)) - - raise database.NoSuchResourceError(resource_id) - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/QMTest/test-framework.rst b/QMTest/test-framework.rst deleted file mode 100644 index 844d99b..0000000 --- a/QMTest/test-framework.rst +++ /dev/null @@ -1,430 +0,0 @@ -======================= -SCons Testing Framework -======================= - -SCons uses extensive automated tests to try to ensure quality. The primary goal -is that users should be able to upgrade from version to version without any surprise -changes in behavior. - -In general, no change goes into SCons unless it has one or more new or modified -tests that demonstrably exercise the bug being fixed or the feature being added. -There are exceptions to this guideline, but they should be just that, ''exceptions''. -When in doubt, make sure it's tested. - -Test Organization -================= - -There are three types of SCons tests: - -*End-to-End Tests* - End-to-end tests of SCons are all Python scripts (``*.py``) underneath - the ``test/`` subdirectory. They use the test infrastructure modules in the - ``QMTest`` subdirectory. - -*Unit Tests* - Unit tests for individual SCons modules live underneath the - ``src/engine/`` subdirectory and are the same base name as the module - with ``Tests.py`` appended--for example, the unit tests for the - ``Builder.py`` module are in the ``BuilderTests.py`` script. - -*External Tests* - For the support of external Tools (in the form of packages, preferably), the - testing framework got extended, such that it can run in standalone mode. - You can start it from the top-level folder of your Tool's source tree, - where it then finds all Python scripts (``*.py``) underneath the - local ``test/`` directory. - This implies that Tool tests have to be kept in a folder named ``test``, - like for the SCons core. - - -Contrasting End-to-End and Unit Tests -##################################### - -In general, anything that we've put into an end-to-end test script should -be considered a hardened part of the interface (that is, it's something -that a user might do) and should not be broken. Unit tests are now -considered more malleable, more for testing internal interfaces that -can change so long as we don't break users' ``SConscript`` files. (This -wasn't always the case, and there's a lot of meaty code in many of the -unit test scripts that does, in fact, capture external interface -behavior. In general, we should try to move those things to end-to-end -scripts as we find them.) - -It's more difficult to debug end-to-end tests. You can actually go -straight into the Python debugger on the unit test scripts by using the -``runtest.py --pdb`` option, but the end-to-end tests treat an SCons -invocation as a "black box" and just look for external effects. -Simple ``print`` statements within the SCons code itself often don't help -debug end-to-end because they end up in SCons output that gets compared -against expected output and cause a test failure. Probably the most -effective technique is to use the internal ``SCons.Debug.Trace()`` function, -which prints output to ``/dev/tty`` on Linux/UNIX systems and ``con`` on -Windows systems, so you can see what's going on. - -Naming conventions -################## - -The end-to-end tests, more or less, stick to the following naming conventions: - -1. All tests end with a .py suffix. - -2. In the *General* form we use - - ``Feature.py`` - for the test of a specified feature; try to - keep this description reasonably short - - ``Feature-x.py`` - for the test of a specified feature using - option ``x`` - -3. The *command line option* tests take the form - - ``option-x.py`` - for a lower-case single-letter option - - ``option--X.py`` - upper-case single-letter option - (with an extra hyphen, so the file names will - be unique on case-insensitive systems) - - ``option--lo.py`` - long option; abbreviate the long - option name to a few characters - - -Running Tests -============= - -The standard set of SCons tests are run from the top-level source directory -by the ``runtest.py`` script. -There is a ``--qmtest`` option that checks whether the ``QMTest`` package -is installed on your system. If it can be found, then the ``runtest.py`` script -will use it to carry out the tests. - -Help is available through the ``-h`` option: - -:: - - $ python runtest.py -h - -To simply run all the tests, use the ``-a`` option: - -:: - - $ python runtest.py -a - -By default, ``runtest.py`` prints a count and percentage message for each test -case, along with the name of the test file. -If you need the output to be more silent, have a look at the ``-q``, ``-s`` and -``-k`` options. - -You may specifically list one or more tests to be run: - -:: - - $ python runtest.py src/engine/SCons/BuilderTests.py - $ python runtest.py test/option-j.py test/Program.py - -Folder names are allowed arguments as well, so you can do a - -:: - - $ python runtest.py test/SWIG - -to run all SWIG tests only. - -You can also use the ``-f`` option to execute just the tests listed in a specified -text file: - -:: - - $ cat testlist.txt - test/option-j.py - test/Program.py - $ python runtest.py -f testlist.txt - - -One test must be listed per line, and any lines that begin with '#' -will be ignored (the intent being to allow you, for example, -to comment out tests that -are currently passing and then uncomment all of the tests in the file -for a final validation run). - -If more than one test is run, the ``runtest.py`` script prints a summary -of how many tests passed, failed, or yielded no result, and lists any -unsuccessful tests. - -The above invocations all test directly the files underneath the ``src/`` -subdirectory, and do not require that a packaging build be performed first. -The ``runtest.py`` script supports additional options to run tests against -unpacked packages in the ``build/test-*/`` subdirectories. - -If you are testing a separate Tool outside of the SCons source tree, you have -to call the ``runtest.py`` script in *external* (stand-alone) mode:: - - $ python ~/scons/runtest.py -e -a - -. This ensures that the testing framework doesn't try to access SCons classes -needed for some of the *internal* test cases. - -Note, that the actual tests are carried out in a temporary folder each, which gets -deleted afterwards. This ensures that your source directories don't get clobbered -with temporary files from the test runs. It also means that you can't simply change -into a folder to "debug things" after a test has gone wrong. For a way around this, -check out the ``PRESERVE`` environment variable. It can be seen in action in -`How to convert old tests`_ below. - -Not Running Tests -================= - -If you simply want to check which tests would get executed, you can call the -``runtest.py`` script with the ``-l`` option:: - - $ python runtest.py -l - -Then there is also the ``-n`` option, which prints the command line for each -single test, but doesn't actually execute them:: - - $ python runtest.py -n - -Finding Tests -============= - -When started in *standard* mode - -:: - - $ python runtest.py -a - - -, ``runtest.py`` assumes that it is run from the SCons top-level source directory. -It then dives into the ``src`` and ``test`` folders, where it tries to find filenames - - ``*Test.py`` - for the ``src`` directory, and - - ``*.py`` - for the ``test`` folder. - -When using fixtures, you may quickly end up in a position where you have supporting -Python script files in a subfolder, but they shouldn't get picked up as test scripts. -In this case you have two options: - -1. Add a file with the name ``sconstest.skip`` to your subfolder. This lets - ``runtest.py`` skip the contents of the directory completely. -2. Create a file ``.exclude_tests`` in each folder in question, and in it list - line-by-line the files to get excluded from testing. - -The same rules apply when testing external Tools by using the ``-e`` option. - - -"Hello, world!" SCons Test Script -================================= - -To illustrate how the end-to-end test scripts work, -let's walk through a simple "Hello, world!" example: - -:: - - #!python - import TestSCons - - test = TestSCons.TestSCons() - - test.write('SConstruct', """\ - Program('hello.c') - """) - - test.write('hello.c', """\ - int - main(int argc, char *argv[]) - { - printf("Hello, world!\\n"); - exit (0); - } - """) - - test.run() - - test.run(program='./hello', stdout="Hello, world!\n") - - test.pass_test() - - -``import TestSCons`` - Imports the main infrastructure for writing SCons tests. This is normally the only part of the infrastructure that needs importing. Sometimes other Python modules are necessary or helpful, and get imported before this line. - -``test = TestSCons.TestSCons()`` - This initializes an object for testing. A fair amount happens under the covers when the object is created, including: - - * A temporary directory is created for all the in-line files that will get created. - * The temporary directory's removal is arranged for when the test is finished. - * We ``os.chdir()`` to the temporary directory. - -``test.write('SConstruct', ...`` - This line creates an ``SConstruct`` file in the temporary directory, to be used as input to the ``scons`` run(s) that we're testing. Note the use of the Python triple-quote syntax for the contents of the ``SConstruct`` file. Because input files for tests are all created from in-line data like this, the tests can sometimes get a little confusing to read, because some of the Python code is found - -``test.write('hello.c', ...`` - This lines creates an ``hello.c`` file in the temporary directory. Note that we have to escape the ``\\n`` in the ``"Hello, world!\\n"`` string so that it ends up as a single backslash in the ``hello.c`` file on disk. - -``test.run()`` - This actually runs SCons. Like the object initialization, things happen under the covers: - - * The exit status is verified; the test exits with a failure if the exit status is not zero. - * The error output is examined, and the test exits with a failure if there is any - -``test.run(program='./hello', stdout="Hello, world!\n")`` - This shows use of the ``TestSCons.run()`` method to execute a program other than ``scons``, in this case the ``hello`` program we just presumably built. The ``stdout=`` keyword argument also tells the ``TestSCons.run()`` method to fail if the program output does not match the expected string ``"Hello, world!\n"``. Like the previous ``test.run()`` line, it will also fail the test if the exit status is non-zero, or there is any error output. - -``test.pass_test()`` - This is always the last line in a test script. It prints ``PASSED`` on the screen and makes sure we exit with a ``0`` status to indicate the test passed. As a side effect of destroying the ``test`` object, the created temporary directory will be removed. - -Working with fixtures -===================== - -In the simple example above, we have seen how to create files in the temporary test directory. -We give a filename to the ``TestSCons.write()`` method, together with its contents, and it gets -written to the test folder right before its start. - -This technique can still be seen throughout most of the end-to-end tests, but there is a better -way. It's much easier to edit, create and maintain real files, instead of copy/pasting -content to/from a Python script. If the test files get longer, the test script -gets longer and is harder to read. - -Against this, we now have the possibility to copy single files or the contents of a -local folder to the test directory. Since we can reuse these files/folders to setup -several tests, we call them *fixtures* in the following. - -Directory fixtures -################## - -The function ``dir_fixture(self, srcdir, dstdir=None)`` in the ``TestCmd`` class -copies the contents of the specified folder ``srcdir`` from -the directory of the called test script, to the current -temporary test directory. -The ``srcdir`` name may be a list, in which case the elements are -concatenated with the ``os.path.join()`` method. The ``dstdir`` is -assumed to be under the temporary working directory, it gets -created automatically, if it does not already exist. - -A short syntax example:: - - test = TestSCons.TestSCons() - test.dir_fixture('image') - test.run() - -would copy all files and subfolders from the local ``image`` folder, to -the temporary directory for the current test. - -If you'd like to see a real example for this in action, refer to the test -named ``test/packaging/convenience-functions/convenience-functions.py``. - -File fixtures -############# - -Like for directory fixtures, ``file_fixture(self, srcfile, dstfile=None)`` -copies the file ``srcfile`` from the directory of -the called script, to the temporary test directory. -The ``dstfile`` is assumed to be under the temporary working -directory, unless it is an absolute path name. -If ``dstfile`` is specified, its target directory gets created -automatically if it doesn't already exist. - -With a:: - - test = TestSCons.TestSCons() - test.file_fixture('SConstruct') - test.file_fixture(['src','main.cpp'],['src','main.cpp']) - test.run() - -you would copy the files ``SConstruct`` and ``src/main.cpp`` to the temporary -test folder, prior to running the test itself. - -Again, a reference example can be found in the current *default* revision of -SCons, it is ``test/packaging/sandbox-test/sandbox-test.py``. - -For even more examples you should check out one of the external Tools, e.g. the -*Qt4* Tool at https://bitbucket.org/dirkbaechle/scons_qt4. Also visit the SCons -Tools Index at http://www.scons.org/wiki/ToolsIndex for a complete -list of available Tools, though not all may have tests yet. - -How to convert old tests -######################## - -We now show how to convert a test, still using the ``TestSCons.write()`` method, to -the fixture based approach. For this, we need to get at the files as they -are written to each temporary test folder. - -Luckily, ``runtest.py`` checks for the existence of an environment variable named -``PRESERVE``. If it is set to a non-zero value, the testing framework doesn't delete -the test folder as ususal, but prints its name to the screen. - -So, you should be able to give the commands - -:: - - $ export PRESERVE=1 - $ python runtest.py test/packaging/sandbox-test.py - -, assuming Linux and a bash-like shell. - -The output should then look something like this:: - - 1/1 (100.00%) /usr/bin/python -tt test/packaging/sandbox-test.py - PASSED - Preserved directory /tmp/testcmd.4060.twlYNI - -and you see that the test files have been kept in the folder ``/tmp/testcmd.4060.twlYNI``, -where you can now copy them from to your new *fixture* folder. Then, in the test -script you simply remove all the tedious ``TestSCons.write()`` statements and -replace them by a single ``TestSCons.dir_fixture()``. - -Finally, you shouldn't forget to clean up and remove the temporary test directory. ``;)`` - -Test Infrastructure -=================== - -The test API is in ``QMTest/TestSCons.py``. ``TestSCons`` is a subclass of -``TestCommon``, which is a subclass of ``TestCmd``; all those python files are -in ``QMTest``. Start in ``QMTest/TestCmd.py`` for the base API definitions, -like how to create files (``test.write()``) and run commands (``test.run()``). - -You want to use ``TestSCons`` for the end-to-end tests in ``test``, but ``TestCmd`` -for the unit tests in the ``src`` folder. - -The match functions work like this: - -TestSCons.match_re:: match each line with a RE - * Splits the lines into a list (unless they already are) - * splits the REs at newlines (unless already a list) and puts ^..$ around each - * then each RE must match each line. This means there must be as many REs as lines. - -TestSCons.match_re_dotall:: match all the lines against a single RE - * Joins the lines with newline (unless already a string) - * joins the REs with newline (unless it's a string) and puts ^..$ around the whole thing - * then whole thing must match with python re.DOTALL. - -Use them in a test like this:: - - test.run(..., match=TestSCons.match_re, ...) - -or:: - - test.must_match(..., match=TestSCons.match_re, ...) - -Avoiding Tests based on Tool existence -====================================== - -Here's an easy sample:: - - #!python - intelc = test.detect_tool('intelc', prog='icpc') - if not intelc: - test.skip_test("Could not load 'intelc' Tool; skipping test(s).\n") - -See ``QMTest/TestSCons.py`` for the ``detect_tool`` method. It calls the tool's -``generate()`` method, and then looks for the given prog (tool name by default) in -``env['ENV']['PATH']``. - - diff --git a/README.rst b/README.rst old mode 100644 new mode 100755 index 258d6a1..ea7667c --- a/README.rst +++ b/README.rst @@ -4,15 +4,28 @@ SCons - a software construction tool .. image:: https://img.shields.io/badge/IRC-scons-blue.svg :target: http://webchat.freenode.net/?channels=%23scons&uio=d4 :alt: IRC - + .. image:: https://img.shields.io/sourceforge/dm/scons.svg :target: https://sourceforge.net/projects/scons :alt: Sourceforge Monthly Downloads - + .. image:: https://img.shields.io/sourceforge/dt/scons.svg :target: https://sourceforge.net/projects/scons :alt: Sourceforge Total Downloads +.. image:: https://travis-ci.org/SCons/scons.svg?branch=master + :target: https://travis-ci.org/SCons/scons + :alt: Travis CI build status + +.. image:: https://ci.appveyor.com/api/projects/status/github/SCons/scons?svg=true&branch=master + :target: https://ci.appveyor.com/project/SCons/scons + :alt: AppVeyor CI build Status + +.. image:: https://codecov.io/gh/SCons/scons/branch/master/graph/badge.svg + :target: https://codecov.io/gh/SCons/scons + :alt: CodeCov Coverage Status + + Welcome to the SCons development tree. The real purpose of this tree is to package SCons for production distribution in a variety of formats, not just to hack SCons code. @@ -53,15 +66,14 @@ Latest Version Before going further, you can check that this package you have is the latest version at the SCons download page: - http://www.scons.org/download.php + http://www.scons.org/pages/download.html Execution Requirements ====================== -Running SCons requires Python version 2.7 or later (Python 3 is not -yet supported). There should be no other dependencies or requirements -to run SCons. +Running SCons requires either Python version 2.7.* or Python 3.5 or higher. +There should be no other dependencies or requirements to run SCons. The default SCons configuration assumes use of the Microsoft Visual C++ compiler suite on WIN32 systems, and assumes a C compiler named 'cc', a C++ @@ -92,13 +104,11 @@ populate the build/scons/ subdirectory. You would do this as follows on a Linux or UNIX system (using sh or a derivative like bash or ksh):: $ setenv MYSCONS=`pwd`/src - $ setenv SCONS_LIB_DIR=$MYSCONS/engine $ python $MYSCONS/script/scons.py [arguments] Or on Windows:: C:\scons>set MYSCONS=%cd%\src - C:\scons>set SCONS_LIB_DIR=%MYSCONS%\engine C:\scons>python %MYSCONS%\script\scons.py [arguments] An alternative approach is to skip the above and use:: @@ -168,7 +178,7 @@ Or on Windows:: By default, the above commands will do the following: -- Install the version-numbered "scons-3.0.0" and "sconsign-3.0.0" scripts in +- Install the version-numbered "scons-3.1.0" and "sconsign-3.0.3" scripts in the default system script directory (/usr/bin or C:\\Python\*\\Scripts, for example). This can be disabled by specifying the "--no-version-script" option on the command line. @@ -180,23 +190,23 @@ By default, the above commands will do the following: before making it the default on your system. On UNIX or Linux systems, you can have the "scons" and "sconsign" scripts be - hard links or symbolic links to the "scons-3.0.0" and "sconsign-3.0.0" + hard links or symbolic links to the "scons-3.0.3" and "sconsign-3.0.3" scripts by specifying the "--hardlink-scons" or "--symlink-scons" options on the command line. -- Install "scons-3.0.0.bat" and "scons.bat" wrapper scripts in the Python +- Install "scons-3.0.3.bat" and "scons.bat" wrapper scripts in the Python prefix directory on Windows (C:\\Python\*, for example). This can be disabled by specifying the "--no-install-bat" option on the command line. On UNIX or Linux systems, the "--install-bat" option may be specified to - have "scons-3.0.0.bat" and "scons.bat" files installed in the default system + have "scons-3.0.3.bat" and "scons.bat" files installed in the default system script directory, which is useful if you want to install SCons in a shared file system directory that can be used to execute SCons from both UNIX/Linux and Windows systems. - Install the SCons build engine (a Python module) in an appropriate - version-numbered SCons library directory (/usr/lib/scons-3.0.0 or - C:\\Python\*\\scons-3.0.0, for example). See below for more options related to + version-numbered SCons library directory (/usr/lib/scons-3.0.3 or + C:\\Python\*\\scons-3.0.3, for example). See below for more options related to installing the build engine library. - Install the troff-format man pages in an appropriate directory on UNIX or @@ -474,7 +484,7 @@ running all of "runtest.py -a". Building Packages ================= -We use SCons (version 3.0.0 or later) to build its own packages. If you +We use SCons (version 3.0.3 or later) to build its own packages. If you already have an appropriate version of SCons installed on your system, you can build everything by simply running it:: @@ -489,18 +499,13 @@ about `Executing SCons Without Installing`_):: Depending on the utilities installed on your system, any or all of the following packages will be built:: - build/dist/scons-3.0.0-1.noarch.rpm - build/dist/scons-3.0.0-1.src.rpm - build/dist/scons-3.0.0.linux-i686.tar.gz - build/dist/scons-3.0.0.tar.gz - build/dist/scons-3.0.0.win32.exe - build/dist/scons-3.0.0.zip - build/dist/scons-doc-3.0.0.tar.gz - build/dist/scons-local-3.0.0.tar.gz - build/dist/scons-local-3.0.0.zip - build/dist/scons-src-3.0.0.tar.gz - build/dist/scons-src-3.0.0.zip - build/dist/scons_3.0.0-1_all.deb + build/dist/scons-3.0.5.tar.gz + build/dist/scons-3.0.5.zip + build/dist/scons-doc-3.0.5.tar.gz + build/dist/scons-local-3.0.5.tar.gz + build/dist/scons-local-3.0.5.zip + build/dist/scons-src-3.0.5.tar.gz + build/dist/scons-src-3.0.5.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. For @@ -524,10 +529,6 @@ system, it should not try to install it.) The runtest.py script supports a -p option that will run the specified tests (individually or collectively via the -a option) against the unpacked build/test-/\* subdirectory:: - $ python runtest.py -p deb - - $ python runtest.py -p rpm - $ python runtest.py -p local-tar-gz $ python runtest.py -p local-zip @@ -664,7 +665,7 @@ section of small examples for getting started using SCons. Additional documentation for SCons is available at: - http://www.scons.org/documentation + http://www.scons.org/documentation.html Licensing @@ -677,18 +678,22 @@ in the LICENSE file. Reporting Bugs ============== -Please report bugs by following the detailed instructions on our Bug -Submission page: +The SCons project welcomes bug reports and feature requests. - http://scons.tigris.org/bug-submission.html +Please make sure you send email with the problem or feature request to +the SCons users mailing list, which you can join via the link below: -You can also send mail to the SCons developers' mailing list: + http://two.pairlist.net/mailman/listinfo/scons-users - scons-dev@scons.org +Once you have discussed your issue on the users mailing list and the +community has confirmed that it is either a new bug or a duplicate of an +existing bug, then please follow the instructions the community provides +to file a new bug or to add yourself to the CC list for an existing bug + +You can explore the list of existing bugs, which may include workarounds +for the problem you've run into on GitHub Issues: -But even if you send email to the mailing list please make sure that you ALSO -submit a bug report to the project page bug tracker, because bug reports in -email often get overlooked in the general flood of messages. + https://github.com/SCons/scons/issues Mailing Lists @@ -760,5 +765,5 @@ many contributors, including but not at all limited to: \... and many others. -Copyright (c) 2001 - 2017 The SCons Foundation +Copyright (c) 2001 - 2019 The SCons Foundation diff --git a/SConstruct b/SConstruct index 68f9e3f..3a14508 100644 --- a/SConstruct +++ b/SConstruct @@ -5,13 +5,13 @@ from __future__ import print_function -copyright_years = '2001 - 2017' +copyright_years = '2001 - 2019' # This gets inserted into the man pages to reflect the month of release. -month_year = 'September 2017' +month_year = 'March 2019' # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -34,6 +34,7 @@ month_year = 'September 2017' # import distutils.util +import distutils.command import fnmatch import os import os.path @@ -42,55 +43,25 @@ import stat import sys import tempfile import time +import socket +import textwrap import bootstrap project = 'scons' -default_version = '3.0.0' +default_version = '3.0.5' copyright = "Copyright (c) %s The SCons Foundation" % copyright_years -platform = distutils.util.get_platform() - -def is_windows(): - if platform.startswith('win'): - return True - else: - return False - SConsignFile() -# -# An internal "whereis" routine to figure out if a given program -# is available on this system. -# -def whereis(file): - exts = [''] - if is_windows(): - exts += ['.exe'] - for dir in os.environ['PATH'].split(os.pathsep): - f = os.path.join(dir, file) - for ext in exts: - f_ext = f + ext - if os.path.isfile(f_ext): - try: - st = os.stat(f_ext) - except: - continue - if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: - return f_ext - return None - # # We let the presence or absence of various utilities determine whether # or not we bother to build certain pieces of things. This should allow # people to still do SCons packaging work even if they don't have all -# of the utilities installed (e.g. RPM). +# of the utilities installed # -dh_builddeb = whereis('dh_builddeb') -fakeroot = whereis('fakeroot') gzip = whereis('gzip') -rpmbuild = whereis('rpmbuild') -hg = os.path.exists('.hg') and whereis('hg') +git = os.path.exists('.git') and whereis('git') unzip = whereis('unzip') zip = whereis('zip') @@ -99,12 +70,7 @@ zip = whereis('zip') # date = ARGUMENTS.get('DATE') if not date: - date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time())) - -# Datestring for debian -# Should look like: Mon, 03 Nov 2016 13:37:42 -0700 -deb_date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) - + date = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))) developer = ARGUMENTS.get('DEVELOPER') if not developer: @@ -112,44 +78,42 @@ if not developer: developer = os.environ.get(variable) if developer: break + if os.environ.get('SOURCE_DATE_EPOCH'): + developer = '_reproducible' build_system = ARGUMENTS.get('BUILD_SYSTEM') if not build_system: - import socket - build_system = socket.gethostname().split('.')[0] + if os.environ.get('SOURCE_DATE_EPOCH'): + build_system = '_reproducible' + else: + build_system = socket.gethostname().split('.')[0] version = ARGUMENTS.get('VERSION', '') if not version: version = default_version -hg_status_lines = [] +git_status_lines = [] -if hg: - cmd = "%s status --all 2> /dev/null" % hg - hg_status_lines = os.popen(cmd, "r").readlines() +if git: + cmd = "%s ls-files 2> /dev/null" % git + git_status_lines = os.popen(cmd, "r").readlines() revision = ARGUMENTS.get('REVISION', '') def generate_build_id(revision): return revision -if not revision and hg: - hg_heads = os.popen("%s heads 2> /dev/null" % hg, "r").read() - cs = re.search('changeset:\s+(\S+)', hg_heads) - if cs: - revision = cs.group(1) - b = re.search('branch:\s+(\S+)', hg_heads) - if b: - revision = b.group(1) + ':' + revision - def generate_build_id(revision): - result = revision - if [l for l in hg_status_lines if l[0] in 'AMR!']: - result = result + '[MODIFIED]' - return result +if not revision and git: + git_hash = os.popen("%s rev-parse HEAD 2> /dev/null" % git, "r").read().strip() + def generate_build_id(revision): + result = git_hash + if [l for l in git_status_lines if 'modified' in l]: + result = result + '[MODIFIED]' + return result + revision = git_hash checkpoint = ARGUMENTS.get('CHECKPOINT', '') if checkpoint: if checkpoint == 'd': - import time checkpoint = time.strftime('%Y%m%d', time.localtime(time.time())) elif checkpoint == 'r': checkpoint = 'r' + revision @@ -162,31 +126,12 @@ if build_id is None: else: build_id = '' -import os.path -import distutils.command - -no_winpack_templates = not os.path.exists(os.path.join(os.path.split(distutils.command.__file__)[0],'wininst-9.0.exe')) -skip_win_packages = ARGUMENTS.get('SKIP_WIN_PACKAGES',False) or no_winpack_templates - -if sys.version_info[0] > 2: - # TODO: Resolve this issue. Currently fails when run on windows with - # File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/distutils/command/bdist_wininst.py", line 262, in create_exe - # cfgdata = cfgdata.encode("mbcs") - # LookupError: unknown encoding: mbcs - print("Temporary PY3: Skipping windows package builds") - skip_win_packages = True - -if skip_win_packages: - print("Skipping the build of Windows packages...") - -python_ver = sys.version[0:3] - # # Adding some paths to sys.path, this is mainly needed # for the doc toolchain. # addpaths = [os.path.abspath(os.path.join(os.getcwd(), 'bin')), - os.path.abspath(os.path.join(os.getcwd(), 'QMTest'))] + os.path.abspath(os.path.join(os.getcwd(), 'testing/framework'))] for a in addpaths: if a not in sys.path: sys.path.append(a) @@ -213,7 +158,8 @@ command_line_variables = [ ("BUILD_SYSTEM=", "The system on which the packages were built. " + "The default is whatever hostname is returned " + - "by socket.gethostname()."), + "by socket.gethostname(). If SOURCE_DATE_EPOCH " + + "env var is set, '_reproducible' is the default."), ("CHECKPOINT=", "The specific checkpoint release being packaged, " + "which will be appended to the VERSION string. " + @@ -231,11 +177,13 @@ command_line_variables = [ ("DEVELOPER=", "The developer who created the packages. " + "The default is the first set environment " + - "variable from the list $USERNAME, $LOGNAME, $USER."), + "variable from the list $USERNAME, $LOGNAME, $USER." + + "If the SOURCE_DATE_EPOCH env var is set, " + + "'_reproducible' is the default."), ("REVISION=", "The revision number of the source being built. " + - "The default is the Subversion revision returned " + - "'hg heads', with an appended string of " + + "The default is the git hash returned " + + "'git rev-parse HEAD', with an appended string of " + "'[MODIFIED]' if there are any changes in the " + "working copy."), @@ -243,35 +191,24 @@ command_line_variables = [ "is the hard-coded value '%s' " % default_version + "from this SConstruct file."), - ("SKIP_WIN_PACKAGES=", "If set, skip building win32 and win64 packages."), ] Default('.', build_dir) packaging_flavors = [ - ('deb', "A .deb package. (This is currently not supported.)"), - - ('rpm', "A RedHat Package Manager file."), - ('tar-gz', "The normal .tar.gz file for end-user installation."), - - ('src-tar-gz', "A .tar.gz file containing all the source " + - "(including tests and documentation)."), - ('local-tar-gz', "A .tar.gz file for dropping into other software " + "for local use."), - ('zip', "The normal .zip file for end-user installation."), - + ('local-zip', "A .zip file for dropping into other software " + + "for local use."), + ('src-tar-gz', "A .tar.gz file containing all the source " + + "(including tests and documentation)."), ('src-zip', "A .zip file containing all the source " + "(including tests and documentation)."), - ('local-zip', "A .zip file for dropping into other software " + - "for local use."), ] -test_deb_dir = os.path.join(build_dir, "test-deb") -test_rpm_dir = os.path.join(build_dir, "test-rpm") test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz") test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz") test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz") @@ -293,7 +230,6 @@ else: -import textwrap indent_fmt = ' %-26s ' @@ -329,137 +265,13 @@ for variable, help_text in command_line_variables: -zcat = 'gzip -d -c' - -# -# 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['CD']) - 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" - - -def SCons_revision(target, source, env): - """Interpolate specific values from the environment into a file. - - This is used to copy files into a tree that gets packaged up - into the source file package. - """ - t = str(target[0]) - s = source[0].rstr() - - try: - with open(s, 'r') as fp: - contents = fp.read() - - - # Note: We construct the __*__ substitution strings here - # so that they don't get replaced when this file gets - # copied into the tree for packaging. - contents = contents.replace('__BUILD' + '__', env['BUILD']) - contents = contents.replace('__BUILDSYS' + '__', env['BUILDSYS']) - contents = contents.replace('__COPYRIGHT' + '__', env['COPYRIGHT']) - contents = contents.replace('__DATE' + '__', env['DATE']) - contents = contents.replace('__DEB_DATE' + '__', env['DEB_DATE']) - - contents = contents.replace('__DEVELOPER' + '__', env['DEVELOPER']) - contents = contents.replace('__FILE' + '__', str(source[0]).replace('\\', '/')) - contents = contents.replace('__MONTH_YEAR'+ '__', env['MONTH_YEAR']) - contents = contents.replace('__REVISION' + '__', env['REVISION']) - contents = contents.replace('__VERSION' + '__', env['VERSION']) - contents = contents.replace('__NULL' + '__', '') - open(t, 'w').write(contents) - except UnicodeDecodeError as e: - print("Error decoding file:%s just copying no revision edit") - with open(s, 'rb') as fp: - contents = fp.read() - open(t, 'wb').write(contents) - - - os.chmod(t, os.stat(s)[0]) - revaction = SCons_revision revbuilder = Builder(action = Action(SCons_revision, varlist=['COPYRIGHT', 'VERSION'])) -def soelim(target, source, env): - """ - Interpolate files included in [gnt]roff source files using the - .so directive. - - This behaves somewhat like the soelim(1) wrapper around groff, but - makes us independent of whether the actual underlying implementation - includes an soelim() command or the corresponding command-line option - to groff(1). The key behavioral difference is that this doesn't - recursively include .so files from the include file. Not yet, anyway. - """ - t = str(target[0]) - s = str(source[0]) - dir, f = os.path.split(s) - tfp = open(t, 'w') - sfp = open(s, 'r') - for line in sfp.readlines(): - if line[:4] in ['.so ', "'so "]: - sofile = os.path.join(dir, line[4:-1]) - tfp.write(open(sofile, 'r').read()) - else: - tfp.write(line) - sfp.close() - tfp.close() - -def soscan(node, env, path): - c = node.get_text_contents() - return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c) -soelimbuilder = Builder(action = Action(soelim), - source_scanner = Scanner(soscan)) - -# When copying local files from a Repository (Aegis), -# just make copies, don't symlink them. +# Just make copies, don't symlink them. SetOption('duplicate', 'copy') env = Environment( @@ -486,11 +298,6 @@ env = Environment( ZCAT = zcat, - RPMBUILD = rpmbuild, - RPM2CPIO = 'rpm2cpio', - - TEST_DEB_DIR = test_deb_dir, - TEST_RPM_DIR = test_rpm_dir, TEST_SRC_TAR_GZ_DIR = test_src_tar_gz_dir, TEST_SRC_ZIP_DIR = test_src_zip_dir, TEST_TAR_GZ_DIR = test_tar_gz_dir, @@ -524,19 +331,18 @@ Version_values = [Value(version), Value(build_id)] # separate packages. # -from distutils.sysconfig import get_python_lib; +from distutils.sysconfig import get_python_lib python_scons = { 'pkg' : 'python-' + project, 'src_subdir' : 'engine', 'inst_subdir' : get_python_lib(), - 'rpm_dir' : '/usr/lib/scons', 'debian_deps' : [ 'debian/changelog', 'debian/compat', - 'debian/control', + 'debian/control', 'debian/copyright', 'debian/dirs', 'debian/docs', @@ -557,67 +363,16 @@ python_scons = { 'buildermap' : {}, - 'extra_rpm_files' : [], - 'explicit_deps' : { 'SCons/__init__.py' : Version_values, }, } -# Figure out the name of a .egg-info file that might be generated -# as part of the RPM package. There are two complicating factors. -# -# First, the RPM spec file we generate will just execute "python", not -# necessarily the one in sys.executable. If *that* version of python has -# a distutils that knows about Python eggs, then setup.py will generate a -# .egg-info file, so we have to execute any distutils logic in a subshell. -# -# Second, we can't just have the subshell check for the existence of the -# distutils.command.install_egg_info module and generate the expected -# file name by hand, the way we used to, because different systems can -# have slightly different .egg-info naming conventions. (Specifically, -# Ubuntu overrides the default behavior to remove the Python version -# string from the .egg-info file name.) The right way to do this is to -# actually call into the install_egg_info() class to have it generate -# the expected name for us. -# -# This is all complicated enough that we do it by writing an in-line -# script to a temporary file and then feeding it to a separate invocation -# of "python" to tell us the actual name of the generated .egg-info file. - -print_egg_info_name = """ -try: - from distutils.dist import Distribution - from distutils.command.install_egg_info import install_egg_info -except ImportError: - pass -else: - dist = Distribution({'name' : "scons", 'version' : '%s'}) - i = install_egg_info(dist) - i.finalize_options() - import os.path - print(os.path.split(i.outputs[0])[1]) -""" % version - -try: - fd, tfname = tempfile.mkstemp() - tfp = os.fdopen(fd, "w") - tfp.write(print_egg_info_name) - tfp.close() - egg_info_file = os.popen("python %s" % tfname).read()[:-1] - if egg_info_file: - python_scons['extra_rpm_files'].append(egg_info_file) -finally: - try: - os.unlink(tfname) - except EnvironmentError: - pass scons_script = { 'pkg' : project + '-script', 'src_subdir' : 'script', 'inst_subdir' : 'bin', - 'rpm_dir' : '/usr/bin', 'debian_deps' : [ 'debian/changelog', @@ -648,13 +403,6 @@ scons_script = { 'buildermap' : {}, - 'extra_rpm_files' : [ - 'scons-' + version, - 'sconsign-' + version, - 'scons-time-' + version, - 'scons-configure-cache-' + version, - ], - 'explicit_deps' : { 'scons' : Version_values, 'sconsign' : Version_values, @@ -685,7 +433,6 @@ scons = { 'sconsign.1', 'scons-time.1', 'script/scons.bat', - #'script/scons-post-install.py', 'setup.cfg', 'setup.py', ], @@ -759,13 +506,13 @@ for p in [ scons ]: # destination files. # manifest_in = File(os.path.join(src, 'MANIFEST.in')).rstr() - src_files = bootstrap.parseManifestLines(src, open(manifest_in).readlines()) + src_files = bootstrap.parseManifestLines(src, manifest_in) raw_files = src_files[:] dst_files = src_files[:] - rpm_files = [] MANIFEST_in_list = [] + if 'subpkgs' in p: # # This package includes some sub-packages. Read up their @@ -777,23 +524,13 @@ for p in [ scons ]: ssubdir = sp['src_subdir'] isubdir = p['subinst_dirs'][sp['pkg']] - MANIFEST_in = File(os.path.join(src, ssubdir, 'MANIFEST.in')).rstr() MANIFEST_in_list.append(MANIFEST_in) - files = bootstrap.parseManifestLines(os.path.join(src, ssubdir), open(MANIFEST_in).readlines()) + files = bootstrap.parseManifestLines(os.path.join(src, ssubdir), MANIFEST_in) raw_files.extend(files) src_files.extend([os.path.join(ssubdir, x) for x in files]) - - for f in files: - r = os.path.join(sp['rpm_dir'], f) - rpm_files.append(r) - if f[-3:] == ".py": - rpm_files.append(r + 'c') - for f in sp.get('extra_rpm_files', []): - r = os.path.join(sp['rpm_dir'], f) - rpm_files.append(r) files = [os.path.join(isubdir, x) for x in files] dst_files.extend(files) for k, f in sp['filemap'].items(): @@ -814,7 +551,7 @@ for p in [ scons ]: # # Now run everything in src_file through the sed command we - # concocted to expand SConstruct, 3.0.0, etc. + # concocted to expand SConstruct, 3.0.5, etc. # for b in src_files: s = p['filemap'].get(b, b) @@ -855,12 +592,6 @@ for p in [ scons ]: distutils_formats = [] distutils_targets = [] - - if not skip_win_packages: - win64_exe = os.path.join(build, 'dist', "%s.win-amd64.exe" % pkg_version) - win32_exe = os.path.join(build, 'dist', "%s.win32.exe" % pkg_version) - distutils_targets.extend([ win32_exe , win64_exe ]) - dist_distutils_targets = [] for target in distutils_targets: @@ -908,7 +639,7 @@ for p in [ scons ]: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, TestSCons.py, + # and under testing/framework (for the testing modules TestCmd.py, TestSCons.py, # etc.). This makes sure that our tests pass with what # we really packaged, not because of something hanging around in # the development directory. @@ -978,7 +709,7 @@ for p in [ scons ]: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, TestSCons.py, + # and under testing/framework (for the testing modules TestCmd.py, TestSCons.py, # etc.). This makes sure that our tests pass with what # we really packaged, not because of something hanging around in # the development directory. @@ -995,91 +726,6 @@ for p in [ scons ]: os.path.join(unpack_zip_dir, pkg_version, 'setup.py'), ]) - if not rpmbuild: - msg = "@echo \"Warning: Can not build 'rpm': no rpmbuild utility found\"" - AlwaysBuild(Alias('rpm', [], msg)) - else: - topdir = os.path.join(build, 'build', - 'bdist.' + platform, 'rpm') - - buildroot = os.path.join(build_dir, 'rpm-buildroot') - - BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version) - RPMSdir = os.path.join(topdir, 'RPMS', 'noarch') - SOURCESdir = os.path.join(topdir, 'SOURCES') - SPECSdir = os.path.join(topdir, 'SPECS') - SRPMSdir = os.path.join(topdir, 'SRPMS') - - specfile_in = os.path.join('rpm', "%s.spec.in" % pkg) - specfile = os.path.join(SPECSdir, "%s-1.spec" % pkg_version) - sourcefile = os.path.join(SOURCESdir, "%s.tar.gz" % pkg_version); - noarch_rpm = os.path.join(RPMSdir, "%s-1.noarch.rpm" % pkg_version) - src_rpm = os.path.join(SRPMSdir, "%s-1.src.rpm" % pkg_version) - - def spec_function(target, source, env): - """Generate the RPM .spec file from the template file. - - This fills in the %files portion of the .spec file with a - list generated from our MANIFEST(s), so we don't have to - maintain multiple lists. - """ - c = open(str(source[0]), 'r').read() - c = c.replace('__VERSION' + '__', env['VERSION']) - c = c.replace('__RPM_FILES' + '__', env['RPM_FILES']) - open(str(target[0]), 'w').write(c) - - rpm_files.sort() - rpm_files_str = "\n".join(rpm_files) + "\n" - rpm_spec_env = env.Clone(RPM_FILES = rpm_files_str) - rpm_spec_action = Action(spec_function, varlist=['RPM_FILES']) - rpm_spec_env.Command(specfile, specfile_in, rpm_spec_action) - - env.InstallAs(sourcefile, tar_gz) - Local(sourcefile) - - targets = [ noarch_rpm, src_rpm ] - cmd = "$RPMBUILD --define '_topdir $(%s$)' --buildroot %s -ba $SOURCES" % (topdir, buildroot) - if not os.path.isdir(BUILDdir): - cmd = ("$( mkdir -p %s; $)" % BUILDdir) + cmd - t = env.Command(targets, specfile, cmd) - env.Depends(t, sourcefile) - - dist_noarch_rpm = env.Install('$DISTDIR', noarch_rpm) - dist_src_rpm = env.Install('$DISTDIR', src_rpm) - Local(dist_noarch_rpm, dist_src_rpm) - AddPostAction(dist_noarch_rpm, Chmod(dist_noarch_rpm, 0o644)) - AddPostAction(dist_src_rpm, Chmod(dist_src_rpm, 0o644)) - - dfiles = [os.path.join(test_rpm_dir, 'usr', x) for x in dst_files] - env.Command(dfiles, - dist_noarch_rpm, - "$RPM2CPIO $SOURCES | (cd $TEST_RPM_DIR && cpio -id)") - - if dh_builddeb and fakeroot: - # Our Debian packaging builds directly into build/dist, - # so we don't need to Install() the .debs. - # The built deb is called just x.y.z, not x.y.z.final.0 so strip those off: - deb_version = version #'.'.join(version.split('.')[0:3]) - deb = os.path.join(build_dir, 'dist', "%s_%s_all.deb" % (pkg, deb_version)) - print("Building deb into %s (version=%s)"%(deb, deb_version)) - for d in p['debian_deps']: - b = env.SCons_revision(os.path.join(build, d), d) - env.Depends(deb, b) - Local(b) - env.Command(deb, build_src_files, [ - "cd %s && fakeroot make -f debian/rules PYTHON=$PYTHON BUILDDEB_OPTIONS=--destdir=../../build/dist binary" % build, - ]) - - old = os.path.join('lib', 'scons', '') - new = os.path.join('lib', 'python' + python_ver, 'site-packages', '') - def xxx(s, old=old, new=new): - if s[:len(old)] == old: - s = new + s[len(old):] - return os.path.join('usr', s) - dfiles = [os.path.join(test_deb_dir, xxx(x)) for x in dst_files] - env.Command(dfiles, - deb, - "dpkg --fsys-tarfile $SOURCES | (cd $TEST_DEB_DIR && tar -xf -)") # @@ -1101,11 +747,6 @@ for p in [ scons ]: commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY sdist --formats=%s" % \ ','.join(distutils_formats)) - if not skip_win_packages: - commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst --plat-name=win32 --user-access-control auto") - - commands.append("$PYTHON $PYTHONFLAGS $SETUP_PY bdist_wininst --plat-name=win-amd64 --user-access-control auto") - env.Command(distutils_targets, build_src_files, commands) # @@ -1189,31 +830,17 @@ for p in [ scons ]: # Export('build_dir', 'env') -SConscript('QMTest/SConscript') +SConscript('testing/framework/SConscript') # # # +sp = env.Install(build_dir, 'runtest.py') +Local(sp) files = [ 'runtest.py', ] -def copy(target, source, env): - t = str(target[0]) - s = str(source[0]) - open(t, 'wb').write(open(s, 'rb').read()) - -for file in files: - # Guarantee that real copies of these files always exist in - # build/. If there's a symlink there, then this is an Aegis - # build and we blow them away now so that they'll get "built" later. - p = os.path.join(build_dir, file) - if os.path.islink(p): - os.unlink(p) - if not os.path.isabs(p): - p = '#' + p - sp = env.Command(p, file, copy) - Local(sp) # # Documentation. @@ -1223,22 +850,22 @@ Export('build_dir', 'env', 'whereis', 'revaction') SConscript('doc/SConscript') # -# If we're running in a Subversion working directory, pack up a complete +# If we're running in a Git working directory, pack up a complete # source archive from the project files and files in the change. # -sfiles = None -if hg_status_lines: - slines = [l for l in hg_status_lines if l[0] in 'ACM'] - sfiles = [l.split()[-1] for l in slines] + +sfiles = [l.split()[-1] for l in git_status_lines] +if git_status_lines: + # slines = [l for l in git_status_lines if 'modified:' in l] + # sfiles = [l.split()[-1] for l in slines] + pass else: - print("Not building in a Mercurial tree; skipping building src package.") + print("Not building in a Git tree; skipping building src package.") if sfiles: remove_patterns = [ - '.hgt/*', - '.svnt/*', - '*.aeignore', + '*.gitignore', '*.hgignore', 'www/*', ] @@ -1305,7 +932,7 @@ if sfiles: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, + # and under testing/framework (for the testing modules TestCmd.py, # TestSCons.py, etc.). This makes sure that our tests pass with # what we really packaged, not because of something hanging around # in the development directory. @@ -1359,7 +986,7 @@ if sfiles: # Run setup.py in the unpacked subdirectory to "install" everything # into our build/test subdirectory. The runtest.py script will set # PYTHONPATH so that the tests only look under build/test-{package}, - # and under QMTest (for the testing modules TestCmd.py, + # and under testing/framework (for the testing modules TestCmd.py, # TestSCons.py, etc.). This makes sure that our tests pass with # what we really packaged, not because of something hanging # around in the development directory. @@ -1397,7 +1024,7 @@ if sfiles: for pf, help_text in packaging_flavors: Alias(pf, [ os.path.join(build_dir, 'test-'+pf), - os.path.join(build_dir, 'QMTest'), + os.path.join(build_dir, 'testing/framework'), os.path.join(build_dir, 'runtest.py'), ]) diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py index cfb4e54..a6654c2 100644 --- a/bin/SConsDoc.py +++ b/bin/SConsDoc.py @@ -168,7 +168,7 @@ xsi = "http://www.w3.org/2001/XMLSchema-instance" # Header comment with copyright copyright_comment = """ -Copyright (c) 2001 - 2017 The SCons Foundation +Copyright (c) 2001 - 2019 The SCons Foundation This file is processed by the bin/SConsDoc.py module. See its __doc__ string for a discussion of the format. @@ -461,6 +461,8 @@ else: return self.decorateWithHeader(t) def validateXml(self, fpath, xmlschema_context): + retval = True + # Create validation context validation_context = xmlschema_context.schemaNewValidCtxt() # Set error/warning handlers @@ -470,17 +472,19 @@ else: doc = libxml2.readFile(fpath, None, libxml2.XML_PARSE_NOENT) doc.xincludeProcessFlags(libxml2.XML_PARSE_NOENT) err = validation_context.schemaValidateDoc(doc) - # Cleanup - doc.freeDoc() - del validation_context if err or eh.errors: for e in eh.errors: print(e.rstrip("\n")) + # import pdb; pdb.set_trace() print("%s fails to validate" % fpath) - return False + retval = False - return True + # Cleanup + doc.freeDoc() + del validation_context + + return retval def findAll(self, root, tag, ns=None, xpath_context=None, nsmap=None): if hasattr(root, 'xpathEval') and xpath_context: diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index 50c4c1a..7491c58 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -39,7 +39,7 @@ # env.Program('foo') # # -# int main() { printf("foo.c\n"); } +# int main(void) { printf("foo.c\n"); } # # # @@ -305,6 +305,11 @@ def createAllExampleOutputs(dpath): examples = readAllExampleInfos(dpath) total = len(examples) idx = 0 + + if len(sys.argv) > 1: + examples_to_run = sys.argv[1:] + examples = { k:v for k,v in examples.items() if k in examples_to_run } + for key, value in examples.items(): # Process all scons_output tags print("%.2f%s (%d/%d) %s" % (float(idx + 1) * 100.0 / float(total), @@ -411,8 +416,8 @@ def exampleNamesAreUnique(dpath): # # ############################################################### -sys.path.append(os.path.join(os.getcwd(), 'QMTest')) -sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest')) +sys.path.append(os.path.join(os.getcwd(), 'testing/framework')) +sys.path.append(os.path.join(os.getcwd(), 'build', 'testing/framework')) scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py') if not os.path.exists(scons_py): @@ -657,21 +662,27 @@ SConscript('SConstruct') """ # "Commands" that we will execute in our examples. -def command_scons(args, c, test, dict): +def command_scons(args, command, test, values): + """ + Fake scons command + """ save_vals = {} delete_keys = [] try: - ce = c.environment + ce = command.environment except AttributeError: pass else: - for arg in c.environment.split(): + for arg in command.environment.split(): key, val = arg.split('=') try: save_vals[key] = os.environ[key] except KeyError: delete_keys.append(key) os.environ[key] = val + + test.write(test.workpath('WORK/SConstruct_created'), Stdin % values) + test.run(interpreter=sys.executable, program=scons_py, # We use ToolSurrogates to capture win32 output by "building" @@ -681,7 +692,7 @@ def command_scons(args, c, test, dict): # Visual C installed. arguments='--warn=no-visual-c-missing -f - ' + ' '.join(args), chdir=test.workpath('WORK'), - stdin=Stdin % dict) + stdin=Stdin % values) os.environ.update(save_vals) for key in delete_keys: del(os.environ[key]) @@ -698,7 +709,7 @@ def command_scons(args, c, test, dict): # sys.stderr.write(err) return lines -def command_touch(args, c, test, dict): +def command_touch(args, command, test, values): if args[0] == '-t': t = int(time.mktime(time.strptime(args[1], '%Y%m%d%H%M'))) times = (t, t) @@ -714,7 +725,7 @@ def command_touch(args, c, test, dict): os.utime(file, times) return [] -def command_edit(args, c, test, dict): +def command_edit(args, c, test, values): if c.edit is None: add_string = 'void edit(void) { ; }\n' else: @@ -728,7 +739,7 @@ def command_edit(args, c, test, dict): open(file, 'wb').write(contents + add_string) return [] -def command_ls(args, c, test, dict): +def command_ls(args, c, test, values): def ls(a): try: return [' '.join(sorted([x for x in os.listdir(a) if x[0] != '.']))] @@ -743,7 +754,7 @@ def command_ls(args, c, test, dict): else: return ls(test.workpath('WORK')) -def command_sleep(args, c, test, dict): +def command_sleep(args, c, test, values): time.sleep(int(args[0])) CommandDict = { @@ -754,17 +765,19 @@ CommandDict = { 'sleep' : command_sleep, } -def ExecuteCommand(args, c, t, dict): +def ExecuteCommand(args, c, t, values): try: func = CommandDict[args[0]] except KeyError: - func = lambda args, c, t, dict: [] - return func(args[1:], c, t, dict) + func = lambda args, c, t, values: [] + return func(args[1:], c, t, values) def create_scons_output(e): - # The real raison d'etre for this script, this is where we - # actually execute SCons to fetch the output. + """ + The real raison d'etre for this script, this is where we + actually execute SCons to fetch the output. + """ # Loop over all outputs for the example for o in e.outputs: @@ -837,37 +850,37 @@ def create_scons_output(e): sroot = stf.newEtreeNode("screen", True) curchild = None content = "" - for c in o.commands: + for command in o.commands: content += Prompt[o.os] if curchild is not None: - if not c.output: + if not command.output: # Append content as tail curchild.tail = content content = "\n" # Add new child for userinput tag curchild = stf.newEtreeNode("userinput") - d = c.cmd.replace('__ROOT__', '') + d = command.cmd.replace('__ROOT__', '') curchild.text = d sroot.append(curchild) else: - content += c.output + '\n' + content += command.output + '\n' else: - if not c.output: + if not command.output: # Add first text to root sroot.text = content content = "\n" # Add new child for userinput tag curchild = stf.newEtreeNode("userinput") - d = c.cmd.replace('__ROOT__', '') + d = command.cmd.replace('__ROOT__', '') curchild.text = d sroot.append(curchild) else: - content += c.output + '\n' + content += command.output + '\n' # Execute command and capture its output - cmd_work = c.cmd.replace('__ROOT__', t.workpath('ROOT')) + cmd_work = command.cmd.replace('__ROOT__', t.workpath('ROOT')) args = cmd_work.split() - lines = ExecuteCommand(args, c, t, {'osname':o.os, 'tools':o.tools}) - if not c.output and lines: + lines = ExecuteCommand(args, command, t, {'osname':o.os, 'tools':o.tools}) + if not command.output and lines: ncontent = '\n'.join(lines) ncontent = address_re.sub(r' at 0x700000>', ncontent) ncontent = engine_re.sub(r' File "bootstrap/src/engine/SCons/', ncontent) diff --git a/bin/import-test.py b/bin/import-test.py index 168208f..4fc4f06 100644 --- a/bin/import-test.py +++ b/bin/import-test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # tree2test.py - turn a directory tree into TestSCons code # @@ -25,7 +25,7 @@ # """ triple-quotes will need to have their contents edited by hand. # -__revision__ = "bin/import-test.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "bin/import-test.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import os.path import sys diff --git a/bin/linecount.py b/bin/linecount.py index 75723d0..57a64e2 100644 --- a/bin/linecount.py +++ b/bin/linecount.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Count statistics about SCons test and source files. This must be run # against a fully-populated tree (for example, one that's been freshly @@ -23,7 +23,7 @@ # interesting one for most purposes. from __future__ import division, print_function -__revision__ = "bin/linecount.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "bin/linecount.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import os.path diff --git a/bin/restore.sh b/bin/restore.sh index 49c95bc..5fcdb08 100644 --- a/bin/restore.sh +++ b/bin/restore.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh # -# Simple hack script to restore __revision__, __COPYRIGHT_, 3.0.0 +# Simple hack script to restore __revision__, __COPYRIGHT_, 3.0.5 # and other similar variables to what gets checked in to source. This # comes in handy when people send in diffs based on the released source. # @@ -22,9 +22,9 @@ header() { for i in `find $DIRS -name '*.py'`; do header $i ed $i <&2 - echo "${USAGE}" >&2 - exit 1 - ;; - esac -done - -shift `expr ${OPTIND} - 1` - -if test "X$1" = "X"; then - echo "${USAGE}" >&2 - exit 1 -fi - -if test "X${AEGIS_PROJECT}" = "X"; then - echo "$PROG: No AEGIS_PROJECT set." >&2 - echo "${USAGE}" >&2 - exit 1 -fi - -if test "X$DO" = "X"; then - DO="alrstz" -fi - -cmd() -{ - $PRINT "$*" - $EXECUTE "$*" -} - -CHANGE=$1 - -if test "X${SANITY_CHECK}" = "Xyes"; then - SCM="cvs" - SCMROOT="/home/scons/CVSROOT/scons" - DELTA=`aegis -l -ter cd ${CHANGE} | sed -n 's/.*, Delta \([0-9]*\)\./\1/p'` - if test "x${DELTA}" = "x"; then - echo "${PROG}: Could not find delta for change ${CHANGE}." >&2 - echo "Has this finished integrating? Change ${CHANGE} not distributed." >&2 - exit 1 - fi - PREV_DELTA=`expr ${DELTA} - 1` - COMMAND="scons-scmcheck -D ${PREV_DELTA} -d q -p ${AEGIS_PROJECT} -s ${SCM} ${SCMROOT}" - $PRINT "${COMMAND}" - OUTPUT=`${COMMAND}` - if test "X${OUTPUT}" != "X"; then - echo "${PROG}: ${SCMROOT} is not up to date:" >&2 - echo "${OUTPUT}" >& 2 - echo "Did you skip any changes? Change ${CHANGE} not distributed." >&2 - exit 1 - fi -fi - -if test X$EXECUTE != "X:" -a "X$SSH_AGENT_PID" = "X"; then - eval `ssh-agent` - ssh-add - trap 'eval `ssh-agent -k`; exit' 0 1 2 3 15 -fi - -cd - -BASELINE=`aesub -p ${AEGIS_PROJECT} -c ${CHANGE} '${Project trunk_name}'` - -TMPBLAE="/tmp/${BASELINE}.ae" -TMPCAE="/tmp/${AEGIS_PROJECT}.C${CHANGE}.ae" - -# Original values for SourceForge. -#SFLOGIN="stevenknight" -#SFHOST="scons.sourceforge.net" -#SFDEST="/home/groups/s/sc/scons/htdocs" - -SCONSLOGIN="scons" -SCONSHOST="manam.pair.com" -#SCONSDEST="public_html/production" -SCONSDEST="public_ftp" - -# -# Copy the baseline .ae to the constant location on SourceForge. -# -case "${DO}" in -*a* ) - cmd "aedist -s -bl -p ${AEGIS_PROJECT} > ${TMPBLAE}" - cmd "scp ${TMPBLAE} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/${BASELINE}.ae" - cmd "rm ${TMPBLAE}" - ;; -esac - -# -# Copy the latest .tar.gz and .zip files to the constant location on -# SourceForge. -# -case "${DO}" in -*z* ) - BUILD_DIST=`aegis -p ${AEGIS_PROJECT} -cd -bl`/build/dist - SCONS_SRC_TAR_GZ=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.tar.gz - SCONS_SRC_ZIP=`echo ${AEGIS_PROJECT} | sed 's/scons./scons-src-/'`*.zip - cmd "scp ${BUILD_DIST}/${SCONS_SRC_TAR_GZ} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.tar.gz" - cmd "scp ${BUILD_DIST}/${SCONS_SRC_ZIP} ${SCONSLOGIN}@${SCONSHOST}:${SCONSDEST}/scons-src-latest.zip" -esac - -# -# Sync Aegis tree with SourceForge. -# -# Cribbed and modified from Peter Miller's same-named script in -# /home/groups/a/ae/aegis/aegis at SourceForge. -# -# Guide to what this does with rsync: -# -# --rsh=ssh use ssh for the transfer -# -l copy symlinks as symlinks -# -p preserve permissions -# -r recursive -# -t preserve times -# -z compress data -# --stats file transfer statistics -# --exclude exclude files matching the pattern -# --delete delete files that don't exist locally -# --delete-excluded delete files that match the --exclude patterns -# --progress show progress during the transfer -# -v verbose -# -# We no longer use the --stats option. -# -case "${DO}" in -*r* ) - LOCAL=/home/scons/scons - REMOTE=/home/groups/s/sc/scons/scons - cmd "/usr/bin/rsync --rsh='ssh -l stevenknight' \ - -l -p -r -t -z \ - --exclude build \ - --exclude '*,D' \ - --exclude '*.pyc' \ - --exclude aegis.log \ - --exclude '.sconsign*' \ - --delete --delete-excluded \ - --progress -v \ - ${LOCAL}/. scons.sourceforge.net:${REMOTE}/." - ;; -esac - -# -# Sync the CVS tree with the local repository. -# -case "${DO}" in -*l* ) - ( - export CVSROOT=/home/scons/CVSROOT/scons - #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/baldmt.com/scons" - cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}" - ) - ;; -esac - -# -# Sync the Subversion tree with Tigris.org. -# -case "${DO}" in -*t* ) - ( - SVN=http://scons.tigris.org/svn/scons - case ${AEGIS_PROJECT} in - scons.0.96 ) - SVN_URL=${SVN}/branches/core - ;; - scons.0.96.513 ) - SVN_URL=${SVN}/branches/sigrefactor - ;; - * ) - echo "$PROG: Don't know SVN branch for '${AEGIS_PROJECT}'" >&2 - exit 1 - ;; - esac - SVN_CO_FLAGS="--username stevenknight" - #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/tigris.org/scons" - cmd "ae-svn-ci ${AEGIS_PROJECT} ${CHANGE} ${SVN_URL} ${SVN_CO_FLAGS}" - ) - ;; -esac - -# -# Sync the CVS tree with SourceForge. -# -case "${DO}" in -*s* ) - ( - export CVS_RSH=ssh - export CVSROOT=:ext:stevenknight@scons.cvs.sourceforge.net:/cvsroot/scons - #cmd "ae2cvs -X -aegis -p ${AEGIS_PROJECT} -c ${CHANGE} -u $HOME/SCons/sourceforge.net/scons" - cmd "ae-cvs-ci ${AEGIS_PROJECT} ${CHANGE}" - ) - ;; -esac - -# -# Send the change .ae to the scons-aedist mailing list -# -# The subject requires editing by hand... -# -#aedist -s -p ${AEGIS_PROJECT} ${CHANGE} > ${TMPCAE} -#aegis -l -p ${AEGIS_PROJECT} -c ${CHANGE} cd | -# pine -attach_and_delete ${TMPCAE} scons-aedist@lists.sourceforge.net diff --git a/bin/scons_dev_master.py b/bin/scons_dev_master.py index 3d67cb5..4b1160f 100644 --- a/bin/scons_dev_master.py +++ b/bin/scons_dev_master.py @@ -11,11 +11,12 @@ import sys from Command import CommandRunner, Usage INITIAL_PACKAGES = [ - 'mercurial', + 'git', ] INSTALL_PACKAGES = [ 'wget', + 'xz-utils', ] PYTHON_PACKAGES = [ @@ -41,6 +42,7 @@ BUILDING_PACKAGES = [ 'python-epydoc', 'rpm', 'tar', + 'lynx' # additional packages that Bill Deegan's web page suggests #'docbook-to-man', @@ -60,8 +62,7 @@ DOCUMENTATION_PACKAGES = [ 'gcc-doc', 'pkg-config', 'python-doc', - 'sun-java5-doc', - 'sun-java6-doc', + 'openjdk-8-doc', 'swig-doc', 'texlive-doc', ] @@ -73,18 +74,20 @@ TESTING_PACKAGES = [ 'flex', 'g++', 'gcc', - 'gcj', + # not on ubuntu 18.04 + # 'gcj', + # 'hg', 'ghostscript', -# 'libgcj7-dev', 'm4', 'openssh-client', 'openssh-server', 'python-profiler', 'python-all-dev', + 'python3-all-dev', + 'pypy-dev', 'rcs', 'rpm', -# 'sun-java5-jdk', - 'sun-java6-jdk', + 'openjdk-8-jdk', 'swig', 'texlive-base-bin', 'texlive-extra-utils', @@ -131,7 +134,7 @@ Usage: scons_dev_master.py [-hnqy] [--password PASSWORD] [--username USER] buildbot Install packages for running BuildBot """ - scons_url = 'https://bdbaddog@bitbucket.org/scons/scons' + scons_url = 'https://github.com/SCons/scons.git' sudo = 'sudo' password = '""' username = 'guest' @@ -180,13 +183,15 @@ Usage: scons_dev_master.py [-hnqy] [--password PASSWORD] [--username USER] cmd.run('%(sudo)s apt-get %(yesflag)s upgrade') elif arg == 'checkout': cmd.run('%(sudo)s apt-get %(yesflag)s install %(initial_packages)s') - cmd.run('hg clone" %(scons_url)s') + cmd.run('git clone" %(scons_url)s') elif arg == 'building': cmd.run('%(sudo)s apt-get %(yesflag)s install %(building_packages)s') elif arg == 'docs': cmd.run('%(sudo)s apt-get %(yesflag)s install %(doc_packages)s') elif arg == 'testing': cmd.run('%(sudo)s apt-get %(yesflag)s install %(testing_packages)s') + #TODO: maybe copy ipkg-build from openwrt git + #cmd.run('%(sudo)s wget https://raw.githubusercontent.com/openwrt/openwrt/master/scripts/ipkg-build SOMEWHERE') elif arg == 'buildbot': cmd.run('%(sudo)s apt-get %(yesflag)s install %(buildbot_packages)s') elif arg == 'python-versions': diff --git a/bin/time-scons.py b/bin/time-scons.py index b7d8ef1..c5cd0cc 100644 --- a/bin/time-scons.py +++ b/bin/time-scons.py @@ -43,7 +43,7 @@ TimeSCons_revision = 4569 # The pieces of the TimeSCons infrastructure that are necessary to # produce consistent timings, even when the rest of the tree is from # an earlier revision that doesn't have these pieces. -TimeSCons_pieces = ['QMTest', 'timings', 'runtest.py'] +TimeSCons_pieces = ['testing/framework', 'timings', 'runtest.py'] class CommandRunner(object): diff --git a/bin/update-release-info.py b/bin/update-release-info.py index a4096d1..de3d164 100644 --- a/bin/update-release-info.py +++ b/bin/update-release-info.py @@ -20,10 +20,10 @@ in various files: - The RELEASE header line in src/CHANGES.txt and src/Announce.txt. - The version string at the top of src/RELEASE.txt. - The version string in the 'default_version' variable in SConstruct - and QMTest/TestSCons.py. - - The copyright years in SConstruct and QMTest/TestSCons.py. + and testing/framework/TestSCons.py. + - The copyright years in SConstruct and testing/framework/TestSCons.py. - The month and year (used for documentation) in SConstruct. - - The unsupported and deprecated Python floors in QMTest/TestSCons.py + - The unsupported and deprecated Python floors in testing/framework/TestSCons.py and src/engine/SCons/Script/Main.py - The version string in the filenames in README. @@ -36,7 +36,7 @@ In 'post' mode, files are prepared for the next release cycle: src/Announce.txt. """ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 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,7 +58,7 @@ In 'post' mode, files are prepared for the next release cycle: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -__revision__ = "bin/update-release-info.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "bin/update-release-info.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import os import sys @@ -81,7 +81,10 @@ else: # Get configuration information config = dict() -exec(open('ReleaseConfig').read(), globals(), config) +with open('ReleaseConfig') as f: + releaseconfig = f.read() +exec(releaseconfig, globals(), config) + try: version_tuple = config['version_tuple'] @@ -152,7 +155,8 @@ class UpdateFile(object): ''' if orig is None: orig = file try: - self.content = open(orig, 'r').read() + with open(orig, 'r') as f: + self.content = f.read() except IOError: # Couldn't open file; don't try to write anything in __del__ self.file = None @@ -180,8 +184,8 @@ class UpdateFile(object): # Determine the pattern to match a version - _rel_types = '(alpha|beta|candidate|final)' - match_pat = '\d+\.\d+\.\d+\.' + _rel_types + '\.(\d+|yyyymmdd)' + _rel_types = r'(alpha|beta|candidate|final)' + match_pat = r'\d+\.\d+\.\d+\.' + _rel_types + r'\.(\d+|yyyymmdd)' match_rel = re.compile(match_pat) def replace_version(self, replacement = version_string, count = 1): @@ -198,14 +202,14 @@ class UpdateFile(object): new_date = 'NEW DATE WILL BE INSERTED HERE' else: min = (time.daylight and time.altzone or time.timezone)//60 - hr = min//60 - min = -(min%60 + hr*100) + hr = min // 60 + min = -(min % 60 + hr * 100) new_date = (time.strftime('%a, %d %b %Y %X', release_date + (0,0,0)) + ' %+.4d' % min) - _days = '(Sun|Mon|Tue|Wed|Thu|Fri|Sat)' - _months = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oce|Nov|Dec)' - match_date = _days+', \d\d '+_months+' \d\d\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d' + _days = r'(Sun|Mon|Tue|Wed|Thu|Fri|Sat)' + _months = r'(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oce|Nov|Dec)' + match_date = r''.join([_days, r', \d\d ', _months, r' \d\d\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d']) match_date = re.compile(match_date) def replace_date(self, replacement = new_date, count = 1): @@ -220,7 +224,8 @@ class UpdateFile(object): ''' if self.file is not None and self.content != self.orig: print('Updating ' + self.file + '...') - open(self.file, 'w').write(self.content) + with open(self.file, 'w') as f: + f.write(self.content) if mode == 'post': # Set up for the next release series. @@ -309,15 +314,15 @@ t.replace_assign('default_version', repr(version_string)) t = UpdateFile('README.rst') if DEBUG: t.file = '/tmp/README.rst' -t.sub('-' + t.match_pat + '\.', '-' + version_string + '.', count = 0) +t.sub('-' + t.match_pat + r'\.', '-' + version_string + '.', count = 0) for suf in ['tar', 'win32', 'zip', 'rpm', 'exe', 'deb']: - t.sub('-(\d+\.\d+\.\d+)\.%s' % suf, + t.sub(r'-(\d+\.\d+\.\d+)\.%s' % suf, '-%s.%s' % (version_string, suf), count = 0) -# Update QMTest/TestSCons.py +# Update testing/framework/TestSCons.py -t = UpdateFile(os.path.join('QMTest', 'TestSCons.py')) +t = UpdateFile(os.path.join('testing','framework', 'TestSCons.py')) if DEBUG: t.file = '/tmp/TestSCons.py' t.replace_assign('copyright_years', repr(copyright_years)) t.replace_assign('default_version', repr(version_string)) diff --git a/bin/upload-release-files.sh b/bin/upload-release-files.sh index c853bda..9a09206 100755 --- a/bin/upload-release-files.sh +++ b/bin/upload-release-files.sh @@ -19,17 +19,10 @@ SF_TOPDIR='/home/frs/project/scons' cd build/dist cp -f ../../src/CHANGES.txt ../../src/RELEASE.txt ../../src/Announce.txt ../../src/README.txt . -cp scons-$VERSION.win32.exe scons-$VERSION-setup.exe -cp scons-$VERSION.win-amd64.exe scons-$VERSION-amd64-setup.exe - set -x # Upload main scons release files: $RSYNC $RSYNCOPTS \ - scons-$VERSION-1.noarch.rpm \ - scons-$VERSION-1.src.rpm \ - scons-$VERSION-setup.exe \ - scons-$VERSION-amd64-setup.exe \ scons-$VERSION.tar.gz \ scons-$VERSION.zip \ Announce.txt CHANGES.txt RELEASE.txt \ @@ -42,12 +35,12 @@ $RSYNC $RSYNCOPTS \ Announce.txt CHANGES.txt RELEASE.txt \ $SF_USER@$SF_MACHINE:$SF_TOPDIR/scons-local/$VERSION/ -# Source packages: +Source packages: $RSYNC $RSYNCOPTS \ - scons-src-$VERSION.tar.gz \ - scons-src-$VERSION.zip \ - Announce.txt CHANGES.txt RELEASE.txt \ - $SF_USER@$SF_MACHINE:$SF_TOPDIR/scons-src/$VERSION/ + scons-src-$VERSION.tar.gz \ + scons-src-$VERSION.zip \ + Announce.txt CHANGES.txt RELEASE.txt \ + $SF_USER@$SF_MACHINE:$SF_TOPDIR/scons-src/$VERSION/ # Readme $RSYNC $RSYNCOPTS \ @@ -80,7 +73,7 @@ ssh scons@scons.org " cd .. rm latest; ln -s $VERSION latest rm production; ln -s $VERSION production - for f in HTML PDF EPUB PS TEXT; do rm \$f; ln -s $VERSION/\$f \$f; done + for f in HTML PDF EPUB PS TEXT; do rm -f \$f; ln -s $VERSION/\$f \$f; done " echo '*****' echo '***** Now manually update index.php, includes/versions.php and news-raw.xhtml on scons.org.' diff --git a/bin/xmlagenda.py b/bin/xmlagenda.py index fcfe53e..7091ee5 100755 --- a/bin/xmlagenda.py +++ b/bin/xmlagenda.py @@ -1,7 +1,9 @@ #!/usr/bin/env python -# Query the scons.tigris.org database for the issues of interest. -# The typical triage query is found on http://www.scons.org/wiki/BugParty +# Query gihub issue tracker for the issues of interest. +# The typical triage query is found on +# https://github.com/scons/scons/wiki/BugParty +# FIXME: this needs reworking for github, and wiki needs updating # Download the issues from Issuezilla as XML; this creates a file # named 'issues.xml'. Run this script in the dir containing diff --git a/bootstrap.py b/bootstrap.py index 58fde15..1629636 100755 --- a/bootstrap.py +++ b/bootstrap.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 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,6 +21,7 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +from __future__ import print_function import os import os.path @@ -74,15 +75,24 @@ the following SCons options: "eaten" by the bootstrap.py script. """ -def parseManifestLines(basedir, lines): - """ Scans the single lines of a MANIFEST file, - and returns the list of source files. - Has basic support for recursive globs '**', - filename wildcards of the form '*.xml' and - comment lines, starting with a '#'. +def parseManifestLines(basedir, manifest): + """ + Scans a MANIFEST file, and returns the list of source files. + + Has basic support for recursive globs '**', + filename wildcards of the form '*.xml' and + comment lines, starting with a '#'. + + :param basedir: base path to find files in. Note this does not + run in an SCons context so path must not need + further processing (e.g. no '#' signs) + :param manifest: path to manifest file + :returns: list of files """ sources = [] basewd = os.path.abspath(basedir) + with open(manifest) as m: + lines = m.readlines() for l in lines: if l.startswith('#'): # Skip comments @@ -107,38 +117,38 @@ def parseManifestLines(basedir, lines): def main(): script_dir = os.path.abspath(os.path.dirname(__file__)) - + bootstrap_dir = os.path.join(script_dir, 'bootstrap') - + pass_through_args = [] update_only = None - + requires_an_argument = 'bootstrap.py: %s requires an argument\n' - + search = [script_dir] - - def find(file, search=search): - for dir in search: - f = os.path.join(dir, file) - if os.path.exists(f): - return os.path.normpath(f) - sys.stderr.write("could not find `%s' in search path:\n" % file) + + def find(filename, search=search): + for dir_name in search: + filepath = os.path.join(dir_name, filename) + if os.path.exists(filepath): + return os.path.normpath(filepath) + sys.stderr.write("could not find `%s' in search path:\n" % filename) sys.stderr.write("\t" + "\n\t".join(search) + "\n") sys.exit(2) - + def must_copy(dst, src): if not os.path.exists(dst): return 1 return not filecmp.cmp(dst,src) - + # Note: We don't use the getopt module to process the command-line # arguments because we'd have to teach it about all of the SCons options. - + command_line_args = sys.argv[1:] - + while command_line_args: arg = command_line_args.pop(0) - + if arg == '--bootstrap_dir': try: bootstrap_dir = command_line_args.pop(0) @@ -147,11 +157,9 @@ def main(): sys.exit(1) elif arg[:16] == '--bootstrap_dir=': bootstrap_dir = arg[16:] - elif arg == '--bootstrap_force': def must_copy(dst, src): return 1 - elif arg == '--bootstrap_src': try: search.insert(0, command_line_args.pop(0)) @@ -160,10 +168,8 @@ def main(): sys.exit(1) elif arg[:16] == '--bootstrap_src=': search.insert(0, arg[16:]) - elif arg == '--bootstrap_update': update_only = 1 - elif arg in ('-C', '--directory'): try: dir = command_line_args.pop(0) @@ -176,51 +182,45 @@ def main(): os.chdir(arg[2:]) elif arg[:12] == '--directory=': os.chdir(arg[12:]) - else: pass_through_args.append(arg) - - + scons_py = os.path.join('src', 'script', 'scons.py') src_engine = os.path.join('src', 'engine') MANIFEST_in = find(os.path.join(src_engine, 'MANIFEST.in')) - MANIFEST_xml_in = find(os.path.join(src_engine, 'MANIFEST-xml.in')) manifest_files = [os.path.join(src_engine, x) - for x in parseManifestLines(os.path.join(script_dir, src_engine), - open(MANIFEST_in).readlines())] - - manifest_xml_files = [os.path.join(src_engine, x) - for x in parseManifestLines(os.path.join(script_dir, src_engine), - open(MANIFEST_xml_in).readlines())] - files = [ scons_py ] + manifest_files + manifest_xml_files - - for file in files: - src = find(file) - dst = os.path.join(bootstrap_dir, file) + for x in parseManifestLines(os.path.join(script_dir, src_engine), + MANIFEST_in)] + + files = [scons_py] + manifest_files + + for filename in files: + src = find(filename) + dst = os.path.join(bootstrap_dir, filename) if must_copy(dst, src): dir = os.path.split(dst)[0] if not os.path.isdir(dir): os.makedirs(dir) - try: os.unlink(dst) - except: pass + try: + os.unlink(dst) + except Exception as e: + pass + + shutil.copyfile(src, dst) - shutil.copyfile(src,dst) - if update_only: sys.exit(0) - - args = [ - sys.executable, - os.path.join(bootstrap_dir, scons_py) - ] + pass_through_args - + + args = [sys.executable, os.path.join(bootstrap_dir, scons_py)] + pass_through_args + sys.stdout.write(" ".join(args) + '\n') sys.stdout.flush() - + os.environ['SCONS_LIB_DIR'] = os.path.join(bootstrap_dir, src_engine) - + sys.exit(subprocess.Popen(args, env=os.environ).wait()) + if __name__ == "__main__": main() diff --git a/doc/SConscript b/doc/SConscript index 46d4533..fd518f9 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -3,7 +3,7 @@ # # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -63,6 +63,7 @@ env = env.Clone() build = os.path.join(build_dir, 'doc') + epydoc_cli = whereis('epydoc') gs = whereis('gs') lynx = whereis('lynx') @@ -130,8 +131,9 @@ if skip_doc: if not os.path.isdir(scdir): os.makedirs(scdir) - import datetime - today = datetime.date.today().strftime("%m/%d/%Y") + import time + today = time.strftime("%Y-%m-%d", + time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))) version = env.subst('$VERSION') for m in man_page_list: man, _ = os.path.splitext(m) @@ -324,7 +326,7 @@ else: # get included by the document XML files in the subdirectories. # manifest = File('MANIFEST').rstr() - src_files = bootstrap.parseManifestLines('.', open(manifest).readlines()) + src_files = bootstrap.parseManifestLines('.', manifest) for s in src_files: if not s: continue @@ -349,7 +351,7 @@ else: if not os.path.exists(os.path.join(build, doc, 'titlepage')): env.Execute(Mkdir(os.path.join(build, doc, 'titlepage'))) manifest = File(os.path.join(doc, 'MANIFEST')).rstr() - src_files = bootstrap.parseManifestLines(doc, open(manifest).readlines()) + src_files = bootstrap.parseManifestLines(doc, manifest) for s in src_files: if not s: continue @@ -569,12 +571,15 @@ if not epydoc_cli and not epydoc: else: # XXX Should be in common with reading the same thing in # the SConstruct file. - e = os.path.join('#src', 'engine') - manifest_in = File(os.path.join(e, 'MANIFEST.in')).rstr() - sources = bootstrap.parseManifestLines(e, open(manifest_in).readlines()) - sources = [x for x in sources if x.find('Platform') == -1] + # bootstrap.py runs outside of SCons, so need to process the path + e = Dir(os.path.join('#src', 'engine')).rstr() + sources = bootstrap.parseManifestLines(e, os.path.join(e, 'MANIFEST.in')) + + # Omit some files: + # + # Don't omit Platform as we need Platform.virtualenv for the examples to be run + # sources = [x for x in sources if x.find('Platform') == -1] sources = [x for x in sources if x.find('Tool') == -1] - # XXX sources = [x for x in sources if x.find('Options') == -1] e = os.path.join(build, '..', 'scons', 'engine') @@ -586,7 +591,7 @@ else: tar_deps.append(htmldir) tar_list.append(htmldir) - if not epydoc_cli: + if sys.platform == 'darwin' or not epydoc_cli: print("doc: command line epydoc is not found, skipping PDF/PS/Tex output") else: # PDF and PostScript and TeX are built from the diff --git a/doc/design/SConstruct b/doc/design/SConstruct index 2bec66f..1fc1741 100644 --- a/doc/design/SConstruct +++ b/doc/design/SConstruct @@ -3,7 +3,7 @@ # # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/doc/design/acks.xml b/doc/design/acks.xml index f9969f4..1ed5eb1 100644 --- a/doc/design/acks.xml +++ b/doc/design/acks.xml @@ -12,7 +12,7 @@ + +The optional +must_exist +argument, if true, causes an exception to be raised if a requested +SConscript file is not found. The current default is false, +causing only a warning to be omitted, but this behavior is deprecated. +For scripts which truly intend to be optional, transition to +explicty supplying +must_exist=False to the call. + + Here are some composite examples: diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index f858aa4..be717e3 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -57,11 +57,11 @@ Sets construction variables for the IBM Visual Age linker. applelink - -Sets construction variables for the Apple linker -(similar to the GNU linker). - -Sets: &cv-link-FRAMEWORKPATHPREFIX;, &cv-link-LDMODULECOM;, &cv-link-LDMODULEFLAGS;, &cv-link-LDMODULEPREFIX;, &cv-link-LDMODULESUFFIX;, &cv-link-LINKCOM;, &cv-link-SHLINKCOM;, &cv-link-SHLINKFLAGS;, &cv-link-_FRAMEWORKPATH;, &cv-link-_FRAMEWORKS;.Uses: &cv-link-FRAMEWORKSFLAGS;. + + Sets construction variables for the Apple linker + (similar to the GNU linker). + + Sets: &cv-link-APPLELINK_COMPATIBILITY_VERSION;, &cv-link-APPLELINK_CURRENT_VERSION;, &cv-link-APPLELINK_NO_COMPATIBILITY_VERSION;, &cv-link-APPLELINK_NO_CURRENT_VERSION;, &cv-link-FRAMEWORKPATHPREFIX;, &cv-link-LDMODULECOM;, &cv-link-LDMODULEFLAGS;, &cv-link-LDMODULEPREFIX;, &cv-link-LDMODULESUFFIX;, &cv-link-LINKCOM;, &cv-link-SHLINKCOM;, &cv-link-SHLINKFLAGS;, &cv-link-_APPLELINK_COMPATIBILITY_VERSION;, &cv-link-_APPLELINK_CURRENT_VERSION;, &cv-link-_FRAMEWORKPATH;, &cv-link-_FRAMEWORKS;.Uses: &cv-link-FRAMEWORKSFLAGS;. ar @@ -586,10 +586,10 @@ Sets construction variables for the javac - -Sets construction variables for the javac compiler. - -Sets: &cv-link-JAVABOOTCLASSPATH;, &cv-link-JAVAC;, &cv-link-JAVACCOM;, &cv-link-JAVACFLAGS;, &cv-link-JAVACLASSPATH;, &cv-link-JAVACLASSSUFFIX;, &cv-link-JAVASOURCEPATH;, &cv-link-JAVASUFFIX;.Uses: &cv-link-JAVACCOMSTR;. + + Sets construction variables for the javac compiler. + + Sets: &cv-link-JAVABOOTCLASSPATH;, &cv-link-JAVAC;, &cv-link-JAVACCOM;, &cv-link-JAVACFLAGS;, &cv-link-JAVACLASSPATH;, &cv-link-JAVACLASSSUFFIX;, &cv-link-JAVAINCLUDES;, &cv-link-JAVASOURCEPATH;, &cv-link-JAVASUFFIX;.Uses: &cv-link-JAVACCOMSTR;. javah @@ -621,7 +621,7 @@ Sets construction variables for the D language compiler LDC2. Sets construction variables for the lex lexical analyser. -Sets: &cv-link-LEX;, &cv-link-LEXCOM;, &cv-link-LEXFLAGS;.Uses: &cv-link-LEXCOMSTR;. +Sets: &cv-link-LEX;, &cv-link-LEXCOM;, &cv-link-LEXFLAGS;, &cv-link-LEXUNISTD;.Uses: &cv-link-LEXCOMSTR;. link @@ -750,8 +750,9 @@ Sets construction variables for the Microsoft Visual C/C++ compiler. msvs - -Sets construction variables for Microsoft Visual Studio. Sets: &cv-link-MSVSBUILDCOM;, &cv-link-MSVSCLEANCOM;, &cv-link-MSVSENCODING;, &cv-link-MSVSPROJECTCOM;, &cv-link-MSVSREBUILDCOM;, &cv-link-MSVSSCONS;, &cv-link-MSVSSCONSCOM;, &cv-link-MSVSSCONSCRIPT;, &cv-link-MSVSSCONSFLAGS;, &cv-link-MSVSSOLUTIONCOM;. + + Sets construction variables for Microsoft Visual Studio. + Sets: &cv-link-MSVSBUILDCOM;, &cv-link-MSVSCLEANCOM;, &cv-link-MSVSENCODING;, &cv-link-MSVSPROJECTCOM;, &cv-link-MSVSREBUILDCOM;, &cv-link-MSVSSCONS;, &cv-link-MSVSSCONSCOM;, &cv-link-MSVSSCONSCRIPT;, &cv-link-MSVSSCONSFLAGS;, &cv-link-MSVSSOLUTIONCOM;. mwcc @@ -778,19 +779,19 @@ Sets construction variables for the Sets: &cv-link-AS;, &cv-link-ASCOM;, &cv-link-ASFLAGS;, &cv-link-ASPPCOM;, &cv-link-ASPPFLAGS;.Uses: &cv-link-ASCOMSTR;, &cv-link-ASPPCOMSTR;. - - Packaging + + packaging -Sets construction variables for the Package Builder. +A framework for building binary and source packages. - - packaging + + Packaging -A framework for building binary and source packages. +Sets construction variables for the Package Builder. diff --git a/doc/generated/tools.mod b/doc/generated/tools.mod index 1209d74..f9bc1d7 100644 --- a/doc/generated/tools.mod +++ b/doc/generated/tools.mod @@ -78,8 +78,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. mwcc"> mwld"> nasm"> -Packaging"> packaging"> +Packaging"> pdf"> pdflatex"> pdftex"> @@ -186,8 +186,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. mwcc"> mwld"> nasm"> -Packaging"> packaging"> +Packaging"> pdf"> pdflatex"> pdftex"> diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 8a8dc99..c7048b4 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -31,6 +31,94 @@ if + + _APPLELINK_COMPATIBILITY_VERSION + + + A macro (by default a generator function) used to create the linker flags to specify + apple's linker's -compatibility_version flag. + The default generator uses $APPLELINK_COMPATIBILITY_VERSION + and $APPLELINK_NO_COMPATIBILITY_VERSION and $SHLIBVERSION + to determine the correct flag. + + + + + APPLELINK_COMPATIBILITY_VERSION + + + On Mac OS X this is used to set the linker flag: + + -compatibility_version + + + The value is specified as X[.Y[.Z]] where X is between 1 and 65535, Y can be omitted or between 1 and + 255, Z can be omitted or between 1 and 255. This value will be derived from $SHLIBVERSION if + not + specified. The lowest digit will be dropped and replaced by a 0. + + + If the $APPLELINK_NO_COMPATIBILITY_VERSION is set then no -compatibility_version will be + output. + + See MacOS's ld manpage for more details + + + + _APPLELINK_CURRENT_VERSION + + + A macro (by default a generator function) used to create the linker flags to specify apple's linker's + -current_version flag. The default generator uses $APPLELINK_CURRENT_VERSION and + $APPLELINK_NO_CURRENT_VERSION and $SHLIBVERSION to determine the correct flag. + + + + + APPLELINK_CURRENT_VERSION + + + On Mac OS X this is used to set the linker flag: + + -current_version + + + The value is specified as X[.Y[.Z]] where X is between 1 and 65535, Y can be omitted or between 1 and + 255, Z can be omitted or between 1 and 255. This value will be set to $SHLIBVERSION if not + specified. + + + If the $APPLELINK_NO_CURRENT_VERSION is set then no -current_version will be + output. + + See MacOS's ld manpage for more details + + + + + APPLELINK_NO_COMPATIBILITY_VERSION + + + Set this to any True (1|True|non-empty string) value to disable adding -compatibility_version flag when + generating versioned shared libraries. + + + This overrides $APPLELINK_COMPATIBILITY_VERSION. + + + + + APPLELINK_NO_CURRENT_VERSION + + + Set this to any True (1|True|non-empty string) value to disable adding -current_version flag when + generating versioned shared libraries. + + + This overrides $APPLELINK_CURRENT_VERSION. + + + AR @@ -51,7 +139,9 @@ This is used to fill in the Architecture: field in an Ipkg control file, -and as part of the name of a generated RPM file. +and the BuildArch: field +in the RPM .spec file, +as well as forming part of the name of a generated RPM package file. @@ -240,7 +330,7 @@ or this: env = Environment() -env['BUILDERS]['NewBuilder'] = foo +env['BUILDERS']['NewBuilder'] = foo @@ -389,7 +479,6 @@ the control for Ipkg, the .wxs for MSI). If set, the function will be called after the SCons template for the file has been written. -XXX @@ -480,7 +569,8 @@ An automatically-generated construction variable containing the C preprocessor command-line options to define values. The value of $_CPPDEFFLAGS is created -by appending $CPPDEFPREFIX and $CPPDEFSUFFIX +by respectively prepending and appending +$CPPDEFPREFIX and $CPPDEFSUFFIX to the beginning and end of each definition in $CPPDEFINES. @@ -503,7 +593,8 @@ If $CPPDEFINES is a strin the values of the $CPPDEFPREFIX and $CPPDEFSUFFIX construction variables -will be added to the beginning and end. +will be respectively prepended and appended to the beginning and end +of each definition in $CPPDEFINES. @@ -517,7 +608,7 @@ If $CPPDEFINES is a list, the values of the $CPPDEFPREFIX and $CPPDEFSUFFIX construction variables -will be appended to the beginning and end +will be respectively prepended and appended to the beginning and end of each element in the list. If any element is a list or tuple, then the first item is the name being @@ -535,7 +626,7 @@ If $CPPDEFINES is a dicti the values of the $CPPDEFPREFIX and $CPPDEFSUFFIX construction variables -will be appended to the beginning and end +will be respectively prepended and appended to the beginning and end of each item from the dictionary. The key of each dictionary item is a name being defined @@ -563,7 +654,7 @@ env = Environment(CPPDEFINES={'B':2, 'A':None}) The prefix used to specify preprocessor definitions on the C compiler command line. -This will be appended to the beginning of each definition +This will be prepended to the beginning of each definition in the $CPPDEFINES construction variable when the $_CPPDEFFLAGS variable is automatically generated. @@ -619,7 +710,7 @@ An automatically-generated construction variable containing the C preprocessor command-line options for specifying directories to be searched for include files. The value of $_CPPINCFLAGS is created -by appending $INCPREFIX and $INCSUFFIX +by respectively prepending and appending $INCPREFIX and $INCSUFFIX to the beginning and end of each directory in $CPPPATH. @@ -661,7 +752,7 @@ through the automatically-generated $_CPPINCFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the value of the $INCPREFIX and $INCSUFFIX construction variables to the beginning and end @@ -2602,7 +2693,8 @@ containing the Fortran compiler command-line options for specifying directories to be searched for include files and module files. The value of $_FORTRANINCFLAGS is created -by prepending/appending $INCPREFIX and $INCSUFFIX +by respectively prepending and appending +$INCPREFIX and $INCSUFFIX to the beginning and end of each directory in $FORTRANPATH. @@ -2625,7 +2717,7 @@ for module files, as well. The prefix used to specify a module directory on the Fortran compiler command line. -This will be appended to the beginning of the directory +This will be prepended to the beginning of the directory in the $FORTRANMODDIR construction variables when the $_FORTRANMODFLAG variables is automatically generated. @@ -2637,7 +2729,7 @@ when the The suffix used to specify a module directory on the Fortran compiler command line. -This will be appended to the beginning of the directory +This will be appended to the end of the directory in the $FORTRANMODDIR construction variables when the $_FORTRANMODFLAG variables is automatically generated. @@ -2653,8 +2745,8 @@ for specifying the directory location where the Fortran compiler should place any module files that happen to get generated during compilation. The value of $_FORTRANMODFLAG is created -by prepending/appending $FORTRANMODDIRPREFIX and -$FORTRANMODDIRSUFFIX +by respectively prepending and appending +$FORTRANMODDIRPREFIX and $FORTRANMODDIRSUFFIX to the beginning and end of the directory in $FORTRANMODDIR. @@ -2727,7 +2819,7 @@ through the automatically-generated $_FORTRANINCFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $INCPREFIX and $INCSUFFIX construction variables to the beginning and end @@ -2797,107 +2889,107 @@ The default list is: FRAMEWORKPATH - -On Mac OS X with gcc, -a list containing the paths to search for frameworks. -Used by the compiler to find framework-style includes like -#include <Fmwk/Header.h>. -Used by the linker to find user-specified frameworks when linking (see -$FRAMEWORKS). -For example: - + + On Mac OS X with gcc, + a list containing the paths to search for frameworks. + Used by the compiler to find framework-style includes like + #include <Fmwk/Header.h>. + Used by the linker to find user-specified frameworks when linking (see + $FRAMEWORKS). + For example: + - - env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') - + + env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') + - -will add - + + will add + - - ... -Fmyframeworkdir - + + ... -Fmyframeworkdir + - -to the compiler and linker command lines. - - + + to the compiler and linker command lines. + + _FRAMEWORKPATH - -On Mac OS X with gcc, an automatically-generated construction variable -containing the linker command-line options corresponding to -$FRAMEWORKPATH. - - + + On Mac OS X with gcc, an automatically-generated construction variable + containing the linker command-line options corresponding to + $FRAMEWORKPATH. + + FRAMEWORKPATHPREFIX - -On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. -(see $FRAMEWORKPATH). -The default value is -. - - + + On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. + (see $FRAMEWORKPATH). + The default value is + . + + FRAMEWORKPREFIX - -On Mac OS X with gcc, -the prefix to be used for linking in frameworks -(see $FRAMEWORKS). -The default value is -. - - + + On Mac OS X with gcc, + the prefix to be used for linking in frameworks + (see $FRAMEWORKS). + The default value is + . + + _FRAMEWORKS - -On Mac OS X with gcc, -an automatically-generated construction variable -containing the linker command-line options -for linking with FRAMEWORKS. - - + + On Mac OS X with gcc, + an automatically-generated construction variable + containing the linker command-line options + for linking with FRAMEWORKS. + + FRAMEWORKS - -On Mac OS X with gcc, a list of the framework names to be linked into a -program or shared library or bundle. -The default value is the empty list. -For example: - + + On Mac OS X with gcc, a list of the framework names to be linked into a + program or shared library or bundle. + The default value is the empty list. + For example: + - - env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) - + + env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) + - + FRAMEWORKSFLAGS - -On Mac OS X with gcc, -general user-supplied frameworks options to be added at -the end of a command -line building a loadable module. -(This has been largely superseded by -the $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX, -$FRAMEWORKPREFIX and $FRAMEWORKS variables -described above.) - - + + On Mac OS X with gcc, + general user-supplied frameworks options to be added at + the end of a command + line building a loadable module. + (This has been largely superseded by + the $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX, + $FRAMEWORKPREFIX and $FRAMEWORKS variables + described above.) + + GS @@ -2939,6 +3031,15 @@ is -dNOPAUSE -dBATCH -sDEVICE=pdfwrite HOST_ARCH + + The name of the host hardware architecture used to create the Environment. + If a platform is specified when creating the Environment, then + that Platform's logic will handle setting this value. + This value is immutable, and should not be changed by the user after + the Environment is initialized. + Currently only set for Win32. + + Sets the host architecture for Visual Studio compiler. If not set, default to the detected host architecture: note that this may depend @@ -2955,16 +3056,7 @@ Valid values are the same as for This is currently only used on Windows, but in the future it will be used on other OSes as well. - - - The name of the host hardware architecture used to create the Environment. - If a platform is specified when creating the Environment, then - that Platform's logic will handle setting this value. - This value is immutable, and should not be changed by the user after - the Environment is initialized. - Currently only set for Win32. - - + HOST_OS @@ -3087,7 +3179,7 @@ env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0) The prefix used to specify an include directory on the C compiler command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $CPPPATH and $FORTRANPATH construction variables when the $_CPPINCFLAGS and $_FORTRANINCFLAGS variables are automatically generated. @@ -3206,7 +3298,7 @@ The command line used to call the Java archive tool. The string displayed when the Java archive tool is called -If this is not set, then $JARCOM (the command line) is displayed. +If this is not set, then $JARCOM (the command line) is displayed. @@ -3216,7 +3308,7 @@ env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET") The string displayed when the Java archive tool is called -If this is not set, then $JARCOM (the command line) is displayed. +If this is not set, then $JARCOM (the command line) is displayed. @@ -3265,107 +3357,111 @@ by default. JAVABOOTCLASSPATH - -Specifies the list of directories that -will be added to the -javac command line -via the option. -The individual directory names will be -separated by the operating system's path separate character -(: on UNIX/Linux/POSIX, -; on Windows). - - + + Specifies the list of directories that + will be added to the + javac command line + via the option. + The individual directory names will be + separated by the operating system's path separate character + (: on UNIX/Linux/POSIX, + ; + on Windows). + + JAVAC - -The Java compiler. - - + + The Java compiler. + + JAVACCOM - -The command line used to compile a directory tree containing -Java source files to -corresponding Java class files. -Any options specified in the $JAVACFLAGS construction variable -are included on this command line. - - + + The command line used to compile a directory tree containing + Java source files to + corresponding Java class files. + Any options specified in the $JAVACFLAGS construction variable + are included on this command line. + + JAVACCOMSTR - -The string displayed when compiling -a directory tree of Java source files to -corresponding Java class files. -If this is not set, then $JAVACCOM (the command line) is displayed. - + + The string displayed when compiling + a directory tree of Java source files to + corresponding Java class files. + If this is not set, then $JAVACCOM (the command line) is displayed. + - -env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") - - + + env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") + + JAVACFLAGS - -General options that are passed to the Java compiler. - - + + General options that are passed to the Java compiler. + + JAVACLASSDIR - -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 -JavaH -builder. - - + + 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 + JavaH + builder. + + JAVACLASSPATH - -Specifies the list of directories that -will be searched for Java -.class file. -The directories in this list will be added to the -javac and javah command lines -via the option. -The individual directory names will be -separated by the operating system's path separate character -(: on UNIX/Linux/POSIX, -; on Windows). - + + Specifies the list of directories that + will be searched for Java + .class + file. + The directories in this list will be added to the + javac and javah command lines + via the option. + The individual directory names will be + separated by the operating system's path separate character + (: on UNIX/Linux/POSIX, + ; + on Windows). + - -Note that this currently just adds the specified -directory via the option. -SCons does not currently search the -$JAVACLASSPATH directories for dependency -.class files. - - + + Note that this currently just adds the specified + directory via the option. + SCons does not currently search the + $JAVACLASSPATH directories for dependency + .class + files. + + JAVACLASSSUFFIX - -The suffix for Java class files; -.class -by default. - - + + The suffix for Java class files; + .class + by default. + + JAVAH @@ -3409,65 +3505,77 @@ for Java classes. + + JAVAINCLUDES + + + Include path for Java header files (such as jni.h) + + + JAVASOURCEPATH - -Specifies the list of directories that -will be searched for input -.java file. -The directories in this list will be added to the -javac command line -via the option. -The individual directory names will be -separated by the operating system's path separate character -(: on UNIX/Linux/POSIX, -; on Windows). - + + Specifies the list of directories that + will be searched for input + .java + file. + The directories in this list will be added to the + javac command line + via the option. + The individual directory names will be + separated by the operating system's path separate character + (: on UNIX/Linux/POSIX, + ; + on Windows). + - -Note that this currently just adds the specified -directory via the option. -SCons does not currently search the -$JAVASOURCEPATH directories for dependency -.java files. - - + + Note that this currently just adds the specified + directory via the option. + SCons does not currently search the + $JAVASOURCEPATH directories for dependency + .java + files. + + JAVASUFFIX - -The suffix for Java files; -.java -by default. - - + + The suffix for Java files; + .java + by default. + + JAVAVERSION - -Specifies the Java version being used by the Java builder. -This is not currently used to select one -version of the Java compiler vs. another. -Instead, you should set this to specify the version of Java -supported by your javac compiler. -The default is 1.4. - + + Specifies the Java version being used by the Java builder. + This is not currently used to select one + version of the Java compiler vs. another. + Instead, you should set this to specify the version of Java + supported by your javac compiler. + The default is 1.4. + - -This is sometimes necessary because -Java 1.5 changed the file names that are created -for nested anonymous inner classes, -which can cause a mismatch with the files -that SCons expects will be generated by the javac compiler. -Setting $JAVAVERSION to 1.5 -(or 1.6, as appropriate) -can make SCons realize that a Java 1.5 or 1.6 -build is actually up to date. - - + + This is sometimes necessary because + Java 1.5 changed the file names that are created + for nested anonymous inner classes, + which can cause a mismatch with the files + that SCons expects will be generated by the javac compiler. + Setting $JAVAVERSION to + 1.5 + (or 1.6, as appropriate) + can make SCons realize that a Java 1.5 or 1.6 + build is actually up to date. + + LATEX @@ -3688,6 +3796,14 @@ env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES") General options passed to the lexical analyzer generator. + + + + LEXUNISTD + + +Used only on windows environments to set a lex flag to prevent 'unistd.h' from being included. The default value is '--nounistd'. + @@ -3698,7 +3814,7 @@ An automatically-generated construction variable containing the linker command-line options for specifying directories to be searched for library. The value of $_LIBDIRFLAGS is created -by appending $LIBDIRPREFIX and $LIBDIRSUFFIX +by respectively prepending and appending $LIBDIRPREFIX and $LIBDIRSUFFIX to the beginning and end of each directory in $LIBPATH. @@ -3709,7 +3825,7 @@ of each directory in $LIBPATH The prefix used to specify a library directory on the linker command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $LIBPATH construction variable when the $_LIBDIRFLAGS variable is automatically generated. @@ -3742,7 +3858,7 @@ An automatically-generated construction variable containing the linker command-line options for specifying libraries to be linked with the resulting target. The value of $_LIBFLAGS is created -by appending $LIBLINKPREFIX and $LIBLINKSUFFIX +by respectively prepending and appending $LIBLINKPREFIX and $LIBLINKSUFFIX to the beginning and end of each filename in $LIBS. @@ -3753,7 +3869,7 @@ of each filename in $LIBS The prefix used to specify a library to link on the linker command line. -This will be appended to the beginning of each library +This will be prepended to the beginning of each library in the $LIBS construction variable when the $_LIBFLAGS variable is automatically generated. @@ -3807,7 +3923,7 @@ through the automatically-generated $_LIBDIRFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $LIBDIRPREFIX and $LIBDIRSUFFIX construction variables to the beginning and end @@ -3863,7 +3979,7 @@ through the automatically-generated $_LIBFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $LIBLINKPREFIX and $LIBLINKSUFFIX construction variables to the beginning and end @@ -3930,10 +4046,10 @@ and these suffixes. LICENSE -The abbreviated name of the license under which -this project is released (gpl, lpgl, bsd etc.). +The abbreviated name, preferably the SPDX code, of the license under which +this project is released (GPL-3.0, LGPL-2.1, BSD-2-Clause etc.). See http://www.opensource.org/licenses/alphabetical -for a list of license names. +for a list of license names and SPDX codes. @@ -4379,6 +4495,7 @@ constructor; setting it later has no effect. Valid values for Windows are +14.1, 14.0, 14.0Exp, 12.0, @@ -4402,254 +4519,311 @@ Versions ending in Exp refer to "Express" or MSVS - -When the Microsoft Visual Studio tools are initialized, they set up this -dictionary with the following keys: - - VERSION - - - the version of MSVS being used (can be set via - $MSVS_VERSION) - - - - - VERSIONS - - - the available versions of MSVS installed - - - - - VCINSTALLDIR - - - installed directory of Visual C++ - - - - - VSINSTALLDIR - - - installed directory of Visual Studio - - - - - FRAMEWORKDIR - - - installed directory of the .NET framework - - - - - FRAMEWORKVERSIONS - - - list of installed versions of the .NET framework, sorted - latest to oldest. - - - - - FRAMEWORKVERSION - - - latest installed version of the .NET - framework - - - - - FRAMEWORKSDKDIR - - - installed location of the .NET SDK. - - - - - PLATFORMSDKDIR - - - installed location of the Platform SDK. - - - - - PLATFORMSDK_MODULES - - - dictionary of installed Platform SDK modules, where the - dictionary keys are keywords for the various modules, and the values - are 2-tuples where the first is the release date, and the second is - the version number. - - - If a value isn't set, it wasn't available in the -registry. + + + When the Microsoft Visual Studio tools are initialized, + they set up this dictionary with the following keys: + + + + VERSION + the version of MSVS being used (can be set via + $MSVS_VERSION) + + + VERSIONS + the available versions of MSVS installed + + + VCINSTALLDIR + installed directory of Visual C++ + + + VSINSTALLDIR + installed directory of Visual Studio + + + FRAMEWORKDIR + installed directory of the .NET framework + + + FRAMEWORKVERSIONS + + list of installed versions of the .NET framework, + sorted latest to oldest. + + + + + FRAMEWORKVERSION + + latest installed version of the .NET framework + + + + FRAMEWORKSDKDIR + + installed location of the .NET SDK. + + + + PLATFORMSDKDIR + + installed location of the Platform SDK. + + + + PLATFORMSDK_MODULES + + + dictionary of installed Platform SDK modules, where the + dictionary keys are keywords for the various modules, + and the values are 2-tuples where the first is the + release date, and the second is the version number. + + + + + If a value is not set, it was not available in the registry. + MSVS_ARCH - Sets -the architecture for which the generated project(s) should build. -The default value is x86. amd64 is -also supported by SCons for some Visual Studio versions. Trying to set -$MSVS_ARCH to an architecture that's not supported for a given Visual -Studio version will generate an error. + + Sets the architecture for which the generated project(s) should build. + + The default value is x86. + amd64 is also supported by SCons for + most Visual Studio versions. Since Visual Studio 2015 + arm is supported, and since Visual Studio + 2017 arm64 is supported. + Trying to set $MSVS_ARCH + to an architecture that's not supported for a given Visual + Studio version will generate an error. + + MSVS_PROJECT_GUID - The string placed in a generated + + + The string placed in a generated Microsoft Visual Studio project file as the value of the -ProjectGUID attribute. There is no default value. If not -defined, a new GUID is generated. + ProjectGUID attribute. There is no default + value. If not +defined, a new GUID is generated. + + + MSVS_SCC_AUX_PATH - The path name placed in a generated + + + The path name placed in a generated Microsoft Visual Studio project file as the value of the -SccAuxPath attribute if the -MSVS_SCC_PROVIDER construction variable is also set. There is -no default value. + SccAuxPath attribute if the + MSVS_SCC_PROVIDER construction variable is + also set. There is +no default value. + + + MSVS_SCC_CONNECTION_ROOT - The root path of projects in -your SCC workspace, i.e the path under which all project and solution files -will be generated. It is used as a reference path from which the relative -paths of the generated Microsoft Visual Studio project and solution files are -computed. The relative project file path is placed as the value of the -SccLocalPath attribute of the project file and as the -values of the -SccProjectFilePathRelativizedFromConnection[i] (where [i] -ranges from 0 to the number of projects in the solution) attributes of the -GlobalSection(SourceCodeControl) section of the Microsoft -Visual Studio solution file. Similarly the relative solution file path is -placed as the values of the SccLocalPath[i] (where [i] -ranges from 0 to the number of projects in the solution) attributes of the -GlobalSection(SourceCodeControl) section of the Microsoft -Visual Studio solution file. This is used only if the -MSVS_SCC_PROVIDER construction variable is also set. The -default value is the current working directory. + + + The root path of projects in your SCC workspace, i.e the + path under which all project and solution files will be + generated. It is used as a reference path from which the + relative paths of the generated Microsoft Visual Studio project + and solution files are computed. The relative project file path + is placed as the value of the SccLocalPath + attribute of the project file and as the values of the + SccProjectFilePathRelativizedFromConnection[i] + (where [i] ranges from 0 to the number of projects in the solution) + attributes of the GlobalSection(SourceCodeControl) + section of the Microsoft Visual Studio solution file. Similarly + the relative solution file path is placed as the values of the + SccLocalPath[i] (where [i] ranges from 0 + to the number of projects in the solution) attributes of the + GlobalSection(SourceCodeControl) section of + the Microsoft Visual Studio solution file. This is used only if + the MSVS_SCC_PROVIDER construction variable is + also set. The default value is the current working directory. + + MSVS_SCC_PROJECT_NAME - The project name placed in -a generated Microsoft Visual Studio project file as the value of the -SccProjectName attribute if the -MSVS_SCC_PROVIDER construction variable is also set. In this -case the string is also placed in the SccProjectName0 -attribute of the GlobalSection(SourceCodeControl) section -of the Microsoft Visual Studio solution file. There is no default value. - + + + The project name placed in a generated Microsoft + Visual Studio project file as the value of the + SccProjectName attribute if the + MSVS_SCC_PROVIDER construction variable + is also set. In this case the string is also placed in + the SccProjectName0 attribute of the + GlobalSection(SourceCodeControl) section + of the Microsoft Visual Studio solution file. There is no + default value. + + MSVS_SCC_PROVIDER - The -string placed in a generated Microsoft Visual Studio project file as the value -of the SccProvider attribute. The string is also placed in -the SccProvider0 attribute of the -GlobalSection(SourceCodeControl) section of the Microsoft -Visual Studio solution file. There is no default value. + + + The string placed in a generated Microsoft + Visual Studio project file as the value of the + SccProvider attribute. The string is + also placed in the SccProvider0 attribute + of the GlobalSection(SourceCodeControl) + section of the Microsoft Visual Studio solution file. There + is no default value. + + MSVS_VERSION - Sets the preferred version -of Microsoft Visual Studio to use. If $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 -MSVS_VERSION variable in the Environment initialization, -setting it to the appropriate version ('6.0' or '7.0', for example). If the -specified version isn't installed, tool initialization will fail. -This is obsolete: use $MSVC_VERSION instead. If $MSVS_VERSION is -set and $MSVC_VERSION is not, $MSVC_VERSION will be set automatically -to $MSVS_VERSION. If both are set to different values, scons will raise an -error. + + Sets the preferred version of Microsoft Visual Studio to use. + + If $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 MSVS_VERSION variable in the + Environment initialization, setting it to the appropriate + version ('6.0' or '7.0', for example). If the specified + version isn't installed, tool initialization will fail. + + + This is obsolete: use $MSVC_VERSION instead. If + $MSVS_VERSION is set and $MSVC_VERSION is + not, $MSVC_VERSION will be set automatically to + $MSVS_VERSION. If both are set to different values, + scons will raise an error. + + MSVSBUILDCOM -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. + + 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. + + MSVSCLEANCOM - 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. + + + 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. + + MSVSENCODING - The encoding string placed in a -generated Microsoft Visual Studio project file. The default is encoding -Windows-1252. + + + The encoding string placed in a generated Microsoft + Visual Studio project file. The default is encoding + Windows-1252. + + MSVSPROJECTCOM - The action used to generate Microsoft -Visual Studio project files. + + The action used to generate Microsoft Visual Studio project files. + MSVSPROJECTSUFFIX - The suffix used for Microsoft Visual -Studio project (DSP) files. The default value is .vcproj -when using Visual Studio version 7.x (.NET) or later version, and -.dsp when using earlier versions of Visual Studio. - + + + The suffix used for Microsoft Visual Studio project (DSP) + files. The default value is .vcproj + when using Visual Studio version 7.x (.NET) or later version, + and .dsp when using earlier versions of + Visual Studio. + + MSVSREBUILDCOM - 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. + + + 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. + + + MSVSSCONS -The SCons used in generated Microsoft Visual Studio project files. The -default is the version of SCons being used to generate the project file. - + + The SCons used in generated Microsoft Visual Studio project + files. The default is the version of SCons being used to + generate the project file. + + MSVSSCONSCOM - The default -SCons command used in generated Microsoft Visual Studio project files. - + + + The default SCons command used in generated Microsoft Visual + Studio project files. + + MSVSSCONSCRIPT - The sconscript -file (that is, SConstruct or SConscript file) that will be invoked by -Visual Studio project files (through the $MSVSSCONSCOM variable). The -default is the same sconscript file that contains the call to MSVSProject -to build the project file. + + + The sconscript file (that is, SConstruct or SConscript + file) that will be invoked by Visual Studio project files + (through the $MSVSSCONSCOM variable). The default + is the same sconscript file that contains the call to + MSVSProject to build the project file. + + MSVSSCONSFLAGS - The -SCons flags used in generated Microsoft Visual Studio project files. - + + + The SCons flags used in generated Microsoft Visual Studio project files. + + MSVSSOLUTIONCOM - The action used to generate Microsoft -Visual Studio solution files. + + The action used to generate Microsoft Visual Studio solution files. + MSVSSOLUTIONSUFFIX - The suffix used for Microsoft -Visual Studio solution (DSW) files. The default value is -.sln when using Visual Studio version 7.x (.NET), and -.dsw when using earlier versions of Visual Studio. - + + + The suffix used for Microsoft Visual Studio solution (DSW) + files. The default value is .sln + when using Visual Studio version 7.x (.NET), and + .dsw when using earlier versions of + Visual Studio. + + MT @@ -5601,7 +5775,8 @@ containing the command-line options for specifying directories to be searched by the resource compiler. The value of $RCINCFLAGS is created -by appending $RCINCPREFIX and $RCINCSUFFIX +by respectively prepending and appending +$RCINCPREFIX and $RCINCSUFFIX to the beginning and end of each directory in $CPPPATH. @@ -5613,7 +5788,7 @@ of each directory in $CPPPATH The prefix (flag) used to specify an include directory on the resource compiler command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $CPPPATH construction variable when the $RCINCFLAGS variable is expanded. @@ -5735,7 +5910,7 @@ An automatically-generated construction variable containing the rpath flags to be used when linking a program with shared libraries. The value of $_RPATH is created -by appending $RPATHPREFIX and $RPATHSUFFIX +by respectively prepending $RPATHPREFIX and appending $RPATHSUFFIX to the beginning and end of each directory in $RPATH. @@ -5763,7 +5938,7 @@ path, you must make it absolute yourself. The prefix used to specify a directory to be searched for shared libraries when running programs. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $RPATH construction variable when the $_RPATH variable is automatically generated. @@ -5863,11 +6038,15 @@ below, for more information. SCONS_HOME - 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 $MSVSSCONS command line executed from Microsoft -Visual Studio project files. + + + 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 $MSVSSCONS command line executed from Microsoft + Visual Studio project files. + + SHCC @@ -6608,16 +6787,6 @@ that are needed. $SHLIBVERSION values include '1', '1.2.3', and '1.2.gitaa412c8b'. - - - - SHLIBVERSIONFLAGS - - -Extra flags added to $SHLINKCOM when building versioned -SharedLibrary. These flags are only used when $SHLIBVERSION is -set. - @@ -6631,6 +6800,16 @@ and some extra dynamically generated options (such as -Wl,-soname=$_SHLIBSONAME. It is unused by "plain" (unversioned) shared libraries. + + + + SHLIBVERSIONFLAGS + + +Extra flags added to $SHLINKCOM when building versioned +SharedLibrary. These flags are only used when $SHLIBVERSION is +set. + @@ -6935,7 +7114,8 @@ An automatically-generated construction variable containing the SWIG command-line options for specifying directories to be searched for included files. The value of $_SWIGINCFLAGS is created -by appending $SWIGINCPREFIX and $SWIGINCSUFFIX +by respectively prepending and appending +$SWIGINCPREFIX and $SWIGINCSUFFIX to the beginning and end of each directory in $SWIGPATH. @@ -6946,7 +7126,7 @@ of each directory in $SWIGPATH The prefix used to specify an include directory on the SWIG command line. -This will be appended to the beginning of each directory +This will be prepended to the beginning of each directory in the $SWIGPATH construction variable when the $_SWIGINCFLAGS variable is automatically generated. @@ -7020,7 +7200,7 @@ through the automatically-generated $_SWIGINCFLAGS construction variable, which is constructed by -appending the values of the +respectively prepending and appending the values of the $SWIGINCPREFIX and $SWIGINCSUFFIX construction variables to the beginning and end @@ -7094,6 +7274,13 @@ that may not be set or used in a construction environment. TARGET_ARCH + + The name of the target hardware architecture for the compiled objects + created by this Environment. + This defaults to the value of HOST_ARCH, and the user can override it. + Currently only set for Win32. + + Sets the target architecture for Visual Studio compiler (i.e. the arch of the binaries generated by the compiler). If not set, default to @@ -7104,29 +7291,28 @@ This variable must be passed as an argument to the Environment() constructor; setting it later has no effect. This is currently only used on Windows, but in the future it will be used on other OSes as well. +If this is set and MSVC_VERSION is not set, this will search for +all installed MSVC's that support the TARGET_ARCH, selecting the +latest version for use. Valid values for Windows are x86, +arm, i386 (for 32 bits); amd64, +arm64, emt64, x86_64 (for 64 bits); and ia64 (Itanium). + For example, if you want to compile 64-bit binaries, you would set TARGET_ARCH='x86_64' in your SCons environment. - - - The name of the target hardware architecture for the compiled objects - created by this Environment. - This defaults to the value of HOST_ARCH, and the user can override it. - Currently only set for Win32. - - + TARGET_OS @@ -7162,12 +7348,28 @@ The suffix used for tar file names. The prefix for a temporary file used -to execute lines longer than $MAXLINELENGTH. -The default is '@'. -This may be set for toolchains that use other values, -such as '-@' for the diab compiler +to store lines lines longer than $MAXLINELENGTH +as operations which call out to a shell will fail +if the line is too long, which particularly +impacts linking. +The default is '@', which works for the Microsoft +and GNU toolchains on Windows. +Set this appropriately for other toolchains, +for example '-@' for the diab compiler or '-via' for ARM toolchain. + + + + TEMPFILESUFFIX + + +The suffix used for the temporary file name +used for long command lines. The name should +include the dot ('.') if one is wanted as +it will not be added automatically. +The default is '.lnk'. + @@ -7542,6 +7744,7 @@ This is used to fill in the BuildRequires: field in the RPM .spec file. +Note this should only be used on a host managed by rpm as the dependencies will not be resolvable at build time otherwise. @@ -7600,7 +7803,8 @@ field in the RPM This is used to fill in the Epoch: -field in the controlling information for RPM packages. +field in the RPM +.spec file. @@ -7624,6 +7828,38 @@ This is used to fill in the field in the RPM .spec file. + + + + X_RPM_EXTRADEFS + + +A list used to supply extra defintions or flags +to be added to the RPM .spec file. +Each item is added as-is with a carriage return appended. +This is useful if some specific RPM feature not otherwise +anticipated by SCons needs to be turned on or off. +Note if this variable is omitted, SCons will by +default supply the value +'%global debug_package %{nil}' +to disable debug package generation. +To enable debug package generation, include this +variable set either to None, or to a custom +list that does not include the default line. +Added in version 3.1. + + + +env.Package( + NAME = 'foo', +... + X_RPM_EXTRADEFS = [ + '%define _unpackaged_files_terminate_build 0' + '%define _missing_doc_files_terminate_build 0' + ], +... ) + + diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index 6ecf6c9..28c08dc 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -10,6 +10,12 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $__LDMODULEVERSIONFLAGS"> $__SHLIBVERSIONFLAGS"> +$_APPLELINK_COMPATIBILITY_VERSION"> +$APPLELINK_COMPATIBILITY_VERSION"> +$_APPLELINK_CURRENT_VERSION"> +$APPLELINK_CURRENT_VERSION"> +$APPLELINK_NO_COMPATIBILITY_VERSION"> +$APPLELINK_NO_CURRENT_VERSION"> $AR"> $ARCHITECTURE"> $ARCOM"> @@ -232,6 +238,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $JAVAHCOM"> $JAVAHCOMSTR"> $JAVAHFLAGS"> +$JAVAINCLUDES"> $JAVASOURCEPATH"> $JAVASUFFIX"> $JAVAVERSION"> @@ -256,6 +263,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $LEXCOM"> $LEXCOMSTR"> $LEXFLAGS"> +$LEXUNISTD"> $_LIBDIRFLAGS"> $LIBDIRPREFIX"> $LIBDIRSUFFIX"> @@ -496,8 +504,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $_SHLIBSONAME"> $SHLIBSUFFIX"> $SHLIBVERSION"> -$SHLIBVERSIONFLAGS"> $_SHLIBVERSIONFLAGS"> +$SHLIBVERSIONFLAGS"> $SHLINK"> $SHLINKCOM"> $SHLINKCOMSTR"> @@ -537,6 +545,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $TARGETS"> $TARSUFFIX"> $TEMPFILEPREFIX"> +$TEMPFILESUFFIX"> $TEX"> $TEXCOM"> $TEXCOMSTR"> @@ -584,6 +593,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $X_RPM_EPOCH"> $X_RPM_EXCLUDEARCH"> $X_RPM_EXLUSIVEARCH"> +$X_RPM_EXTRADEFS"> $X_RPM_GROUP"> $X_RPM_GROUP_lang"> $X_RPM_ICON"> @@ -639,6 +649,12 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $__LDMODULEVERSIONFLAGS"> $__SHLIBVERSIONFLAGS"> +$_APPLELINK_COMPATIBILITY_VERSION"> +$APPLELINK_COMPATIBILITY_VERSION"> +$_APPLELINK_CURRENT_VERSION"> +$APPLELINK_CURRENT_VERSION"> +$APPLELINK_NO_COMPATIBILITY_VERSION"> +$APPLELINK_NO_CURRENT_VERSION"> $AR"> $ARCHITECTURE"> $ARCOM"> @@ -861,6 +877,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $JAVAHCOM"> $JAVAHCOMSTR"> $JAVAHFLAGS"> +$JAVAINCLUDES"> $JAVASOURCEPATH"> $JAVASUFFIX"> $JAVAVERSION"> @@ -885,6 +902,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $LEXCOM"> $LEXCOMSTR"> $LEXFLAGS"> +$LEXUNISTD"> $_LIBDIRFLAGS"> $LIBDIRPREFIX"> $LIBDIRSUFFIX"> @@ -1125,8 +1143,8 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $_SHLIBSONAME"> $SHLIBSUFFIX"> $SHLIBVERSION"> -$SHLIBVERSIONFLAGS"> $_SHLIBVERSIONFLAGS"> +$SHLIBVERSIONFLAGS"> $SHLINK"> $SHLINKCOM"> $SHLINKCOMSTR"> @@ -1166,6 +1184,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $TARGETS"> $TARSUFFIX"> $TEMPFILEPREFIX"> +$TEMPFILESUFFIX"> $TEX"> $TEXCOM"> $TEXCOMSTR"> @@ -1213,6 +1232,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $X_RPM_EPOCH"> $X_RPM_EXCLUDEARCH"> $X_RPM_EXLUSIVEARCH"> +$X_RPM_EXTRADEFS"> $X_RPM_GROUP"> $X_RPM_GROUP_lang"> $X_RPM_ICON"> diff --git a/doc/man/SConstruct b/doc/man/SConstruct index dedf83e..f17ffd4 100644 --- a/doc/man/SConstruct +++ b/doc/man/SConstruct @@ -3,7 +3,7 @@ # # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/doc/man/epub.xsl b/doc/man/epub.xsl index d8d7f13..450f7de 100644 --- a/doc/man/epub.xsl +++ b/doc/man/epub.xsl @@ -1,7 +1,7 @@ @@ -853,14 +862,31 @@ and ultimately removed. --debug=time -Prints various time profiling information: -the time spent executing each individual build command; -the total build time (time SCons ran from beginning to end); -the total time spent reading and executing SConscript files; -the total time spent SCons itself spend running -(that is, not counting reading and executing SConscript files); -and both the total time spent executing all build commands -and the elapsed wall-clock time spent executing those build commands. +Prints various time profiling information: + + +The time spent executing each individual build command + + +The total build time (time SCons ran from beginning to end) + + +The total time spent reading and executing SConscript files + + +The total time spent SCons itself spend running +(that is, not counting reading and executing SConscript files) + + +The total time spent executing all build commands + +The elapsed wall-clock time spent executing those build commands + + +The time spent processing each file passed to the SConscript() function + + + (When scons is executed without the @@ -884,7 +910,8 @@ be significantly smaller than the total time spent executing all the build commands, since multiple build commands and intervening SCons processing -should take place in parallel.) +should take place in parallel.) + @@ -967,6 +994,12 @@ the mechanisms in the specified order. + + + + --enable-virtualenv + +Import virtualenv-related variables to SCons. @@ -1024,6 +1057,12 @@ are used, the directories are searched in the order specified. + + --ignore-virtualenv + +Suppress importing virtualenv-related variables to SCons. + + --implicit-cache @@ -1640,9 +1679,12 @@ scons --tree=all,prune,status target Walks up the directory structure until an SConstruct , -Sconstruct +Sconstruct , +sconstruct , +SConstruct.py +Sconstruct.py or -sconstruct +sconstruct.py file is found, and uses that as the top of the directory tree. If no targets are specified on the command line, @@ -1861,26 +1903,6 @@ These warnings are enabled by default. --warn=missing-sconscript, --warn=no-missing-sconscript Enables or disables warnings about missing SConscript files. -These warnings are enabled by default. - - - - - --warn=no-md5-module, --warn=no-no-md5-module - -Enables or disables warnings about the version of Python -not having an MD5 checksum module available. -These warnings are enabled by default. - - - - - --warn=no-metaclass-support, --warn=no-no-metaclass-support - -Enables or disables warnings about the version of Python -not supporting metaclasses when the - -option is used. These warnings are enabled by default. @@ -2299,12 +2321,12 @@ that are not absolute path names / on POSIX systems or -\fR +\ on Windows systems, with or without an optional drive letter) are interpreted relative to the directory containing the -SConscript +SConscript file being read. An initial # @@ -2992,7 +3014,7 @@ function to get at the path name for each Node. print(str(DEFAULT_TARGETS[0])) -if 'foo' in map(str, DEFAULT_TARGETS): +if 'foo' in [str(t) for t in DEFAULT_TARGETS]: print("Don't forget to test the `foo' program!") @@ -3006,13 +3028,13 @@ list change on on each successive call to the function: -print(map(str, DEFAULT_TARGETS)) # originally [] +print([str(t) for t in DEFAULT_TARGETS]) # originally [] Default('foo') -print(map(str, DEFAULT_TARGETS)) # now a node ['foo'] +print([str(t) for t in DEFAULT_TARGETS]) # now a node ['foo'] Default('bar') -print(map(str, DEFAULT_TARGETS)) # now a node ['foo', 'bar'] +print([str(t) for t in DEFAULT_TARGETS]) # now a node ['foo', 'bar'] Default(None) -print(map(str, DEFAULT_TARGETS)) # back to [] +print([str(t) for t in DEFAULT_TARGETS]) # back to [] Consequently, be sure to use @@ -4056,7 +4078,9 @@ and return -1, 0 or 1 (like the standard Python cmp -function). +function). + +Optionally a Boolean value of True for sort will cause a standard alphabetical sort to be performed Help(vars.GenerateHelpText(env)) @@ -5255,11 +5279,11 @@ arguments may be lists of Node objects if there is more than one target file or source file. The actual target and source file name(s) may be retrieved from their Node objects -via the built-in Python str() function: +via the built-in Python str function: target_file_name = str(target) -source_file_names = map(lambda x: str(x), source) +source_file_names = [str(x) for x in source] The function should return diff --git a/doc/man/scons_title.xsl b/doc/man/scons_title.xsl index cc5ae5e..0c50c96 100644 --- a/doc/man/scons_title.xsl +++ b/doc/man/scons_title.xsl @@ -1,7 +1,7 @@ - SCONSIGN 1 -SCons 3.0.0 -SCons 3.0.0 +SCons 3.0.5 +SCons 3.0.5 sconsign @@ -42,7 +42,7 @@ - sconsign + sconsign options file ... @@ -51,7 +51,7 @@ DESCRIPTION -The +The sconsign command displays the contents of one or more @@ -94,17 +94,21 @@ was specified by the function). Any file -argument that does not end in -.dbm +argument that ends in +.dblite is assumed to be a traditional .sconsign file containing the signature entries for a single directory. -An explicit format +If neither of those is true, +sconsign +attempts to guess the format. +If that does not work, +an explicit format may be specified using the or - + options. diff --git a/doc/python10/SConstruct b/doc/python10/SConstruct index 3603298..5b6eb6f 100644 --- a/doc/python10/SConstruct +++ b/doc/python10/SConstruct @@ -3,7 +3,7 @@ # # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/doc/python10/abstract.xml b/doc/python10/abstract.xml index c1715e8..55cb734 100644 --- a/doc/python10/abstract.xml +++ b/doc/python10/abstract.xml @@ -10,7 +10,7 @@ - - - -
- Installing &SCons; on Windows Systems - - - - &SCons; provides a Windows installer - that makes installation extremely easy. - Download the scons-&buildversion;.win32.exe - file from the &SCons; download page at - http://scons.org/pages/download.html. - Then all you need to do is execute the file - (usually by clicking on its icon in Windows Explorer). - These will take you through a small - sequence of windows that will install - &SCons; on your system. - - - - - - - - + +% python -m pip install --user scons + -
+ + &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. + Some distributions have two &SCons; packages available, one which + uses Python 2 and one which uses Python 3. If you need a specific + version of &SCons; that is different from the package available, + pip has a version option or you can follow + the instructions in the next section. + @@ -387,25 +200,21 @@ Python 2.5.1 Building and Installing &SCons; on Any System - If a pre-built &SCons; package is not available for your system, + and installing using pip is not suitable, then you can still easily build and install &SCons; using the native Python distutils package. - - The first step is to download either the scons-&buildversion;.tar.gz or scons-&buildversion;.zip, which are available from the SCons download page at http://www.scons.org/download.html. - - Unpack the archive you downloaded, using a utility like tar on Linux or UNIX, @@ -415,7 +224,6 @@ Python 2.5.1 usually in your local directory. Then change your working directory to that directory and install &SCons; by executing the following commands: - @@ -429,11 +237,11 @@ Python 2.5.1 install the &scons; script in the python which is used to run the setup.py's scripts directory (/usr/local/bin or - C:\Python25\Scripts), + C:\Python27\Scripts), and will install the &SCons; build engine in the corresponding library directory for the python used (/usr/local/lib/scons or - C:\Python25\scons). + C:\Python27\scons). Because these are system directories, you may need root (on Linux or UNIX) or Administrator (on Windows) privileges to install &SCons; like this. @@ -489,7 +297,7 @@ Python 2.5.1 in the /usr/lib/scons-&buildversion; or - C:\Python25\scons-&buildversion; + C:\Python27\scons-&buildversion; directory, for example.
diff --git a/doc/user/builders-built-in.xml b/doc/user/builders-built-in.xml index d64b63d..f059bca 100644 --- a/doc/user/builders-built-in.xml +++ b/doc/user/builders-built-in.xml @@ -22,7 +22,7 @@ + --> -
- +
+ + SCons User's Guide Copyright (c) 2004-2019 Steven Knight + +
- SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight - -
-
diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 29a79a5..35372e7 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -22,7 +22,7 @@ %s"%df) + if df: + return df + + if not df: + try: + # this should yield a path which matches what's in the sconsign + c_str = self.get_path() + df = dmap.get(c_str, None) + if MD5_TIMESTAMP_DEBUG: print("-->%s"%df) + if df: + return df + + if os.altsep: + c_str = c_str.replace(os.sep, os.altsep) + df = dmap.get(c_str, None) + if MD5_TIMESTAMP_DEBUG: print("-->%s"%df) + if df: + return df + + except AttributeError as e: + raise FileBuildInfoFileToCsigMappingError("No mapping from file name to content signature for :%s"%c_str) + + return df + + def changed_timestamp_then_content(self, target, prev_ni, node=None): + """ + Used when decider for file is Timestamp-MD5 + + NOTE: If the timestamp hasn't changed this will skip md5'ing the + file and just copy the prev_ni provided. If the prev_ni + is wrong. It will propagate it. + See: https://github.com/SCons/scons/issues/2980 + + Args: + self - dependency + target - target + prev_ni - The NodeInfo object loaded from previous builds .sconsign + node - Node instance. This is the only changed* function which requires + node to function. So if we detect that it's not passed. + we throw DeciderNeedsNode, and caller should handle this and pass node. + + Returns: + Boolean - Indicates if node(File) has changed. + """ + if node is None: + # We need required node argument to get BuildInfo to function + raise DeciderNeedsNode(self.changed_timestamp_then_content) + + # Now get sconsign name -> csig map and then get proper prev_ni if possible + bi = node.get_stored_info().binfo + rebuilt = False + try: + dependency_map = bi.dependency_map + except AttributeError as e: + dependency_map = self._build_dependency_map(bi) + rebuilt = True + + if len(dependency_map) == 0: + # If there's no dependency map, there's no need to find the + # prev_ni as there aren't any + # shortcut the rest of the logic + if MD5_TIMESTAMP_DEBUG: print("Skipping checks len(dmap)=0") + + # We still need to get the current file's csig + # This should be slightly faster than calling self.changed_content(target, new_prev_ni) + self.get_csig() + return True + + new_prev_ni = self._get_previous_signatures(dependency_map) + new = self.changed_timestamp_match(target, new_prev_ni) + + if MD5_TIMESTAMP_DEBUG: + old = self.changed_timestamp_match(target, prev_ni) + + if old != new: + print("Mismatch self.changed_timestamp_match(%s, prev_ni) old:%s new:%s"%(str(target), old, new)) + new_prev_ni = self._get_previous_signatures(dependency_map) + + + if not new: try: - self.get_ninfo().csig = prev_ni.csig + # NOTE: We're modifying the current node's csig in a query. + self.get_ninfo().csig = new_prev_ni.csig except AttributeError: pass return False - return self.changed_content(target, prev_ni) + return self.changed_content(target, new_prev_ni) def changed_timestamp_newer(self, target, prev_ni): try: @@ -3251,6 +3450,12 @@ class File(Base): return 1 def changed_timestamp_match(self, target, prev_ni): + """ + Return True if the timestamps don't match or if there is no previous timestamp + :param target: + :param prev_ni: Information about the node from the previous build + :return: + """ try: return self.get_timestamp() != prev_ni.timestamp except AttributeError: @@ -3272,7 +3477,9 @@ class File(Base): # ...and they'd like a local copy. e = LocalCopy(self, r, None) if isinstance(e, SCons.Errors.BuildError): - raise + # Likely this should be re-raising exception e + # (which would be BuildError) + raise e SCons.Node.store_info_map[self.store_info](self) if T: Trace(' 1\n') return 1 @@ -3293,11 +3500,14 @@ class File(Base): result = self if not self.exists(): norm_name = _my_normcase(self.name) - for dir in self.dir.get_all_rdirs(): - try: node = dir.entries[norm_name] - except KeyError: node = dir.file_on_disk(self.name) + for repo_dir in self.dir.get_all_rdirs(): + try: + node = repo_dir.entries[norm_name] + except KeyError: + node = repo_dir.file_on_disk(self.name) + if node and node.exists() and \ - (isinstance(node, File) or isinstance(node, Entry) \ + (isinstance(node, File) or isinstance(node, Entry) or not node.is_derived()): result = node # Copy over our local attributes to the repository @@ -3317,6 +3527,28 @@ class File(Base): self._memo['rfile'] = result return result + def find_repo_file(self): + """ + For this node, find if there exists a corresponding file in one or more repositories + :return: list of corresponding files in repositories + """ + retvals = [] + + norm_name = _my_normcase(self.name) + for repo_dir in self.dir.get_all_rdirs(): + try: + node = repo_dir.entries[norm_name] + except KeyError: + node = repo_dir.file_on_disk(self.name) + + if node and node.exists() and \ + (isinstance(node, File) or isinstance(node, Entry) \ + or not node.is_derived()): + retvals.append(node) + + return retvals + + def rstr(self): return str(self.rfile()) @@ -3374,6 +3606,8 @@ class File(Base): because multiple targets built by the same action will all have the same build signature, and we have to differentiate them somehow. + + Signature should normally be string of hex digits. """ try: return self.cachesig @@ -3383,10 +3617,13 @@ class File(Base): # Collect signatures for all children children = self.children() sigs = [n.get_cachedir_csig() for n in children] + # Append this node's signature... sigs.append(self.get_contents_sig()) + # ...and it's path sigs.append(self.get_internal_path()) + # Merge this all into a single signature result = self.cachesig = SCons.Util.MD5collect(sigs) return result diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 3a36894..021d433 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -22,7 +22,7 @@ # from __future__ import division, print_function -__revision__ = "src/engine/SCons/Node/FSTests.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Node/FSTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import SCons.compat @@ -41,6 +41,7 @@ import SCons.Errors import SCons.Node.FS import SCons.Util import SCons.Warnings +import SCons.Environment built_it = None @@ -605,7 +606,7 @@ class VariantDirTestCase(unittest.TestCase): print("File `%s' alter_targets() `%s' != expected `%s'" % (f, tp, expect)) errors = errors + 1 - self.failIf(errors) + self.assertFalse(errors) class BaseTestCase(_tempdirTestCase): def test_stat(self): @@ -1133,7 +1134,10 @@ class FSTestCase(_tempdirTestCase): e1 = fs.Entry(p) e2 = fs.Entry(path) assert e1 is e2, (e1, e2) - assert str(e1) is str(e2), (str(e1), str(e2)) + a=str(e1) + b=str(e2) + assert a == b, ("Strings should match for same file/node\n%s\n%s"%(a,b)) + # Test for a bug in 0.04 that did not like looking up # dirs with a trailing slash on Windows. @@ -1657,7 +1661,12 @@ class FSTestCase(_tempdirTestCase): import ntpath x = test.workpath(*dirs) drive, path = ntpath.splitdrive(x) - unc, path = ntpath.splitunc(path) + try: + unc, path = ntpath.splitunc(path) + except AttributeError: + # could be python 3.7 or newer, make sure splitdrive can do UNC + assert ntpath.splitdrive(r'\\split\drive\test')[0] == r'\\split\drive' + pass path = strip_slash(path) return '//' + path[1:] @@ -2455,6 +2464,139 @@ class FileTestCase(_tempdirTestCase): assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1) assert not os.path.exists(build_f1.get_abspath()), "%s did not get removed after %s was removed" % (build_f1, src_f1) + def test_changed(self): + """ + Verify that changes between BuildInfo's list of souces, depends, and implicit + dependencies do not corrupt content signature values written to .SConsign + when using CacheDir and Timestamp-MD5 decider. + This is for issue #2980 + """ + # node should have + # 1 source (for example main.cpp) + # 0 depends + # N implicits (for example ['alpha.h', 'beta.h', 'gamma.h', '/usr/bin/g++']) + + class ChangedNode(SCons.Node.FS.File): + def __init__(self, name, directory=None, fs=None): + SCons.Node.FS.File.__init__(self, name, directory, fs) + self.name = name + self.Tag('found_includes', []) + self.stored_info = None + self.build_env = None + self.changed_since_last_build = 4 + self.timestamp = 1 + + def get_stored_info(self): + return self.stored_info + + def get_build_env(self): + return self.build_env + + def get_timestamp(self): + """ Fake timestamp so they always match""" + return self.timestamp + + def get_contents(self): + return self.name + + def get_ninfo(self): + """ mocked to ensure csig will equal the filename""" + try: + return self.ninfo + except AttributeError: + self.ninfo = FakeNodeInfo(self.name, self.timestamp) + return self.ninfo + + def get_csig(self): + ninfo = self.get_ninfo() + try: + return ninfo.csig + except AttributeError: + pass + + return "Should Never Happen" + + class ChangedEnvironment(SCons.Environment.Base): + + def __init__(self): + SCons.Environment.Base.__init__(self) + self.decide_source = self._changed_timestamp_then_content + + class FakeNodeInfo(object): + def __init__(self, csig, timestamp): + self.csig = csig + self.timestamp = timestamp + + #Create nodes + fs = SCons.Node.FS.FS() + d = self.fs.Dir('.') + + node = ChangedNode('main.o',d,fs) # main.o + s1 = ChangedNode('main.cpp',d,fs) # main.cpp + s1.timestamp = 2 # this changed + i1 = ChangedNode('alpha.h',d,fs) # alpha.h - The bug is caused because the second build adds this file + i1.timestamp = 2 # This is the new file. + i2 = ChangedNode('beta.h',d,fs) # beta.h + i3 = ChangedNode('gamma.h',d,fs) # gamma.h - In the bug beta.h's csig from binfo overwrites this ones + i4 = ChangedNode('/usr/bin/g++',d,fs) # /usr/bin/g++ + + node.add_source([s1]) + node.add_dependency([]) + node.implicit = [i1, i2, i3, i4] + node.implicit_set = set() + # node._add_child(node.implicit, node.implicit_set, [n7, n8, n9]) + # node._add_child(node.implicit, node.implicit_set, [n10, n11, n12]) + + # Mock out node's scan method + # node.scan = lambda *args: None + + # Mock out nodes' children() ? + # Should return Node's. + # All those nodes should have changed_since_last_build set to match Timestamp-MD5's + # decider method... + + # Generate sconsign entry needed + sconsign_entry = SCons.SConsign.SConsignEntry() + sconsign_entry.binfo = node.new_binfo() + sconsign_entry.ninfo = node.new_ninfo() + + # mock out loading info from sconsign + # This will cause node.get_stored_info() to return our freshly created sconsign_entry + node.stored_info = sconsign_entry + + # binfo = information from previous build (from sconsign) + # We'll set the following attributes (which are lists): "bsources", "bsourcesigs", + # "bdepends","bdependsigs", "bimplicit", "bimplicitsigs" + bi = sconsign_entry.binfo + bi.bsources = ['main.cpp'] + bi.bsourcesigs=[FakeNodeInfo('main.cpp',1),] + + bi.bdepends = [] + bi.bdependsigs = [] + + bi.bimplicit = ['beta.h','gamma.h'] + bi.bimplicitsigs = [FakeNodeInfo('beta.h',1), FakeNodeInfo('gamma.h',1)] + + ni = sconsign_entry.ninfo + # We'll set the following attributes (which are lists): sources, depends, implicit lists + + #Set timestamp-md5 + #Call changed + #Check results + node.build_env = ChangedEnvironment() + + changed = node.changed() + + # change to true to debug + if False: + print("Changed:%s"%changed) + print("%15s -> csig:%s"%(s1.name, s1.ninfo.csig)) + print("%15s -> csig:%s"%(i1.name, i1.ninfo.csig)) + print("%15s -> csig:%s"%(i2.name, i2.ninfo.csig)) + print("%15s -> csig:%s"%(i3.name, i3.ninfo.csig)) + print("%15s -> csig:%s"%(i4.name, i4.ninfo.csig)) + + self.assertEqual(i2.name,i2.ninfo.csig, "gamma.h's fake csig should equal gamma.h but equals:%s"%i2.ninfo.csig) class GlobTestCase(_tempdirTestCase): @@ -3747,38 +3889,7 @@ class AbsolutePathTestCase(unittest.TestCase): if __name__ == "__main__": - suite = unittest.TestSuite() - suite.addTest(VariantDirTestCase()) - suite.addTest(find_fileTestCase()) - suite.addTest(StringDirTestCase()) - suite.addTest(stored_infoTestCase()) - suite.addTest(has_src_builderTestCase()) - suite.addTest(prepareTestCase()) - suite.addTest(SConstruct_dirTestCase()) - suite.addTest(clearTestCase()) - suite.addTest(disambiguateTestCase()) - suite.addTest(postprocessTestCase()) - suite.addTest(SpecialAttrTestCase()) - suite.addTest(SaveStringsTestCase()) - tclasses = [ - AbsolutePathTestCase, - BaseTestCase, - CacheDirTestCase, - DirTestCase, - DirBuildInfoTestCase, - DirNodeInfoTestCase, - EntryTestCase, - FileTestCase, - FileBuildInfoTestCase, - FileNodeInfoTestCase, - FSTestCase, - GlobTestCase, - RepositoryTestCase, - ] - for tclass in tclasses: - names = unittest.getTestCaseNames(tclass, 'test_') - suite.addTests(list(map(tclass, names))) - TestUnit.run(suite) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index e50616a..39b928b 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 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 +20,7 @@ # 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__ = "src/engine/SCons/Node/NodeTests.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Node/NodeTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import SCons.compat @@ -30,8 +30,6 @@ import re import sys import unittest -import TestUnit - import SCons.Errors import SCons.Node import SCons.Util @@ -1337,9 +1335,9 @@ class NodeListTestCase(unittest.TestCase): assert s == "['n3', 'n2', 'n1']", s r = repr(nl) - r = re.sub('at (0[xX])?[0-9a-fA-F]+', 'at 0x', r) + r = re.sub(r'at (0[xX])?[0-9a-fA-F]+', 'at 0x', r) # Don't care about ancestry: just leaf value of MyNode - r = re.sub('<.*?\.MyNode', ' +(scripts, [exports, variant_dir, duplicate, must_exist]) + -(dirs=subdirs, [name=script, exports, variant_dir, duplicate]) - +(dirs=subdirs, [name=script, exports, variant_dir, duplicate, must_exist]) + @@ -563,6 +567,17 @@ TODO??? SConscript('build/SConscript', src_dir='src') --> + +The optional +must_exist +argument, if true, causes an exception to be raised if a requested +&SConscript; file is not found. The current default is false, +causing only a warning to be omitted, but this behavior is deprecated. +For scripts which truly intend to be optional, transition to +explicty supplying +must_exist=False to the call. + + Here are some composite examples: diff --git a/src/engine/SCons/Script/SConscriptTests.py b/src/engine/SCons/Script/SConscriptTests.py index 2b10446..fb06ac4 100644 --- a/src/engine/SCons/Script/SConscriptTests.py +++ b/src/engine/SCons/Script/SConscriptTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 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,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Script/SConscriptTests.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Script/SConscriptTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import SCons.Script.SConscript diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index ffafadf..834c70d 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -12,7 +12,7 @@ it goes here. """ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -34,7 +34,7 @@ it goes here. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Script/__init__.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Script/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import time start_time = time.time() @@ -81,8 +81,8 @@ import SCons.Action import SCons.Builder import SCons.Environment import SCons.Node.FS -import SCons.Options import SCons.Platform +import SCons.Platform.virtualenv import SCons.Scanner import SCons.SConf import SCons.Subst @@ -150,6 +150,7 @@ Environment = SCons.Environment.Environment #OptParser = SCons.SConsOptions.OptParser FindPathDirs = SCons.Scanner.FindPathDirs Platform = SCons.Platform.Platform +Virtualenv = SCons.Platform.virtualenv.Virtualenv Return = _SConscript.Return Scanner = SCons.Scanner.Base Tool = SCons.Tool.Tool @@ -162,12 +163,6 @@ ListVariable = SCons.Variables.ListVariable PackageVariable = SCons.Variables.PackageVariable PathVariable = SCons.Variables.PathVariable -# Deprecated names that will go away some day. -BoolOption = SCons.Options.BoolOption -EnumOption = SCons.Options.EnumOption -ListOption = SCons.Options.ListOption -PackageOption = SCons.Options.PackageOption -PathOption = SCons.Options.PathOption # Action factories. Chmod = SCons.Defaults.Chmod @@ -283,12 +278,20 @@ def HelpFunction(text, append=False): # Will be non-zero if we are reading an SConscript file. sconscript_reading = 0 +_no_missing_sconscript = False +_warn_missing_sconscript_deprecated = True + +def set_missing_sconscript_error(flag=1): + """Set behavior on missing file in SConscript() call. Returns previous value""" + global _no_missing_sconscript + old = _no_missing_sconscript + _no_missing_sconscript = flag + return old + # def Variables(files=[], args=ARGUMENTS): return SCons.Variables.Variables(files, args) -def Options(files=[], args=ARGUMENTS): - return SCons.Options.Options(files, args) # The list of global functions to add to the SConscript name space # that end up calling corresponding methods or Builders in the @@ -374,7 +377,9 @@ GlobalDefaultBuilders = [ 'SharedObject', 'StaticLibrary', 'StaticObject', + 'Substfile', 'Tar', + 'Textfile', 'TypeLibrary', 'Zip', 'Package', diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 90c12a0..4fc8461 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -5,7 +5,7 @@ SCons string substitution. """ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -26,7 +26,7 @@ SCons string substitution. # 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__ = "src/engine/SCons/Subst.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Subst.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import collections import re @@ -86,6 +86,9 @@ class Literal(object): def __neq__(self, other): return not self.__eq__(other) + def __hash__(self): + return hash(self.lstr) + class SpecialAttrWrapper(object): """This is a wrapper for what we call a 'Node special attribute.' This is any of the attributes of a Node that we can reference from @@ -338,7 +341,10 @@ SUBST_RAW = 1 SUBST_SIG = 2 _rm = re.compile(r'\$[()]') -_rm_split = re.compile(r'(\$[()])') + +# Note the pattern below only matches $( or $) when there is no +# preceeding $. (Thus the (? -%scons; - -%builders-mod; - -%functions-mod; - -%tools-mod; - -%variables-mod; -]> + + %scons; + + %builders-mod; + + %functions-mod; + + %tools-mod; + + %variables-mod; + ]> - - - -Sets construction variables for the Apple linker -(similar to the GNU linker). - - - -FRAMEWORKPATHPREFIX -_FRAMEWORKPATH -_FRAMEWORKS -LINKCOM -SHLINKFLAGS -SHLINKCOM -LDMODULEPREFIX -LDMODULESUFFIX -LDMODULEFLAGS -LDMODULECOM - - -FRAMEWORKSFLAGS - - - -"> - - -On Mac OS X with gcc, -general user-supplied frameworks options to be added at -the end of a command -line building a loadable module. -(This has been largely superseded by -the &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKPATHPREFIX;, -&cv-link-FRAMEWORKPREFIX; and &cv-link-FRAMEWORKS; variables -described above.) - - - - - - - -On Mac OS X with gcc, a list of the framework names to be linked into a -program or shared library or bundle. -The default value is the empty list. -For example: - - - - env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) - - - - - - - - -On Mac OS X with gcc, -the prefix to be used for linking in frameworks -(see &cv-link-FRAMEWORKS;). -The default value is -. - - - - - - - -On Mac OS X with gcc, -an automatically-generated construction variable -containing the linker command-line options -for linking with FRAMEWORKS. - - - - - - - -On Mac OS X with gcc, -a list containing the paths to search for frameworks. -Used by the compiler to find framework-style includes like -#include <Fmwk/Header.h>. -Used by the linker to find user-specified frameworks when linking (see -&cv-link-FRAMEWORKS;). -For example: - - - - env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') - - - -will add - - - - ... -Fmyframeworkdir - - - -to the compiler and linker command lines. - - - - - - - -On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. -(see &cv-link-FRAMEWORKPATH;). -The default value is -. - - - - - - - -On Mac OS X with gcc, an automatically-generated construction variable -containing the linker command-line options corresponding to -&cv-link-FRAMEWORKPATH;. - - - + + + + Sets construction variables for the Apple linker + (similar to the GNU linker). + + + + FRAMEWORKPATHPREFIX + _FRAMEWORKPATH + _FRAMEWORKS + LINKCOM + SHLINKFLAGS + SHLINKCOM + LDMODULEPREFIX + LDMODULESUFFIX + LDMODULEFLAGS + LDMODULECOM + APPLELINK_CURRENT_VERSION + APPLELINK_COMPATIBILITY_VERSION + APPLELINK_NO_CURRENT_VERSION + APPLELINK_NO_COMPATIBILITY_VERSION + _APPLELINK_CURRENT_VERSION + _APPLELINK_COMPATIBILITY_VERSION + + + FRAMEWORKSFLAGS + + + + + + + + On Mac OS X this is used to set the linker flag: + + -compatibility_version + + + The value is specified as X[.Y[.Z]] where X is between 1 and 65535, Y can be omitted or between 1 and + 255, Z can be omitted or between 1 and 255. This value will be derived from &cv-link-SHLIBVERSION; if + not + specified. The lowest digit will be dropped and replaced by a 0. + + + If the &cv-link-APPLELINK_NO_COMPATIBILITY_VERSION; is set then no -compatibility_version will be + output. + + See MacOS's ld manpage for more details + + + + + + + Set this to any True (1|True|non-empty string) value to disable adding -compatibility_version flag when + generating versioned shared libraries. + + + This overrides &cv-link-APPLELINK_COMPATIBILITY_VERSION;. + + + + + + + + + A macro (by default a generator function) used to create the linker flags to specify + apple's linker's -compatibility_version flag. + The default generator uses &cv-link-APPLELINK_COMPATIBILITY_VERSION; + and &cv-link-APPLELINK_NO_COMPATIBILITY_VERSION; and &cv-link-SHLIBVERSION; + to determine the correct flag. + + + + + + + + + On Mac OS X this is used to set the linker flag: + + -current_version + + + The value is specified as X[.Y[.Z]] where X is between 1 and 65535, Y can be omitted or between 1 and + 255, Z can be omitted or between 1 and 255. This value will be set to &cv-link-SHLIBVERSION; if not + specified. + + + If the &cv-link-APPLELINK_NO_CURRENT_VERSION; is set then no -current_version will be + output. + + See MacOS's ld manpage for more details + + + + + + + + + Set this to any True (1|True|non-empty string) value to disable adding -current_version flag when + generating versioned shared libraries. + + + This overrides &cv-link-APPLELINK_CURRENT_VERSION;. + + + + + + + + A macro (by default a generator function) used to create the linker flags to specify apple's linker's + -current_version flag. The default generator uses &cv-link-APPLELINK_CURRENT_VERSION; and + &cv-link-APPLELINK_NO_CURRENT_VERSION; and &cv-link-SHLIBVERSION; to determine the correct flag. + + + + + + "> + + + On Mac OS X with gcc, + general user-supplied frameworks options to be added at + the end of a command + line building a loadable module. + (This has been largely superseded by + the &cv-link-FRAMEWORKPATH;, &cv-link-FRAMEWORKPATHPREFIX;, + &cv-link-FRAMEWORKPREFIX; and &cv-link-FRAMEWORKS; variables + described above.) + + + + + + + + On Mac OS X with gcc, a list of the framework names to be linked into a + program or shared library or bundle. + The default value is the empty list. + For example: + + + + env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration')) + + + + + + + + + On Mac OS X with gcc, + the prefix to be used for linking in frameworks + (see &cv-link-FRAMEWORKS;). + The default value is + . + + + + + + + + On Mac OS X with gcc, + an automatically-generated construction variable + containing the linker command-line options + for linking with FRAMEWORKS. + + + + + + + + On Mac OS X with gcc, + a list containing the paths to search for frameworks. + Used by the compiler to find framework-style includes like + #include <Fmwk/Header.h>. + Used by the linker to find user-specified frameworks when linking (see + &cv-link-FRAMEWORKS;). + For example: + + + + env.AppendUnique(FRAMEWORKPATH='#myframeworkdir') + + + + will add + + + + ... -Fmyframeworkdir + + + + to the compiler and linker command lines. + + + + + + + + On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries. + (see &cv-link-FRAMEWORKPATH;). + The default value is + . + + + + + + + + On Mac OS X with gcc, an automatically-generated construction variable + containing the linker command-line options corresponding to + &cv-link-FRAMEWORKPATH;. + + + diff --git a/src/engine/SCons/Tool/ar.py b/src/engine/SCons/Tool/ar.py index 4d2a005..bef719f 100644 --- a/src/engine/SCons/Tool/ar.py +++ b/src/engine/SCons/Tool/ar.py @@ -9,7 +9,7 @@ selection method. """ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ar.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ar.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/ar.xml b/src/engine/SCons/Tool/ar.xml index 9b9e0a0..573c80b 100644 --- a/src/engine/SCons/Tool/ar.xml +++ b/src/engine/SCons/Tool/ar.xml @@ -1,6 +1,6 @@ -%scons; - -%builders-mod; - -%functions-mod; - -%tools-mod; - -%variables-mod; -]> + + %scons; + + %builders-mod; + + %functions-mod; + + %tools-mod; + + %variables-mod; + ]> - - - -Sets construction variables for the &javac; compiler. - - - -JAVAC -JAVACFLAGS -JAVACCOM -JAVACLASSSUFFIX -JAVASUFFIX -JAVABOOTCLASSPATH -JAVACLASSPATH -JAVASOURCEPATH - - -JAVACCOMSTR - - + + + + Sets construction variables for the &javac; compiler. + + + + JAVAC + JAVACFLAGS + JAVACCOM + JAVACLASSSUFFIX + JAVAINCLUDES + JAVASUFFIX + JAVABOOTCLASSPATH + JAVACLASSPATH + JAVASOURCEPATH + + + JAVACCOMSTR + + - - - -Builds one or more Java class files. -The sources may be any combination of explicit -.java files, -or directory trees which will be scanned -for .java files. - + + + + Builds one or more Java class files. + The sources may be any combination of explicit + .java + files, + or directory trees which will be scanned + for .java files. + - -SCons will parse each source .java file -to find the classes -(including inner classes) -defined within that file, -and from that figure out the -target .class files that will be created. -The class files will be placed underneath -the specified target directory. - + + SCons will parse each source .java file + to find the classes + (including inner classes) + defined within that file, + and from that figure out the + target .class files that will be created. + The class files will be placed underneath + the specified target directory. + - -SCons will also search each Java file -for the Java package name, -which it assumes can be found on a line -beginning with the string -package -in the first column; -the resulting .class files -will be placed in a directory reflecting -the specified package name. -For example, -the file -Foo.java -defining a single public -Foo -class and -containing a package name of -sub.dir -will generate a corresponding -sub/dir/Foo.class -class file. - + + SCons will also search each Java file + for the Java package name, + which it assumes can be found on a line + beginning with the string + package + in the first column; + the resulting .class files + will be placed in a directory reflecting + the specified package name. + For example, + the file + Foo.java + defining a single public + Foo + class and + containing a package name of + sub.dir + will generate a corresponding + sub/dir/Foo.class + class file. + - -Examples: - + + Examples: + - -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']) + - -Java source files can use the native encoding for the underlying OS. -Since SCons compiles in simple ASCII mode by default, -the compiler will generate warnings about unmappable characters, -which may lead to errors as the file is processed further. -In this case, the user must specify the LANG -environment variable to tell the compiler what encoding is used. -For portibility, it's best if the encoding is hard-coded -so that the compile will work if it is done on a system -with a different encoding. - + + Java source files can use the native encoding for the underlying OS. + Since SCons compiles in simple ASCII mode by default, + the compiler will generate warnings about unmappable characters, + which may lead to errors as the file is processed further. + In this case, the user must specify the + LANG + environment variable to tell the compiler what encoding is used. + For portibility, it's best if the encoding is hard-coded + so that the compile will work if it is done on a system + with a different encoding. + - -env = Environment() -env['ENV']['LANG'] = 'en_GB.UTF-8' - - - + + env = Environment() + env['ENV']['LANG'] = 'en_GB.UTF-8' + + + - - - -Specifies the list of directories that -will be added to the -&javac; command line -via the option. -The individual directory names will be -separated by the operating system's path separate character -(: on UNIX/Linux/POSIX, -; on Windows). - - - + + + + Specifies the list of directories that + will be added to the + &javac; command line + via the option. + The individual directory names will be + separated by the operating system's path separate character + (: on UNIX/Linux/POSIX, + ; + on Windows). + + + - - - -The Java compiler. - - - + + + + Include path for Java header files (such as jni.h) + + + - - - -The command line used to compile a directory tree containing -Java source files to -corresponding Java class files. -Any options specified in the &cv-link-JAVACFLAGS; construction variable -are included on this command line. - - - + + + + The Java compiler. + + + - - - -The string displayed when compiling -a directory tree of Java source files to -corresponding Java class files. -If this is not set, then &cv-link-JAVACCOM; (the command line) is displayed. - + + + + The command line used to compile a directory tree containing + Java source files to + corresponding Java class files. + Any options specified in the &cv-link-JAVACFLAGS; construction variable + are included on this command line. + + + - -env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") - - - + + + + The string displayed when compiling + a directory tree of Java source files to + corresponding Java class files. + If this is not set, then &cv-link-JAVACCOM; (the command line) is displayed. + - - - -General options that are passed to the Java compiler. - - - + + env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES") + + + - - - -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 -JavaH -builder. - - - + + + + General options that are passed to the Java compiler. + + + - - - -Specifies the list of directories that -will be searched for Java -.class file. -The directories in this list will be added to the -&javac; and &javah; command lines -via the option. -The individual directory names will be -separated by the operating system's path separate character -(: on UNIX/Linux/POSIX, -; on Windows). - + + + + 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 + JavaH + builder. + + + - -Note that this currently just adds the specified -directory via the option. -&SCons; does not currently search the -&cv-JAVACLASSPATH; directories for dependency -.class files. - - - + + + + Specifies the list of directories that + will be searched for Java + .class + file. + The directories in this list will be added to the + &javac; and &javah; command lines + via the option. + The individual directory names will be + separated by the operating system's path separate character + (: on UNIX/Linux/POSIX, + ; + on Windows). + - - - -The suffix for Java class files; -.class -by default. - - - + + Note that this currently just adds the specified + directory via the option. + &SCons; does not currently search the + &cv-JAVACLASSPATH; directories for dependency + .class + files. + + + - - - -Specifies the list of directories that -will be searched for input -.java file. -The directories in this list will be added to the -&javac; command line -via the option. -The individual directory names will be -separated by the operating system's path separate character -(: on UNIX/Linux/POSIX, -; on Windows). - + + + + The suffix for Java class files; + .class + by default. + + + - -Note that this currently just adds the specified -directory via the option. -&SCons; does not currently search the -&cv-JAVASOURCEPATH; directories for dependency -.java files. - - - + + + + Specifies the list of directories that + will be searched for input + .java + file. + The directories in this list will be added to the + &javac; command line + via the option. + The individual directory names will be + separated by the operating system's path separate character + (: on UNIX/Linux/POSIX, + ; + on Windows). + - - - -The suffix for Java files; -.java -by default. - - - + + Note that this currently just adds the specified + directory via the option. + &SCons; does not currently search the + &cv-JAVASOURCEPATH; directories for dependency + .java + files. + + + - - - -Specifies the Java version being used by the &b-Java; builder. -This is not currently used to select one -version of the Java compiler vs. another. -Instead, you should set this to specify the version of Java -supported by your &javac; compiler. -The default is 1.4. - + + + + The suffix for Java files; + .java + by default. + + + - -This is sometimes necessary because -Java 1.5 changed the file names that are created -for nested anonymous inner classes, -which can cause a mismatch with the files -that &SCons; expects will be generated by the &javac; compiler. -Setting &cv-JAVAVERSION; to 1.5 -(or 1.6, as appropriate) -can make &SCons; realize that a Java 1.5 or 1.6 -build is actually up to date. - - - + + + + Specifies the Java version being used by the &b-Java; builder. + This is not currently used to select one + version of the Java compiler vs. another. + Instead, you should set this to specify the version of Java + supported by your &javac; compiler. + The default is 1.4. + + + + This is sometimes necessary because + Java 1.5 changed the file names that are created + for nested anonymous inner classes, + which can cause a mismatch with the files + that &SCons; expects will be generated by the &javac; compiler. + Setting &cv-JAVAVERSION; to + 1.5 + (or 1.6, as appropriate) + can make &SCons; realize that a Java 1.5 or 1.6 + build is actually up to date. + + + diff --git a/src/engine/SCons/Tool/javacTests.py b/src/engine/SCons/Tool/javacTests.py index 0debbb6..067ff3e 100644 --- a/src/engine/SCons/Tool/javacTests.py +++ b/src/engine/SCons/Tool/javacTests.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -24,8 +24,6 @@ import os import unittest -import TestUnit - import SCons.Tool.javac class DummyNode(object): @@ -40,14 +38,14 @@ class pathoptTestCase(unittest.TestCase): popt = SCons.Tool.javac.pathopt('-foopath', 'FOOPATH') env = {'FOOPATH': path} actual = popt(None, None, env, None) - self.assertEquals(expect, actual) + self.assertEqual(expect, actual) def assert_pathopt_default(self, expect, path, default): popt = SCons.Tool.javac.pathopt('-foopath', 'FOOPATH', default='DPATH') env = {'FOOPATH': path, 'DPATH': default} actual = popt(None, None, env, None) - self.assertEquals(expect, actual) + self.assertEqual(expect, actual) def test_unset(self): self.assert_pathopt([], None) @@ -101,5 +99,4 @@ class pathoptTestCase(unittest.TestCase): '') if __name__ == "__main__": - suite = unittest.makeSuite(pathoptTestCase, 'test_') - TestUnit.run(suite) + unittest.main() diff --git a/src/engine/SCons/Tool/javah.py b/src/engine/SCons/Tool/javah.py index 0e1885c..84183cb 100644 --- a/src/engine/SCons/Tool/javah.py +++ b/src/engine/SCons/Tool/javah.py @@ -9,7 +9,7 @@ selection method. """ # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/javah.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog" +__revision__ = "src/engine/SCons/Tool/javah.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" import os.path @@ -40,6 +40,8 @@ import SCons.Builder import SCons.Node.FS import SCons.Tool.javac import SCons.Util +from SCons.Tool.JavaCommon import get_java_install_dirs + def emit_java_headers(target, source, env): """Create and return lists of Java stub header files that will @@ -120,6 +122,14 @@ def generate(env): java_javah = SCons.Tool.CreateJavaHBuilder(env) java_javah.emitter = emit_java_headers + if env['PLATFORM'] == 'win32': + # Ensure that we have a proper path for clang + javah = SCons.Tool.find_program_path(env, 'javah', + default_paths=get_java_install_dirs(env['PLATFORM'])) + if javah: + javah_bin_dir = os.path.dirname(javah) + env.AppendENVPath('PATH', javah_bin_dir) + env['_JAVAHOUTFLAG'] = JavaHOutFlagGenerator env['JAVAH'] = 'javah' env['JAVAHFLAGS'] = SCons.Util.CLVar('') diff --git a/src/engine/SCons/Tool/javah.xml b/src/engine/SCons/Tool/javah.xml index f362e3e..f72a6cd 100644 --- a/src/engine/SCons/Tool/javah.xml +++ b/src/engine/SCons/Tool/javah.xml @@ -1,6 +1,6 @@ sconsign options - file - ... + file @@ -54,30 +53,38 @@ The sconsign command -displays the contents of one or more -.sconsign +displays the contents of one or more signature +("sconsign") files specified by the user. By default, sconsign dumps the entire contents of the specified file(s). -Each entry is printed in the following format: +Without the verbose option, +each entry is printed in the following format: - file: signature timestamp length - implicit_dependency_1: signature timestamp length - implicit_dependency_2: signature timestamp length - action_signature [action string] + +file: signature timestamp length + implicit_dependency_1: signature timestamp length + implicit_dependency_2: signature timestamp length + ... + action_signature [action string] + None is printed -in place of any missing timestamp, bsig, or csig +in place of any missing timestamp, build signature ("bsig"), +or content signature ("csig") values for any entry or any of its dependencies. If the entry has no implicit dependencies, or no build action, -the lines are simply omitted. +the lines are simply omitted. +The verbose option expands the display into a more human +readable format. + By default, sconsign @@ -90,7 +97,7 @@ signature entries for more than one directory (that is, was specified by the -SConsignFile () +SConsignFile function). Any file @@ -103,13 +110,21 @@ for a single directory. If neither of those is true, sconsign attempts to guess the format. -If that does not work, +If that does not work, an explicit format may be specified using the or -options. +options. + + +If there are no +file +arguments, the name +.sconsign.dblite +is assumed. + @@ -175,7 +190,7 @@ Legal values are dbm (the DBM format used when the -SConsignFile() +SConsignFile function is used) or sconsign @@ -197,7 +212,7 @@ file in each directory). -i, --implicit Prints the list of cached implicit dependencies -for all entries or the the specified entries. +for all entries or for the specified entries. @@ -206,8 +221,8 @@ for all entries or the the specified entries. Prints a pretty-printed representation of the raw Python dictionary that holds -build information about individual entry -(both the entry itself or its implicit dependencies). +build information about individual entries +(both the entry itself and its implicit dependencies). An entry's build action is still printed in its usual format. @@ -244,7 +259,8 @@ for all entries or the specified entries. SCONS_LIB_DIR Specifies the directory that contains the SCons Python module directory -(e.g. /home/aroach/scons-src-0.01/src/engine). +(e.g. +/home/aroach/scons-src-0.01/src/engine). on the command line. diff --git a/doc/scons.mod b/doc/scons.mod index ea9ad4e..cf46e3b 100644 --- a/doc/scons.mod +++ b/doc/scons.mod @@ -67,6 +67,7 @@ Action"> ActionBase"> +BuildInfo"> CommandAction"> FunctionAction"> ListAction"> diff --git a/doc/user/depends.xml b/doc/user/depends.xml index 35372e7..bb0a142 100644 --- a/doc/user/depends.xml +++ b/doc/user/depends.xml @@ -517,7 +517,7 @@ cc -o hello hello.o Program('hello.c') -def decide_if_changed(dependency, target, prev_ni): +def decide_if_changed(dependency, target, prev_ni, repo_node=None): if dependency.get_timestamp() != prev_ni.timestamp: dep = str(dependency) tgt = str(target) @@ -561,6 +561,13 @@ int main() { printf("Hello, world!\n"); } + + The fourth argument repo_node, + is the &Node; to use if it is not None when comparing &BuildInfo;. + This is typically only set when the target node only exists in a + &Repository; + + @@ -637,7 +644,7 @@ int main() { printf("Hello, world!\n"); } env = Environment() -def config_file_decider(dependency, target, prev_ni): +def config_file_decider(dependency, target, prev_ni, repo_node=None): import os.path # We always have to init the .csig value... diff --git a/doc/user/less-simple.xml b/doc/user/less-simple.xml index 6de1075..e8ff44a 100644 --- a/doc/user/less-simple.xml +++ b/doc/user/less-simple.xml @@ -2,7 +2,7 @@ %scons; - + %builders-mod; @@ -257,7 +257,7 @@ Program('program', Glob('*.c')) (see , below) and repositories (see , below), - excluding some files + excluding some files and returning strings rather than Nodes. @@ -311,7 +311,7 @@ Program('hello', ['hello.c']) - + Although &SCons; functions @@ -359,7 +359,7 @@ Program('program2', common_sources + ['program2.c']) One drawback to the use of a Python list - for source files is that + for source files is that each file name must be enclosed in quotes (either single quotes or double quotes). This can get cumbersome and difficult to read @@ -675,8 +675,10 @@ env.SharedLibrary('word', 'word.cpp', - It is also possible to use the parse_flags keyword argument in an - override: + It is also possible to use the parse_flags + keyword argument in an override to merge command-line + style arguments into the appropriate construction + variables (see &f-link-env-MergeFlags;). @@ -688,7 +690,7 @@ env.SharedLibrary('word', 'word.cpp', -env = Program('hello', 'hello.c', parse_flags = '-Iinclude -DEBUG -lm') +env = Program('hello', 'hello.c', parse_flags='-Iinclude -DEBUG -lm') @@ -701,4 +703,4 @@ env = Program('hello', 'hello.c', parse_flags = '-Iinclude -DEBUG -lm') - \ No newline at end of file + diff --git a/site_scons/soe_utils.py b/site_scons/soe_utils.py index 451c7de..3b87dee 100644 --- a/site_scons/soe_utils.py +++ b/site_scons/soe_utils.py @@ -17,16 +17,14 @@ def soelim(target, source, env): t = str(target[0]) s = str(source[0]) dir, f = os.path.split(s) - tfp = open(t, 'w') - sfp = open(s, 'r') - for line in sfp.readlines(): - if line[:4] in ['.so ', "'so "]: - sofile = os.path.join(dir, line[4:-1]) - tfp.write(open(sofile, 'r').read()) - else: - tfp.write(line) - sfp.close() - tfp.close() + with open(t, 'w') as tfp, open(s, 'r') as sfp: + for line in sfp.readlines(): + if line[:4] in ['.so ', "'so "]: + sofile = os.path.join(dir, line[4:-1]) + with open(sofile, 'r') as f: + tfp.write(f.read()) + else: + tfp.write(line) def soscan(node, env, path): c = node.get_text_contents() diff --git a/src/Announce.txt b/src/Announce.txt index 324b9fc..2ceb8bc 100755 --- a/src/Announce.txt +++ b/src/Announce.txt @@ -18,7 +18,7 @@ So that everyone using SCons can help each other learn how to use it more effectively, please go to http://scons.org/lists.html#users to sign up for the scons-users mailing list. -RELEASE 3.0.5 - Mon, 26 Mar 2019 15:04:42 -0700 +RELEASE 3.1.0 - Mon, 20 Jul 2019 16:59:23 -0700 Please consult the RELEASE.txt file for a summary of changes since the last release and consult the CHANGES.txt file for complete a list of changes diff --git a/src/CHANGES.txt b/src/CHANGES.txt index c46a804..a23140c 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -4,6 +4,102 @@ Change Log +RELEASE 3.1.0 - Mon, 20 Jul 2019 16:59:23 -0700 + + From Joseph Brill: + - Code to supply correct version-specifier argument to vswhere for + VS version selection. + + From William Deegan: + - Enhanced --debug=explain output. Now the separate components of the dependency list are split up + as follows: + + scons: rebuilding `file3' because: + the dependency order changed: + ->Sources + Old:xxx New:zzz + Old:yyy New:yyy + Old:zzz New:xxx + ->Depends + ->Implicit + Old:/usr/bin/python New:/usr/bin/python + - Fix Issue #3350 - SCons Exception EnvironmentError is conflicting with Python's EnvironmentError. + - Fix spurious rebuilds on second build for cases where builder has > 1 target and the source file + is generated. This was causing the > 1th target to not have it's implicit list cleared when the source + file was actually built, leaving an implicit list similar to follows for 2nd and higher target + ['/usr/bin/python', 'xxx', 'yyy', 'zzz'] + This was getting persisted to SConsign and on rebuild it would be corrected to be similar to this + ['zzz', 'yyy', 'xxx', '/usr/bin/python'] + Which would trigger a rebuild because the order changed. + The fix involved added logic to mark all shared targets as peers and then ensure they're implicit + list is all cleared together. + - Fix Issue #3349 - SCons Exception EnvironmentError is conflicting with Python's EnvironmentError. + Renamed to SConsEnvironmentError + - Fix Issue #3350 - mslink failing when too many objects. This is resolved by adding TEMPFILEARGJOIN variable + which specifies what character to join all the argements output into the tempfile. The default remains a space + when mslink, msvc, or mslib tools are loaded they change the TEMPFILEARGJOIN to be a line separator (\r\n on win32) + - Fix performance degradation for MD5-timestamp decider. NOTE: This changes the Decider() function arguments. + From: + def my_decider(dependency, target, prev_ni): + To: + def my_decider(dependency, target, prev_ni, repo_node): + Where repo_node is the repository (or other) node to use to check if the node is out of date instead of dependency. + + From Peter Diener: + - Additional fix to issue #3135 - Also handle 'pure' and 'elemental' type bound procedures + - Fix issue #3135 - Handle Fortran submodules and type bound procedures + + From Adam Gross: + - Upgraded and improved Visual Studio solution/project generation code using the MSVSProject builder. + - Added support for Visual Studio 2017 and 2019. + - Added support for the following per-variant parameters to the builder: + - cpppaths: Provides per-variant include paths. + - cppdefines: Provides per-variant preprocessor definitions. + + From Michael Hartmann: + - Fix handling of Visual Studio Compilers to properly reject any unknown HOST_PLATFORM or TARGET_PLATFORM + + From Bert Huijben: + - Added support for Visual Studio 2019 toolset. + + From Mathew Robinson: + - Update cache debug output to include cache hit rate. + - No longer unintentionally hide exceptions in Action.py + - Allow builders and pseudo-builders to inherit from OverrideEnvironments + + From Leonard de Ruijter: + - Add logic to derive correct version argument to vswhere + + From Lukas Schrangl: + - Enable LaTeX scanner to find more than one include per line + + From Mats Wichmann: + - scons-time takes more care closing files and uses safer mkdtemp to avoid + possible races on multi-job runs. + - Use importlib to dynamically load tool and platform modules instead of imp module + - sconsign: default to .sconsign.dblite if no filename is specified. + Be more informative in case of unsupported pickle protocol (py2 only). + - Fix issue #3336 - on Windows, paths were being added to PATH even if + tools were not found in those paths. + - More fixes for newer Java versions (since 9): handle new jdk directory + naming (jdk-X.Y instead of jdkX.Y) on Windows; handle two-digit major + version. Docstrings improved. + - Fixups for pylint: exception types, redefined functions, + globals, etc. Some old code removed to resolve issues (hashlib is + always present on modern Pythons; no longer need the code for + 2.5-and-earlier optparse). cmp is not a builtin function in Py3, + drop one (unused) use; replace one. Fix another instance of + renaming to SConsEnvironmentError. Trailing whitespace. + Consistently use not is/in (if not x is y -> if x is not y). + - Add a PY3-only function for setting up the cachedir that should be less + prone to races. Add a hack to the PY2 version (from Issue #3351) to + be less prone to a race in the check for old-style cache. + - Fix coding error in docbook tool only exercised when using python lxml + - Recognize two additional GNU compiler header directory options in + ParseFlags: -iquote and -idirafter. + - Fix more re patterns that contain \ but not specified as raw strings + (affects scanners for D, LaTeX, swig) + RELEASE 3.0.5 - Mon, 26 Mar 2019 15:04:42 -0700 @@ -24,7 +120,7 @@ RELEASE 3.0.5 - Mon, 26 Mar 2019 15:04:42 -0700 From Maciej Kumorek: - Update the MSVC tool to include the nologo flag by default in RCFLAGS - From Daniel Moody: +From Daniel Moody: - Change the default for AppendENVPath to delete_existing=0, so path order will not be changed, unless explicitly set (Issue #3276) - Fixed bug which threw error when running SCons on windows system with no MSVC installed. diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 90cc745..9d0e092 100755 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -1,43 +1,77 @@ -A new SCons release, 3.0.5, is now available on the SCons download page: + A new SCons checkpoint release, 3.1.0, is now available + on the SCons download page: - https://scons.org/pages/download.html + https://scons.org/pages/download.html + Here is a summary of the changes since 3.0.5: -Here is a summary of the changes since 3.0.4: + NEW FUNCTIONALITY -CHANGED/ENHANCED EXISTING FUNCTIONALITY + - Added variable TEMPFILEARGJOIN to specify how to join arguments written + to temp files used when command lines exceed MAXLINELENGTH when the + command uses $TEMPFILE{...} + - Support for MSVC 2019 + - Upgraded and improved Visual Studio solution/project generation code using the MSVSProject builder. + - Added support for Visual Studio 2017 and 2019. + - Added support for the following per-variant parameters to the builder: + - cpppaths: Provides per-variant include paths. + - cppdefines: Provides per-variant preprocessor definitions. - - Change the default for AppendENVPath to delete_existing=0, so path - order will not be changed, unless explicitly set (Issue #3276) - - Add lex construction variable LEXUNISTD for turning off unix headers on windows - - Update lex tool to use win_flex on windows if available - - Add the textfile tool to the default tool list -FIXES + CHANGED/ENHANCED EXISTING FUNCTIONALITY - - Fix Issue #3283 - Handle using --config=force in combination with Decider('MD5-timestamp'). - 3.0.2 in fix for issue #2980 added that deciders can throw DeciderNeedsNode exception. - The Configure logic directly calls the decider when using --config=force but wasn't handling - that exception. This would yield minimally configure tests using TryLink() not running and - leaving TypeError Nonetype exception in config.log - - Fix Issue #3303 - Handle --config=force overwriting the Environment passed into Configure()'s - Decider and not clearing it when the configure context is completed. - - Add default paths for yacc tool on windows to include cygwin, mingw, and chocolatey - - Fix issue #2799 - Fix mingw tool to respect SHCCCOMSTR, SHLINKCOMSTR and LDMODULECOMSTR - - Fix Issue #3329 - Add support for MS SDK V10.0A (which is commonly installed with VS2017) - - Fix Issue #3333 - Add support for finding vswhere under 32 bit windows installs. - - Update the MSVC tool to include the nologo flag by default in RCFLAGS - - Fixed bug which threw error when running SCons on windows system with no MSVC installed. + - Fix performance degradation for MD5-timestamp decider. NOTE: This changes the Decider() function arguments. + From: + def my_decider(dependency, target, prev_ni): + To: + def my_decider(dependency, target, prev_ni, repo_node): + Where repo_node is the repository (or other) node to use to check if the node is out of date instead of dependency. + - Enhanced --debug=explain output. Now the separate components of the dependency list are split up + as follows: -IMPROVEMENTS - - Do not store build host+user name if reproducible builds are wanted + scons: rebuilding `file3' because: + the dependency order changed: + ->Sources + Old:xxx New:zzz + Old:yyy New:yyy + Old:zzz New:xxx + ->Depends + ->Implicit + Old:/usr/bin/python New:/usr/bin/python + - Changed: Pseudo-builders now inherit OverrideEnvironments. For + example when calling a pseudo-builder from another + pseudo-builder the override variables passed to the first + pseudo-builder call had to be explicitly passed on to the + internal pseudo-builder call. Now the second pseudo-builder call + will automatically inherit these override values. -git shortlog --no-merges -ns 3.0.4..HEAD - 34 William Deegan - 33 Mats Wichmann - 18 Daniel - 4 Daniel Moody - 3 Bernhard M. Wiedemann - 2 Maciej Kumorek + FIXES + - Fix Issue #3350 - SCons Exception EnvironmentError is conflicting with Python's EnvironmentError. + - Fix spurious rebuilds on second build for cases where builder has > 1 target and the source file + is generated. This was causing the > 1th target to not have it's implicit list cleared when the source + file was actually built, leaving an implicit list similar to follows for 2nd and higher target + ['/usr/bin/python', 'xxx', 'yyy', 'zzz'] + This was getting persisted to SConsign and on rebuild it would be corrected to be similar to this + ['zzz', 'yyy', 'xxx', '/usr/bin/python'] + Which would trigger a rebuild because the order changed. + The fix involved added logic to mark all shared targets as peers and then ensure they're implicit + list is all cleared together. + - Fix Issue #3349 - SCons Exception EnvironmentError is conflicting with Python's EnvironmentError. + Renamed to SConsEnvironmentError + - Fix Issue #3350 - mslink failing when too many objects. This is resolved by adding TEMPFILEARGJOIN variable + which specifies what character to join all the argements output into the tempfile. The default remains a space + when mslink, msvc, or mslib tools are loaded they change the TEMPFILEARGJOIN to be a line separator (\r\n on win32) + - Additional fix to issue #3135 - Also handle 'pure' and 'elemental' type bound procedures + + - Fix handling of Visual Studio Compilers to properly reject any unknown HOST_PLATFORM or TARGET_PLATFORM + - Enable LaTeX scanner to find more than one include per line + + under which they would be observed), or major code cleanups + + Thanks to CURLY, LARRY, and MOE for their contributions to this release. + Contributors are listed alphabetically by their last name. + +Copyright (c) 2001 - 2019 The SCons Foundation +src/RELEASE.txt e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 39aba6c..3125824 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -1,4 +1,5 @@ SCons/__init__.py +SCons/__main__.py SCons/Action.py SCons/Builder.py SCons/compat/*.py diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index f1ca2cf..b541256 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -98,7 +98,7 @@ way for wrapping up the functions. # 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__ = "src/engine/SCons/Action.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Action.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import pickle @@ -211,7 +211,7 @@ def _object_contents(obj): def _code_contents(code, docstring=None): - """Return the signature contents of a code object. + r"""Return the signature contents of a code object. By providing direct access to the code object of the function, Python makes this extremely easy. Hooray! @@ -534,7 +534,7 @@ class ActionBase(object): result = self.get_presig(target, source, env) if not isinstance(result,(bytes, bytearray)): - result = bytearray("",'utf-8').join([ SCons.Util.to_bytes(r) for r in result ]) + result = bytearray(result, 'utf-8') else: # Make a copy and put in bytearray, without this the contents returned by get_presig # can be changed by the logic below, appending with each call and causing very @@ -767,16 +767,22 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): it'll have to be tweaked to get the full desired functionality. one special arg (so far?), 'error', to tell what to do with exceptions. """ - # allow std{in,out,err} to be "'devnull'" - io = kw.get('stdin') - if is_String(io) and io == 'devnull': - kw['stdin'] = open(os.devnull) - io = kw.get('stdout') - if is_String(io) and io == 'devnull': - kw['stdout'] = open(os.devnull, 'w') - io = kw.get('stderr') - if is_String(io) and io == 'devnull': - kw['stderr'] = open(os.devnull, 'w') + # allow std{in,out,err} to be "'devnull'". This is like + # subprocess.DEVNULL, which does not exist for Py2. Use the + # subprocess one if possible. + # Clean this up when Py2 support is dropped + try: + from subprocess import DEVNULL + except ImportError: + DEVNULL = None + + for stream in 'stdin', 'stdout', 'stderr': + io = kw.get(stream) + if is_String(io) and io == 'devnull': + if DEVNULL: + kw[stream] = DEVNULL + else: + kw[stream] = open(os.devnull, "r+") # Figure out what shell environment to use ENV = kw.get('env', None) @@ -802,7 +808,7 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): kw['env'] = new_env try: - pobj = subprocess.Popen(cmd, **kw) + pobj = subprocess.Popen(cmd, **kw) except EnvironmentError as e: if error == 'raise': raise # return a dummy Popen instance that only returns error @@ -820,9 +826,10 @@ def _subproc(scons_env, cmd, error = 'ignore', **kw): finally: # clean up open file handles stored in parent's kw for k, v in kw.items(): - if hasattr(v, 'close'): + if inspect.ismethod(getattr(v, 'close', None)): v.close() - return pobj + + return pobj class CommandAction(_ActionAction): @@ -1091,7 +1098,7 @@ class LazyAction(CommandGeneratorAction, CommandAction): def get_parent_class(self, env): c = env.get(self.var) - if is_String(c) and not '\n' in c: + if is_String(c) and '\n' not in c: return CommandAction return CommandGeneratorAction diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 1b11eea..9372240 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/ActionTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/ActionTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" # Define a null function and a null class for use as builder actions. @@ -44,6 +44,7 @@ import re import sys import types import unittest +import subprocess import SCons.Action import SCons.Environment @@ -1529,6 +1530,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase): (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = [ @@ -1554,19 +1556,19 @@ class CommandGeneratorActionTestCase(unittest.TestCase): assert c == func_matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + repr( func_matches[sys.version_info[:2]]) - def f_global(target, source, env, for_signature): + def f_global2(target, source, env, for_signature): return SCons.Action.Action(GlobalFunc, varlist=['XYZ']) - def f_local(target, source, env, for_signature): + def f_local2(target, source, env, for_signature): return SCons.Action.Action(LocalFunc, varlist=['XYZ']) matches_foo = func_matches[sys.version_info[:2]] + b'foo' - a = self.factory(f_global) + a = self.factory(f_global2) c = a.get_contents(target=[], source=[], env=env) assert c in matches_foo, repr(c) - a = self.factory(f_local) + a = self.factory(f_local2) c = a.get_contents(target=[], source=[], env=env) assert c in matches_foo, repr(c) @@ -1707,6 +1709,7 @@ class FunctionActionTestCase(unittest.TestCase): (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } @@ -1715,6 +1718,7 @@ class FunctionActionTestCase(unittest.TestCase): (3, 5): bytearray(b'1, 1, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 8): bytearray(b'1, 1, 0, 0,(),(),(d\x00S\x00),(),()'), } def factory(act, **kw): @@ -1959,6 +1963,7 @@ class LazyActionTestCase(unittest.TestCase): (3, 5): bytearray(b'0, 0, 0, 0,(),(),(d\x00\x00S),(),()'), (3, 6): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), (3, 7): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), + (3, 8): bytearray(b'0, 0, 0, 0,(),(),(d\x00S\x00),(),()'), } meth_matches = [ @@ -2017,6 +2022,7 @@ class ActionCallerTestCase(unittest.TestCase): (3, 5): b'd\x00\x00S', (3, 6): b'd\x00S\x00', (3, 7): b'd\x00S\x00', + (3, 8): b'd\x00S\x00', } @@ -2217,6 +2223,7 @@ class ObjectContentsTestCase(unittest.TestCase): (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),(),()'), } c = SCons.Action._function_contents(func1) @@ -2242,6 +2249,8 @@ class ObjectContentsTestCase(unittest.TestCase): b"{TestClass:__main__}[[[(, ()), [(, (,))]]]]{{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__}[[[(, ()), [(, (,))]]]]{{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__}[[[(, ()), [(, (,))]]]]{{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}}}"), } assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\n" + repr( @@ -2259,11 +2268,28 @@ class ObjectContentsTestCase(unittest.TestCase): (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)'), } assert c == expected[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected \n" + "\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.wait() + except EnvironmentError: + pass + except Exception: + # pass the test + return + + raise Exception("expected a non-EnvironmentError exception") if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 512fcf7..7777436 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -98,7 +98,7 @@ There are the following methods for internal use within this module: # 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__ = "src/engine/SCons/Builder.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Builder.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import collections @@ -274,7 +274,7 @@ def Builder(**kw): result = BuilderBase(**kw) - if not composite is None: + if composite is not None: result = CompositeBuilder(result, composite) return result @@ -293,7 +293,7 @@ def _node_errors(builder, env, tlist, slist): if t.has_explicit_builder(): # Check for errors when the environments are different # No error if environments are the same Environment instance - if (not t.env is None and not t.env is env and + if (t.env is not None and t.env is not env and # Check OverrideEnvironment case - no error if wrapped Environments # are the same instance, and overrides lists match not (getattr(t.env, '__subject', 0) is getattr(env, '__subject', 1) and @@ -309,7 +309,7 @@ def _node_errors(builder, env, tlist, slist): else: try: msg = "Two environments with different actions were specified for the same target: %s\n(action 1: %s)\n(action 2: %s)" % (t,t_contents.decode('utf-8'),contents.decode('utf-8')) - except UnicodeDecodeError as e: + except UnicodeDecodeError: msg = "Two environments with different actions were specified for the same target: %s"%t raise UserError(msg) if builder.multi: @@ -424,7 +424,7 @@ class BuilderBase(object): if name: self.name = name self.executor_kw = {} - if not chdir is _null: + if chdir is not _null: self.executor_kw['chdir'] = chdir self.is_explicit = is_explicit @@ -554,8 +554,10 @@ class BuilderBase(object): result = [] if target is None: target = [None]*len(source) for tgt, src in zip(target, source): - if not tgt is None: tgt = [tgt] - if not src is None: src = [src] + if tgt is not None: + tgt = [tgt] + if src is not None: + src = [src] result.extend(self._execute(env, tgt, src, overwarn)) return SCons.Node.NodeList(result) @@ -563,6 +565,13 @@ class BuilderBase(object): tlist, slist = self._create_nodes(env, target, source) + # If there is more than one target ensure that if we need to reset + # the implicit list to new scan of dependency all targets implicit lists + # are cleared. (SCons GH Issue #2811 and MongoDB SERVER-33111) + if len(tlist) > 1: + for t in tlist: + t.target_peers = tlist + # Check for errors with the specified target/source lists. _node_errors(self, env, tlist, slist) @@ -744,7 +753,7 @@ class BuilderBase(object): for s in SCons.Util.flatten(source): if SCons.Util.is_String(s): match_suffix = match_src_suffix(env.subst(s)) - if not match_suffix and not '.' in s: + if not match_suffix and '.' not in s: src_suf = self.get_src_suffix(env) s = self._adjustixes(s, None, src_suf)[0] else: diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 255eced..dd9a917 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -22,7 +22,7 @@ # from __future__ import print_function -__revision__ = "src/engine/SCons/BuilderTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/BuilderTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -409,10 +409,6 @@ class BuilderTestCase(unittest.TestCase): builder = SCons.Builder.Builder(generator=generator) assert builder.action.generator == generator - def test_get_name(self): - """Test the get_name() method - """ - def test_cmp(self): """Test simple comparisons of Builder objects """ @@ -552,7 +548,7 @@ class BuilderTestCase(unittest.TestCase): def test_src_suffix(self): """Test Builder creation with a specified source file suffix - + Make sure that the '.' separator is appended to the beginning if it isn't already present. """ @@ -685,7 +681,7 @@ class BuilderTestCase(unittest.TestCase): pass if (len(source) == 1 and len(target) == 1): env['CNT'][0] = env['CNT'][0] + 1 - + env = Environment() infiles = [] outfiles = [] @@ -732,8 +728,8 @@ class BuilderTestCase(unittest.TestCase): pass else: assert 0 - - + + def test_lists(self): """Testing handling lists of targets and source""" def function2(target, source, env, tlist = [outfile, outfile2], **kw): @@ -881,7 +877,7 @@ class BuilderTestCase(unittest.TestCase): def func(self): pass - + scanner = SCons.Scanner.Base(func, name='fooscan') b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner) @@ -890,8 +886,8 @@ class BuilderTestCase(unittest.TestCase): assert b1 == b2 assert b1 != b3 - - def test_src_scanner(slf): + + def test_src_scanner(self): """Testing ability to set a source file scanner through a builder.""" class TestScanner(object): def key(self, env): @@ -1245,7 +1241,7 @@ class BuilderTestCase(unittest.TestCase): for t in target: t.builder = nb return [nn], source - + builder=SCons.Builder.Builder(action='foo', emitter=emit, target_factory=MyNode, @@ -1321,7 +1317,7 @@ class BuilderTestCase(unittest.TestCase): builder2 = SCons.Builder.Builder(action='foo', emitter='$EMITTERLIST', node_factory=MyNode) - + env = Environment(EMITTERLIST = [emit2a, emit2b]) tgts = builder2(env, target='target-2', source='aaa.2') @@ -1419,7 +1415,7 @@ class BuilderTestCase(unittest.TestCase): b6 = SCons.Builder.Builder(action='foo') assert isinstance(b4, SCons.Builder.CompositeBuilder) assert isinstance(b4.action, SCons.Action.CommandGeneratorAction) - + env = Environment(BUILDERS={'bldr1': b1, 'bldr2': b2, 'bldr3': b3, @@ -1481,7 +1477,7 @@ class CompositeBuilderTestCase(unittest.TestCase): tgt = builder(env, source=[]) assert tgt == [], tgt - + assert isinstance(builder, SCons.Builder.CompositeBuilder) assert isinstance(builder.action, SCons.Action.CommandGeneratorAction) diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py index 73df784..c9e8ea7 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/CacheDir.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/CacheDir.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ CacheDir support @@ -35,6 +35,7 @@ import sys import SCons.Action import SCons.Warnings +from SCons.Util import PY3 cache_enabled = True cache_debug = False @@ -46,10 +47,12 @@ def CacheRetrieveFunc(target, source, env): t = target[0] fs = t.fs cd = env.get_CacheDir() + cd.requests += 1 cachedir, cachefile = cd.cachepath(t) if not fs.exists(cachefile): cd.CacheDebug('CacheRetrieve(%s): %s not in cache\n', t, cachefile) return 1 + cd.hits += 1 cd.CacheDebug('CacheRetrieve(%s): retrieving from %s\n', t, cachefile) if SCons.Action.execute_actions: if fs.islink(cachefile): @@ -111,7 +114,7 @@ def CachePushFunc(target, source, env): # has beaten us creating the directory. if not fs.isdir(cachedir): msg = errfmt % (str(target), cachefile) - raise SCons.Errors.EnvironmentError(msg) + raise SCons.Errors.SConsEnvironmentError(msg) try: if fs.islink(t.get_internal_path()): @@ -139,34 +142,97 @@ warned = dict() class CacheDir(object): def __init__(self, path): + """ + Initialize a CacheDir object. + + The cache configuration is stored in the object. It + is read from the config file in the supplied path if + one exists, if not the config file is created and + the default config is written, as well as saved in the object. + """ + self.requests = 0 + self.hits = 0 self.path = path self.current_cache_debug = None self.debugFP = None self.config = dict() if path is None: return - # See if there's a config file in the cache directory. If there is, - # use it. If there isn't, and the directory exists and isn't empty, - # produce a warning. If the directory doesn't exist or is empty, - # write a config file. + + if PY3: + self._readconfig3(path) + else: + self._readconfig2(path) + + + def _readconfig3(self, path): + """ + Python3 version of reading the cache config. + + If directory or config file do not exist, create. Take advantage + of Py3 capability in os.makedirs() and in file open(): just try + the operation and handle failure appropriately. + + Omit the check for old cache format, assume that's old enough + there will be none of those left to worry about. + + :param path: path to the cache directory + """ + config_file = os.path.join(path, 'config') + try: + os.makedirs(path, exist_ok=True) + except FileExistsError: + pass + except OSError: + msg = "Failed to create cache directory " + path + raise SCons.Errors.EnvironmentError(msg) + + try: + with open(config_file, 'x') as config: + self.config['prefix_len'] = 2 + try: + json.dump(self.config, config) + except Exception: + msg = "Failed to write cache configuration for " + path + raise SCons.Errors.EnvironmentError(msg) + except FileExistsError: + try: + with open(config_file) as config: + self.config = json.load(config) + except ValueError: + msg = "Failed to read cache configuration for " + path + raise SCons.Errors.EnvironmentError(msg) + + + def _readconfig2(self, path): + """ + Python2 version of reading cache config. + + See if there is a config file in the cache directory. If there is, + use it. If there isn't, and the directory exists and isn't empty, + produce a warning. If the directory does not exist or is empty, + write a config file. + + :param path: path to the cache directory + """ config_file = os.path.join(path, 'config') if not os.path.exists(config_file): - # A note: There is a race hazard here, if two processes start and + # A note: There is a race hazard here if two processes start and # attempt to create the cache directory at the same time. However, - # python doesn't really give you the option to do exclusive file - # creation (it doesn't even give you the option to error on opening - # an existing file for writing...). The ordering of events here - # as an attempt to alleviate this, on the basis that it's a pretty - # unlikely occurence (it'd require two builds with a brand new cache + # Python 2.x does not give you the option to do exclusive file + # creation (not even the option to error on opening an existing + # file for writing...). The ordering of events here is an attempt + # to alleviate this, on the basis that it's a pretty unlikely + # occurrence (would require two builds with a brand new cache # directory) - if os.path.isdir(path) and len(os.listdir(path)) != 0: + if os.path.isdir(path) and any(f != "config" for f in os.listdir(path)): self.config['prefix_len'] = 1 # When building the project I was testing this on, the warning # was output over 20 times. That seems excessive global warned if self.path not in warned: msg = "Please upgrade your cache by running " +\ - " scons-configure-cache.py " + self.path + "scons-configure-cache.py " + self.path SCons.Warnings.warn(SCons.Warnings.CacheVersionWarning, msg) warned[self.path] = True else: @@ -177,24 +243,24 @@ class CacheDir(object): # If someone else is trying to create the directory at # the same time as me, bad things will happen msg = "Failed to create cache directory " + path - raise SCons.Errors.EnvironmentError(msg) - + raise SCons.Errors.SConsEnvironmentError(msg) + self.config['prefix_len'] = 2 if not os.path.exists(config_file): try: with open(config_file, 'w') as config: json.dump(self.config, config) - except: + except Exception: msg = "Failed to write cache configuration for " + path - raise SCons.Errors.EnvironmentError(msg) + raise SCons.Errors.SConsEnvironmentError(msg) else: try: with open(config_file) as config: self.config = json.load(config) except ValueError: msg = "Failed to read cache configuration for " + path - raise SCons.Errors.EnvironmentError(msg) - + raise SCons.Errors.SConsEnvironmentError(msg) + def CacheDebug(self, fmt, target, cachefile): if cache_debug != self.current_cache_debug: @@ -207,9 +273,19 @@ class CacheDir(object): self.current_cache_debug = cache_debug if self.debugFP: self.debugFP.write(fmt % (target, os.path.split(cachefile)[1])) + self.debugFP.write("requests: %d, hits: %d, misses: %d, hit rate: %.2f%%\n" % + (self.requests, self.hits, self.misses, self.hit_ratio)) + + @property + def hit_ratio(self): + return (100.0 * self.hits / self.requests if self.requests > 0 else 100) + + @property + def misses(self): + return self.requests - self.hits def is_enabled(self): - return cache_enabled and not self.path is None + return cache_enabled and self.path is not None def is_readonly(self): return cache_readonly diff --git a/src/engine/SCons/CacheDirTests.py b/src/engine/SCons/CacheDirTests.py index 6b644d3..f96dab5 100644 --- a/src/engine/SCons/CacheDirTests.py +++ b/src/engine/SCons/CacheDirTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/CacheDirTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/CacheDirTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import shutil diff --git a/src/engine/SCons/Conftest.py b/src/engine/SCons/Conftest.py index 84aa992..1163aa3 100644 --- a/src/engine/SCons/Conftest.py +++ b/src/engine/SCons/Conftest.py @@ -354,7 +354,7 @@ def CheckHeader(context, header_name, header = None, language = None, context.Display("Checking for %s header file %s... " % (lang, header_name)) ret = context.CompileProg(text, suffix) - _YesNoResult(context, ret, "HAVE_" + header_name, text, + _YesNoResult(context, ret, "HAVE_" + header_name, text, "Define to 1 if you have the <%s> header file." % header_name) return ret @@ -439,7 +439,7 @@ def CheckTypeSize(context, type_name, header = None, language = None, expect = N Returns: status : int 0 if the check failed, or the found size of the type if the check succeeded.""" - + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename @@ -454,8 +454,8 @@ def CheckTypeSize(context, type_name, header = None, language = None, expect = N context.Display("Cannot check for %s type: %s\n" % (type_name, msg)) return msg - src = includetext + header - if not expect is None: + src = includetext + header + if expect is not None: # Only check if the given size is the right one context.Display('Checking %s is %d bytes... ' % (type_name, expect)) @@ -477,7 +477,7 @@ int main(void) st = context.CompileProg(src % (type_name, expect), suffix) if not st: context.Display("yes\n") - _Have(context, "SIZEOF_%s" % type_name, expect, + _Have(context, "SIZEOF_%s" % type_name, expect, "The size of `%s', as computed by sizeof." % type_name) return expect else: @@ -541,7 +541,7 @@ def CheckDeclaration(context, symbol, includes = None, language = None): Returns: status : bool True if the check failed, False if succeeded.""" - + # Include "confdefs.h" first, so that the header can use HAVE_HEADER_H. if context.headerfilename: includetext = '#include "%s"' % context.headerfilename @@ -556,7 +556,7 @@ def CheckDeclaration(context, symbol, includes = None, language = None): context.Display("Cannot check for declaration %s: %s\n" % (symbol, msg)) return msg - src = includetext + includes + src = includetext + includes context.Display('Checking whether %s is declared... ' % symbol) src = src + r""" @@ -677,7 +677,7 @@ return 0; "Define to 1 if you have the `%s' library." % lib_name) if oldLIBS != -1 and (ret or not autoadd): context.SetLIBS(oldLIBS) - + if not ret: return ret @@ -704,7 +704,7 @@ def CheckProg(context, prog_name): # def _YesNoResult(context, ret, key, text, comment = None): - """ + r""" Handle the result of a test with a "yes" or "no" result. :Parameters: @@ -723,7 +723,7 @@ def _YesNoResult(context, ret, key, text, comment = None): def _Have(context, key, have, comment = None): - """ + r""" Store result of a test in context.havedict and context.headerfilename. :Parameters: @@ -751,7 +751,7 @@ def _Have(context, key, have, comment = None): line = "#define %s %d\n" % (key_up, have) else: line = "#define %s %s\n" % (key_up, str(have)) - + if comment is not None: lines = "\n/* %s */\n" % comment + line else: diff --git a/src/engine/SCons/Debug.py b/src/engine/SCons/Debug.py index 34ce70d..c19862a 100644 --- a/src/engine/SCons/Debug.py +++ b/src/engine/SCons/Debug.py @@ -31,7 +31,7 @@ caller_trace() # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Debug.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Debug.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index c2dc70a..30c0052 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -33,7 +33,7 @@ from distutils.msvccompiler. # from __future__ import division -__revision__ = "src/engine/SCons/Defaults.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Defaults.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os @@ -193,7 +193,7 @@ def chmod_func(dest, mode): SCons.Node.FS.invalidate_node_memos(dest) if not SCons.Util.is_List(dest): dest = [dest] - if SCons.Util.is_String(mode) and not 0 in [i in digits for i in mode]: + if SCons.Util.is_String(mode) and 0 not in [i in digits for i in mode]: mode = int(mode, 8) if not SCons.Util.is_String(mode): for element in dest: @@ -582,6 +582,7 @@ ConstructionEnvironment = { '__DSHLIBVERSIONFLAGS' : '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}', 'TEMPFILE' : NullCmdGenerator, + 'TEMPFILEARGJOIN': ' ', 'Dir' : Variable_Method_Caller('TARGET', 'Dir'), 'Dirs' : Variable_Method_Caller('TARGET', 'Dirs'), 'File' : Variable_Method_Caller('TARGET', 'File'), diff --git a/src/engine/SCons/DefaultsTests.py b/src/engine/SCons/DefaultsTests.py index 5a4e1cb..4e0328a 100644 --- a/src/engine/SCons/DefaultsTests.py +++ b/src/engine/SCons/DefaultsTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/DefaultsTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/DefaultsTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -69,11 +69,11 @@ class DefaultsTestCase(unittest.TestCase): test.write(file, "test\n") try: mkdir_func(file) - except os.error as e: + except OSError as e: pass else: - fail("expected os.error") - + self.fail("expected OSError") + if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 759ff16..31a4721 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -31,7 +31,7 @@ Environment # 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__ = "src/engine/SCons/Environment.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Environment.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import copy @@ -608,7 +608,7 @@ class SubstitutionEnvironment(object): Removes the specified function's MethodWrapper from the added_methods list, so we don't re-bind it when making a clone. """ - self.added_methods = [dm for dm in self.added_methods if not dm.method is function] + self.added_methods = [dm for dm in self.added_methods if dm.method is not function] def Override(self, overrides): """ @@ -719,6 +719,12 @@ class SubstitutionEnvironment(object): elif append_next_arg_to == '-isystem': t = ('-isystem', arg) dict['CCFLAGS'].append(t) + elif append_next_arg_to == '-iquote': + t = ('-iquote', arg) + dict['CCFLAGS'].append(t) + elif append_next_arg_to == '-idirafter': + t = ('-idirafter', arg) + dict['CCFLAGS'].append(t) elif append_next_arg_to == '-arch': t = ('-arch', arg) dict['CCFLAGS'].append(t) @@ -791,7 +797,7 @@ class SubstitutionEnvironment(object): elif arg[0] == '+': dict['CCFLAGS'].append(arg) dict['LINKFLAGS'].append(arg) - elif arg in ['-include', '-isysroot', '-isystem', '-arch']: + elif arg in ['-include', '-isysroot', '-isystem', '-iquote', '-idirafter', '-arch']: append_next_arg_to = arg else: dict['CCFLAGS'].append(arg) @@ -858,18 +864,21 @@ class SubstitutionEnvironment(object): return self -def default_decide_source(dependency, target, prev_ni): +def default_decide_source(dependency, target, prev_ni, repo_node=None): f = SCons.Defaults.DefaultEnvironment().decide_source - return f(dependency, target, prev_ni) + return f(dependency, target, prev_ni, repo_node) -def default_decide_target(dependency, target, prev_ni): + +def default_decide_target(dependency, target, prev_ni, repo_node=None): f = SCons.Defaults.DefaultEnvironment().decide_target - return f(dependency, target, prev_ni) + return f(dependency, target, prev_ni, repo_node) + def default_copy_from_cache(src, dst): f = SCons.Defaults.DefaultEnvironment().copy_from_cache return f(src, dst) + class Base(SubstitutionEnvironment): """Base class for "real" construction Environments. These are the primary objects used to communicate dependency and construction @@ -1342,7 +1351,7 @@ class Base(SubstitutionEnvironment): dk = list(filter(lambda x, val=val: x not in val, dk)) self._dict[key] = dk + [val] else: - if not val in dk: + if val not in dk: self._dict[key] = dk + [val] else: if key == 'CPPDEFINES': @@ -1428,30 +1437,30 @@ class Base(SubstitutionEnvironment): _warn_copy_deprecated = False return self.Clone(*args, **kw) - def _changed_build(self, dependency, target, prev_ni): - if dependency.changed_state(target, prev_ni): + def _changed_build(self, dependency, target, prev_ni, repo_node=None): + if dependency.changed_state(target, prev_ni, repo_node): return 1 - return self.decide_source(dependency, target, prev_ni) + return self.decide_source(dependency, target, prev_ni, repo_node) - def _changed_content(self, dependency, target, prev_ni): - return dependency.changed_content(target, prev_ni) + def _changed_content(self, dependency, target, prev_ni, repo_node=None): + return dependency.changed_content(target, prev_ni, repo_node) - def _changed_source(self, dependency, target, prev_ni): + def _changed_source(self, dependency, target, prev_ni, repo_node=None): target_env = dependency.get_build_env() type = target_env.get_tgt_sig_type() if type == 'source': - return target_env.decide_source(dependency, target, prev_ni) + return target_env.decide_source(dependency, target, prev_ni, repo_node) else: - return target_env.decide_target(dependency, target, prev_ni) + return target_env.decide_target(dependency, target, prev_ni, repo_node) - def _changed_timestamp_then_content(self, dependency, target, prev_ni): - return dependency.changed_timestamp_then_content(target, prev_ni) + def _changed_timestamp_then_content(self, dependency, target, prev_ni, repo_node=None): + return dependency.changed_timestamp_then_content(target, prev_ni, repo_node) - def _changed_timestamp_newer(self, dependency, target, prev_ni): - return dependency.changed_timestamp_newer(target, prev_ni) + def _changed_timestamp_newer(self, dependency, target, prev_ni, repo_node=None): + return dependency.changed_timestamp_newer(target, prev_ni, repo_node) - def _changed_timestamp_match(self, dependency, target, prev_ni): - return dependency.changed_timestamp_match(target, prev_ni) + def _changed_timestamp_match(self, dependency, target, prev_ni, repo_node=None): + return dependency.changed_timestamp_match(target, prev_ni, repo_node) def _copy_from_cache(self, src, dst): return self.fs.copy(src, dst) @@ -1722,7 +1731,7 @@ class Base(SubstitutionEnvironment): dk = [x for x in dk if x not in val] self._dict[key] = [val] + dk else: - if not val in dk: + if val not in dk: self._dict[key] = [val] + dk else: if delete_existing: @@ -2299,7 +2308,20 @@ class OverrideEnvironment(Base): # Methods that make this class act like a proxy. def __getattr__(self, name): - return getattr(self.__dict__['__subject'], name) + attr = getattr(self.__dict__['__subject'], name) + # Here we check if attr is one of the Wrapper classes. For + # example when a pseudo-builder is being called from an + # OverrideEnvironment. + # + # These wrappers when they're constructed capture the + # Environment they are being constructed with and so will not + # have access to overrided values. So we rebuild them with the + # OverrideEnvironment so they have access to overrided values. + if isinstance(attr, (MethodWrapper, BuilderWrapper)): + return attr.clone(self) + else: + return attr + def __setattr__(self, name, value): setattr(self.__dict__['__subject'], name, value) diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml index 71b11e6..9b392d5 100644 --- a/src/engine/SCons/Environment.xml +++ b/src/engine/SCons/Environment.xml @@ -549,7 +549,7 @@ string, in which case a list will be returned instead of a string. -If +If delete_existing is 0, then adding a path that already exists will not move it to the end; it will stay where it is in the list. @@ -588,7 +588,7 @@ then any value(s) that already exist in the construction variable will not be added again to the list. -However, if delete_existing is 1, +However, if delete_existing is 1, existing matching values are removed first, so existing values in the arg list move to the end of the list. @@ -883,12 +883,14 @@ env4 = env.Clone(tools = ['msvc', MyTool]) The parse_flags -keyword argument is also recognized: +keyword argument is also recognized to allow merging command-line +style arguments into the appropriate construction +variables (see &f-link-env-MergeFlags;). # create an environment for compiling programs that use wxWidgets -wx_env = env.Clone(parse_flags = '!wx-config --cflags --cxxflags') +wx_env = env.Clone(parse_flags='!wx-config --cflags --cxxflags') @@ -1211,6 +1213,17 @@ size, or content signature.
+ +repo_node + + +Use this node instead of the one specified by +dependency + to determine if the dependency has changed. + + + + @@ -1247,7 +1260,7 @@ Example: -def my_decider(dependency, target, prev_ni): +def my_decider(dependency, target, prev_ni, repo_node=None): return not os.path.exists(str(target)) env.Decider(my_decider) @@ -1880,9 +1893,9 @@ as the dependency. -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. +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. See the third and forth examples below. @@ -1954,8 +1967,8 @@ not as separate arguments to New values are prepended to the environment variable by default, -unless prepend=0 is specified. -Duplicate values are always eliminated, +unless prepend=0 is specified. +Duplicate values are always eliminated, since this function calls &f-link-AppendENVPath; or @@ -2294,6 +2307,9 @@ and added to the following construction variables: -frameworkdir= FRAMEWORKPATH -include CCFLAGS -isysroot CCFLAGS, LINKFLAGS +-isystem CCFLAGS +-iquote CCFLAGS +-idirafter CCFLAGS -I CPPPATH -l LIBS -L LIBPATH @@ -2489,7 +2505,7 @@ then any value(s) that already exist in the construction variable will not be added again to the list. -However, if delete_existing is 1, +However, if delete_existing is 1, existing matching values are removed first, so existing values in the arg list move to the front of the list. diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index b97ac5c..1ce1007 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -23,7 +23,7 @@ from __future__ import print_function -__revision__ = "src/engine/SCons/EnvironmentTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/EnvironmentTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -74,7 +74,7 @@ def diff_dict(d1, d2): s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n" else: s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n" - elif k in env2: + elif k in d2: s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n" s1 = s1 + "}\n" s2 = s2 + "}\n" @@ -263,39 +263,39 @@ class SubstitutionTestCase(unittest.TestCase): nodes = env.arg2nodes("Util.py UtilTests.py", Factory) assert len(nodes) == 1, nodes assert isinstance(nodes[0], X) - assert nodes[0].name == "Util.py UtilTests.py" + assert nodes[0].name == "Util.py UtilTests.py", nodes[0].name nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory) assert len(nodes) == 1, nodes assert isinstance(nodes[0], X) - assert nodes[0].name == u"Util.py UtilTests.py" + assert nodes[0].name == u"Util.py UtilTests.py", nodes[0].name nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory) assert len(nodes) == 2, nodes assert isinstance(nodes[0], X) assert isinstance(nodes[1], X) - assert nodes[0].name == "Util.py" - assert nodes[1].name == "UtilTests.py" + assert nodes[0].name == "Util.py", nodes[0].name + assert nodes[1].name == "UtilTests.py", nodes[1].name n1 = Factory("Util.py") nodes = env.arg2nodes([n1, "UtilTests.py"], Factory) assert len(nodes) == 2, nodes assert isinstance(nodes[0], X) assert isinstance(nodes[1], X) - assert nodes[0].name == "Util.py" - assert nodes[1].name == "UtilTests.py" + assert nodes[0].name == "Util.py", nodes[0].name + assert nodes[1].name == "UtilTests.py", nodes[1].name class SConsNode(SCons.Node.Node): pass nodes = env.arg2nodes(SConsNode()) assert len(nodes) == 1, nodes - assert isinstance(nodes[0], SConsNode), node + assert isinstance(nodes[0], SConsNode), nodes[0] class OtherNode(object): pass nodes = env.arg2nodes(OtherNode()) assert len(nodes) == 1, nodes - assert isinstance(nodes[0], OtherNode), node + assert isinstance(nodes[0], OtherNode), nodes[0] def lookup_a(str, F=Factory): if str[0] == 'a': @@ -484,7 +484,7 @@ class SubstitutionTestCase(unittest.TestCase): env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c') l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB") - assert l == [["c", "cA", "cB", "c"]], mystr + assert l == [["c", "cA", "cB", "c"]], l env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ]) lst = env.subst_list([ "$AAA", "B $CCC" ]) @@ -800,7 +800,9 @@ sys.exit(0) "-fopenmp " + \ "-mno-cygwin -mwindows " + \ "-arch i386 -isysroot /tmp " + \ - "-isystem /usr/include/foo " + \ + "-iquote /usr/include/foo1 " + \ + "-isystem /usr/include/foo2 " + \ + "-idirafter /usr/include/foo3 " + \ "+DD64 " + \ "-DFOO -DBAR=value -D BAZ " @@ -809,10 +811,12 @@ sys.exit(0) assert d['ASFLAGS'] == ['-as'], d['ASFLAGS'] assert d['CFLAGS'] == ['-std=c99'] assert d['CCFLAGS'] == ['-X', '-Wa,-as', - '-pthread', '-fopenmp', '-mno-cygwin', - ('-arch', 'i386'), ('-isysroot', '/tmp'), - ('-isystem', '/usr/include/foo'), - '+DD64'], repr(d['CCFLAGS']) + '-pthread', '-fopenmp', '-mno-cygwin', + ('-arch', 'i386'), ('-isysroot', '/tmp'), + ('-iquote', '/usr/include/foo1'), + ('-isystem', '/usr/include/foo2'), + ('-idirafter', '/usr/include/foo3'), + '+DD64'], 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'] @@ -1195,7 +1199,7 @@ env4.builder1.env, env3) test_it('foo.bar') test_it('foo-bar') - def test_autogenerate(dict): + def test_autogenerate(self): """Test autogenerating variables in a dictionary.""" drive, p = os.path.splitdrive(os.getcwd()) @@ -1206,9 +1210,9 @@ env4.builder1.env, env3) drive, path = os.path.splitdrive(path) return drive.lower() + path - env = dict.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ], - LIBLINKPREFIX = 'foo', - LIBLINKSUFFIX = 'bar') + env = self.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ], + LIBLINKPREFIX = 'foo', + LIBLINKSUFFIX = 'bar') def RDirs(pathlist, fs=env.fs): return fs.Dir('xx').Rfindalldirs(pathlist) @@ -2022,7 +2026,9 @@ def generate(env): "-pthread " + \ "-mno-cygwin -mwindows " + \ "-arch i386 -isysroot /tmp " + \ - "-isystem /usr/include/foo " + \ + "-iquote /usr/include/foo1 " + \ + "-isystem /usr/include/foo2 " + \ + "-idirafter /usr/include/foo3 " + \ "+DD64 " + \ "-DFOO -DBAR=value") env.ParseConfig("fake $COMMAND") @@ -2031,7 +2037,9 @@ def generate(env): assert env['CCFLAGS'] == ['', '-X', '-Wa,-as', '-pthread', '-mno-cygwin', ('-arch', 'i386'), ('-isysroot', '/tmp'), - ('-isystem', '/usr/include/foo'), + ('-iquote', '/usr/include/foo1'), + ('-isystem', '/usr/include/foo2'), + ('-idirafter', '/usr/include/foo3'), '+DD64'], env['CCFLAGS'] assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES'] assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS'] @@ -2433,16 +2441,16 @@ f5: \ exc_caught = None try: env.Tool('does_not_exist') - except SCons.Errors.EnvironmentError: + except SCons.Errors.SConsEnvironmentError: exc_caught = 1 - assert exc_caught, "did not catch expected EnvironmentError" + assert exc_caught, "did not catch expected SConsEnvironmentError" exc_caught = None try: env.Tool('$NONE') - except SCons.Errors.EnvironmentError: + except SCons.Errors.SConsEnvironmentError: exc_caught = 1 - assert exc_caught, "did not catch expected EnvironmentError" + assert exc_caught, "did not catch expected SConsEnvironmentError" # Use a non-existent toolpath directory just to make sure we # can call Tool() with the keyword argument. @@ -3272,11 +3280,11 @@ def generate(env): s = e.src_builder() assert s is None, s - def test_SourceSignatures(type): + def test_SourceSignatures(self): """Test the SourceSignatures() method""" import SCons.Errors - env = type.TestEnvironment(M = 'MD5', T = 'timestamp') + env = self.TestEnvironment(M = 'MD5', T = 'timestamp') exc_caught = None try: @@ -3312,7 +3320,7 @@ def generate(env): def test_Split(self): """Test the Split() method""" - env = self.TestEnvironment(FOO='fff', BAR='bbb') + env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb') s = env.Split("foo bar") assert s == ["foo", "bar"], s s = env.Split("$FOO bar") @@ -3326,11 +3334,11 @@ def generate(env): s = env.Split("$FOO$BAR") assert s == ["fffbbb"], s - def test_TargetSignatures(type): + def test_TargetSignatures(self): """Test the TargetSignatures() method""" import SCons.Errors - env = type.TestEnvironment(B = 'build', C = 'content') + env = self.TestEnvironment(B='build', C='content') exc_caught = None try: @@ -3397,7 +3405,7 @@ def generate(env): - def test_Environment_global_variable(type): + def test_Environment_global_variable(self): """Test setting Environment variable to an Environment.Base subclass""" class MyEnv(SCons.Environment.Base): def xxx(self, string): @@ -3579,6 +3587,10 @@ class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture): def setUp(self): env = Environment() env._dict = {'XXX' : 'x', 'YYY' : 'y'} + def verify_value(env, key, value, *args, **kwargs): + """Verifies that key is value on the env this is called with.""" + assert env[key] == value + env.AddMethod(verify_value) env2 = OverrideEnvironment(env, {'XXX' : 'x2'}) env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}) self.envs = [ env, env2, env3 ] @@ -3769,6 +3781,13 @@ class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture): # """Test the OverrideEnvironment WhereIs() method""" # pass + def test_PseudoBuilderInherits(self): + """Test that pseudo-builders inherit the overrided values.""" + env, env2, env3 = self.envs + env.verify_value('XXX', 'x') + env2.verify_value('XXX', 'x2') + env3.verify_value('XXX', 'x3') + def test_Dir(self): """Test the OverrideEnvironment Dir() method""" env, env2, env3 = self.envs diff --git a/src/engine/SCons/Errors.py b/src/engine/SCons/Errors.py index 58ef92e..28f58be 100644 --- a/src/engine/SCons/Errors.py +++ b/src/engine/SCons/Errors.py @@ -28,7 +28,7 @@ and user errors in SCons. """ -__revision__ = "src/engine/SCons/Errors.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Errors.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import shutil import SCons.Util @@ -124,7 +124,7 @@ class UserError(Exception): class StopError(Exception): pass -class EnvironmentError(Exception): +class SConsEnvironmentError(Exception): pass class MSVCError(IOError): @@ -184,7 +184,7 @@ def convert_to_BuildError(status, exc_info=None): filename=filename, exc_info=exc_info) - elif isinstance(status, (EnvironmentError, OSError, IOError)): + elif isinstance(status, (SConsEnvironmentError, OSError, IOError)): # If an IOError/OSError happens, raise a BuildError. # Report the name of the file or directory that caused the # error, which might be different from the target being built diff --git a/src/engine/SCons/ErrorsTests.py b/src/engine/SCons/ErrorsTests.py index 088224d..a1bc048 100644 --- a/src/engine/SCons/ErrorsTests.py +++ b/src/engine/SCons/ErrorsTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/ErrorsTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/ErrorsTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import errno import os @@ -101,10 +101,10 @@ class ErrorsTestCase(unittest.TestCase): assert e.node == "node" def test_convert_EnvironmentError_to_BuildError(self): - """Test the convert_to_BuildError function on EnvironmentError + """Test the convert_to_BuildError function on SConsEnvironmentError exceptions. """ - ee = SCons.Errors.EnvironmentError("test env error") + ee = SCons.Errors.SConsEnvironmentError("test env error") be = SCons.Errors.convert_to_BuildError(ee) assert be.errstr == "test env error" assert be.status == 2 diff --git a/src/engine/SCons/Executor.py b/src/engine/SCons/Executor.py index 10fc73f..c6ffe81 100644 --- a/src/engine/SCons/Executor.py +++ b/src/engine/SCons/Executor.py @@ -28,7 +28,7 @@ Nodes. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -__revision__ = "src/engine/SCons/Executor.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Executor.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import collections @@ -36,15 +36,16 @@ import SCons.Debug from SCons.Debug import logInstanceCreation import SCons.Errors import SCons.Memoize +import SCons.Util from SCons.compat import with_metaclass, NoSlotsPyPy class Batch(object): """Remembers exact association between targets and sources of executor.""" - + __slots__ = ('targets', 'sources') - + def __init__(self, targets=[], sources=[]): self.targets = targets self.sources = sources @@ -71,7 +72,7 @@ class TSList(collections.UserList): return nl[i] def __getslice__(self, i, j): nl = self.func() - i = max(i, 0); j = max(j, 0) + i, j = max(i, 0), max(j, 0) return nl[i:j] def __str__(self): nl = self.func() @@ -127,13 +128,13 @@ def execute_action_list(obj, target, kw): status = act(*args, **kw) if isinstance(status, SCons.Errors.BuildError): status.executor = obj - raise status + raise status # TODO pylint E0702: raising int not allowed elif status: msg = "Error %s" % status raise SCons.Errors.BuildError( - errstr=msg, + errstr=msg, node=obj.batches[0].targets, - executor=obj, + executor=obj, action=act) return status @@ -572,7 +573,6 @@ def AddBatchExecutor(key, executor): nullenv = None -import SCons.Util class NullEnvironment(SCons.Util.Null): import SCons.CacheDir _CacheDir_path = None @@ -597,7 +597,7 @@ class Null(object, with_metaclass(NoSlotsPyPy)): disassociate Builders from Nodes entirely, so we're not going to worry about unit tests for this--at least for now. """ - + __slots__ = ('pre_actions', 'post_actions', 'env', @@ -613,9 +613,10 @@ class Null(object, with_metaclass(NoSlotsPyPy)): 'action_list', '_do_execute', '_execute_str') - + def __init__(self, *args, **kw): - if SCons.Debug.track_instances: logInstanceCreation(self, 'Executor.Null') + if SCons.Debug.track_instances: + logInstanceCreation(self, 'Executor.Null') self.batches = [Batch(kw['targets'][:], [])] def get_build_env(self): return get_NullEnvironment() @@ -649,7 +650,7 @@ class Null(object, with_metaclass(NoSlotsPyPy)): """Morph this Null executor to a real Executor object.""" batches = self.batches self.__class__ = Executor - self.__init__([]) + self.__init__([]) self.batches = batches # The following methods require morphing this Null Executor to a diff --git a/src/engine/SCons/ExecutorTests.py b/src/engine/SCons/ExecutorTests.py index 03b8480..353e6f2 100644 --- a/src/engine/SCons/ExecutorTests.py +++ b/src/engine/SCons/ExecutorTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/ExecutorTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/ExecutorTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Job.py b/src/engine/SCons/Job.py index 45bca62..12ba19f 100644 --- a/src/engine/SCons/Job.py +++ b/src/engine/SCons/Job.py @@ -29,7 +29,7 @@ stop, and wait on jobs. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Job.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Job.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -53,14 +53,14 @@ interrupt_msg = 'Build interrupted.' class InterruptState(object): - def __init__(self): - self.interrupted = False + def __init__(self): + self.interrupted = False - def set(self): - self.interrupted = True + def set(self): + self.interrupted = True - def __call__(self): - return self.interrupted + def __call__(self): + return self.interrupted class Jobs(object): @@ -87,7 +87,7 @@ class Jobs(object): stack_size = explicit_stack_size if stack_size is None: stack_size = default_stack_size - + try: self.job = Parallel(taskmaster, num, stack_size) self.num_jobs = num @@ -172,14 +172,14 @@ class Serial(object): """ def __init__(self, taskmaster): - """Create a new serial job given a taskmaster. + """Create a new serial job given a taskmaster. The taskmaster's next_task() method should return the next task that needs to be executed, or None if there are no more tasks. The taskmaster's executed() method will be called for each task when it is successfully executed, or failed() will be called if it failed to execute (e.g. execute() raised an exception).""" - + self.taskmaster = taskmaster self.interrupted = InterruptState() @@ -188,7 +188,7 @@ class Serial(object): and executing them, and return when there are no more tasks. If a task fails to execute (i.e. execute() raises an exception), then the job will stop.""" - + while True: task = self.taskmaster.next_task() @@ -199,7 +199,7 @@ class Serial(object): task.prepare() if task.needs_execute(): task.execute() - except Exception as e: + except Exception: if self.interrupted(): try: raise SCons.Errors.BuildError( @@ -269,7 +269,7 @@ else: def __init__(self, num, stack_size, interrupted): """Create the request and reply queues, and 'num' worker threads. - + One must specify the stack size of the worker threads. The stack size is specified in kilobytes. """ @@ -277,11 +277,11 @@ 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. - if not explicit_stack_size is None: + 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) @@ -322,7 +322,7 @@ else: self.requestQueue.put(None) # Wait for all of the workers to terminate. - # + # # If we don't do this, later Python versions (2.4, 2.5) often # seem to raise exceptions during shutdown. This happens # in requestQueue.get(), as an assertion failure that @@ -339,7 +339,7 @@ else: self.workers = [] class Parallel(object): - """This class is used to execute tasks in parallel, and is somewhat + """This class is used to execute tasks in parallel, and is somewhat less efficient than Serial, but is appropriate for parallel builds. This class is thread safe. @@ -373,7 +373,7 @@ else: an exception), then the job will stop.""" jobs = 0 - + while True: # Start up as many available tasks as we're # allowed to. diff --git a/src/engine/SCons/JobTests.py b/src/engine/SCons/JobTests.py index 7db99e7..d46ab30 100644 --- a/src/engine/SCons/JobTests.py +++ b/src/engine/SCons/JobTests.py @@ -20,7 +20,7 @@ # 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__ = "src/engine/SCons/JobTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/JobTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import unittest import random @@ -73,7 +73,7 @@ class DummyLock(object): def release(self): pass -class NoThreadsException(object): +class NoThreadsException(Exception): "raised by the ParallelTestCase if threads are not supported" def __str__(self): @@ -206,7 +206,7 @@ class Taskmaster(object): self.parallel_list = [0] * (n+1) self.found_parallel = False self.Task = Task - + # 'guard' guards 'task_begin_list' and 'task_end_list' try: import threading diff --git a/src/engine/SCons/Memoize.py b/src/engine/SCons/Memoize.py index 85670c1..f7b45bf 100644 --- a/src/engine/SCons/Memoize.py +++ b/src/engine/SCons/Memoize.py @@ -22,7 +22,7 @@ # from __future__ import print_function -__revision__ = "src/engine/SCons/Memoize.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Memoize.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Memoizer diff --git a/src/engine/SCons/MemoizeTests.py b/src/engine/SCons/MemoizeTests.py index 790791f..f3b5329 100644 --- a/src/engine/SCons/MemoizeTests.py +++ b/src/engine/SCons/MemoizeTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/MemoizeTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/MemoizeTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Node/Alias.py b/src/engine/SCons/Node/Alias.py index 9ea2fe0..a9defb6 100644 --- a/src/engine/SCons/Node/Alias.py +++ b/src/engine/SCons/Node/Alias.py @@ -30,7 +30,7 @@ This creates a hash of global Aliases (dummy targets). # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/Alias.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/Alias.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import collections diff --git a/src/engine/SCons/Node/AliasTests.py b/src/engine/SCons/Node/AliasTests.py index 21829b6..764ac67 100644 --- a/src/engine/SCons/Node/AliasTests.py +++ b/src/engine/SCons/Node/AliasTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/AliasTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/AliasTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 6319ace..21ccecc 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -33,7 +33,7 @@ that can be used by scripts or modules looking for the canonical default. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -__revision__ = "src/engine/SCons/Node/FS.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/FS.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import fnmatch import os @@ -57,7 +57,6 @@ import SCons.Util import SCons.Warnings from SCons.Debug import Trace -from . import DeciderNeedsNode print_duplicate = 0 @@ -282,7 +281,7 @@ def set_duplicate(duplicate): 'copy' : _copy_func } - if not duplicate in Valid_Duplicates: + if duplicate not in Valid_Duplicates: raise SCons.Errors.InternalError("The argument of set_duplicate " "should be in Valid_Duplicates") global Link_Funcs @@ -480,7 +479,7 @@ class EntryProxy(SCons.Util.Proxy): return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix") def __get_windows_path(self): - """Return the path with \ as the path separator, + r"""Return the path with \ as the path separator, regardless of platform.""" if OS_SEP == '\\': return self @@ -531,7 +530,7 @@ class EntryProxy(SCons.Util.Proxy): except KeyError: try: attr = SCons.Util.Proxy.__getattr__(self, name) - except AttributeError as e: + except AttributeError: # Raise our own AttributeError subclass with an # overridden __str__() method that identifies the # name of the entry that caused the exception. @@ -699,13 +698,13 @@ class Base(SCons.Node.Node): @SCons.Memoize.CountMethodCall def stat(self): - try: + try: return self._memo['stat'] - except KeyError: + except KeyError: pass - try: + try: result = self.fs.stat(self.get_abspath()) - except os.error: + except os.error: result = None self._memo['stat'] = result @@ -719,16 +718,16 @@ class Base(SCons.Node.Node): def getmtime(self): st = self.stat() - if st: + if st: return st[stat.ST_MTIME] - else: + else: return None def getsize(self): st = self.stat() - if st: + if st: return st[stat.ST_SIZE] - else: + else: return None def isdir(self): @@ -1418,7 +1417,7 @@ class FS(LocalFS): self.Top.addRepository(d) def PyPackageDir(self, modulename): - """Locate the directory of a given python module name + r"""Locate the directory of a given python module name For example scons might resolve to Windows: C:\Python27\Lib\site-packages\scons-2.5.1 @@ -1689,7 +1688,7 @@ class Dir(Base): return result def addRepository(self, dir): - if dir != self and not dir in self.repositories: + if dir != self and dir not in self.repositories: self.repositories.append(dir) dir._tpath = '.' self.__clearRepositoryCache() @@ -1729,7 +1728,7 @@ class Dir(Base): if self is other: result = '.' - elif not other in self._path_elements: + elif other not in self._path_elements: try: other_dir = other.get_dir() except AttributeError: @@ -2261,7 +2260,7 @@ class RootDir(Dir): this directory. """ - __slots__ = ['_lookupDict'] + __slots__ = ('_lookupDict', ) def __init__(self, drive, fs): if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.RootDir') @@ -2467,7 +2466,7 @@ class FileNodeInfo(SCons.Node.NodeInfoBase): """ state = getattr(self, '__dict__', {}).copy() for obj in type(self).mro(): - for name in getattr(obj,'__slots__',()): + for name in getattr(obj, '__slots__', ()): if hasattr(self, name): state[name] = getattr(self, name) @@ -2511,7 +2510,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase): or count of any of these could yield writing wrong csig, and then false positive rebuilds """ - __slots__ = ('dependency_map') + __slots__ = ['dependency_map', ] current_version_id = 2 def __setattr__(self, key, value): @@ -3283,14 +3282,14 @@ class File(Base): self._memo['changed'] = has_changed return has_changed - def changed_content(self, target, prev_ni): + def changed_content(self, target, prev_ni, repo_node=None): cur_csig = self.get_csig() try: return cur_csig != prev_ni.csig except AttributeError: return 1 - def changed_state(self, target, prev_ni): + def changed_state(self, target, prev_ni, repo_node=None): return self.state != SCons.Node.up_to_date @@ -3317,13 +3316,26 @@ class File(Base): len(binfo.bimplicitsigs)) == 0: return {} - - # store this info so we can avoid regenerating it. - binfo.dependency_map = { str(child):signature for child, signature in zip(chain(binfo.bsources, binfo.bdepends, binfo.bimplicit), + binfo.dependency_map = { child:signature for child, signature in zip(chain(binfo.bsources, binfo.bdepends, binfo.bimplicit), chain(binfo.bsourcesigs, binfo.bdependsigs, binfo.bimplicitsigs))} return binfo.dependency_map + # @profile + def _add_strings_to_dependency_map(self, dmap): + """ + In the case comparing node objects isn't sufficient, we'll add the strings for the nodes to the dependency map + :return: + """ + + first_string = str(next(iter(dmap))) + + # print("DMAP:%s"%id(dmap)) + if first_string not in dmap: + string_dict = {str(child): signature for child, signature in dmap.items()} + dmap.update(string_dict) + return dmap + def _get_previous_signatures(self, dmap): """ Return a list of corresponding csigs from previous @@ -3342,37 +3354,62 @@ class File(Base): if len(dmap) == 0: if MD5_TIMESTAMP_DEBUG: print("Nothing dmap shortcutting") return None + elif MD5_TIMESTAMP_DEBUG: print("len(dmap):%d"%len(dmap)) + - if MD5_TIMESTAMP_DEBUG: print("len(dmap):%d"%len(dmap)) - # First try the simple name for node - c_str = str(self) - if MD5_TIMESTAMP_DEBUG: print("Checking :%s"%c_str) - df = dmap.get(c_str, None) + # First try retrieving via Node + if MD5_TIMESTAMP_DEBUG: print("Checking if self is in map:%s id:%s type:%s"%(str(self), id(self), type(self))) + df = dmap.get(self, False) if df: return df - + + # Now check if self's repository file is in map. + rf = self.rfile() + if MD5_TIMESTAMP_DEBUG: print("Checking if self.rfile is in map:%s id:%s type:%s"%(str(rf), id(rf), type(rf))) + rfm = dmap.get(rf, False) + if rfm: + return rfm + + # get default string for node and then also string swapping os.altsep for os.sep (/ for \) + c_strs = [str(self)] + if os.altsep: - c_str = c_str.replace(os.sep, os.altsep) - df = dmap.get(c_str, None) - if MD5_TIMESTAMP_DEBUG: print("-->%s"%df) + c_strs.append(c_strs[0].replace(os.sep, os.altsep)) + + # In some cases the dependency_maps' keys are already strings check. + # Check if either string is now in dmap. + for s in c_strs: + if MD5_TIMESTAMP_DEBUG: print("Checking if str(self) is in map :%s" % s) + df = dmap.get(s, False) + if df: + return df + + # Strings don't exist in map, add them and try again + # If there are no strings in this dmap, then add them. + # This may not be necessary, we could walk the nodes in the dmap and check each string + # rather than adding ALL the strings to dmap. In theory that would be n/2 vs 2n str() calls on node + # if not dmap.has_strings: + dmap = self._add_strings_to_dependency_map(dmap) + + # In some cases the dependency_maps' keys are already strings check. + # Check if either string is now in dmap. + for s in c_strs: + if MD5_TIMESTAMP_DEBUG: print("Checking if str(self) is in map (now with strings) :%s" % s) + df = dmap.get(s, False) if df: return df + # Lastly use nodes get_path() to generate string and see if that's in dmap if not df: try: # this should yield a path which matches what's in the sconsign c_str = self.get_path() - df = dmap.get(c_str, None) - if MD5_TIMESTAMP_DEBUG: print("-->%s"%df) - if df: - return df - if os.altsep: c_str = c_str.replace(os.sep, os.altsep) - df = dmap.get(c_str, None) - if MD5_TIMESTAMP_DEBUG: print("-->%s"%df) - if df: - return df + + if MD5_TIMESTAMP_DEBUG: print("Checking if self.get_path is in map (now with strings) :%s" % s) + + df = dmap.get(c_str, None) except AttributeError as e: raise FileBuildInfoFileToCsigMappingError("No mapping from file name to content signature for :%s"%c_str) @@ -3387,21 +3424,17 @@ class File(Base): file and just copy the prev_ni provided. If the prev_ni is wrong. It will propagate it. See: https://github.com/SCons/scons/issues/2980 - + Args: self - dependency target - target prev_ni - The NodeInfo object loaded from previous builds .sconsign - node - Node instance. This is the only changed* function which requires - node to function. So if we detect that it's not passed. - we throw DeciderNeedsNode, and caller should handle this and pass node. + node - Node instance. Check this node for file existence/timestamp + if specified. - Returns: + Returns: Boolean - Indicates if node(File) has changed. """ - if node is None: - # We need required node argument to get BuildInfo to function - raise DeciderNeedsNode(self.changed_timestamp_then_content) # Now get sconsign name -> csig map and then get proper prev_ni if possible bi = node.get_stored_info().binfo @@ -3433,7 +3466,6 @@ class File(Base): print("Mismatch self.changed_timestamp_match(%s, prev_ni) old:%s new:%s"%(str(target), old, new)) new_prev_ni = self._get_previous_signatures(dependency_map) - if not new: try: # NOTE: We're modifying the current node's csig in a query. @@ -3443,13 +3475,13 @@ class File(Base): return False return self.changed_content(target, new_prev_ni) - def changed_timestamp_newer(self, target, prev_ni): + def changed_timestamp_newer(self, target, prev_ni, repo_node=None): try: return self.get_timestamp() > target.get_timestamp() except AttributeError: return 1 - def changed_timestamp_match(self, target, prev_ni): + def changed_timestamp_match(self, target, prev_ni, repo_node=None): """ Return True if the timestamps don't match or if there is no previous timestamp :param target: @@ -3462,14 +3494,18 @@ class File(Base): return 1 def is_up_to_date(self): + """Check for whether the Node is current + In all cases self is the target we're checking to see if it's up to date + """ + T = 0 if T: Trace('is_up_to_date(%s):' % self) if not self.exists(): if T: Trace(' not self.exists():') - # The file doesn't exist locally... + # The file (always a target) doesn't exist locally... r = self.rfile() if r != self: - # ...but there is one in a Repository... + # ...but there is one (always a target) in a Repository... if not self.changed(r): if T: Trace(' changed(%s):' % r) # ...and it's even up-to-date... diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 021d433..2366a4d 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -22,7 +22,7 @@ # from __future__ import division, print_function -__revision__ = "src/engine/SCons/Node/FSTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/FSTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -47,54 +47,73 @@ built_it = None scanner_count = 0 + class Scanner(object): def __init__(self, node=None): global scanner_count scanner_count = scanner_count + 1 self.hash = scanner_count self.node = node + def path(self, env, dir, target=None, source=None): return () + def __call__(self, node, env, path): return [self.node] + def __hash__(self): return self.hash + def select(self, node): return self + def recurse_nodes(self, nodes): return nodes + class Environment(object): def __init__(self): self.scanner = Scanner() + def Dictionary(self, *args): return {} + def autogenerate(self, **kw): return {} + def get_scanner(self, skey): return self.scanner + def Override(self, overrides): return self + def _update(self, dict): pass + class Action(object): def __call__(self, targets, sources, env, **kw): global built_it if kw.get('execute', 1): built_it = 1 return 0 + def show(self, string): pass + def get_contents(self, target, source, env): - return bytearray("",'utf-8') + return bytearray("", 'utf-8') + def genstring(self, target, source, env): return "" + def strfunction(self, targets, sources, env): return "" + def get_implicit_deps(self, target, source, env): return [] + class Builder(object): def __init__(self, factory, action=Action()): self.factory = factory @@ -110,6 +129,7 @@ class Builder(object): def source_factory(self, name): return self.factory(name) + class _tempdirTestCase(unittest.TestCase): def setUp(self): self.save_cwd = os.getcwd() @@ -121,10 +141,11 @@ class _tempdirTestCase(unittest.TestCase): def tearDown(self): os.chdir(self.save_cwd) + class VariantDirTestCase(unittest.TestCase): def runTest(self): """Test variant dir functionality""" - test=TestCmd(workdir='') + test = TestCmd(workdir='') fs = SCons.Node.FS.FS() f1 = fs.File('build/test1') @@ -169,30 +190,30 @@ class VariantDirTestCase(unittest.TestCase): test.subdir(['rep1', 'build', 'var2']) # A source file in the source directory - test.write([ 'work', 'src', 'test.in' ], 'test.in') + test.write(['work', 'src', 'test.in'], 'test.in') # A source file in a subdir of the source directory - test.subdir([ 'work', 'src', 'new_dir' ]) - test.write([ 'work', 'src', 'new_dir', 'test9.out' ], 'test9.out\n') + test.subdir(['work', 'src', 'new_dir']) + test.write(['work', 'src', 'new_dir', 'test9.out'], 'test9.out\n') # A source file in the repository - test.write([ 'rep1', 'src', 'test2.in' ], 'test2.in') + test.write(['rep1', 'src', 'test2.in'], 'test2.in') # Some source files in the variant directory - test.write([ 'work', 'build', 'var2', 'test.in' ], 'test.old') - test.write([ 'work', 'build', 'var2', 'test2.in' ], 'test2.old') + test.write(['work', 'build', 'var2', 'test.in'], 'test.old') + test.write(['work', 'build', 'var2', 'test2.in'], 'test2.old') # An old derived file in the variant directories - test.write([ 'work', 'build', 'var1', 'test.out' ], 'test.old') - test.write([ 'work', 'build', 'var2', 'test.out' ], 'test.old') + test.write(['work', 'build', 'var1', 'test.out'], 'test.old') + test.write(['work', 'build', 'var2', 'test.out'], 'test.old') # And just in case we are weird, a derived file in the source # dir. - test.write([ 'work', 'src', 'test.out' ], 'test.out.src') + test.write(['work', 'src', 'test.out'], 'test.out.src') # A derived file in the repository - test.write([ 'rep1', 'build', 'var1', 'test2.out' ], 'test2.out_rep') - test.write([ 'rep1', 'build', 'var2', 'test2.out' ], 'test2.out_rep') + test.write(['rep1', 'build', 'var1', 'test2.out'], 'test2.out_rep') + test.write(['rep1', 'build', 'var2', 'test2.out'], 'test2.out_rep') os.chdir(test.workpath('work')) @@ -211,8 +232,8 @@ class VariantDirTestCase(unittest.TestCase): f2out_2.builder = 1 fs.Repository(test.workpath('rep1')) - assert f1.srcnode().get_internal_path() == os.path.normpath('src/test.in'),\ - f1.srcnode().get_internal_path() + assert f1.srcnode().get_internal_path() == os.path.normpath('src/test.in'), \ + f1.srcnode().get_internal_path() # str(node) returns source path for duplicate = 0 assert str(f1) == os.path.normpath('src/test.in'), str(f1) # Build path does not exist @@ -222,26 +243,26 @@ class VariantDirTestCase(unittest.TestCase): # And duplicate=0 should also work just like a Repository assert f1.rexists() # rfile() should point to the source path - assert f1.rfile().get_internal_path() == os.path.normpath('src/test.in'),\ - f1.rfile().get_internal_path() + assert f1.rfile().get_internal_path() == os.path.normpath('src/test.in'), \ + f1.rfile().get_internal_path() - assert f2.srcnode().get_internal_path() == os.path.normpath('src/test.in'),\ - f2.srcnode().get_internal_path() + assert f2.srcnode().get_internal_path() == os.path.normpath('src/test.in'), \ + f2.srcnode().get_internal_path() # str(node) returns build path for duplicate = 1 assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2) # Build path exists assert f2.exists() # ...and exists() should copy the file from src to build path - assert test.read(['work', 'build', 'var2', 'test.in']) == bytearray('test.in','utf-8'),\ - test.read(['work', 'build', 'var2', 'test.in']) + assert test.read(['work', 'build', 'var2', 'test.in']) == bytearray('test.in', 'utf-8'), \ + test.read(['work', 'build', 'var2', 'test.in']) # Since exists() is true, so should rexists() be assert f2.rexists() f3 = fs.File('build/var1/test2.in') f4 = fs.File('build/var2/test2.in') - assert f3.srcnode().get_internal_path() == os.path.normpath('src/test2.in'),\ - f3.srcnode().get_internal_path() + assert f3.srcnode().get_internal_path() == os.path.normpath('src/test2.in'), \ + f3.srcnode().get_internal_path() # str(node) returns source path for duplicate = 0 assert str(f3) == os.path.normpath('src/test2.in'), str(f3) # Build path does not exist @@ -251,22 +272,22 @@ class VariantDirTestCase(unittest.TestCase): # But we do have a file in the Repository assert f3.rexists() # rfile() should point to the source path - assert f3.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/src/test2.in')),\ - f3.rfile().get_internal_path() + assert f3.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/src/test2.in')), \ + f3.rfile().get_internal_path() - assert f4.srcnode().get_internal_path() == os.path.normpath('src/test2.in'),\ - f4.srcnode().get_internal_path() + assert f4.srcnode().get_internal_path() == os.path.normpath('src/test2.in'), \ + f4.srcnode().get_internal_path() # str(node) returns build path for duplicate = 1 assert str(f4) == os.path.normpath('build/var2/test2.in'), str(f4) # Build path should exist assert f4.exists() # ...and copy over the file into the local build path - assert test.read(['work', 'build', 'var2', 'test2.in']) == bytearray('test2.in','utf-8') + assert test.read(['work', 'build', 'var2', 'test2.in']) == bytearray('test2.in', 'utf-8') # should exist in repository, since exists() is true assert f4.rexists() # rfile() should point to ourselves - assert f4.rfile().get_internal_path() == os.path.normpath('build/var2/test2.in'),\ - f4.rfile().get_internal_path() + assert f4.rfile().get_internal_path() == os.path.normpath('build/var2/test2.in'), \ + f4.rfile().get_internal_path() f5 = fs.File('build/var1/test.out') f6 = fs.File('build/var2/test.out') @@ -274,12 +295,12 @@ class VariantDirTestCase(unittest.TestCase): assert f5.exists() # We should not copy the file from the source dir, since this is # a derived file. - assert test.read(['work', 'build', 'var1', 'test.out']) == bytearray('test.old','utf-8') + assert test.read(['work', 'build', 'var1', 'test.out']) == bytearray('test.old', 'utf-8') assert f6.exists() # We should not copy the file from the source dir, since this is # a derived file. - assert test.read(['work', 'build', 'var2', 'test.out']) == bytearray('test.old','utf-8') + assert test.read(['work', 'build', 'var2', 'test.out']) == bytearray('test.old', 'utf-8') f7 = fs.File('build/var1/test2.out') f8 = fs.File('build/var2/test2.out') @@ -292,8 +313,8 @@ class VariantDirTestCase(unittest.TestCase): assert not f8.exists() assert f8.rexists() - assert f8.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/build/var2/test2.out')),\ - f8.rfile().get_internal_path() + assert f8.rfile().get_internal_path() == os.path.normpath(test.workpath('rep1/build/var2/test2.out')), \ + f8.rfile().get_internal_path() # Verify the Mkdir and Link actions are called d9 = fs.Dir('build/var2/new_dir') @@ -302,6 +323,7 @@ class VariantDirTestCase(unittest.TestCase): class MkdirAction(Action): def __init__(self, dir_made): self.dir_made = dir_made + def __call__(self, target, source, env, executor=None): if executor: target = executor.get_all_targets() @@ -310,8 +332,10 @@ class VariantDirTestCase(unittest.TestCase): save_Link = SCons.Node.FS.Link link_made = [] + def link_func(target, source, env, link_made=link_made): link_made.append(target) + SCons.Node.FS.Link = link_func try: @@ -332,10 +356,10 @@ class VariantDirTestCase(unittest.TestCase): # happen if you switch from duplicate=1 to duplicate=0, then # delete a source file. At one time, this would cause exists() # to return a 1 but get_contents() to throw. - test.write([ 'work', 'build', 'var1', 'asourcefile' ], 'stuff') + test.write(['work', 'build', 'var1', 'asourcefile'], 'stuff') f10 = fs.File('build/var1/asourcefile') assert f10.exists() - assert f10.get_contents() == bytearray('stuff','utf-8'), f10.get_contents() + assert f10.get_contents() == bytearray('stuff', 'utf-8'), f10.get_contents() f11 = fs.File('src/file11') t, m = f11.alter_targets() @@ -361,8 +385,10 @@ class VariantDirTestCase(unittest.TestCase): fIO = fs.File("build/var2/IOError") save_Link = SCons.Node.FS.Link + def Link_IOError(target, source, env): raise IOError(17, "Link_IOError") + SCons.Node.FS.Link = SCons.Action.Action(Link_IOError, None) test.write(['work', 'src', 'IOError'], "work/src/IOError\n") @@ -379,16 +405,16 @@ class VariantDirTestCase(unittest.TestCase): SCons.Node.FS.Link = save_Link # Test to see if Link() works... - test.subdir('src','build') + test.subdir('src', 'build') test.write('src/foo', 'src/foo\n') os.chmod(test.workpath('src/foo'), stat.S_IRUSR) SCons.Node.FS.Link(fs.File(test.workpath('build/foo')), fs.File(test.workpath('src/foo')), None) os.chmod(test.workpath('src/foo'), stat.S_IRUSR | stat.S_IWRITE) - st=os.stat(test.workpath('build/foo')) + st = os.stat(test.workpath('build/foo')) assert (stat.S_IMODE(st[stat.ST_MODE]) & stat.S_IWRITE), \ - stat.S_IMODE(st[stat.ST_MODE]) + stat.S_IMODE(st[stat.ST_MODE]) # This used to generate a UserError when we forbid the source # directory from being outside the top-level SConstruct dir. @@ -404,8 +430,8 @@ class VariantDirTestCase(unittest.TestCase): exc_caught = 1 assert exc_caught, "Should have caught a UserError." finally: - test.unlink( "src/foo" ) - test.unlink( "build/foo" ) + test.unlink("src/foo") + test.unlink("build/foo") fs = SCons.Node.FS.FS() fs.VariantDir('build', 'src1') @@ -424,18 +450,20 @@ class VariantDirTestCase(unittest.TestCase): # Test against a former bug. Make sure we can get a repository # path for the variant directory itself! - fs=SCons.Node.FS.FS(test.workpath('work')) - test.subdir('work') + fs = SCons.Node.FS.FS(test.workpath('work')) + + # not needed this subdir is created above near line 188 + # test.subdir('work') fs.VariantDir('build/var3', 'src', duplicate=0) d1 = fs.Dir('build/var3') r = d1.rdir() assert r == d1, "%s != %s" % (r, d1) # verify the link creation attempts in file_link() - class LinkSimulator (object): + class LinkSimulator(object): """A class to intercept os.[sym]link() calls and track them.""" - def __init__( self, duplicate, link, symlink, copy ) : + def __init__(self, duplicate, link, symlink, copy): self.duplicate = duplicate self.have = {} self.have['hard'] = link @@ -447,27 +475,28 @@ class VariantDirTestCase(unittest.TestCase): if self.have[link]: self.links_to_be_called.append(link) - def link_fail( self , src , dest ) : + def link_fail(self, src, dest): next_link = self.links_to_be_called.pop(0) assert next_link == "hard", \ - "Wrong link order: expected %s to be called "\ - "instead of hard" % next_link - raise OSError( "Simulating hard link creation error." ) + "Wrong link order: expected %s to be called " \ + "instead of hard" % next_link + raise OSError("Simulating hard link creation error.") - def symlink_fail( self , src , dest ) : + def symlink_fail(self, src, dest): next_link = self.links_to_be_called.pop(0) assert next_link == "soft", \ - "Wrong link order: expected %s to be called "\ - "instead of soft" % next_link - raise OSError( "Simulating symlink creation error." ) + "Wrong link order: expected %s to be called " \ + "instead of soft" % next_link + raise OSError("Simulating symlink creation error.") - def copy( self , src , dest ) : + def copy(self, src, dest): next_link = self.links_to_be_called.pop(0) assert next_link == "copy", \ - "Wrong link order: expected %s to be called "\ - "instead of copy" % next_link + "Wrong link order: expected %s to be called " \ + "instead of copy" % next_link # copy succeeds, but use the real copy self.have['copy'](src, dest) + # end class LinkSimulator try: @@ -539,33 +568,33 @@ class VariantDirTestCase(unittest.TestCase): fs.VariantDir('work/src/b1/b2', 'work/src') dir_list = [ - 'work/src', - 'work/src/b1', - 'work/src/b1/b2', - 'work/src/b1/b2/b1', - 'work/src/b1/b2/b1/b2', - 'work/src/b1/b2/b1/b2/b1', - 'work/src/b1/b2/b1/b2/b1/b2', + 'work/src', + 'work/src/b1', + 'work/src/b1/b2', + 'work/src/b1/b2/b1', + 'work/src/b1/b2/b1/b2', + 'work/src/b1/b2/b1/b2/b1', + 'work/src/b1/b2/b1/b2/b1/b2', ] srcnode_map = { - 'work/src/b1/b2' : 'work/src', - 'work/src/b1/b2/f' : 'work/src/f', - 'work/src/b1/b2/b1' : 'work/src/b1/', - 'work/src/b1/b2/b1/f' : 'work/src/b1/f', - 'work/src/b1/b2/b1/b2' : 'work/src/b1/b2', - 'work/src/b1/b2/b1/b2/f' : 'work/src/b1/b2/f', - 'work/src/b1/b2/b1/b2/b1' : 'work/src/b1/b2/b1', - 'work/src/b1/b2/b1/b2/b1/f' : 'work/src/b1/b2/b1/f', - 'work/src/b1/b2/b1/b2/b1/b2' : 'work/src/b1/b2/b1/b2', - 'work/src/b1/b2/b1/b2/b1/b2/f' : 'work/src/b1/b2/b1/b2/f', + 'work/src/b1/b2': 'work/src', + 'work/src/b1/b2/f': 'work/src/f', + 'work/src/b1/b2/b1': 'work/src/b1/', + 'work/src/b1/b2/b1/f': 'work/src/b1/f', + 'work/src/b1/b2/b1/b2': 'work/src/b1/b2', + 'work/src/b1/b2/b1/b2/f': 'work/src/b1/b2/f', + 'work/src/b1/b2/b1/b2/b1': 'work/src/b1/b2/b1', + 'work/src/b1/b2/b1/b2/b1/f': 'work/src/b1/b2/b1/f', + 'work/src/b1/b2/b1/b2/b1/b2': 'work/src/b1/b2/b1/b2', + 'work/src/b1/b2/b1/b2/b1/b2/f': 'work/src/b1/b2/b1/b2/f', } alter_map = { - 'work/src' : 'work/src/b1/b2', - 'work/src/f' : 'work/src/b1/b2/f', - 'work/src/b1' : 'work/src/b1/b2/b1', - 'work/src/b1/f' : 'work/src/b1/b2/b1/f', + 'work/src': 'work/src/b1/b2', + 'work/src/f': 'work/src/b1/b2/f', + 'work/src/b1': 'work/src/b1/b2/b1', + 'work/src/b1/f': 'work/src/b1/b2/b1/f', } errors = 0 @@ -608,6 +637,7 @@ class VariantDirTestCase(unittest.TestCase): self.assertFalse(errors) + class BaseTestCase(_tempdirTestCase): def test_stat(self): """Test the Base.stat() method""" @@ -703,18 +733,21 @@ class BaseTestCase(_tempdirTestCase): nonexistent = fs.Entry('nonexistent') assert not nonexistent.islink() + class DirNodeInfoTestCase(_tempdirTestCase): def test___init__(self): """Test DirNodeInfo initialization""" ddd = self.fs.Dir('ddd') ni = SCons.Node.FS.DirNodeInfo() + class DirBuildInfoTestCase(_tempdirTestCase): def test___init__(self): """Test DirBuildInfo initialization""" ddd = self.fs.Dir('ddd') bi = SCons.Node.FS.DirBuildInfo() + class FileNodeInfoTestCase(_tempdirTestCase): def test___init__(self): """Test FileNodeInfo initialization""" @@ -760,6 +793,7 @@ class FileNodeInfoTestCase(_tempdirTestCase): size = st[stat.ST_SIZE] assert ni.size != size, (ni.size, size) + class FileBuildInfoTestCase(_tempdirTestCase): def test___init__(self): """Test File.BuildInfo initialization""" @@ -821,6 +855,7 @@ class FileBuildInfoTestCase(_tempdirTestCase): format = bi1.format() assert format == expect, (repr(expect), repr(format)) + class FSTestCase(_tempdirTestCase): def test_needs_normpath(self): """Test the needs_normpath Regular expression @@ -862,7 +897,6 @@ class FSTestCase(_tempdirTestCase): "/a./", "/a../", - ".a", "..a", "/.a", @@ -871,7 +905,7 @@ class FSTestCase(_tempdirTestCase): "..a/", "/.a/", "/..a/", - ] + ] for p in do_not_need_normpath: assert needs_normpath_match(p) is None, p @@ -919,7 +953,7 @@ class FSTestCase(_tempdirTestCase): "../a", "a/./a", "a/../a", - ] + ] for p in needs_normpath: assert needs_normpath_match(p) is not None, p @@ -952,7 +986,7 @@ class FSTestCase(_tempdirTestCase): assert isinstance(d1, SCons.Node.FS.Dir) assert d1.cwd is d1, d1 - f1 = fs.File('f1', directory = d1) + f1 = fs.File('f1', directory=d1) assert isinstance(f1, SCons.Node.FS.File) d1_f1 = os.path.join('d1', 'f1') @@ -1035,6 +1069,7 @@ class FSTestCase(_tempdirTestCase): if p[0] == os.sep: p = drive + p return p + path = strip_slash(path_) abspath = strip_slash(abspath_) up_path = strip_slash(up_path_) @@ -1048,51 +1083,51 @@ class FSTestCase(_tempdirTestCase): name = os.sep if dir.up() is None: - dir_up_path = dir.get_internal_path() + dir_up_path = dir.get_internal_path() else: - dir_up_path = dir.up().get_internal_path() + dir_up_path = dir.up().get_internal_path() assert dir.name == name, \ - "dir.name %s != expected name %s" % \ - (dir.name, name) + "dir.name %s != expected name %s" % \ + (dir.name, name) assert dir.get_internal_path() == path, \ - "dir.path %s != expected path %s" % \ - (dir.get_internal_path(), path) + "dir.path %s != expected path %s" % \ + (dir.get_internal_path(), path) assert str(dir) == path, \ - "str(dir) %s != expected path %s" % \ - (str(dir), path) + "str(dir) %s != expected path %s" % \ + (str(dir), path) assert dir.get_abspath() == abspath, \ - "dir.abspath %s != expected absolute path %s" % \ - (dir.get_abspath(), abspath) + "dir.abspath %s != expected absolute path %s" % \ + (dir.get_abspath(), abspath) assert dir_up_path == up_path, \ - "dir.up().path %s != expected parent path %s" % \ - (dir_up_path, up_path) + "dir.up().path %s != expected parent path %s" % \ + (dir_up_path, up_path) for sep in seps: def Dir_test(lpath, path_, abspath_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, abspath_, up_path_, sep) - - Dir_test('/', '/', '/', '/') - Dir_test('', './', sub_dir, sub) - Dir_test('foo', 'foo/', sub_dir_foo, './') - Dir_test('foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') - Dir_test('/foo', '/foo/', '/foo/', '/') - Dir_test('/foo/bar', '/foo/bar/', '/foo/bar/', '/foo/') - Dir_test('..', sub, sub, wp) - Dir_test('foo/..', './', sub_dir, sub) - Dir_test('../foo', sub_foo, sub_foo, sub) - Dir_test('.', './', sub_dir, sub) - Dir_test('./.', './', sub_dir, sub) - Dir_test('foo/./bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') - Dir_test('#../foo', sub_foo, sub_foo, sub) - Dir_test('#/../foo', sub_foo, sub_foo, sub) - Dir_test('#foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') - Dir_test('#/foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') - Dir_test('#', './', sub_dir, sub) + + Dir_test('/', '/', '/', '/') + Dir_test('', './', sub_dir, sub) + Dir_test('foo', 'foo/', sub_dir_foo, './') + Dir_test('foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') + Dir_test('/foo', '/foo/', '/foo/', '/') + Dir_test('/foo/bar', '/foo/bar/', '/foo/bar/', '/foo/') + Dir_test('..', sub, sub, wp) + Dir_test('foo/..', './', sub_dir, sub) + Dir_test('../foo', sub_foo, sub_foo, sub) + Dir_test('.', './', sub_dir, sub) + Dir_test('./.', './', sub_dir, sub) + Dir_test('foo/./bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') + Dir_test('#../foo', sub_foo, sub_foo, sub) + Dir_test('#/../foo', sub_foo, sub_foo, sub) + Dir_test('#foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') + Dir_test('#/foo/bar', 'foo/bar/', sub_dir_foo_bar, 'foo/') + Dir_test('#', './', sub_dir, sub) try: - f2 = fs.File(sep.join(['f1', 'f2']), directory = d1) + f2 = fs.File(sep.join(['f1', 'f2']), directory=d1) except TypeError as x: assert str(x) == ("Tried to lookup File '%s' as a Dir." % d1_f1), x @@ -1134,16 +1169,15 @@ class FSTestCase(_tempdirTestCase): e1 = fs.Entry(p) e2 = fs.Entry(path) assert e1 is e2, (e1, e2) - a=str(e1) - b=str(e2) - assert a == b, ("Strings should match for same file/node\n%s\n%s"%(a,b)) - + a = str(e1) + b = str(e2) + assert a == b, ("Strings should match for same file/node\n%s\n%s" % (a, b)) # Test for a bug in 0.04 that did not like looking up # dirs with a trailing slash on Windows. - d=fs.Dir('./') + d = fs.Dir('./') assert d.get_internal_path() == '.', d.get_abspath() - d=fs.Dir('foo/') + d = fs.Dir('foo/') assert d.get_internal_path() == 'foo', d.get_abspath() # Test for sub-classing of node building. @@ -1151,7 +1185,7 @@ class FSTestCase(_tempdirTestCase): built_it = None assert not built_it - d1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE + d1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE d1.builder_set(Builder(fs.File)) d1.reset_executor() d1.env_set(Environment()) @@ -1160,7 +1194,7 @@ class FSTestCase(_tempdirTestCase): built_it = None assert not built_it - f1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE + f1.add_source([SCons.Node.Node()]) # XXX FAKE SUBCLASS ATTRIBUTE f1.builder_set(Builder(fs.File)) f1.reset_executor() f1.env_set(Environment()) @@ -1244,9 +1278,11 @@ class FSTestCase(_tempdirTestCase): class MyScanner(Scanner): call_count = 0 + def __call__(self, node, env, path): self.call_count = self.call_count + 1 return Scanner.__call__(self, node, env, path) + s = MyScanner(xyz) deps = f12.get_found_includes(env, s, t1) @@ -1265,8 +1301,6 @@ class FSTestCase(_tempdirTestCase): assert deps == [xyz], deps assert s.call_count == 3, s.call_count - - # Make sure we can scan this file even if the target isn't # a file that has a scanner (it might be an Alias, e.g.). class DummyNode(object): @@ -1296,7 +1330,7 @@ class FSTestCase(_tempdirTestCase): f1 = fs.File(test.workpath("do_i_exist")) assert not f1.exists() - test.write("do_i_exist","\n") + test.write("do_i_exist", "\n") assert not f1.exists(), "exists() call not cached" f1.built() assert f1.exists(), "exists() call caching not reset" @@ -1310,14 +1344,14 @@ class FSTestCase(_tempdirTestCase): # get_contents() returns the binary contents. test.write("binary_file", "Foo\x1aBar") f1 = fs.File(test.workpath("binary_file")) - assert f1.get_contents() == bytearray("Foo\x1aBar",'utf-8'), f1.get_contents() + assert f1.get_contents() == bytearray("Foo\x1aBar", 'utf-8'), f1.get_contents() # This tests to make sure we can decode UTF-8 text files. test_string = u"Foo\x1aBar" test.write("utf8_file", test_string.encode('utf-8')) f1 = fs.File(test.workpath("utf8_file")) assert eval('f1.get_text_contents() == u"Foo\x1aBar"'), \ - f1.get_text_contents() + f1.get_text_contents() # Check for string which doesn't have BOM and isn't valid # ASCII @@ -1325,11 +1359,11 @@ class FSTestCase(_tempdirTestCase): test.write('latin1_file', test_string) f1 = fs.File(test.workpath("latin1_file")) assert f1.get_text_contents() == test_string.decode('latin-1'), \ - f1.get_text_contents() + f1.get_text_contents() def nonexistent(method, s): try: - x = method(s, create = 0) + x = method(s, create=0) except SCons.Errors.UserError: pass else: @@ -1367,15 +1401,15 @@ class FSTestCase(_tempdirTestCase): f = fs.File('f_local') assert f._local == 0 - #XXX test_is_up_to_date() for directories + # XXX test_is_up_to_date() for directories - #XXX test_sconsign() for directories + # XXX test_sconsign() for directories - #XXX test_set_signature() for directories + # XXX test_set_signature() for directories - #XXX test_build() for directories + # XXX test_build() for directories - #XXX test_root() + # XXX test_root() # test Entry.get_contents() e = fs.Entry('does_not_exist') @@ -1387,7 +1421,7 @@ class FSTestCase(_tempdirTestCase): try: e = fs.Entry('file') c = e.get_contents() - assert c == bytearray("file\n",'utf-8'), c + assert c == bytearray("file\n", 'utf-8'), c assert e.__class__ == SCons.Node.FS.File finally: test.unlink("file") @@ -1541,7 +1575,7 @@ class FSTestCase(_tempdirTestCase): f = fs.Entry('foo/bar/baz') assert f.for_signature() == 'baz', f.for_signature() assert f.get_string(0) == os.path.normpath('foo/bar/baz'), \ - f.get_string(0) + f.get_string(0) assert f.get_string(1) == 'baz', f.get_string(1) def test_drive_letters(self): @@ -1556,26 +1590,26 @@ class FSTestCase(_tempdirTestCase): drive, path = os.path.splitdrive(x) return 'X:' + path - wp = drive_workpath(['']) + wp = drive_workpath(['']) if wp[-1] in (os.sep, '/'): - tmp = os.path.split(wp[:-1])[0] + tmp = os.path.split(wp[:-1])[0] else: - tmp = os.path.split(wp)[0] + tmp = os.path.split(wp)[0] - parent_tmp = os.path.split(tmp)[0] + parent_tmp = os.path.split(tmp)[0] if parent_tmp == 'X:': parent_tmp = 'X:' + os.sep - tmp_foo = os.path.join(tmp, 'foo') + tmp_foo = os.path.join(tmp, 'foo') - foo = drive_workpath(['foo']) - foo_bar = drive_workpath(['foo', 'bar']) - sub = drive_workpath(['sub', '']) - sub_dir = drive_workpath(['sub', 'dir', '']) - sub_dir_foo = drive_workpath(['sub', 'dir', 'foo', '']) + foo = drive_workpath(['foo']) + foo_bar = drive_workpath(['foo', 'bar']) + sub = drive_workpath(['sub', '']) + sub_dir = drive_workpath(['sub', 'dir', '']) + sub_dir_foo = drive_workpath(['sub', 'dir', 'foo', '']) sub_dir_foo_bar = drive_workpath(['sub', 'dir', 'foo', 'bar', '']) - sub_foo = drive_workpath(['sub', 'foo', '']) + sub_foo = drive_workpath(['sub', 'foo', '']) fs = SCons.Node.FS.FS() @@ -1594,22 +1628,23 @@ class FSTestCase(_tempdirTestCase): if p[-1] == os.sep and len(p) > 3: p = p[:-1] return p + path = strip_slash(path_) up_path = strip_slash(up_path_) name = path.split(os.sep)[-1] assert dir.name == name, \ - "dir.name %s != expected name %s" % \ - (dir.name, name) + "dir.name %s != expected name %s" % \ + (dir.name, name) assert dir.get_internal_path() == path, \ - "dir.path %s != expected path %s" % \ - (dir.get_internal_path(), path) + "dir.path %s != expected path %s" % \ + (dir.get_internal_path(), path) assert str(dir) == path, \ - "str(dir) %s != expected path %s" % \ - (str(dir), path) + "str(dir) %s != expected path %s" % \ + (str(dir), path) assert dir.up().get_internal_path() == up_path, \ - "dir.up().path %s != expected parent path %s" % \ - (dir.up().get_internal_path(), up_path) + "dir.up().path %s != expected parent path %s" % \ + (dir.up().get_internal_path(), up_path) save_os_path = os.path save_os_sep = os.sep @@ -1620,26 +1655,25 @@ class FSTestCase(_tempdirTestCase): SCons.Node.FS.initialize_do_splitdrive() for sep in seps: - def Dir_test(lpath, path_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, up_path_, sep) - Dir_test('#X:', wp, tmp) - Dir_test('X:foo', foo, wp) - Dir_test('X:foo/bar', foo_bar, foo) - Dir_test('X:/foo', 'X:/foo', 'X:/') - Dir_test('X:/foo/bar', 'X:/foo/bar/', 'X:/foo/') - Dir_test('X:..', tmp, parent_tmp) - Dir_test('X:foo/..', wp, tmp) - Dir_test('X:../foo', tmp_foo, tmp) - Dir_test('X:.', wp, tmp) - Dir_test('X:./.', wp, tmp) - Dir_test('X:foo/./bar', foo_bar, foo) - Dir_test('#X:../foo', tmp_foo, tmp) - Dir_test('#X:/../foo', tmp_foo, tmp) - Dir_test('#X:foo/bar', foo_bar, foo) - Dir_test('#X:/foo/bar', foo_bar, foo) - Dir_test('#X:/', wp, tmp) + Dir_test('#X:', wp, tmp) + Dir_test('X:foo', foo, wp) + Dir_test('X:foo/bar', foo_bar, foo) + Dir_test('X:/foo', 'X:/foo', 'X:/') + Dir_test('X:/foo/bar', 'X:/foo/bar/', 'X:/foo/') + Dir_test('X:..', tmp, parent_tmp) + Dir_test('X:foo/..', wp, tmp) + Dir_test('X:../foo', tmp_foo, tmp) + Dir_test('X:.', wp, tmp) + Dir_test('X:./.', wp, tmp) + Dir_test('X:foo/./bar', foo_bar, foo) + Dir_test('#X:../foo', tmp_foo, tmp) + Dir_test('#X:/../foo', tmp_foo, tmp) + Dir_test('#X:foo/bar', foo_bar, foo) + Dir_test('#X:/foo/bar', foo_bar, foo) + Dir_test('#X:/', wp, tmp) finally: os.path = save_os_path os.sep = save_os_sep @@ -1670,24 +1704,24 @@ class FSTestCase(_tempdirTestCase): path = strip_slash(path) return '//' + path[1:] - wp = unc_workpath(['']) + wp = unc_workpath(['']) if wp[-1] in (os.sep, '/'): - tmp = os.path.split(wp[:-1])[0] + tmp = os.path.split(wp[:-1])[0] else: - tmp = os.path.split(wp)[0] + tmp = os.path.split(wp)[0] - parent_tmp = os.path.split(tmp)[0] + parent_tmp = os.path.split(tmp)[0] - tmp_foo = os.path.join(tmp, 'foo') + tmp_foo = os.path.join(tmp, 'foo') - foo = unc_workpath(['foo']) - foo_bar = unc_workpath(['foo', 'bar']) - sub = unc_workpath(['sub', '']) - sub_dir = unc_workpath(['sub', 'dir', '']) - sub_dir_foo = unc_workpath(['sub', 'dir', 'foo', '']) + foo = unc_workpath(['foo']) + foo_bar = unc_workpath(['foo', 'bar']) + sub = unc_workpath(['sub', '']) + sub_dir = unc_workpath(['sub', 'dir', '']) + sub_dir_foo = unc_workpath(['sub', 'dir', 'foo', '']) sub_dir_foo_bar = unc_workpath(['sub', 'dir', 'foo', 'bar', '']) - sub_foo = unc_workpath(['sub', 'foo', '']) + sub_foo = unc_workpath(['sub', 'foo', '']) fs = SCons.Node.FS.FS() @@ -1708,22 +1742,22 @@ class FSTestCase(_tempdirTestCase): name = path.split(os.sep)[-1] if dir.up() is None: - dir_up_path = dir.get_internal_path() + dir_up_path = dir.get_internal_path() else: - dir_up_path = dir.up().get_internal_path() + dir_up_path = dir.up().get_internal_path() assert dir.name == name, \ - "dir.name %s != expected name %s" % \ - (dir.name, name) + "dir.name %s != expected name %s" % \ + (dir.name, name) assert dir.get_internal_path() == path, \ - "dir.path %s != expected path %s" % \ - (dir.get_internal_path(), path) + "dir.path %s != expected path %s" % \ + (dir.get_internal_path(), path) assert str(dir) == path, \ - "str(dir) %s != expected path %s" % \ - (str(dir), path) + "str(dir) %s != expected path %s" % \ + (str(dir), path) assert dir_up_path == up_path, \ - "dir.up().path %s != expected parent path %s" % \ - (dir.up().get_internal_path(), up_path) + "dir.up().path %s != expected parent path %s" % \ + (dir.up().get_internal_path(), up_path) save_os_path = os.path save_os_sep = os.sep @@ -1734,28 +1768,27 @@ class FSTestCase(_tempdirTestCase): SCons.Node.FS.initialize_do_splitdrive() for sep in seps: - def Dir_test(lpath, path_, up_path_, sep=sep, func=_do_Dir_test): return func(lpath, path_, up_path_, sep) - Dir_test('//foo', '//foo', '//') - Dir_test('//foo/bar', '//foo/bar', '//foo') - Dir_test('//', '//', '//') - Dir_test('//..', '//', '//') - Dir_test('//foo/..', '//', '//') - Dir_test('//../foo', '//foo', '//') - Dir_test('//.', '//', '//') - Dir_test('//./.', '//', '//') - Dir_test('//foo/./bar', '//foo/bar', '//foo') - Dir_test('//foo/../bar', '//bar', '//') - Dir_test('//foo/../../bar', '//bar', '//') - Dir_test('//foo/bar/../..', '//', '//') - Dir_test('#//', wp, tmp) - Dir_test('#//../foo', tmp_foo, tmp) - Dir_test('#//../foo', tmp_foo, tmp) - Dir_test('#//foo/bar', foo_bar, foo) - Dir_test('#//foo/bar', foo_bar, foo) - Dir_test('#//', wp, tmp) + Dir_test('//foo', '//foo', '//') + Dir_test('//foo/bar', '//foo/bar', '//foo') + Dir_test('//', '//', '//') + Dir_test('//..', '//', '//') + Dir_test('//foo/..', '//', '//') + Dir_test('//../foo', '//foo', '//') + Dir_test('//.', '//', '//') + Dir_test('//./.', '//', '//') + Dir_test('//foo/./bar', '//foo/bar', '//foo') + Dir_test('//foo/../bar', '//bar', '//') + Dir_test('//foo/../../bar', '//bar', '//') + Dir_test('//foo/bar/../..', '//', '//') + Dir_test('#//', wp, tmp) + Dir_test('#//../foo', tmp_foo, tmp) + Dir_test('#//../foo', tmp_foo, tmp) + Dir_test('#//foo/bar', foo_bar, foo) + Dir_test('#//foo/bar', foo_bar, foo) + Dir_test('#//', wp, tmp) finally: os.path = save_os_path os.sep = save_os_sep @@ -1810,7 +1843,7 @@ class FSTestCase(_tempdirTestCase): d1 = fs.Dir('d1') d2 = d1.Dir('d2') dirs = os.path.normpath(d2.get_abspath()).split(os.sep) - above_path = os.path.join(*['..']*len(dirs) + ['above']) + above_path = os.path.join(*['..'] * len(dirs) + ['above']) above = d2.Dir(above_path) def test_lookup_abs(self): @@ -1828,13 +1861,13 @@ class FSTestCase(_tempdirTestCase): return test = self.test fs = self.fs - path='//servername/C$/foo' + path = '//servername/C$/foo' f = self.fs._lookup('//servername/C$/foo', fs.Dir('#'), SCons.Node.FS.File) # before the fix in this commit, this returned 'C:\servername\C$\foo' # Should be a normalized Windows UNC path as below. assert str(f) == r'\\servername\C$\foo', \ - 'UNC path %s got looked up as %s'%(path, f) - + 'UNC path %s got looked up as %s' % (path, f) + def test_unc_drive_letter(self): """Test drive-letter lookup for windows UNC-style directories""" if sys.platform not in ('win32',): @@ -1869,56 +1902,56 @@ class FSTestCase(_tempdirTestCase): d3_d4_f = d3_d4.File('f') cases = [ - d1, d1, '.', - d1, d1_f, 'f', - d1, d1_d2, 'd2', - d1, d1_d2_f, 'd2/f', - d1, d3, '../d3', - d1, d3_f, '../d3/f', - d1, d3_d4, '../d3/d4', - d1, d3_d4_f, '../d3/d4/f', - - d1_f, d1, '.', - d1_f, d1_f, 'f', - d1_f, d1_d2, 'd2', - d1_f, d1_d2_f, 'd2/f', - d1_f, d3, '../d3', - d1_f, d3_f, '../d3/f', - d1_f, d3_d4, '../d3/d4', - d1_f, d3_d4_f, '../d3/d4/f', - - d1_d2, d1, '..', - d1_d2, d1_f, '../f', - d1_d2, d1_d2, '.', - d1_d2, d1_d2_f, 'f', - d1_d2, d3, '../../d3', - d1_d2, d3_f, '../../d3/f', - d1_d2, d3_d4, '../../d3/d4', - d1_d2, d3_d4_f, '../../d3/d4/f', - - d1_d2_f, d1, '..', - d1_d2_f, d1_f, '../f', - d1_d2_f, d1_d2, '.', - d1_d2_f, d1_d2_f, 'f', - d1_d2_f, d3, '../../d3', - d1_d2_f, d3_f, '../../d3/f', - d1_d2_f, d3_d4, '../../d3/d4', - d1_d2_f, d3_d4_f, '../../d3/d4/f', + d1, d1, '.', + d1, d1_f, 'f', + d1, d1_d2, 'd2', + d1, d1_d2_f, 'd2/f', + d1, d3, '../d3', + d1, d3_f, '../d3/f', + d1, d3_d4, '../d3/d4', + d1, d3_d4_f, '../d3/d4/f', + + d1_f, d1, '.', + d1_f, d1_f, 'f', + d1_f, d1_d2, 'd2', + d1_f, d1_d2_f, 'd2/f', + d1_f, d3, '../d3', + d1_f, d3_f, '../d3/f', + d1_f, d3_d4, '../d3/d4', + d1_f, d3_d4_f, '../d3/d4/f', + + d1_d2, d1, '..', + d1_d2, d1_f, '../f', + d1_d2, d1_d2, '.', + d1_d2, d1_d2_f, 'f', + d1_d2, d3, '../../d3', + d1_d2, d3_f, '../../d3/f', + d1_d2, d3_d4, '../../d3/d4', + d1_d2, d3_d4_f, '../../d3/d4/f', + + d1_d2_f, d1, '..', + d1_d2_f, d1_f, '../f', + d1_d2_f, d1_d2, '.', + d1_d2_f, d1_d2_f, 'f', + d1_d2_f, d3, '../../d3', + d1_d2_f, d3_f, '../../d3/f', + d1_d2_f, d3_d4, '../../d3/d4', + d1_d2_f, d3_d4_f, '../../d3/d4/f', ] if sys.platform in ('win32',): - x_d1 = fs.Dir(r'X:\d1') - x_d1_d2 = x_d1.Dir('d2') - y_d1 = fs.Dir(r'Y:\d1') - y_d1_d2 = y_d1.Dir('d2') - y_d2 = fs.Dir(r'Y:\d2') + x_d1 = fs.Dir(r'X:\d1') + x_d1_d2 = x_d1.Dir('d2') + y_d1 = fs.Dir(r'Y:\d1') + y_d1_d2 = y_d1.Dir('d2') + y_d2 = fs.Dir(r'Y:\d2') win32_cases = [ - x_d1, x_d1, '.', - x_d1, x_d1_d2, 'd2', - x_d1, y_d1, r'Y:\d1', - x_d1, y_d1_d2, r'Y:\d1\d2', - x_d1, y_d2, r'Y:\d2', + x_d1, x_d1, '.', + x_d1, x_d1_d2, 'd2', + x_d1, y_d1, r'Y:\d1', + x_d1, y_d1_d2, r'Y:\d1\d2', + x_d1, y_d2, r'Y:\d2', ] cases.extend(win32_cases) @@ -1939,14 +1972,15 @@ class FSTestCase(_tempdirTestCase): def test_proxy(self): """Test a Node.FS object wrapped in a proxy instance""" f1 = self.fs.File('fff') + class MyProxy(SCons.Util.Proxy): __str__ = SCons.Util.Delegate('__str__') + p = MyProxy(f1) f2 = self.fs.Entry(p) assert f1 is f2, (f1, str(f1), f2, str(f2)) - class DirTestCase(_tempdirTestCase): def test__morph(self): @@ -1963,8 +1997,10 @@ class DirTestCase(_tempdirTestCase): def test_subclass(self): """Test looking up subclass of Dir nodes""" + class DirSubclass(SCons.Node.FS.Dir): pass + sd = self.fs._lookup('special_dir', None, DirSubclass, create=1) sd.must_be_same(SCons.Node.FS.Dir) @@ -2011,9 +2047,9 @@ class DirTestCase(_tempdirTestCase): test.subdir('d') test.write(['d', 'g'], "67890\n") test.write(['d', 'f'], "12345\n") - test.subdir(['d','sub']) - test.write(['d', 'sub','h'], "abcdef\n") - test.subdir(['d','empty']) + test.subdir(['d', 'sub']) + test.write(['d', 'sub', 'h'], "abcdef\n") + test.subdir(['d', 'empty']) d = self.fs.Dir('d') g = self.fs.File(os.path.join('d', 'g')) @@ -2026,10 +2062,10 @@ class DirTestCase(_tempdirTestCase): assert e.get_contents() == '', e.get_contents() assert e.get_text_contents() == '', e.get_text_contents() - assert e.get_csig()+" empty" == files[0], files - assert f.get_csig()+" f" == files[1], files - assert g.get_csig()+" g" == files[2], files - assert s.get_csig()+" sub" == files[3], files + assert e.get_csig() + " empty" == files[0], files + assert f.get_csig() + " f" == files[1], files + assert g.get_csig() + " g" == files[2], files + assert s.get_csig() + " sub" == files[3], files def test_implicit_re_scans(self): """Test that adding entries causes a directory to be re-scanned @@ -2087,7 +2123,7 @@ class DirTestCase(_tempdirTestCase): d = self.fs.Dir('d') r = self.fs.Dir('r') d.addRepository(r) - + assert d.rentry_exists_on_disk('exists') assert d.rentry_exists_on_disk('rexists') assert not d.rentry_exists_on_disk('does_not_exist') @@ -2193,7 +2229,7 @@ class DirTestCase(_tempdirTestCase): SCons.Node._is_derived_map[2] = return_true SCons.Node._exists_map[5] = return_true - + test.subdir('src0') test.write(['src0', 'on-disk-f1'], "src0/on-disk-f1\n") test.write(['src0', 'on-disk-f2'], "src0/on-disk-f2\n") @@ -2341,6 +2377,7 @@ class DirTestCase(_tempdirTestCase): r = sub.file_on_disk('dir') assert not r, r + class EntryTestCase(_tempdirTestCase): def test_runTest(self): """Test methods specific to the Entry sub-class. @@ -2391,16 +2428,20 @@ class EntryTestCase(_tempdirTestCase): class MyCalc(object): def __init__(self, val): self.max_drift = 0 + class M(object): def __init__(self, val): self.val = val + def collect(self, args): result = 0 for a in args: result += a return result + def signature(self, executor): return self.val + 222 + self.module = M(val) test.subdir('e5d') @@ -2412,13 +2453,14 @@ class EntryTestCase(_tempdirTestCase): self.fs.Entry('#topdir/a/b/c') - class FileTestCase(_tempdirTestCase): def test_subclass(self): """Test looking up subclass of File nodes""" + class FileSubclass(SCons.Node.FS.File): pass + sd = self.fs._lookup('special_file', None, FileSubclass, create=1) sd.must_be_same(SCons.Node.FS.File) @@ -2462,7 +2504,8 @@ class FileTestCase(_tempdirTestCase): build_f1.clear() build_f1.linked = None assert not build_f1.exists(), "%s did not realize that %s disappeared" % (build_f1, src_f1) - assert not os.path.exists(build_f1.get_abspath()), "%s did not get removed after %s was removed" % (build_f1, src_f1) + assert not os.path.exists(build_f1.get_abspath()), "%s did not get removed after %s was removed" % ( + build_f1, src_f1) def test_changed(self): """ @@ -2471,6 +2514,7 @@ class FileTestCase(_tempdirTestCase): when using CacheDir and Timestamp-MD5 decider. This is for issue #2980 """ + # node should have # 1 source (for example main.cpp) # 0 depends @@ -2527,18 +2571,18 @@ class FileTestCase(_tempdirTestCase): self.csig = csig self.timestamp = timestamp - #Create nodes + # Create nodes fs = SCons.Node.FS.FS() d = self.fs.Dir('.') - node = ChangedNode('main.o',d,fs) # main.o - s1 = ChangedNode('main.cpp',d,fs) # main.cpp - s1.timestamp = 2 # this changed - i1 = ChangedNode('alpha.h',d,fs) # alpha.h - The bug is caused because the second build adds this file - i1.timestamp = 2 # This is the new file. - i2 = ChangedNode('beta.h',d,fs) # beta.h - i3 = ChangedNode('gamma.h',d,fs) # gamma.h - In the bug beta.h's csig from binfo overwrites this ones - i4 = ChangedNode('/usr/bin/g++',d,fs) # /usr/bin/g++ + node = ChangedNode('main.o', d, fs) # main.o + s1 = ChangedNode('main.cpp', d, fs) # main.cpp + s1.timestamp = 2 # this changed + i1 = ChangedNode('alpha.h', d, fs) # alpha.h - The bug is caused because the second build adds this file + i1.timestamp = 2 # This is the new file. + i2 = ChangedNode('beta.h', d, fs) # beta.h + i3 = ChangedNode('gamma.h', d, fs) # gamma.h - In the bug beta.h's csig from binfo overwrites this ones + i4 = ChangedNode('/usr/bin/g++', d, fs) # /usr/bin/g++ node.add_source([s1]) node.add_dependency([]) @@ -2569,34 +2613,35 @@ class FileTestCase(_tempdirTestCase): # "bdepends","bdependsigs", "bimplicit", "bimplicitsigs" bi = sconsign_entry.binfo bi.bsources = ['main.cpp'] - bi.bsourcesigs=[FakeNodeInfo('main.cpp',1),] + bi.bsourcesigs = [FakeNodeInfo('main.cpp', 1), ] bi.bdepends = [] bi.bdependsigs = [] - bi.bimplicit = ['beta.h','gamma.h'] - bi.bimplicitsigs = [FakeNodeInfo('beta.h',1), FakeNodeInfo('gamma.h',1)] + bi.bimplicit = ['beta.h', 'gamma.h'] + bi.bimplicitsigs = [FakeNodeInfo('beta.h', 1), FakeNodeInfo('gamma.h', 1)] ni = sconsign_entry.ninfo # We'll set the following attributes (which are lists): sources, depends, implicit lists - #Set timestamp-md5 - #Call changed - #Check results + # Set timestamp-md5 + # Call changed + # Check results node.build_env = ChangedEnvironment() changed = node.changed() # change to true to debug if False: - print("Changed:%s"%changed) - print("%15s -> csig:%s"%(s1.name, s1.ninfo.csig)) - print("%15s -> csig:%s"%(i1.name, i1.ninfo.csig)) - print("%15s -> csig:%s"%(i2.name, i2.ninfo.csig)) - print("%15s -> csig:%s"%(i3.name, i3.ninfo.csig)) - print("%15s -> csig:%s"%(i4.name, i4.ninfo.csig)) + print("Changed:%s" % changed) + print("%15s -> csig:%s" % (s1.name, s1.ninfo.csig)) + print("%15s -> csig:%s" % (i1.name, i1.ninfo.csig)) + print("%15s -> csig:%s" % (i2.name, i2.ninfo.csig)) + print("%15s -> csig:%s" % (i3.name, i3.ninfo.csig)) + print("%15s -> csig:%s" % (i4.name, i4.ninfo.csig)) - self.assertEqual(i2.name,i2.ninfo.csig, "gamma.h's fake csig should equal gamma.h but equals:%s"%i2.ninfo.csig) + self.assertEqual(i2.name, i2.ninfo.csig, + "gamma.h's fake csig should equal gamma.h but equals:%s" % i2.ninfo.csig) class GlobTestCase(_tempdirTestCase): @@ -2665,7 +2710,6 @@ class GlobTestCase(_tempdirTestCase): self.sub_dir3_jjj = self.sub_dir3.File('jjj') self.sub_dir3_lll = self.sub_dir3.File('lll') - def do_cases(self, cases, **kwargs): # First, execute all of the cases with string=True and verify @@ -2713,17 +2757,17 @@ class GlobTestCase(_tempdirTestCase): join = os.path.join cases = ( - ('ggg', ['ggg'], [self.ggg]), + ('ggg', ['ggg'], [self.ggg]), - ('subdir1', ['subdir1'], [self.subdir1]), + ('subdir1', ['subdir1'], [self.subdir1]), - ('subdir1/jjj', [join('subdir1', 'jjj')], [self.subdir1_jjj]), + ('subdir1/jjj', [join('subdir1', 'jjj')], [self.subdir1_jjj]), - ('disk-aaa', ['disk-aaa'], None), + ('disk-aaa', ['disk-aaa'], None), - ('disk-sub', ['disk-sub'], None), + ('disk-sub', ['disk-sub'], None), - ('both-aaa', ['both-aaa'], []), + ('both-aaa', ['both-aaa'], []), ) self.do_cases(cases) @@ -2818,8 +2862,8 @@ class GlobTestCase(_tempdirTestCase): """Test globbing for things that don't exist""" cases = ( - ('does_not_exist', [], []), - ('no_subdir/*', [], []), + ('does_not_exist', [], []), + ('no_subdir/*', [], []), ('subdir?/no_file', [], []), ) @@ -2927,7 +2971,7 @@ class GlobTestCase(_tempdirTestCase): assert g == expect, str(g) + " is not sorted, but should be!" g = self.fs.Glob('disk-*', strings=True) - expect = [ 'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub' ] + expect = ['disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub'] assert g == expect, str(g) + " is not sorted, but should be!" @@ -3060,10 +3104,13 @@ class RepositoryTestCase(_tempdirTestCase): def test_rdir(self): """Test the Dir.rdir() method""" + def return_true(obj): return 1 + def return_false(obj): return 0 + SCons.Node._exists_map[5] = return_true SCons.Node._exists_map[6] = return_false SCons.Node._is_derived_map[2] = return_true @@ -3113,10 +3160,13 @@ class RepositoryTestCase(_tempdirTestCase): def test_rfile(self): """Test the File.rfile() method""" + def return_true(obj): return 1 + def return_false(obj): return 0 + SCons.Node._exists_map[5] = return_true SCons.Node._exists_map[6] = return_false SCons.Node._is_derived_map[2] = return_true @@ -3255,7 +3305,7 @@ class RepositoryTestCase(_tempdirTestCase): test.write(["rep3", "contents"], "Con\x1aTents\n") try: c = fs.File("contents").get_contents() - assert c == bytearray("Con\x1aTents\n",'utf-8'), "got '%s'" % c + assert c == bytearray("Con\x1aTents\n", 'utf-8'), "got '%s'" % c finally: test.unlink(["rep3", "contents"]) @@ -3274,8 +3324,8 @@ class RepositoryTestCase(_tempdirTestCase): class FakeUnicodeString(collections.UserString): def encode(self, encoding): return str(self) - test_string = FakeUnicodeString("Con\x1aTents\n") + test_string = FakeUnicodeString("Con\x1aTents\n") # Test with ASCII. test.write(["rep3", "contents"], test_string.encode('ascii')) @@ -3301,14 +3351,13 @@ class RepositoryTestCase(_tempdirTestCase): finally: test.unlink(["rep3", "contents"]) - #def test_is_up_to_date(self): - + # def test_is_up_to_date(self): class find_fileTestCase(unittest.TestCase): def runTest(self): """Testing find_file function""" - test = TestCmd(workdir = '') + test = TestCmd(workdir='') test.write('./foo', 'Some file\n') test.write('./foo2', 'Another file\n') test.subdir('same') @@ -3321,9 +3370,9 @@ class find_fileTestCase(unittest.TestCase): os.chdir(test.workpath("")) node_derived = fs.File(test.workpath('bar/baz')) - node_derived.builder_set(1) # Any non-zero value. + node_derived.builder_set(1) # Any non-zero value. node_pseudo = fs.File(test.workpath('pseudo')) - node_pseudo.set_src_builder(1) # Any non-zero value. + node_pseudo.set_src_builder(1) # Any non-zero value. paths = tuple(map(fs.Dir, ['.', 'same', './bar'])) nodes = [SCons.Node.FS.find_file('foo', paths)] @@ -3376,12 +3425,13 @@ class find_fileTestCase(unittest.TestCase): finally: sys.stdout = save_sys_stdout + class StringDirTestCase(unittest.TestCase): def runTest(self): """Test using a string as the second argument of File() and Dir()""" - test = TestCmd(workdir = '') + test = TestCmd(workdir='') test.subdir('sub') fs = SCons.Node.FS.FS(test.workpath('')) @@ -3392,10 +3442,11 @@ class StringDirTestCase(unittest.TestCase): assert str(f) == os.path.join('sub', 'file') assert not f.exists() + class stored_infoTestCase(unittest.TestCase): def runTest(self): """Test how we store build information""" - test = TestCmd(workdir = '') + test = TestCmd(workdir='') test.subdir('sub') fs = SCons.Node.FS.FS(test.workpath('')) @@ -3408,9 +3459,10 @@ class stored_infoTestCase(unittest.TestCase): class Null(object): def __init__(self): self.xyzzy = 7 + def get_entry(self, name): return self.Null() - + def test_sconsign(node): return MySConsign() @@ -3420,10 +3472,11 @@ class stored_infoTestCase(unittest.TestCase): bi = f.get_stored_info() assert bi.xyzzy == 7, bi + class has_src_builderTestCase(unittest.TestCase): def runTest(self): """Test the has_src_builder() method""" - test = TestCmd(workdir = '') + test = TestCmd(workdir='') fs = SCons.Node.FS.FS(test.workpath('')) os.chdir(test.workpath('')) test.subdir('sub1') @@ -3442,9 +3495,9 @@ class has_src_builderTestCase(unittest.TestCase): sub1.set_src_builder(b1) test.write(['sub1', 'f2'], "sub1/f2\n") - h = f1.has_src_builder() # cached from previous call + h = f1.has_src_builder() # cached from previous call assert not h, h - h = f1.has_builder() # cached from previous call + h = f1.has_builder() # cached from previous call assert not h, h h = f2.has_src_builder() assert not h, h @@ -3456,6 +3509,7 @@ class has_src_builderTestCase(unittest.TestCase): assert h, h assert f3.builder is b1, f3.builder + class prepareTestCase(unittest.TestCase): def runTest(self): """Test the prepare() method""" @@ -3463,6 +3517,7 @@ class prepareTestCase(unittest.TestCase): class MyFile(SCons.Node.FS.File): def _createDir(self, update=None): raise SCons.Errors.StopError + def exists(self): return None @@ -3479,6 +3534,7 @@ class prepareTestCase(unittest.TestCase): class MkdirAction(Action): def __init__(self, dir_made): self.dir_made = dir_made + def __call__(self, target, source, env, executor=None): if executor: target = executor.get_all_targets() @@ -3503,7 +3559,6 @@ class prepareTestCase(unittest.TestCase): dir.prepare() - class SConstruct_dirTestCase(unittest.TestCase): def runTest(self): """Test setting the SConstruct directory""" @@ -3513,7 +3568,6 @@ class SConstruct_dirTestCase(unittest.TestCase): assert fs.SConstruct_dir.get_internal_path() == 'xxx' - class CacheDirTestCase(unittest.TestCase): def test_get_cachedir_csig(self): @@ -3524,7 +3578,6 @@ class CacheDirTestCase(unittest.TestCase): assert r == 'd41d8cd98f00b204e9800998ecf8427e', r - class clearTestCase(unittest.TestCase): def runTest(self): """Test clearing FS nodes of cached data.""" @@ -3534,11 +3587,11 @@ class clearTestCase(unittest.TestCase): e = fs.Entry('e') assert not e.exists() assert not e.rexists() - assert str(e) == 'e', str(d) + assert str(e) == 'e', str(e) e.clear() assert not e.exists() assert not e.rexists() - assert str(e) == 'e', str(d) + assert str(e) == 'e', str(e) d = fs.Dir(test.workpath('d')) test.subdir('d') @@ -3552,10 +3605,10 @@ class clearTestCase(unittest.TestCase): assert str(d) == test.workpath('d'), str(d) # Now verify clear() resets the cache d.clear() - assert not d.exists() + assert not d.exists() assert not d.rexists() assert str(d) == test.workpath('d'), str(d) - + f = fs.File(test.workpath('f')) test.write(test.workpath('f'), 'file f') assert f.exists() @@ -3573,7 +3626,6 @@ class clearTestCase(unittest.TestCase): assert str(f) == test.workpath('f'), str(f) - class disambiguateTestCase(unittest.TestCase): def runTest(self): """Test calling the disambiguate() method.""" @@ -3635,6 +3687,7 @@ class disambiguateTestCase(unittest.TestCase): f = build_nonexistant.disambiguate() assert f.__class__ is fff.__class__, f.__class__ + class postprocessTestCase(unittest.TestCase): def runTest(self): """Test calling the postprocess() method.""" @@ -3650,11 +3703,10 @@ class postprocessTestCase(unittest.TestCase): f.postprocess() - class SpecialAttrTestCase(unittest.TestCase): def runTest(self): """Test special attributes of file nodes.""" - test=TestCmd(workdir='') + test = TestCmd(workdir='') fs = SCons.Node.FS.FS(test.workpath('work')) f = fs.Entry('foo/bar/baz.blat').get_subst_proxy() @@ -3809,11 +3861,10 @@ class SpecialAttrTestCase(unittest.TestCase): assert caught, "did not catch expected AttributeError" - class SaveStringsTestCase(unittest.TestCase): def runTest(self): """Test caching string values of nodes.""" - test=TestCmd(workdir='') + test = TestCmd(workdir='') def setup(fs): fs.Dir('src') @@ -3867,13 +3918,13 @@ class SaveStringsTestCase(unittest.TestCase): s = list(map(str, nodes)) expect = list(map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b'])) - assert s == expect, 'node str() not cached: %s'%s + assert s == expect, 'node str() not cached: %s' % s class AbsolutePathTestCase(unittest.TestCase): def test_root_lookup_equivalence(self): """Test looking up /fff vs. fff in the / directory""" - test=TestCmd(workdir='') + test = TestCmd(workdir='') fs = SCons.Node.FS.FS('/') @@ -3887,7 +3938,6 @@ class AbsolutePathTestCase(unittest.TestCase): os.chdir(save_cwd) - if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 39b928b..ce44b7d 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -20,7 +20,7 @@ # 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__ = "src/engine/SCons/Node/NodeTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/NodeTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -271,7 +271,7 @@ class NodeInfoBaseTestCase(unittest.TestCase): f = ni1.format() assert f == ['x', 'y', 'z'], f - + field_list = ['xxx', 'zzz', 'aaa'] f = ni1.format(field_list) @@ -751,7 +751,7 @@ class NodeTestCase(unittest.TestCase): e = node.exists() assert e == 1, e - def test_exists(self): + def test_exists_repo(self): """Test evaluating whether a Node exists locally or in a repository. """ node = SCons.Node.Node() diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py index 30daf9a..d338051 100644 --- a/src/engine/SCons/Node/Python.py +++ b/src/engine/SCons/Node/Python.py @@ -27,7 +27,7 @@ Python nodes. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/Python.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/Python.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Node diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index 2b35f00..98137c7 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Node/PythonTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/PythonTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 564e8de..5455bd6 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -43,13 +43,18 @@ from __future__ import print_function # 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__ = "src/engine/SCons/Node/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Node/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import collections import copy from itertools import chain +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + import SCons.Debug from SCons.Debug import logInstanceCreation import SCons.Executor @@ -103,9 +108,9 @@ implicit_deps_changed = 0 # A variable that can be set to an interface-specific function be called # to annotate a Node with information about its creation. -def do_nothing(node): pass +def do_nothing_node(node): pass -Annotate = do_nothing +Annotate = do_nothing_node # Gets set to 'True' if we're running in interactive mode. Is # currently used to release parts of a target's info during @@ -248,25 +253,10 @@ _target_from_source_map = {0 : target_from_source_none, # used by it. # - -class DeciderNeedsNode(Exception): - """ - Indicate that the decider needs the node as well as the target and the dependency. - Normally the node and the target are the same, but in the case of repository - They may be different. Also the NodeInfo is retrieved from the node - """ - def __init__(self, call_this_decider): - """ - :param call_this_decider: to return the decider to call directly since deciders - are called through several levels of indirection - """ - self.decider = call_this_decider - - # # First, the single decider functions # -def changed_since_last_build_node(node, target, prev_ni): +def changed_since_last_build_node(node, target, prev_ni, repo_node=None): """ Must be overridden in a specific subclass to return True if this @@ -287,7 +277,7 @@ def changed_since_last_build_node(node, target, prev_ni): raise NotImplementedError -def changed_since_last_build_alias(node, target, prev_ni): +def changed_since_last_build_alias(node, target, prev_ni, repo_node=None): cur_csig = node.get_csig() try: return cur_csig != prev_ni.csig @@ -295,24 +285,24 @@ def changed_since_last_build_alias(node, target, prev_ni): return 1 -def changed_since_last_build_entry(node, target, prev_ni): +def changed_since_last_build_entry(node, target, prev_ni, repo_node=None): node.disambiguate() - return _decider_map[node.changed_since_last_build](node, target, prev_ni) + return _decider_map[node.changed_since_last_build](node, target, prev_ni, repo_node) -def changed_since_last_build_state_changed(node, target, prev_ni): +def changed_since_last_build_state_changed(node, target, prev_ni, repo_node=None): return node.state != SCons.Node.up_to_date -def decide_source(node, target, prev_ni): - return target.get_build_env().decide_source(node, target, prev_ni) +def decide_source(node, target, prev_ni, repo_node=None): + return target.get_build_env().decide_source(node, target, prev_ni, repo_node) -def decide_target(node, target, prev_ni): - return target.get_build_env().decide_target(node, target, prev_ni) +def decide_target(node, target, prev_ni, repo_node=None): + return target.get_build_env().decide_target(node, target, prev_ni, repo_node) -def changed_since_last_build_python(node, target, prev_ni): +def changed_since_last_build_python(node, target, prev_ni, repo_node=None): cur_csig = node.get_csig() try: return cur_csig != prev_ni.csig @@ -529,6 +519,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): __slots__ = ['sources', 'sources_set', + 'target_peers', '_specific_sources', 'depends', 'depends_set', @@ -784,6 +775,25 @@ class Node(object, with_metaclass(NoSlotsPyPy)): for parent in self.waiting_parents: parent.implicit = None + # Handle issue where builder emits more than one target and + # the source file for the builder is generated. + # in that case only the first target was getting it's .implicit + # cleared when the source file is built (second scan). + # leaving only partial implicits from scan before source file is generated + # typically the compiler only. Then scanned files are appended + # This is persisted to sconsign and rebuild causes false rebuilds + # because the ordering of the implicit list then changes to what it + # should have been. + # This is at least the following bugs + # https://github.com/SCons/scons/issues/2811 + # https://jira.mongodb.org/browse/SERVER-33111 + try: + for peer in parent.target_peers: + peer.implicit = None + except AttributeError: + pass + + self.clear() if self.pseudo: @@ -1160,7 +1170,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) if self._specific_sources: - sources = [s for s in self.sources if not s in ignore_set] + sources = [s for s in self.sources if s not in ignore_set] else: sources = executor.get_unignored_sources(self, self.ignore) @@ -1480,17 +1490,11 @@ class Node(object, with_metaclass(NoSlotsPyPy)): result = True for child, prev_ni in zip(children, then): - try: - if _decider_map[child.changed_since_last_build](child, self, prev_ni): - if t: Trace(': %s changed' % child) - result = True - except DeciderNeedsNode as e: - if e.decider(self, prev_ni, node=node): - if t: Trace(': %s changed' % child) - result = True + if _decider_map[child.changed_since_last_build](child, self, prev_ni, node): + if t: Trace(': %s changed' % child) + result = True if self.has_builder(): - import SCons.Util contents = self.get_executor().get_contents() newsig = SCons.Util.MD5signature(contents) if bi.bactsig != newsig: @@ -1647,14 +1651,14 @@ class Node(object, with_metaclass(NoSlotsPyPy)): lines = [] - removed = [x for x in old_bkids if not x in new_bkids] + removed = [x for x in old_bkids if x not in new_bkids] if removed: removed = [stringify(r) for r in removed] fmt = "`%s' is no longer a dependency\n" lines.extend([fmt % s for s in removed]) for k in new_bkids: - if not k in old_bkids: + if k not in old_bkids: lines.append("`%s' is a new dependency\n" % stringify(k)) else: try: @@ -1666,9 +1670,16 @@ class Node(object, with_metaclass(NoSlotsPyPy)): lines.append("`%s' changed\n" % stringify(k)) if len(lines) == 0 and old_bkids != new_bkids: - lines.append("the dependency order changed:\n" + - "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + - "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) + lines.append("the dependency order changed:\n") + lines.append("->Sources\n") + for (o,n) in zip_longest(old.bsources, new.bsources, fillvalue=None): + lines.append("Old:%s\tNew:%s\n"%(o,n)) + lines.append("->Depends\n") + for (o,n) in zip_longest(old.bdepends, new.bdepends, fillvalue=None): + lines.append("Old:%s\tNew:%s\n"%(o,n)) + lines.append("->Implicit\n") + for (o,n) in zip_longest(old.bimplicit, new.bimplicit, fillvalue=None): + lines.append("Old:%s\tNew:%s\n"%(o,n)) if len(lines) == 0: def fmt_with_title(title, strlines): @@ -1711,7 +1722,6 @@ class Walker(object): This is depth-first, children are visited before the parent. The Walker object can be initialized with any node, and returns the next node on the descent with each get_next() call. - 'kids_func' is an optional function that will be called to get the children of a node instead of calling 'children'. 'cycle_func' is an optional function that will be called when a cycle is detected. diff --git a/src/engine/SCons/PathList.py b/src/engine/SCons/PathList.py index 98a160c..d6a01e7 100644 --- a/src/engine/SCons/PathList.py +++ b/src/engine/SCons/PathList.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/PathList.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/PathList.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """SCons.PathList diff --git a/src/engine/SCons/PathListTests.py b/src/engine/SCons/PathListTests.py index ea9e3ff..0a18fbe 100644 --- a/src/engine/SCons/PathListTests.py +++ b/src/engine/SCons/PathListTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/PathListTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/PathListTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py index 21dbf18..7941625 100644 --- a/src/engine/SCons/Platform/PlatformTests.py +++ b/src/engine/SCons/Platform/PlatformTests.py @@ -21,18 +21,20 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/PlatformTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/PlatformTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat import collections import unittest +import os import SCons.Errors import SCons.Platform import SCons.Environment import SCons.Action + class Environment(collections.UserDict): def Detect(self, cmd): return cmd @@ -40,6 +42,7 @@ class Environment(collections.UserDict): def AppendENVPath(self, key, value): pass + class PlatformTestCase(unittest.TestCase): def test_Platform(self): """Test the Platform() function""" @@ -110,13 +113,14 @@ class PlatformTestCase(unittest.TestCase): p = SCons.Platform.Platform('_does_not_exist_') except SCons.Errors.UserError: pass - else: + else: # TODO pylint E0704: bare raise not inside except raise env = Environment() SCons.Platform.Platform()(env) assert env != {}, env + class TempFileMungeTestCase(unittest.TestCase): def test_MAXLINELENGTH(self): """ Test different values for MAXLINELENGTH with the same @@ -138,22 +142,58 @@ class TempFileMungeTestCase(unittest.TestCase): env['OVERSIMPLIFIED'] = 'command' expanded_cmd = env.subst(defined_cmd) # Call the tempfile munger - cmd = t(None,None,env,0) + cmd = t(None, None, env, 0) assert cmd == defined_cmd, cmd # Let MAXLINELENGTH equal the string's length env['MAXLINELENGTH'] = len(expanded_cmd) - cmd = t(None,None,env,0) + cmd = t(None, None, env, 0) assert cmd == defined_cmd, cmd # Finally, let the actual tempfile mechanism kick in # Disable printing of actions... old_actions = SCons.Action.print_actions SCons.Action.print_actions = 0 env['MAXLINELENGTH'] = len(expanded_cmd)-1 - cmd = t(None,None,env,0) + cmd = t(None, None, env, 0) # ...and restoring its setting. SCons.Action.print_actions = old_actions assert cmd != defined_cmd, cmd + def test_TEMPFILEARGJOINBYTE(self): + """ + Test argument join byte TEMPFILEARGJOINBYTE + """ + + # Init class with cmd, such that the fully expanded + # string reads "a test command line". + # Note, how we're using a command string here that is + # actually longer than the substituted one. This is to ensure + # that the TempFileMunge class internally really takes the + # length of the expanded string into account. + defined_cmd = "a $VERY $OVERSIMPLIFIED line" + t = SCons.Platform.TempFileMunge(defined_cmd) + env = SCons.Environment.SubstitutionEnvironment(tools=[]) + # Setting the line length high enough... + env['MAXLINELENGTH'] = 1024 + env['VERY'] = 'test' + env['OVERSIMPLIFIED'] = 'command' + env['TEMPFILEARGJOINBYTE'] = os.linesep + expanded_cmd = env.subst(defined_cmd) + + # For tempfilemunge to operate. + old_actions = SCons.Action.print_actions + SCons.Action.print_actions = 0 + env['MAXLINELENGTH'] = len(expanded_cmd)-1 + cmd = t(None, None, env, 0) + # print("CMD is:%s"%cmd) + + with open(cmd[-1],'rb') as f: + file_content = f.read() + # print("Content is:[%s]"%file_content) + # ...and restoring its setting. + SCons.Action.print_actions = old_actions + assert file_content != env['TEMPFILEARGJOINBYTE'].join(['test','command','line']) + + def test_tempfilecreation_once(self): # Init class with cmd, such that the fully expanded # string reads "a test command line". @@ -173,9 +213,11 @@ class TempFileMungeTestCase(unittest.TestCase): old_actions = SCons.Action.print_actions SCons.Action.print_actions = 0 # Create an instance of object derived class to allow setattrb - class Node(object) : + + class Node(object): class Attrs(object): pass + def __init__(self): self.attributes = self.Attrs() target = [Node()] @@ -185,6 +227,7 @@ class TempFileMungeTestCase(unittest.TestCase): assert cmd != defined_cmd, cmd assert cmd == getattr(target[0].attributes, 'tempfile_cmdlist', None) + class PlatformEscapeTestCase(unittest.TestCase): def test_posix_escape(self): """ Check that paths with parens are escaped properly diff --git a/src/engine/SCons/Platform/__init__.py b/src/engine/SCons/Platform/__init__.py index ab4b293..8da1e9d 100644 --- a/src/engine/SCons/Platform/__init__.py +++ b/src/engine/SCons/Platform/__init__.py @@ -43,11 +43,11 @@ their own platform definition. # from __future__ import print_function -__revision__ = "src/engine/SCons/Platform/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat -import imp +import importlib import os import sys import tempfile @@ -101,13 +101,8 @@ def platform_module(name = platform_default()): eval(full_name) else: try: - file, path, desc = imp.find_module(name, - sys.modules['SCons.Platform'].__path__) - try: - mod = imp.load_module(full_name, file, path, desc) - finally: - if file: - file.close() + # the specific platform module is a relative import + mod = importlib.import_module("." + name, __name__) except ImportError: try: import zipimport @@ -231,8 +226,10 @@ class TempFileMunge(object): prefix = '@' args = list(map(SCons.Subst.quote_spaces, cmd[1:])) - os.write(fd, bytearray(" ".join(args) + "\n",'utf-8')) + join_char = env.get('TEMPFILEARGJOIN',' ') + os.write(fd, bytearray(join_char.join(args) + "\n",'utf-8')) os.close(fd) + # XXX Using the SCons.Action.print_actions value directly # like this is bogus, but expedient. This class should # really be rewritten as an Action that defines the diff --git a/src/engine/SCons/Platform/__init__.xml b/src/engine/SCons/Platform/__init__.xml index 60bfb38..d4c2a21 100644 --- a/src/engine/SCons/Platform/__init__.xml +++ b/src/engine/SCons/Platform/__init__.xml @@ -257,4 +257,15 @@ The default is '.lnk'. + + + +The string (or character) to be used to join the arguments passed to TEMPFILE when command line exceeds the limit set by &cv-MAXLINELENGTH;. +The default value is a space. However for MSVC, MSLINK the default is a line seperator characters as defined by os.linesep. +Note this value is used literally and not expanded by the subst logic. + + + + + diff --git a/src/engine/SCons/Platform/aix.py b/src/engine/SCons/Platform/aix.py index ba10ae6..70fbb35 100644 --- a/src/engine/SCons/Platform/aix.py +++ b/src/engine/SCons/Platform/aix.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/aix.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/aix.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import subprocess diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py index 7538f0a..70b6443 100644 --- a/src/engine/SCons/Platform/cygwin.py +++ b/src/engine/SCons/Platform/cygwin.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/cygwin.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/cygwin.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys diff --git a/src/engine/SCons/Platform/darwin.py b/src/engine/SCons/Platform/darwin.py index fc6e890..d5861d9 100644 --- a/src/engine/SCons/Platform/darwin.py +++ b/src/engine/SCons/Platform/darwin.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/darwin.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/darwin.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import posix import os @@ -56,12 +56,11 @@ def generate(env): for file in filelist: if os.path.isfile(file): - f = open(file, 'r') - lines = f.readlines() - for line in lines: - if line: - env.AppendENVPath('PATHOSX', line.strip('\n')) - f.close() + with open(file, 'r') as f: + lines = f.readlines() + for line in lines: + if line: + env.AppendENVPath('PATHOSX', line.strip('\n')) # Not sure why this wasn't the case all along? if env['ENV'].get('PATHOSX', False) and os.environ.get('SCONS_USE_MAC_PATHS', False): diff --git a/src/engine/SCons/Platform/hpux.py b/src/engine/SCons/Platform/hpux.py index ccda01e..6867212 100644 --- a/src/engine/SCons/Platform/hpux.py +++ b/src/engine/SCons/Platform/hpux.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/hpux.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/hpux.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import posix diff --git a/src/engine/SCons/Platform/irix.py b/src/engine/SCons/Platform/irix.py index 38f0f26..a382e71 100644 --- a/src/engine/SCons/Platform/irix.py +++ b/src/engine/SCons/Platform/irix.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/irix.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/irix.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import posix diff --git a/src/engine/SCons/Platform/mingw.py b/src/engine/SCons/Platform/mingw.py index 95fb2e4..c03c9c8 100644 --- a/src/engine/SCons/Platform/mingw.py +++ b/src/engine/SCons/Platform/mingw.py @@ -27,7 +27,7 @@ Platform-specific initialization for the MinGW system. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/mingw.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/mingw.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys diff --git a/src/engine/SCons/Platform/os2.py b/src/engine/SCons/Platform/os2.py index bc49c88..ebabe3d 100644 --- a/src/engine/SCons/Platform/os2.py +++ b/src/engine/SCons/Platform/os2.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/os2.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/os2.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import win32 def generate(env): diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py index 27c3f3a..780c764 100644 --- a/src/engine/SCons/Platform/posix.py +++ b/src/engine/SCons/Platform/posix.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/posix.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/posix.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import errno import os diff --git a/src/engine/SCons/Platform/sunos.py b/src/engine/SCons/Platform/sunos.py index b54be76..6aa54aa 100644 --- a/src/engine/SCons/Platform/sunos.py +++ b/src/engine/SCons/Platform/sunos.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/sunos.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/sunos.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import posix diff --git a/src/engine/SCons/Platform/virtualenv.py b/src/engine/SCons/Platform/virtualenv.py index e24fb7a..9fb3e45 100644 --- a/src/engine/SCons/Platform/virtualenv.py +++ b/src/engine/SCons/Platform/virtualenv.py @@ -26,7 +26,7 @@ Support for virtualenv. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/virtualenv.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/virtualenv.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys diff --git a/src/engine/SCons/Platform/virtualenvTests.py b/src/engine/SCons/Platform/virtualenvTests.py index f560f2e..891fdb3 100644 --- a/src/engine/SCons/Platform/virtualenvTests.py +++ b/src/engine/SCons/Platform/virtualenvTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/virtualenvTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/virtualenvTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index f64081c..ea567a2 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Platform/win32.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Platform/win32.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path @@ -193,7 +193,7 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): # actually do the spawn try: - args = [sh, '/C', escape(' '.join(args)) ] + args = [sh, '/C', escape(' '.join(args))] ret = spawnve(os.P_WAIT, sh, args, env) except OSError as e: # catch any error @@ -207,15 +207,17 @@ def piped_spawn(sh, escape, cmd, args, env, stdout, stderr): # and do clean up stuff if stdout is not None and stdoutRedirected == 0: try: - stdout.write(open( tmpFileStdout, "r" ).read()) - os.remove( tmpFileStdout ) + with open(tmpFileStdout, "r" ) as tmp: + stdout.write(tmp.read()) + os.remove(tmpFileStdout) except (IOError, OSError): pass if stderr is not None and stderrRedirected == 0: try: - stderr.write(open( tmpFileStderr, "r" ).read()) - os.remove( tmpFileStderr ) + with open(tmpFileStderr, "r" ) as tmp: + stderr.write(tmp.read()) + os.remove(tmpFileStderr) except (IOError, OSError): pass return ret diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index e0cd35d..7409c50 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -35,7 +35,7 @@ libraries are installed, if some command line options are supported etc. # from __future__ import print_function -__revision__ = "src/engine/SCons/SConf.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/SConf.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -56,7 +56,6 @@ import SCons.Warnings import SCons.Conftest from SCons.Debug import Trace -from SCons.Node import DeciderNeedsNode # Turn off the Conftest error logging SCons.Conftest.LogInputFiles = 0 @@ -247,6 +246,7 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask): # ConfigureCacheError and if yes, reraise the exception exc_type = self.exc_info()[0] if issubclass(exc_type, SConfError): + # TODO pylint E0704: bare raise not inside except raise elif issubclass(exc_type, SCons.Errors.BuildError): # we ignore Build Errors (occurs, when a test doesn't pass) @@ -406,12 +406,10 @@ class SConfBase(object): # that the correct .sconsign info will get calculated # and keep the build state consistent. def force_build(dependency, target, prev_ni, - env_decider=env.decide_source, - node=None): + repo_node=None, + env_decider=env.decide_source): try: - env_decider(dependency, target, prev_ni) - except DeciderNeedsNode as e: - e.decider(target, prev_ni, node=target) + env_decider(dependency, target, prev_ni, repo_node) except Exception as e: raise e return True @@ -787,7 +785,7 @@ class SConfBase(object): self.active = 0 sconf_global = None - if not self.config_h is None: + if self.config_h is not None: _ac_config_hs[self.config_h] = self.config_h_text self.env.fs = self.lastEnvFs diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index 678147c..bfa4871 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/SConfTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/SConfTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat diff --git a/src/engine/SCons/SConsign.py b/src/engine/SCons/SConsign.py index e01026d..e003933 100644 --- a/src/engine/SCons/SConsign.py +++ b/src/engine/SCons/SConsign.py @@ -29,7 +29,7 @@ Writing and reading information to the .sconsign file or files. from __future__ import print_function -__revision__ = "src/engine/SCons/SConsign.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/SConsign.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -76,7 +76,8 @@ def Get_DataBase(dir): except KeyError: path = d.entry_abspath(DB_Name) try: db = DataBase[d] = DB_Module.open(path, mode) - except (IOError, OSError): pass + except (IOError, OSError): + pass else: if mode != "r": DB_sync_list.append(db) @@ -334,10 +335,15 @@ class DirFile(Dir): Dir.__init__(self, fp, dir) except KeyboardInterrupt: raise - except: + except Exception: SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning, "Ignoring corrupt .sconsign file: %s"%self.sconsign) + try: + fp.close() + except AttributeError: + pass + global sig_files sig_files.append(self) @@ -417,7 +423,7 @@ def File(name, dbm_module=None): else: ForDirectory = DB DB_Name = name - if not dbm_module is None: + if dbm_module is not None: DB_Module = dbm_module # Local Variables: diff --git a/src/engine/SCons/SConsignTests.py b/src/engine/SCons/SConsignTests.py index e8e9231..1ae1f47 100644 --- a/src/engine/SCons/SConsignTests.py +++ b/src/engine/SCons/SConsignTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/SConsignTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/SConsignTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py index 703ccc3..206de5f 100644 --- a/src/engine/SCons/Scanner/C.py +++ b/src/engine/SCons/Scanner/C.py @@ -27,7 +27,7 @@ This module implements the dependency scanner for C/C++ code. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/C.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/C.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Node.FS import SCons.Scanner diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py index d39f206..8e1e3b3 100644 --- a/src/engine/SCons/Scanner/CTests.py +++ b/src/engine/SCons/Scanner/CTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/CTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/CTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat diff --git a/src/engine/SCons/Scanner/D.py b/src/engine/SCons/Scanner/D.py index 2fc577c..1ad9e89 100644 --- a/src/engine/SCons/Scanner/D.py +++ b/src/engine/SCons/Scanner/D.py @@ -30,7 +30,7 @@ Coded by Andy Friesen # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/D.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/D.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Scanner @@ -46,7 +46,7 @@ class D(SCons.Scanner.Classic): name = "DScanner", suffixes = '$DSUFFIXES', path_variable = 'DPATH', - regex = '(?:import\s+)([\w\s=,.]+)(?:\s*:[\s\w,=]+)?(?:;)' + regex = r'(?:import\s+)([\w\s=,.]+)(?:\s*:[\s\w,=]+)?(?:;)' ) def find_include(self, include, source_dir, path): diff --git a/src/engine/SCons/Scanner/DTests.py b/src/engine/SCons/Scanner/DTests.py index e649a88..f115eb0 100644 --- a/src/engine/SCons/Scanner/DTests.py +++ b/src/engine/SCons/Scanner/DTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/DTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/DTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import unittest diff --git a/src/engine/SCons/Scanner/Dir.py b/src/engine/SCons/Scanner/Dir.py index ea4a351..be8ae6c 100644 --- a/src/engine/SCons/Scanner/Dir.py +++ b/src/engine/SCons/Scanner/Dir.py @@ -20,7 +20,7 @@ # 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__ = "src/engine/SCons/Scanner/Dir.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/Dir.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Node.FS import SCons.Scanner diff --git a/src/engine/SCons/Scanner/DirTests.py b/src/engine/SCons/Scanner/DirTests.py index 3ce0fa1..71e527f 100644 --- a/src/engine/SCons/Scanner/DirTests.py +++ b/src/engine/SCons/Scanner/DirTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/DirTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/DirTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py index 0431660..d315bc9 100644 --- a/src/engine/SCons/Scanner/Fortran.py +++ b/src/engine/SCons/Scanner/Fortran.py @@ -26,7 +26,7 @@ This module implements the dependency scanner for Fortran code. # 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__ = "src/engine/SCons/Scanner/Fortran.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/Fortran.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import re @@ -187,7 +187,7 @@ def FortranScan(path_variable="FORTRANPATH"): # (\w+) : match the module name that is being USE'd # # - use_regex = "(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" + use_regex = r"(?i)(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)" # The INCLUDE statement regex matches the following: @@ -275,7 +275,7 @@ def FortranScan(path_variable="FORTRANPATH"): # set of semicolon-separated INCLUDE statements # (as allowed by the F2003 standard) - include_regex = """(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" + include_regex = r"""(?i)(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" # The MODULE statement regex finds module definitions by matching # the following: @@ -285,21 +285,29 @@ def FortranScan(path_variable="FORTRANPATH"): # but *not* the following: # # MODULE PROCEDURE procedure_name +# MODULE SUBROUTINE subroutine_name +# MODULE FUNCTION function_name +# MODULE PURE SUBROUTINE|FUNCTION subroutine_name|function_name +# MODULE ELEMENTAL SUBROUTINE|FUNCTION subroutine_name|function_name # # Here is a breakdown of the regex: # -# (?i) : regex is case insensitive -# ^\s* : any amount of white space -# MODULE : match the string MODULE, case insensitive -# \s+ : match one or more white space characters -# (?!PROCEDURE) : but *don't* match if the next word matches -# PROCEDURE (negative lookahead assertion), -# case insensitive -# (\w+) : match one or more alphanumeric characters -# that make up the defined module name and -# save it in a group - - def_regex = """(?i)^\s*MODULE\s+(?!PROCEDURE)(\w+)""" +# (?i) : regex is case insensitive +# ^\s* : any amount of white space +# MODULE : match the string MODULE, case +# insensitive +# \s+ : match one or more white space +# characters +# (?!PROCEDURE|SUBROUTINE|FUNCTION|PURE|ELEMENTAL) +# : but *don't* match if the next word +# matches PROCEDURE, SUBROUTINE, +# FUNCTION, PURE or ELEMENTAL (negative +# lookahead assertion), case insensitive +# (\w+) : match one or more alphanumeric +# characters that make up the defined +# module name and save it in a group + + def_regex = r"""(?i)^\s*MODULE\s+(?!PROCEDURE|SUBROUTINE|FUNCTION|PURE|ELEMENTAL)(\w+)""" scanner = F90Scanner("FortranScan", "$FORTRANSUFFIXES", diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py index 08072aa..5bf4be6 100644 --- a/src/engine/SCons/Scanner/FortranTests.py +++ b/src/engine/SCons/Scanner/FortranTests.py @@ -21,11 +21,10 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/FortranTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/FortranTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path -import sys import unittest import SCons.Scanner.Fortran @@ -33,17 +32,16 @@ import SCons.Node.FS import SCons.Warnings import TestCmd -import TestUnit original = os.getcwd() -test = TestCmd.TestCmd(workdir = '') +test = TestCmd.TestCmd(workdir='') os.chdir(test.workpath('')) # create some source files and headers: -test.write('fff1.f',""" +test.write('fff1.f', """ PROGRAM FOO INCLUDE 'f1.f' include 'f2.f' @@ -51,7 +49,7 @@ test.write('fff1.f',""" END """) -test.write('fff2.f',""" +test.write('fff2.f', """ PROGRAM FOO INCLUDE 'f2.f' include 'd1/f2.f' @@ -60,30 +58,28 @@ test.write('fff2.f',""" END """) -test.write('fff3.f',""" +test.write('fff3.f', """ PROGRAM FOO INCLUDE 'f3.f' ; INCLUDE\t'd1/f3.f' STOP END """) - # for Emacs -> " test.subdir('d1', ['d1', 'd2']) -headers = ['fi.f', 'never.f', - 'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f', - 'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f', - 'd1/d2/f4.f', 'd1/d2/fi.f'] +test_headers = ['fi.f', 'never.f', + 'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f', + 'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f', + 'd1/d2/f4.f', 'd1/d2/fi.f'] -for h in headers: +for h in test_headers: test.write(h, "\n") - test.subdir('include', 'subdir', ['subdir', 'include']) -test.write('fff4.f',""" +test.write('fff4.f', """ PROGRAM FOO INCLUDE 'f4.f' STOP @@ -93,7 +89,7 @@ test.write('fff4.f',""" test.write('include/f4.f', "\n") test.write('subdir/include/f4.f', "\n") -test.write('fff5.f',""" +test.write('fff5.f', """ PROGRAM FOO INCLUDE 'f5.f' INCLUDE 'not_there.f' @@ -104,7 +100,7 @@ test.write('fff5.f',""" test.write('f5.f', "\n") test.subdir('repository', ['repository', 'include'], - [ 'repository', 'src' ]) + ['repository', 'src']) test.subdir('work', ['work', 'src']) test.write(['repository', 'include', 'iii.f'], "\n") @@ -117,26 +113,25 @@ test.write(['work', 'src', 'fff.f'], """ END """) -test.write([ 'work', 'src', 'aaa.f'], """ +test.write(['work', 'src', 'aaa.f'], """ PROGRAM FOO INCLUDE 'bbb.f' STOP END """) -test.write([ 'work', 'src', 'bbb.f'], "\n") +test.write(['work', 'src', 'bbb.f'], "\n") -test.write([ 'repository', 'src', 'ccc.f'], """ +test.write(['repository', 'src', 'ccc.f'], """ PROGRAM FOO INCLUDE 'ddd.f' STOP END """) -test.write([ 'repository', 'src', 'ddd.f'], "\n") +test.write(['repository', 'src', 'ddd.f'], "\n") - -test.write('fff90a.f90',""" +test.write('fff90a.f90', """ PROGRAM FOO ! Test comments - these includes should NOT be picked up @@ -194,18 +189,19 @@ USE mod25 ! Test USE statement at the beginning of line END """) -modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod', - 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod', - 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod', - 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod', - 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod'] +test_modules = ['mod01.mod', 'mod02.mod', 'mod03.mod', 'mod04.mod', 'mod05.mod', + 'mod06.mod', 'mod07.mod', 'mod08.mod', 'mod09.mod', 'mod10.mod', + 'mod11.mod', 'mod12.mod', 'mod13.mod', 'mod14.mod', 'mod15.mod', + 'mod16.mod', 'mod17.mod', 'mod18.mod', 'mod19.mod', 'mod20.mod', + 'mod21.mod', 'mod22.mod', 'mod23.mod', 'mod24.mod', 'mod25.mod'] -for m in modules: +for m in test_modules: test.write(m, "\n") test.subdir('modules') test.write(['modules', 'use.mod'], "\n") + # define some helpers: class DummyEnvironment(object): @@ -215,7 +211,7 @@ class DummyEnvironment(object): def Dictionary(self, *args): if not args: - return { 'FORTRANPATH': self.path, 'FORTRANMODSUFFIX' : ".mod" } + return {'FORTRANPATH': self.path, 'FORTRANMODSUFFIX': ".mod"} elif len(args) == 1 and args[0] == 'FORTRANPATH': return self.path else: @@ -224,13 +220,13 @@ class DummyEnvironment(object): def has_key(self, key): return key in self.Dictionary() - def __getitem__(self,key): + def __getitem__(self, key): return self.Dictionary()[key] - def __setitem__(self,key,value): + def __setitem__(self, key, value): self.Dictionary()[key] = value - def __delitem__(self,key): + def __delitem__(self, key): del self.Dictionary()[key] def subst(self, arg, target=None, source=None, conv=None): @@ -255,11 +251,13 @@ class DummyEnvironment(object): def File(self, filename): return self.fs.File(filename) + def deps_match(self, deps, headers): scanned = list(map(os.path.normpath, list(map(str, deps)))) expect = list(map(os.path.normpath, headers)) self.assertTrue(scanned == expect, "expect %s != scanned %s" % (expect, scanned)) + # define some tests: class FortranScannerTestCase1(unittest.TestCase): @@ -275,6 +273,7 @@ class FortranScannerTestCase1(unittest.TestCase): test.unlink('f1.f') test.unlink('f2.f') + class FortranScannerTestCase2(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") @@ -288,6 +287,7 @@ class FortranScannerTestCase2(unittest.TestCase): test.unlink('f1.f') test.unlink('f2.f') + class FortranScannerTestCase3(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) @@ -297,6 +297,7 @@ class FortranScannerTestCase3(unittest.TestCase): headers = ['d1/f1.f', 'd1/f2.f'] deps_match(self, deps, headers) + class FortranScannerTestCase4(unittest.TestCase): def runTest(self): test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n") @@ -308,6 +309,7 @@ class FortranScannerTestCase4(unittest.TestCase): deps_match(self, deps, headers) test.write(['d1', 'f2.f'], "\n") + class FortranScannerTestCase5(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1")]) @@ -317,6 +319,7 @@ class FortranScannerTestCase5(unittest.TestCase): headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/f2.f'] deps_match(self, deps, headers) + class FortranScannerTestCase6(unittest.TestCase): def runTest(self): test.write('f2.f', "\n") @@ -324,19 +327,21 @@ class FortranScannerTestCase6(unittest.TestCase): s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) - headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] + headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f2.f') + class FortranScannerTestCase7(unittest.TestCase): def runTest(self): env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")]) s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) - headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f'] + headers = ['d1/f2.f', 'd1/d2/f2.f', 'd1/d2/f2.f'] deps_match(self, deps, headers) + class FortranScannerTestCase8(unittest.TestCase): def runTest(self): test.write('f2.f', "\n") @@ -344,10 +349,11 @@ class FortranScannerTestCase8(unittest.TestCase): s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps = s(env.File('fff2.f'), env, path) - headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] + headers = ['d1/f2.f', 'd1/d2/f2.f', 'f2.f'] deps_match(self, deps, headers) test.unlink('f2.f') + class FortranScannerTestCase9(unittest.TestCase): def runTest(self): test.write('f3.f', "\n") @@ -356,9 +362,11 @@ class FortranScannerTestCase9(unittest.TestCase): path = s.path(env) n = env.File('fff3.f') + def my_rexists(s): s.Tag('rexists_called', 1) return SCons.Node._rexists_map[s.GetTag('old_rexists')](s) + n.Tag('old_rexists', n._func_rexists) SCons.Node._rexists_map[3] = my_rexists n._func_rexists = 3 @@ -369,10 +377,11 @@ class FortranScannerTestCase9(unittest.TestCase): # scanned, essential for cooperation with VariantDir functionality. assert n.GetTag('rexists_called') - headers = ['d1/f3.f', 'f3.f'] + headers = ['d1/f3.f', 'f3.f'] deps_match(self, deps, headers) test.unlink('f3.f') + class FortranScannerTestCase10(unittest.TestCase): def runTest(self): env = DummyEnvironment(["include"]) @@ -380,18 +389,20 @@ class FortranScannerTestCase10(unittest.TestCase): path = s.path(env) deps1 = s(env.File('fff4.f'), env, path) env.fs.chdir(env.Dir('subdir')) - dir = env.fs.getcwd() + test_dir = env.fs.getcwd() env.fs.chdir(env.Dir('')) - path = s.path(env, dir) + path = s.path(env, test_dir) deps2 = s(env.File('#fff4.f'), env, path) - headers1 = list(map(test.workpath, ['include/f4.f'])) - headers2 = ['include/f4.f'] + headers1 = [test.workpath(f) for f in ['include/f4.f']] + headers2 = ['include/f4.f'] deps_match(self, deps1, headers1) deps_match(self, deps2, headers2) + class FortranScannerTestCase11(unittest.TestCase): def runTest(self): SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) + class TestOut(object): def __call__(self, x): self.out = x @@ -407,7 +418,8 @@ class FortranScannerTestCase11(unittest.TestCase): # Did we catch the warning from not finding not_there.f? assert to.out - deps_match(self, deps, [ 'f5.f' ]) + deps_match(self, deps, ['f5.f']) + class FortranScannerTestCase12(unittest.TestCase): def runTest(self): @@ -421,6 +433,7 @@ class FortranScannerTestCase12(unittest.TestCase): deps_match(self, deps, ['f4.f']) test.unlink('include/fff4.f') + class FortranScannerTestCase13(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) @@ -429,9 +442,9 @@ class FortranScannerTestCase13(unittest.TestCase): # Create a derived file in a directory that does not exist yet. # This was a bug at one time. - f1=fs.File('include2/jjj.f') - f1.builder=1 - env = DummyEnvironment(['include','include2']) + f1 = fs.File('include2/jjj.f') + f1.builder = 1 + env = DummyEnvironment(['include', 'include2']) env.fs = fs s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) @@ -439,6 +452,7 @@ class FortranScannerTestCase13(unittest.TestCase): deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f']) os.chdir(test.workpath('')) + class FortranScannerTestCase14(unittest.TestCase): def runTest(self): os.chdir(test.workpath('work')) @@ -451,15 +465,16 @@ class FortranScannerTestCase14(unittest.TestCase): s = SCons.Scanner.Fortran.FortranScan() path = s.path(env) deps1 = s(fs.File('build1/aaa.f'), env, path) - deps_match(self, deps1, [ 'build1/bbb.f' ]) + deps_match(self, deps1, ['build1/bbb.f']) deps2 = s(fs.File('build2/aaa.f'), env, path) - deps_match(self, deps2, [ 'src/bbb.f' ]) + deps_match(self, deps2, ['src/bbb.f']) deps3 = s(fs.File('build1/ccc.f'), env, path) - deps_match(self, deps3, [ 'build1/ddd.f' ]) + deps_match(self, deps3, ['build1/ddd.f']) deps4 = s(fs.File('build2/ccc.f'), env, path) - deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ]) + deps_match(self, deps4, [test.workpath('repository/src/ddd.f')]) os.chdir(test.workpath('')) + class FortranScannerTestCase15(unittest.TestCase): def runTest(self): class SubstEnvironment(DummyEnvironment): @@ -468,6 +483,7 @@ class FortranScannerTestCase15(unittest.TestCase): return test.workpath("d1") else: return arg + test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n") env = SubstEnvironment(["$junk"]) s = SCons.Scanner.Fortran.FortranScan() @@ -477,6 +493,7 @@ class FortranScannerTestCase15(unittest.TestCase): deps_match(self, deps, headers) test.write(['d1', 'f2.f'], "\n") + class FortranScannerTestCase16(unittest.TestCase): def runTest(self): test.write('f1.f', "\n") @@ -512,28 +529,10 @@ class FortranScannerTestCase16(unittest.TestCase): test.unlink('f9.f') test.unlink('f10.f') -def suite(): - suite = unittest.TestSuite() - suite.addTest(FortranScannerTestCase1()) - suite.addTest(FortranScannerTestCase2()) - suite.addTest(FortranScannerTestCase3()) - suite.addTest(FortranScannerTestCase4()) - suite.addTest(FortranScannerTestCase5()) - suite.addTest(FortranScannerTestCase6()) - suite.addTest(FortranScannerTestCase7()) - suite.addTest(FortranScannerTestCase8()) - suite.addTest(FortranScannerTestCase9()) - suite.addTest(FortranScannerTestCase10()) - suite.addTest(FortranScannerTestCase11()) - suite.addTest(FortranScannerTestCase12()) - suite.addTest(FortranScannerTestCase13()) - suite.addTest(FortranScannerTestCase14()) - suite.addTest(FortranScannerTestCase15()) - suite.addTest(FortranScannerTestCase16()) - return suite + if __name__ == "__main__": - TestUnit.run(suite()) + unittest.main() # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Scanner/IDL.py b/src/engine/SCons/Scanner/IDL.py index 52bd1c5..88226e6 100644 --- a/src/engine/SCons/Scanner/IDL.py +++ b/src/engine/SCons/Scanner/IDL.py @@ -28,7 +28,7 @@ Definition Language) files. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/IDL.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/IDL.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Node.FS import SCons.Scanner diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py index 6ad35c5..9bc4e5a 100644 --- a/src/engine/SCons/Scanner/IDLTests.py +++ b/src/engine/SCons/Scanner/IDLTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/IDLTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/IDLTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import unittest import sys diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py index 0385ca0..8afe5b0 100644 --- a/src/engine/SCons/Scanner/LaTeX.py +++ b/src/engine/SCons/Scanner/LaTeX.py @@ -27,7 +27,7 @@ This module implements the dependency scanner for LaTeX code. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/LaTeX.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/LaTeX.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import re @@ -179,15 +179,7 @@ class LaTeX(SCons.Scanner.Base): 'inputfrom', 'subinputfrom'] def __init__(self, name, suffixes, graphics_extensions, *args, **kw): - - # We have to include \n with the % we exclude from the first part - # part of the regex because the expression is compiled with re.M. - # Without the \n, the ^ could match the beginning of a *previous* - # line followed by one or more newline characters (i.e. blank - # lines), interfering with a match on the next line. - # add option for whitespace before the '[options]' or the '{filename}' regex = r''' - ^[^%\n]* \\( include | includegraphics(?:\s*\[[^\]]+\])? @@ -348,7 +340,7 @@ class LaTeX(SCons.Scanner.Base): # Cache the includes list in node so we only scan it once: # path_dict = dict(list(path)) # add option for whitespace (\s) before the '[' - noopt_cre = re.compile('\s*\[.*$') + noopt_cre = re.compile(r'\s*\[.*$') if node.includes is not None: includes = node.includes else: @@ -372,9 +364,9 @@ class LaTeX(SCons.Scanner.Base): inc_list = include[2].split(',') else: inc_list = include[1].split(',') - for j in range(len(inc_list)): - split_includes.append( (inc_type, inc_subdir, inc_list[j]) ) - # + for inc in inc_list: + split_includes.append((inc_type, inc_subdir, inc)) + includes = split_includes node.includes = includes diff --git a/src/engine/SCons/Scanner/LaTeXTests.py b/src/engine/SCons/Scanner/LaTeXTests.py index 4f8ec16..8750000 100644 --- a/src/engine/SCons/Scanner/LaTeXTests.py +++ b/src/engine/SCons/Scanner/LaTeXTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/LaTeXTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/LaTeXTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -61,6 +61,12 @@ test.write('test3.latex',r""" \includegraphics[width=60mm]{inc5.xyz} """) +test.write('test4.latex',r""" +\include{inc1}\include{inc2} +\only<1>{\includegraphics{inc5.xyz}}% +\only<2>{\includegraphics{inc7.png}} +""") + test.subdir('subdir') test.write('inc1.tex',"\n") @@ -73,6 +79,7 @@ test.write(['subdir', 'inc3c.tex'], "\n") test.write(['subdir', 'inc4.eps'], "\n") test.write('inc5.xyz', "\n") test.write('inc6.tex', "\n") +test.write('inc7.png', "\n") test.write('incNO.tex', "\n") # define some helpers: @@ -155,6 +162,15 @@ class LaTeXScannerTestCase3(unittest.TestCase): files = ['inc5.xyz', 'subdir/inc4.eps'] deps_match(self, deps, files) +class LaTeXScannerTestCase4(unittest.TestCase): + def runTest(self): + env = DummyEnvironment(TEXINPUTS=[test.workpath("subdir")],LATEXSUFFIXES = [".tex", ".ltx", ".latex"]) + s = SCons.Scanner.LaTeX.LaTeXScanner() + path = s.path(env) + deps = s(env.File('test4.latex'), env, path) + files = ['inc1.tex', 'inc2.tex', 'inc5.xyz', 'inc7.png'] + deps_match(self, deps, files) + if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py index cabe6af..2ae3c68 100644 --- a/src/engine/SCons/Scanner/Prog.py +++ b/src/engine/SCons/Scanner/Prog.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/Prog.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/Prog.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Node import SCons.Node.FS diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py index 619f844..7dae37c 100644 --- a/src/engine/SCons/Scanner/ProgTests.py +++ b/src/engine/SCons/Scanner/ProgTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/ProgTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/ProgTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys diff --git a/src/engine/SCons/Scanner/RC.py b/src/engine/SCons/Scanner/RC.py index ccd7ebd..4f0cee2 100644 --- a/src/engine/SCons/Scanner/RC.py +++ b/src/engine/SCons/Scanner/RC.py @@ -28,7 +28,7 @@ Definition Language) files. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/RC.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/RC.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import re @@ -48,9 +48,9 @@ def RCScan(): """Return a prototype Scanner instance for scanning RC source files""" res_re= r'^(?:\s*#\s*(?:include)|' \ - '.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ - '\s*.*?)' \ - '\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' + r'.*?\s+(?:ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)' \ + r'\s*.*?)' \ + r'\s*(<|"| )([^>"\s]+)(?:[>"\s])*$' resScanner = SCons.Scanner.ClassicCPP("ResourceScanner", "$RCSUFFIXES", "CPPPATH", diff --git a/src/engine/SCons/Scanner/RCTests.py b/src/engine/SCons/Scanner/RCTests.py index 3012350..2915985 100644 --- a/src/engine/SCons/Scanner/RCTests.py +++ b/src/engine/SCons/Scanner/RCTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/RCTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/RCTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import unittest import sys diff --git a/src/engine/SCons/Scanner/SWIG.py b/src/engine/SCons/Scanner/SWIG.py index 777474c..5f2a3e3 100644 --- a/src/engine/SCons/Scanner/SWIG.py +++ b/src/engine/SCons/Scanner/SWIG.py @@ -27,14 +27,14 @@ This module implements the dependency scanner for SWIG code. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/SWIG.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/SWIG.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Scanner SWIGSuffixes = [ '.i' ] def SWIGScanner(): - expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' + expr = r'^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' scanner = SCons.Scanner.ClassicCPP("SWIGScanner", ".i", "SWIGPATH", expr) return scanner diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py index 7e2720c..64842d4 100644 --- a/src/engine/SCons/Scanner/ScannerTests.py +++ b/src/engine/SCons/Scanner/ScannerTests.py @@ -20,7 +20,7 @@ # 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__ = "src/engine/SCons/Scanner/ScannerTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/ScannerTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py index 693fae7..feff12a 100644 --- a/src/engine/SCons/Scanner/__init__.py +++ b/src/engine/SCons/Scanner/__init__.py @@ -27,7 +27,7 @@ The Scanner package for the SCons software construction utility. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Scanner/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Scanner/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import re @@ -207,7 +207,7 @@ class Base(object): self = self.select(node) - if not self.argument is _null: + if self.argument is not _null: node_list = self.function(node, env, path, self.argument) else: node_list = self.function(node, env, path) diff --git a/src/engine/SCons/Script/Interactive.py b/src/engine/SCons/Script/Interactive.py index 35e32c2..3d70728 100644 --- a/src/engine/SCons/Script/Interactive.py +++ b/src/engine/SCons/Script/Interactive.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -__revision__ = "src/engine/SCons/Script/Interactive.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/Interactive.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ SCons interactive mode diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 0d626cb..878f824 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -38,7 +38,7 @@ deprecated_python_version = (2, 7, 0) # 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__ = "src/engine/SCons/Script/Main.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/Main.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -68,6 +68,20 @@ import SCons.Warnings import SCons.Script.Interactive +# Global variables +first_command_start = None +last_command_end = None +print_objects = 0 +print_memoizer = 0 +print_stacktrace = 0 +print_time = 0 +sconscript_time = 0 +cumulative_command_time = 0 +exit_status = 0 # final exit status, assume success by default +this_build_status = 0 # "exit status" of an individual build +num_jobs = None +delayed_warnings = [] + def fetch_win32_parallel_msg(): # A subsidiary function that exists solely to isolate this import @@ -87,15 +101,14 @@ def revert_io(): sys.stderr = sys.__stderr__ sys.stdout = sys.__stdout__ + class SConsPrintHelpException(Exception): pass + display = SCons.Util.display progress_display = SCons.Util.DisplayEngine() -first_command_start = None -last_command_end = None - class Progressor(object): prev = '' @@ -443,19 +456,6 @@ def python_version_deprecated(version=sys.version_info): return version < deprecated_python_version -# Global variables - -print_objects = 0 -print_memoizer = 0 -print_stacktrace = 0 -print_time = 0 -sconscript_time = 0 -cumulative_command_time = 0 -exit_status = 0 # final exit status, assume success by default -this_build_status = 0 # "exit status" of an individual build -num_jobs = None -delayed_warnings = [] - class FakeOptionParser(object): """ A do-nothing option parser, used for the initial OptionsParser variable. @@ -1170,7 +1170,7 @@ def _build_targets(fs, options, targets, target_top): # -U, local SConscript Default() targets target_top = fs.Dir(target_top) def check_dir(x, target_top=target_top): - if hasattr(x, 'cwd') and not x.cwd is None: + if hasattr(x, 'cwd') and x.cwd is not None: cwd = x.cwd.srcnode() return cwd == target_top else: diff --git a/src/engine/SCons/Script/MainTests.py b/src/engine/SCons/Script/MainTests.py index 64d3e0f..752ea0a 100644 --- a/src/engine/SCons/Script/MainTests.py +++ b/src/engine/SCons/Script/MainTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Script/MainTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/MainTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import unittest diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py index 8f31fd8..5651c96 100644 --- a/src/engine/SCons/Script/SConsOptions.py +++ b/src/engine/SCons/Script/SConsOptions.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Script/SConsOptions.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/SConsOptions.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import optparse import re @@ -147,7 +147,7 @@ class SConsValues(optparse.Values): """ Sets an option from an SConscript file. """ - if not name in self.settable: + if name not in self.settable: raise SCons.Errors.UserError("This option is not settable from a SConscript file: %s"%name) if name == 'num_jobs': @@ -167,7 +167,7 @@ class SConsValues(optparse.Values): value = str(value) except ValueError: raise SCons.Errors.UserError("A string is required: %s"%repr(value)) - if not value in SCons.Node.FS.Valid_Duplicates: + if value not in SCons.Node.FS.Valid_Duplicates: raise SCons.Errors.UserError("Not a valid duplication style: %s" % value) # Set the duplicate style right away so it can affect linking # of SConscript files. @@ -226,39 +226,8 @@ class SConsOption(optparse.Option): fmt = "option %s: nargs='?' is incompatible with short options" raise SCons.Errors.UserError(fmt % self._short_opts[0]) - try: - _orig_CONST_ACTIONS = optparse.Option.CONST_ACTIONS - - _orig_CHECK_METHODS = optparse.Option.CHECK_METHODS - - except AttributeError: - # optparse.Option had no CONST_ACTIONS before Python 2.5. - - _orig_CONST_ACTIONS = ("store_const",) - - def _check_const(self): - if self.action not in self.CONST_ACTIONS and self.const is not None: - raise OptionError( - "'const' must not be supplied for action %r" % self.action, - self) - - # optparse.Option collects its list of unbound check functions - # up front. This sucks because it means we can't just override - # the _check_const() function like a normal method, we have to - # actually replace it in the list. This seems to be the most - # straightforward way to do that. - - _orig_CHECK_METHODS = [optparse.Option._check_action, - optparse.Option._check_type, - optparse.Option._check_choice, - optparse.Option._check_dest, - _check_const, - optparse.Option._check_nargs, - optparse.Option._check_callback] - - CHECK_METHODS = _orig_CHECK_METHODS + [_check_nargs_optional] - - CONST_ACTIONS = _orig_CONST_ACTIONS + optparse.Option.TYPED_ACTIONS + CHECK_METHODS = optparse.Option.CHECK_METHODS + [_check_nargs_optional] + CONST_ACTIONS = optparse.Option.CONST_ACTIONS + optparse.Option.TYPED_ACTIONS class SConsOptionGroup(optparse.OptionGroup): """ @@ -364,7 +333,7 @@ class SConsOptionParser(optparse.OptionParser): in self.largs, so that any value overridden on the command line is immediately available if the user turns around and does a GetOption() right away. - + We mimic the processing of the single args in the original OptionParser._process_args(), but here we allow exact matches for long-opts only (no partial @@ -375,7 +344,7 @@ class SConsOptionParser(optparse.OptionParser): command-line arguments that 1. haven't been processed so far (self.largs), but 2. are possibly not added to the list of options yet. - + So, when we only have a value for "--myargument" yet, a command-line argument of "--myarg=test" would set it. Responsible for this behaviour is the method @@ -384,7 +353,7 @@ class SConsOptionParser(optparse.OptionParser): be unique. This would lead to further confusion, because we might want to add another option "--myarg" later on (see issue #2929). - + """ rargs = [] largs_restore = [] @@ -401,7 +370,7 @@ class SConsOptionParser(optparse.OptionParser): if "=" in l: # Split into option and value lopt = l.split("=", 1) - + if lopt[0] in self._long_opt: # Argument is already known rargs.append('='.join(lopt)) @@ -416,7 +385,7 @@ class SConsOptionParser(optparse.OptionParser): skip = True else: rargs.append(l) - + # Parse the filtered list self.parse_args(rargs, self.values) # Restore the list of remaining arguments for the @@ -690,7 +659,7 @@ def Parser(version): metavar="TYPE") def opt_duplicate(option, opt, value, parser): - if not value in SCons.Node.FS.Valid_Duplicates: + if value not in SCons.Node.FS.Valid_Duplicates: raise OptionValueError(opt_invalid('duplication', value, SCons.Node.FS.Valid_Duplicates)) setattr(parser.values, option.dest, value) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index ea239e0..9687c29 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -27,7 +27,7 @@ files. # 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__ = "src/engine/SCons/Script/SConscript.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/SConscript.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons import SCons.Action diff --git a/src/engine/SCons/Script/SConscriptTests.py b/src/engine/SCons/Script/SConscriptTests.py index fb06ac4..c1232e8 100644 --- a/src/engine/SCons/Script/SConscriptTests.py +++ b/src/engine/SCons/Script/SConscriptTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Script/SConscriptTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/SConscriptTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Script.SConscript diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index 834c70d..cf2e472 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -34,7 +34,7 @@ it goes here. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Script/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Script/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import time start_time = time.time() diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 4fc8461..1e0d423 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -26,7 +26,7 @@ SCons string substitution. # 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__ = "src/engine/SCons/Subst.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Subst.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import collections import re @@ -350,7 +350,7 @@ _rm_split = re.compile(r'(?|' + r'/\*|\*/|\[\])') + class OuterState(object): """The initial state for parsing a Java file for classes, interfaces, and anonymous inner classes.""" - def __init__(self, version=default_java_version): - if not version in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', - '1.8', '5', '6', '9.0', '10.0', '11.0'): + def __init__(self, version=default_java_version): + if version not in ('1.1', '1.2', '1.3', '1.4', '1.5', '1.6', '1.7', + '1.8', '5', '6', '9.0', '10.0', '11.0', '12.0'): msg = "Java version %s not supported" % version raise NotImplementedError(msg) @@ -131,15 +159,15 @@ if java_parsing: def closeBracket(self): self.brackets = self.brackets - 1 if len(self.stackBrackets) and \ - self.brackets == self.stackBrackets[-1]: + self.brackets == self.stackBrackets[-1]: self.listOutputs.append('$'.join(self.listClasses)) self.localClasses.pop() self.listClasses.pop() self.anonStacksStack.pop() self.stackBrackets.pop() if len(self.stackAnonClassBrackets) and \ - self.brackets == self.stackAnonClassBrackets[-1] and \ - self.version not in scopeStateVersions: + self.brackets == self.stackAnonClassBrackets[-1] and \ + self.version not in scopeStateVersions: self._getAnonStack().pop() self.stackAnonClassBrackets.pop() @@ -152,13 +180,13 @@ if java_parsing: self.openBracket() elif token == '}': self.closeBracket() - elif token in [ '"', "'" ]: + elif token in ['"', "'"]: return IgnoreState(token, self) elif token == "new": # anonymous inner class if len(self.listClasses) > 0: return self.__getAnonClassState() - return self.__getSkipState() # Skip the class name + return self.__getSkipState() # Skip the class name elif token in ['class', 'interface', 'enum']: if len(self.listClasses) == 0: self.nextAnon = 1 @@ -178,7 +206,7 @@ if java_parsing: if self.version in ('1.1', '1.2', '1.3', '1.4'): clazz = self.listClasses[0] self.listOutputs.append('%s$%d' % (clazz, self.nextAnon)) - elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0', '11.0'): + elif self.version in ('1.5', '1.6', '1.7', '1.8', '5', '6', '9.0', '10.0', '11.0', '12.0'): self.stackAnonClassBrackets.append(self.brackets) className = [] className.extend(self.listClasses) @@ -193,11 +221,13 @@ if java_parsing: def setPackage(self, package): self.package = package + class ScopeState(object): """ A state that parses code within a scope normally, within the confines of a scope. """ + def __init__(self, old_state): self.outer_state = old_state.outer_state self.old_state = old_state @@ -259,13 +289,16 @@ if java_parsing: return self.__getSkipState() return self + class AnonClassState(object): """A state that looks for anonymous inner classes.""" + def __init__(self, old_state): # outer_state is always an instance of OuterState self.outer_state = old_state.outer_state self.old_state = old_state self.brace_level = 0 + def parseToken(self, token): # This is an anonymous class if and only if the next # non-whitespace token is a bracket. Everything between @@ -293,26 +326,32 @@ if java_parsing: if token == '{': self.outer_state.addAnonClass() if self.outer_state.version in scopeStateVersions: - return ScopeState(old_state = self.old_state).parseToken(token) + return ScopeState(old_state=self.old_state).parseToken(token) return self.old_state.parseToken(token) + class SkipState(object): """A state that will skip a specified number of tokens before reverting to the previous state.""" + def __init__(self, tokens_to_skip, old_state): self.tokens_to_skip = tokens_to_skip self.old_state = old_state + def parseToken(self, token): self.tokens_to_skip = self.tokens_to_skip - 1 if self.tokens_to_skip < 1: return self.old_state return self + class ClassState(object): """A state we go into when we hit a class or interface keyword.""" + def __init__(self, outer_state): # outer_state is always an instance of OuterState self.outer_state = outer_state + def parseToken(self, token): # the next non-whitespace token should be the name of the class if token == '\n': @@ -322,12 +361,12 @@ if java_parsing: # 'Foo$1Inner' # https://github.com/SCons/scons/issues/2087 if self.outer_state.localClasses and \ - self.outer_state.stackBrackets[-1] > \ - self.outer_state.stackBrackets[-2]+1: + self.outer_state.stackBrackets[-1] > \ + self.outer_state.stackBrackets[-2] + 1: locals = self.outer_state.localClasses[-1] try: idx = locals[token] - locals[token] = locals[token]+1 + locals[token] = locals[token] + 1 except KeyError: locals[token] = 1 token = str(locals[token]) + token @@ -336,32 +375,40 @@ if java_parsing: self.outer_state.anonStacksStack.append([0]) return self.outer_state + class IgnoreState(object): """A state that will ignore all tokens until it gets to a specified token.""" + def __init__(self, ignore_until, old_state): self.ignore_until = ignore_until self.old_state = old_state + def parseToken(self, token): if self.ignore_until == token: return self.old_state return self + class PackageState(object): """The state we enter when we encounter the package keyword. We assume the next token will be the package name.""" + def __init__(self, outer_state): # outer_state is always an instance of OuterState self.outer_state = outer_state + def parseToken(self, token): self.outer_state.setPackage(token) return self.outer_state + def parse_java_file(fn, version=default_java_version): with open(fn, 'r') as f: data = f.read() return parse_java(data, version) + def parse_java(contents, version=default_java_version, trace=None): """Parse a .java file and return a double of package directory, plus a list of .class files that compiling that .java file will @@ -395,81 +442,68 @@ else: return os.path.split(fn) - -java_win32_version_dir_glob = 'C:/Program Files*/Java/jdk%s*/bin' -java_win32_dir_glob = 'C:/Program Files*/Java/jdk*/bin' - -java_macos_include_dir = '/System/Library/Frameworks/JavaVM.framework/Headers/' -java_macos_version_include_dir = '/System/Library/Frameworks/JavaVM.framework/Versions/%s*/Headers/' - -java_linux_include_dirs = ['/usr/lib/jvm/default-java/include', - '/usr/lib/jvm/java-*/include'] -# Need to match path like below (from Centos 7) -# /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64/include/ -java_linux_version_include_dirs = ['/usr/lib/jvm/java-*-sun-%s*/include', - '/usr/lib/jvm/java-%s*-openjdk*/include', - '/usr/java/jdk%s*/include'] - - - def get_java_install_dirs(platform, version=None): """ - Using patterns above find the java jdk install dir - :param platform: + Find the java jdk installation directories. + + This list is intended to supply as "default paths" for use when looking + up actual java binaries. + + :param platform: selector for search algorithm. :param version: If specified, only look for java sdk's of this version :return: list of default paths for java. """ + paths = [] if platform == 'win32': if version: - paths = glob.glob(java_win32_version_dir_glob%version) + paths = glob.glob(java_win32_version_dir_glob % version) else: paths = glob.glob(java_win32_dir_glob) else: - # do nothing for now + # other platforms, do nothing for now pass - paths=sorted(paths) + return sorted(paths) - return paths def get_java_include_paths(env, javac, version): """ - Return java include paths - :param platform: - :param javac: - :return: + Find java include paths for JNI building. + + :param env: construction environment, used to extract platform. + :param javac: path to detected javac. + :return: list of paths. """ + paths = [] if not javac: # there are no paths if we've not detected javac. pass elif env['PLATFORM'] == 'win32': + # on Windows, we have the right path to javac, so look locally javac_bin_dir = os.path.dirname(javac) java_inc_dir = os.path.normpath(os.path.join(javac_bin_dir, '..', 'include')) paths = [java_inc_dir, os.path.join(java_inc_dir, 'win32')] elif env['PLATFORM'] == 'darwin': if not version: - paths = [java_macos_include_dir] + paths = [java_macos_include_dir_glob] else: - paths = sorted(glob.glob(java_macos_version_include_dir%version)) + paths = sorted(glob.glob(java_macos_version_include_dir_glob % version)) else: - base_paths=[] + base_paths = [] if not version: - for p in java_linux_include_dirs: + for p in java_linux_include_dirs_glob: base_paths.extend(glob.glob(p)) else: - for p in java_linux_version_include_dirs: - base_paths.extend(glob.glob(p%version)) + for p in java_linux_version_include_dirs_glob: + base_paths.extend(glob.glob(p % version)) for p in base_paths: - paths.extend([p, os.path.join(p,'linux')]) - - #print("PATHS:%s"%paths) - return paths - - + paths.extend([p, os.path.join(p, 'linux')]) + # print("PATHS:%s"%paths) + return paths # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/JavaCommonTests.py b/src/engine/SCons/Tool/JavaCommonTests.py index c70e445..45a8a0b 100644 --- a/src/engine/SCons/Tool/JavaCommonTests.py +++ b/src/engine/SCons/Tool/JavaCommonTests.py @@ -21,15 +21,17 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/JavaCommonTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/JavaCommonTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys import unittest +import fnmatch import SCons.Scanner.IDL import SCons.Tool.JavaCommon +import TestSCons # Adding trace=trace to any of the parse_jave() calls below will cause # the parser to spit out trace messages of the tokens it sees and the @@ -607,6 +609,76 @@ public class AnonDemo { pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.8') assert expect == classes, (expect, classes) + def test_jdk_globs(self): + """ + Verify that the java path globs work with specific examples. + :return: + """ + from SCons.Tool.JavaCommon import java_linux_include_dirs_glob, java_linux_version_include_dirs_glob, java_win32_dir_glob, java_win32_version_dir_glob, java_macos_include_dir_glob, java_macos_version_include_dir_glob + + # Test windows globs + win_java_dirs = [ + ('C:/Program Files/Java/jdk1.8.0_201/bin', '1.8.0'), + ('C:/Program Files/Java/jdk-11.0.2/bin', '11.0.2'), + ('C:/Program Files/Java/jdk1.7.0_80/bin', '1.7.0') + ] + + for (wjd, version) in win_java_dirs: + if not fnmatch.fnmatch(wjd, java_win32_dir_glob): + self.fail("Didn't properly match %s with pattern %s" % (wjd, java_win32_dir_glob)) + if not fnmatch.fnmatch(wjd, java_win32_version_dir_glob % version): + self.fail("Didn't properly match %s with version (%s) specific pattern %s" % ( + wjd, version, java_win32_version_dir_glob % version)) + + non_win_java_include_dirs = [ + ('/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-0.el7_5.x86_64/include', '1.8.0'), + ('/usr/lib/jvm/java-1.8.0-openjdk-amd64/include', '1.8.0'), + ('/usr/lib/jvm/java-8-openjdk-amd64/include', '8'), + ] + + # Test non-windows/non-macos globs + for (wjd, version) in non_win_java_include_dirs: + match = False + globs_tried =[] + for jlig in java_linux_include_dirs_glob: + globs_tried.append(jlig) + + if fnmatch.fnmatch(wjd, jlig): + match = True + break + + if not match: + self.fail("Didn't properly match %s with pattern %s" % (wjd, globs_tried)) + + match = False + globs_tried = [] + for jlvig in java_linux_version_include_dirs_glob: + globs_tried.append(jlvig%version) + if fnmatch.fnmatch(wjd, jlvig % version): + match = True + break + + if not match: + self.fail("Didn't properly match %s with version (%s) specific pattern %s" % ( + wjd, version, globs_tried)) + + # Test macos globs + # Test windows globs + macos_java_dirs = [ + # ('/System/Library/Frameworks/JavaVM.framework/Headers/', None), + ('/System/Library/Frameworks/JavaVM.framework/Versions/11.0.2/Headers/', '11.0.2'), + ] + + if not fnmatch.fnmatch('/System/Library/Frameworks/JavaVM.framework/Headers/', java_macos_include_dir_glob): + self.fail("Didn't properly match %s with pattern %s" % ('/System/Library/Frameworks/JavaVM.framework/Headers/', java_macos_include_dir_glob)) + + for (wjd, version) in macos_java_dirs: + if not fnmatch.fnmatch(wjd, java_macos_version_include_dir_glob % version): + self.fail("Didn't properly match %s with version (%s) specific pattern %s" % ( + wjd, version, java_macos_version_include_dir_glob % version)) + + + if __name__ == "__main__": unittest.main() diff --git a/src/engine/SCons/Tool/MSCommon/__init__.py b/src/engine/SCons/Tool/MSCommon/__init__.py index 7faff33..e3c4471 100644 --- a/src/engine/SCons/Tool/MSCommon/__init__.py +++ b/src/engine/SCons/Tool/MSCommon/__init__.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ Common functions for Microsoft Visual Studio and Visual C/C++. diff --git a/src/engine/SCons/Tool/MSCommon/arch.py b/src/engine/SCons/Tool/MSCommon/arch.py index 6965fbf..6312541 100644 --- a/src/engine/SCons/Tool/MSCommon/arch.py +++ b/src/engine/SCons/Tool/MSCommon/arch.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/arch.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module to define supported Windows chip architectures. """ diff --git a/src/engine/SCons/Tool/MSCommon/common.py b/src/engine/SCons/Tool/MSCommon/common.py index c8db141..8353944 100644 --- a/src/engine/SCons/Tool/MSCommon/common.py +++ b/src/engine/SCons/Tool/MSCommon/common.py @@ -25,7 +25,7 @@ Common helper functions for working with the Microsoft tool chain. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/MSCommon/common.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/common.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import copy import os @@ -34,7 +34,6 @@ import re import SCons.Util - LOGFILE = os.environ.get('SCONS_MSCOMMON_DEBUG') if LOGFILE == '-': def debug(message): @@ -117,7 +116,7 @@ def normalize_env(env, keys, force=False): normenv[k] = copy.deepcopy(env[k]) for k in keys: - if k in os.environ and (force or not k in normenv): + if k in os.environ and (force or k not in normenv): normenv[k] = os.environ[k] # This shouldn't be necessary, since the default environment should include system32, diff --git a/src/engine/SCons/Tool/MSCommon/netframework.py b/src/engine/SCons/Tool/MSCommon/netframework.py index 01bb5b7..edd9fd9 100644 --- a/src/engine/SCons/Tool/MSCommon/netframework.py +++ b/src/engine/SCons/Tool/MSCommon/netframework.py @@ -20,7 +20,7 @@ # 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__ = "src/engine/SCons/Tool/MSCommon/netframework.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/netframework.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ """ @@ -68,7 +68,7 @@ def query_versions(): # sequence comparison in python is lexicographical # which is exactly what we want. # Note we sort backwards so the highest version is first. - return cmp(bbl,aal) + return (aal > bbl) - (aal < bbl) versions.sort(versrt) else: diff --git a/src/engine/SCons/Tool/MSCommon/sdk.py b/src/engine/SCons/Tool/MSCommon/sdk.py index 2814d97..e664a30 100644 --- a/src/engine/SCons/Tool/MSCommon/sdk.py +++ b/src/engine/SCons/Tool/MSCommon/sdk.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/sdk.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module to detect the Platform/Windows SDK diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index 5de85d6..fa7be96 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -30,7 +30,7 @@ # * test on 64 bits XP + VS 2005 (and VS 6 if possible) # * SDK # * Assembly -__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module for Visual C/C++ detection and configuration. """ @@ -60,7 +60,10 @@ class VisualCException(Exception): class UnsupportedVersion(VisualCException): pass -class UnsupportedArch(VisualCException): +class MSVCUnsupportedHostArch(VisualCException): + pass + +class MSVCUnsupportedTargetArch(VisualCException): pass class MissingConfiguration(VisualCException): @@ -89,17 +92,15 @@ _ARCH_TO_CANONICAL = { "aarch64" : "arm64", } -# get path to the cl.exe dir for newer VS versions -# based off a tuple of (host, target) platforms _HOST_TARGET_TO_CL_DIR_GREATER_THAN_14 = { - ("amd64","amd64") : "Hostx64\\x64", - ("amd64","x86") : "Hostx64\\x86", - ("amd64","arm") : "Hostx64\\arm", - ("amd64","arm64") : "Hostx64\\arm64", - ("x86","amd64") : "Hostx86\\x64", - ("x86","x86") : "Hostx86\\x86", - ("x86","arm") : "Hostx86\\arm", - ("x86","arm64") : "Hostx86\\arm64", + ("amd64","amd64") : ("Hostx64","x64"), + ("amd64","x86") : ("Hostx64","x86"), + ("amd64","arm") : ("Hostx64","arm"), + ("amd64","arm64") : ("Hostx64","arm64"), + ("x86","amd64") : ("Hostx86","x64"), + ("x86","x86") : ("Hostx86","x86"), + ("x86","arm") : ("Hostx86","arm"), + ("x86","arm64") : ("Hostx86","arm64"), } # get path to the cl.exe dir for older VS versions @@ -174,25 +175,33 @@ def get_host_target(env): try: host = _ARCH_TO_CANONICAL[host_platform.lower()] - except KeyError as e: + except KeyError: msg = "Unrecognized host architecture %s" - raise ValueError(msg % repr(host_platform)) + raise MSVCUnsupportedHostArch(msg % repr(host_platform)) try: target = _ARCH_TO_CANONICAL[target_platform.lower()] - except KeyError as e: + except KeyError: all_archs = str(list(_ARCH_TO_CANONICAL.keys())) - raise ValueError("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) + raise MSVCUnsupportedTargetArch("Unrecognized target architecture %s\n\tValid architectures: %s" % (target_platform, all_archs)) return (host, target,req_target_platform) # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the # MSVC_VERSION documentation in Tool/msvc.xml. -_VCVER = ["14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] +_VCVER = ["14.2", "14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] + +# if using vswhere, a further mapping is needed +_VCVER_TO_VSWHERE_VER = { + '14.2' : '[16.0, 17.0)', + '14.1' : '[15.0, 16.0)', +} _VCVER_TO_PRODUCT_DIR = { + '14.2' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # VS 2019 doesn't set this key '14.1' : [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # Visual Studio 2017 doesn't set this registry key anymore + (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # VS 2017 doesn't set this key '14.0' : [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], '14.0Exp' : [ @@ -253,42 +262,40 @@ def msvc_version_to_maj_min(msvc_version): raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) def is_host_target_supported(host_target, msvc_version): - """Check if the given (host, target) tuple is supported for given version. + """Check if (host, target) pair is supported for a VC version. - Args: - host_target: tuple - tuple of (canonalized) host-targets, e.g. ("x86", "amd64") - for cross compilation from 32 bit Windows to 64 bits. - msvc_version: str - msvc version (major.minor, e.g. 10.0) - - Returns: - bool: - - Note: - This only checks whether a given version *may* support the given (host, + :note: only checks whether a given version *may* support the given (host, target), not that the toolchain is actually present on the machine. + :param tuple host_target: canonalized host-targets pair, e.g. + ("x86", "amd64") for cross compilation from 32 bit Windows to 64 bits. + :param str msvc_version: Visual C++ version (major.minor), e.g. "10.0" + :returns: True or False """ # We assume that any Visual Studio version supports x86 as a target if host_target[1] != "x86": maj, min = msvc_version_to_maj_min(msvc_version) if maj < 8: return False - return True def find_vc_pdir_vswhere(msvc_version): """ - Find the MSVC product directory using vswhere.exe. + Find the MSVC product directory using the vswhere program. - Run it asking for specified version and get MSVS install location - :param msvc_version: + :param msvc_version: MSVC version to search for :return: MSVC install dir or None + :raises UnsupportedVersion: if the version is not known by this file """ + try: + vswhere_version = _VCVER_TO_VSWHERE_VER[msvc_version] + except KeyError: + debug("Unknown version of MSVC: %s" % msvc_version) + raise UnsupportedVersion("Unknown version %s" % msvc_version) + # For bug 3333 - support default location of vswhere for both 64 and 32 bit windows - # installs. + # installs. for pf in ['Program Files (x86)', 'Program Files']: vswhere_path = os.path.join( 'C:\\', @@ -300,31 +307,36 @@ def find_vc_pdir_vswhere(msvc_version): if os.path.exists(vswhere_path): # If we found vswhere, then use it. break + else: + # No vswhere on system, no install info available + return None - vswhere_cmd = [vswhere_path, '-products', '*', '-version', msvc_version, '-property', 'installationPath'] - - if os.path.exists(vswhere_path): - #TODO PY27 cannot use Popen as context manager - # try putting it back to the old way for now - sp = subprocess.Popen(vswhere_cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - vsdir, err = sp.communicate() - if vsdir: - vsdir = vsdir.decode("mbcs").splitlines() - # vswhere could easily return multiple lines - # we could define a way to pick the one we prefer, but since - # this data is currently only used to make a check for existence, - # returning the first hit should be good enough for now. - vc_pdir = os.path.join(vsdir[0], 'VC') - return vc_pdir + vswhere_cmd = [vswhere_path, + '-products', '*', + '-version', vswhere_version, + '-property', 'installationPath'] + + #TODO PY27 cannot use Popen as context manager + # try putting it back to the old way for now + sp = subprocess.Popen(vswhere_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + vsdir, err = sp.communicate() + if vsdir: + vsdir = vsdir.decode("mbcs").splitlines() + # vswhere could easily return multiple lines + # we could define a way to pick the one we prefer, but since + # this data is currently only used to make a check for existence, + # returning the first hit should be good enough for now. + vc_pdir = os.path.join(vsdir[0], 'VC') + return vc_pdir else: # No vswhere on system, no install info available return None def find_vc_pdir(msvc_version): - """Find the product directory for the given version. + """Find the MSVC product directory for the given version. Tries to look up the path using a registry key from the table _VCVER_TO_PRODUCT_DIR; if there is no key, calls find_vc_pdir_wshere @@ -425,6 +437,8 @@ def find_batch_file(env,msvc_version,host_arch,target_arch): __INSTALLED_VCS_RUN = None +_VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt'] +_VC_TOOLS_VERSION_FILE = os.sep.join(_VC_TOOLS_VERSION_FILE_PATH) def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): """Find the cl.exe on the filesystem in the vc_dir depending on @@ -459,7 +473,7 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): host_platform = _ARCH_TO_CANONICAL[host_platform] target_platform = _ARCH_TO_CANONICAL[target_platform] - debug('_check_cl_exists_in_vc_dir(): host platform %s, target platform %s' % (host_platform, target_platform)) + debug('_check_cl_exists_in_vc_dir(): host platform %s, target platform %s for version %s' % (host_platform, target_platform, msvc_version)) ver_num = float(get_msvc_version_numeric(msvc_version)) @@ -468,7 +482,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. # Just get the default tool version for now #TODO: support setting a specific minor VC version - default_toolset_file = os.path.join(vc_dir, r'Auxiliary\Build\Microsoft.VCToolsVersion.default.txt') + default_toolset_file = os.path.join(vc_dir, _VC_TOOLS_VERSION_FILE) try: with open(default_toolset_file) as f: vc_specific_version = f.readlines()[0].strip() @@ -480,11 +494,11 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): return False host_trgt_dir = _HOST_TARGET_TO_CL_DIR_GREATER_THAN_14.get((host_platform, target_platform), None) - if not host_trgt_dir: - debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo') + if host_trgt_dir is None: + debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo: (%s,%s)'%(host_platform, target_platform)) return False - cl_path = os.path.join(vc_dir, r'Tools\MSVC', vc_specific_version, 'bin', host_trgt_dir, _CL_EXE_NAME) + cl_path = os.path.join(vc_dir, 'Tools','MSVC', vc_specific_version, 'bin', host_trgt_dir[0], host_trgt_dir[1], _CL_EXE_NAME) debug('_check_cl_exists_in_vc_dir(): checking for ' + _CL_EXE_NAME + ' at ' + cl_path) if os.path.exists(cl_path): debug('_check_cl_exists_in_vc_dir(): found ' + _CL_EXE_NAME + '!') @@ -492,8 +506,10 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): elif ver_num <= 14 and ver_num >= 8: + # Set default value to be -1 as "" which is the value for x86/x86 yields true when tested + # if not host_trgt_dir host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get((host_platform, target_platform), None) - if not host_trgt_dir: + if host_trgt_dir is None: debug('_check_cl_exists_in_vc_dir(): unsupported host/target platform combo') return False @@ -505,8 +521,11 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): # older versions of visual studio only had x86 binaries, # so if the host platform is amd64, we need to check cross # compile options (x86 binary compiles some other target on a 64 bit os) + + # Set default value to be -1 as "" which is the value for x86/x86 yields true when tested + # if not host_trgt_dir host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get(('x86', target_platform), None) - if not host_trgt_dir: + if host_trgt_dir is None: return False cl_path = os.path.join(vc_dir, 'bin', host_trgt_dir, _CL_EXE_NAME) @@ -554,6 +573,10 @@ def get_installed_vcs(env=None): debug('find_vc_pdir no compiler found %s' % ver) else: debug('find_vc_pdir return None for ver %s' % ver) + except (MSVCUnsupportedTargetArch, MSVCUnsupportedHostArch): + # Allow this exception to propagate further as it should cause + # SCons to exit with an error code + raise except VisualCException as e: debug('did not find VC %s: caught exception %s' % (ver, str(e))) return installed_versions diff --git a/src/engine/SCons/Tool/MSCommon/vcTests.py b/src/engine/SCons/Tool/MSCommon/vcTests.py new file mode 100644 index 0000000..25537f3 --- /dev/null +++ b/src/engine/SCons/Tool/MSCommon/vcTests.py @@ -0,0 +1,180 @@ +# +# Copyright (c) 2001 - 2019 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. +# +# from typing import Dict, Any + +__revision__ = "src/engine/SCons/Tool/MSCommon/vcTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" + +import os +import os.path +import unittest + +import SCons.Node.FS +import SCons.Warnings +import SCons.Tool.MSCommon.vc + +import TestCmd + +original = os.getcwd() + +test = TestCmd.TestCmd(workdir='') + +os.chdir(test.workpath('')) + +MSVCUnsupportedHostArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedHostArch +MSVCUnsupportedTargetArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedTargetArch + +MS_TOOLS_VERSION='1.1.1' + +class MSVcTestCase(unittest.TestCase): + + @staticmethod + def _createDummyCl(path, add_bin=True): + """ + Creates a dummy cl.ex in the correct directory. + It will create all missing parent directories as well + + Args: + path: Relative path to cl.exe for the version about to be tested. + """ + + # print("PATH:%s"%path) + + path = path.replace('\\', os.sep) + if add_bin: + create_path = os.path.join(path,'bin') + else: + 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. + Verify that improper HOST_PLATFORM and/or TARGET_PLATFORM are properly handled. + by SCons.Tool.MSCommon.vc._check_cl_exists_in_vc_dir() + """ + + check = SCons.Tool.MSCommon.vc._check_cl_exists_in_vc_dir + + env={'TARGET_ARCH':'x86'} + p = SCons.Tool.MSCommon.vc._HOST_TARGET_TO_CL_DIR[('x86','x86')] + MSVcTestCase._createDummyCl(p) + + # print("retval:%s"%check(env, '.', '8.0')) + + + # Setup for VC 14+ tests + + # Create the VC minor/major version file + tools_version_file = SCons.Tool.MSCommon.vc._VC_TOOLS_VERSION_FILE + tools_dir = os.path.dirname(tools_version_file) + if not os.path.isdir(tools_dir): + os.makedirs(tools_dir) + try: + 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)) + + + # Now walk all the valid combinations of host/target for VC 14 + + vc_gt_14_map = SCons.Tool.MSCommon.vc._HOST_TARGET_TO_CL_DIR_GREATER_THAN_14 + + for key, value in vc_gt_14_map.items(): + # print("GT 14 Got: %s -> %s"%(key,value)) + + env={'TARGET_ARCH':key[1], 'HOST_ARCH':key[0]} + path = os.path.join('.','Tools','MSVC', MS_TOOLS_VERSION, 'bin', value[0], value[1]) + MSVcTestCase._createDummyCl(path, add_bin=False) + result=check(env, '.', '14.1') + # print("for:%s got :%s"%(key[1], result)) + self.assertTrue(result, "Checking host: %s target: %s"%(value[0], value[1])) + + # Now test bogus value for HOST_ARCH + env={'TARGET_ARCH':'x86', 'HOST_ARCH':'GARBAGE'} + try: + result=check(env, '.', '14.1') + # print("for:%s got :%s"%(env, result)) + self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s"%(value[0], value[1])) + except MSVCUnsupportedHostArch: + pass + else: + 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'} + try: + result=check(env, '.', '14.1') + # print("for:%s got :%s"%(env, result)) + self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s"%(value[0], value[1])) + except MSVCUnsupportedTargetArch: + pass + else: + self.fail('Did not fail when HOST_ARCH specified as: %s'%env['TARGET_ARCH']) + + # Test >8 < 14 VC versions + vc_map = SCons.Tool.MSCommon.vc._HOST_TARGET_TO_CL_DIR + for key,value in vc_map.items(): + # print("LT 14 Got: %s -> %s"%(key,value)) + env={'TARGET_ARCH':key[1], 'HOST_ARCH':key[0]} + path = os.path.join('.', 'bin', value ) + MSVcTestCase._createDummyCl(path, add_bin=False) + result=check(env, '.', '9.0') + # print("for:%s got :%s"%(key[1], result)) + self.assertTrue(result, "Checking host: %s target: %s"%(key[0], key[1])) + + # Now test bogus value for HOST_ARCH + env={'TARGET_ARCH':'x86', 'HOST_ARCH':'GARBAGE'} + 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'])) + except MSVCUnsupportedHostArch: + pass + else: + 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'} + 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'])) + except MSVCUnsupportedTargetArch: + pass + else: + self.fail('Did not fail when HOST_ARCH specified as: %s'%env['TARGET_ARCH']) + + + +if __name__ == "__main__": + unittest.main() diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py index 72d4421..731f328 100644 --- a/src/engine/SCons/Tool/MSCommon/vs.py +++ b/src/engine/SCons/Tool/MSCommon/vs.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/MSCommon/vs.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """Module to detect Visual Studio and/or Visual C/C++ """ @@ -198,6 +198,17 @@ class VisualStudio(object): # Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml. SupportedVSList = [ + # Visual Studio 2019 + VisualStudio('14.2', + vc_version='14.2', + sdk_version='10.0A', + hkeys=[], + common_tools_var='VS160COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'VC\Auxiliary\Build\vsvars32.bat', + supported_arch=['x86', 'amd64', "arm"], + ), + # Visual Studio 2017 VisualStudio('14.1', vc_version='14.1', @@ -520,7 +531,7 @@ def get_default_arch(env): if not msvs: arch = 'x86' - elif not arch in msvs.get_supported_arch(): + elif arch not in msvs.get_supported_arch(): fmt = "Visual Studio version %s does not support architecture %s" raise SCons.Errors.UserError(fmt % (env['MSVS_VERSION'], arch)) diff --git a/src/engine/SCons/Tool/PharLapCommon.py b/src/engine/SCons/Tool/PharLapCommon.py index 103e21c..623f2a8 100644 --- a/src/engine/SCons/Tool/PharLapCommon.py +++ b/src/engine/SCons/Tool/PharLapCommon.py @@ -29,7 +29,7 @@ Phar Lap ETS tool chain. Right now, this is linkloc and # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/PharLapCommon.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/PharLapCommon.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path @@ -79,7 +79,8 @@ def getPharLapVersion(): include_path = os.path.join(getPharLapPath(), os.path.normpath("include/embkern.h")) if not os.path.exists(include_path): raise SCons.Errors.UserError("Cannot find embkern.h in ETS include directory.\nIs Phar Lap ETS installed properly?") - mo = REGEX_ETS_VER.search(open(include_path, 'r').read()) + with open(include_path, 'r') as f: + mo = REGEX_ETS_VER.search(f.read()) if mo: return int(mo.group(1)) # Default return for Phar Lap 9.1 diff --git a/src/engine/SCons/Tool/ToolTests.py b/src/engine/SCons/Tool/ToolTests.py index 81b6491..25e9b2d 100644 --- a/src/engine/SCons/Tool/ToolTests.py +++ b/src/engine/SCons/Tool/ToolTests.py @@ -21,8 +21,9 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ToolTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ToolTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" +import os import sys import unittest @@ -31,29 +32,49 @@ import TestUnit import SCons.Errors import SCons.Tool + +class DummyEnvironment(object): + def __init__(self): + self.dict = {} + def Detect(self, progs): + if not SCons.Util.is_List(progs): + progs = [ progs ] + return progs[0] + def Append(self, **kw): + self.dict.update(kw) + def __getitem__(self, key): + return self.dict[key] + def __setitem__(self, key, val): + self.dict[key] = val + def __contains__(self, key): + return self.dict.__contains__(key) + def has_key(self, key): + return key in self.dict + def subst(self, string, *args, **kwargs): + return string + + PHONY_PATH = "/usr/phony/bin" + def WhereIs(self, key_program): + # for pathfind test for Issue #3336: + # need to fake the case where extra paths are searched, and + # if one has a "hit" after some fails, the fails are left in + # the environment's PATH. So construct a positive answer if + # we see a magic known path component in PATH; answer in + # the negative otherwise. + paths = self['ENV']['PATH'] + if self.PHONY_PATH in paths: + return os.path.join(self.PHONY_PATH, key_program) + return None + def AppendENVPath(self, pathvar, path): + # signature matches how called from find_program_path() + self['ENV'][pathvar] = self['ENV'][pathvar] + os.pathsep + path + + class ToolTestCase(unittest.TestCase): def test_Tool(self): """Test the Tool() function""" - class Environment(object): - def __init__(self): - self.dict = {} - def Detect(self, progs): - if not SCons.Util.is_List(progs): - progs = [ progs ] - return progs[0] - def Append(self, **kw): - self.dict.update(kw) - def __getitem__(self, key): - return self.dict[key] - def __setitem__(self, key, val): - self.dict[key] = val - def __contains__(self, key): - return self.dict.__contains__(key) - def has_key(self, key): - return key in self.dict - def subst(self, string, *args, **kwargs): - return string - env = Environment() + + env = DummyEnvironment() env['BUILDERS'] = {} env['ENV'] = {} env['PLATFORM'] = 'test' @@ -67,17 +88,34 @@ class ToolTestCase(unittest.TestCase): SCons.Tool.Tool() except TypeError: pass - else: + else: # TODO pylint E0704: bare raise not inside except raise try: p = SCons.Tool.Tool('_does_not_exist_') - except SCons.Errors.EnvironmentError: + except SCons.Errors.SConsEnvironmentError: pass - else: + else: # TODO pylint E0704: bare raise not inside except raise + def test_pathfind(self): + """Test that find_program_path() does not alter PATH""" + + 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['ENV'] = {} + env['ENV']['PATH'] = '/usr/local/bin:/opt/bin:/bin:/usr/bin' + pre_path = env['ENV']['PATH'] + _ = SCons.Tool.find_program_path(env, 'no_tool', default_paths=PHONY_PATHS) + assert env['ENV']['PATH'] == pre_path, env['ENV']['PATH'] + + if __name__ == "__main__": suite = unittest.makeSuite(ToolTestCase, 'test_') TestUnit.run(suite) diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index b7d5dcc..271f214 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -35,10 +35,8 @@ tool definition. # 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__ = "src/engine/SCons/Tool/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" -import imp -import importlib import sys import re import os @@ -53,7 +51,12 @@ import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog import SCons.Scanner.SWIG -import collections +try: + # Python 3 + from collections.abc import Callable +except ImportError: + # Python 2.7 + from collections import Callable DefaultToolpath = [] @@ -106,7 +109,9 @@ TOOL_ALIASES = { class Tool(object): - def __init__(self, name, toolpath=[], **kw): + def __init__(self, name, toolpath=None, **kw): + if toolpath is None: + toolpath = [] # Rename if there's a TOOL_ALIAS for this tool self.name = TOOL_ALIASES.get(name, name) @@ -121,6 +126,8 @@ class Tool(object): 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 @@ -149,7 +156,7 @@ class Tool(object): except ImportError as e: splitname = self.name.split('.') if str(e) != "No module named %s" % splitname[0]: - raise SCons.Errors.EnvironmentError(e) + raise SCons.Errors.SConsEnvironmentError(e) try: import zipimport except ImportError: @@ -211,13 +218,13 @@ class Tool(object): if spec is None: error_string = "No module named %s" % self.name - raise SCons.Errors.EnvironmentError(error_string) + raise SCons.Errors.SConsEnvironmentError(error_string) module = importlib.util.module_from_spec(spec) if module is None: if debug: print("MODULE IS NONE:%s" % self.name) error_string = "No module named %s" % self.name - raise SCons.Errors.EnvironmentError(error_string) + raise SCons.Errors.SConsEnvironmentError(error_string) # Don't reload a tool we already loaded. sys_modules_value = sys.modules.get(found_name, False) @@ -258,7 +265,7 @@ class Tool(object): return module except ImportError as e: if str(e) != "No module named %s" % self.name: - raise SCons.Errors.EnvironmentError(e) + raise SCons.Errors.SConsEnvironmentError(e) try: import zipimport importer = zipimport.zipimporter(sys.modules['SCons.Tool'].__path__[0]) @@ -267,10 +274,10 @@ class Tool(object): return module except ImportError as e: m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.EnvironmentError(m) + raise SCons.Errors.SConsEnvironmentError(m) except ImportError as e: m = "No tool named '%s': %s" % (self.name, e) - raise SCons.Errors.EnvironmentError(m) + raise SCons.Errors.SConsEnvironmentError(m) def __call__(self, env, *args, **kw): if self.init_kw is not None: @@ -376,7 +383,7 @@ def _call_linker_cb(env, callback, args, result=None): if Verbose: print('_call_linker_cb: env["LINKCALLBACKS"][%r] found' % callback) print('_call_linker_cb: env["LINKCALLBACKS"][%r]=%r' % (callback, cbfun)) - if isinstance(cbfun, collections.Callable): + if isinstance(cbfun, Callable): if Verbose: print('_call_linker_cb: env["LINKCALLBACKS"][%r] is callable' % callback) result = cbfun(env, *args) @@ -394,7 +401,8 @@ def _call_env_subst(env, string, *args, **kw): class _ShLibInfoSupport(object): - def get_libtype(self): + @property + def libtype(self): return 'ShLib' def get_lib_prefix(self, env, *args, **kw): @@ -411,7 +419,8 @@ class _ShLibInfoSupport(object): class _LdModInfoSupport(object): - def get_libtype(self): + @property + def libtype(self): return 'LdMod' def get_lib_prefix(self, env, *args, **kw): @@ -428,7 +437,8 @@ class _LdModInfoSupport(object): class _ImpLibInfoSupport(object): - def get_libtype(self): + @property + def libtype(self): return 'ImpLib' def get_lib_prefix(self, env, *args, **kw): @@ -480,25 +490,21 @@ class _LibInfoGeneratorBase(object): 'ImpLib': _ImpLibInfoSupport} def __init__(self, libtype, infoname): - self.set_libtype(libtype) - self.set_infoname(infoname) + self.libtype = libtype + self.infoname = infoname + + @property + def libtype(self): + return self._support.libtype - def set_libtype(self, libtype): + @libtype.setter + def libtype(self, libtype): try: support_class = self._support_classes[libtype] except KeyError: raise ValueError('unsupported libtype %r' % libtype) self._support = support_class() - def get_libtype(self): - return self._support.get_libtype() - - def set_infoname(self, infoname): - self.infoname = infoname - - def get_infoname(self): - return self.infoname - def get_lib_prefix(self, env, *args, **kw): return self._support.get_lib_prefix(env, *args, **kw) @@ -518,9 +524,8 @@ class _LibInfoGeneratorBase(object): try: libtype = kw['generator_libtype'] except KeyError: - libtype = self.get_libtype() - infoname = self.get_infoname() - return 'Versioned%s%s' % (libtype, infoname) + libtype = self.libtype + return 'Versioned%s%s' % (libtype, self.infoname) def generate_versioned_lib_info(self, env, args, result=None, **kw): callback = self.get_versioned_lib_info_generator(**kw) @@ -730,7 +735,7 @@ class _LibSonameGenerator(_LibInfoGeneratorBase): if not soname: # fallback to library name (as returned by appropriate _LibNameGenerator) - soname = _LibNameGenerator(self.get_libtype())(env, libnode) + soname = _LibNameGenerator(self.libtype)(env, libnode) if Verbose: print("_LibSonameGenerator: FALLBACK: soname=%r" % soname) @@ -1316,30 +1321,35 @@ def tool_list(platform, env): return [x for x in tools if x] -def find_program_path(env, key_program, default_paths=[]): +def find_program_path(env, key_program, default_paths=None): """ - Find the location of key_program and then return the path it was located at. - Checking the default install locations. - Mainly for windows where tools aren't all installed in /usr/bin,etc - :param env: Current Environment() - :param key_program: Program we're using to locate the directory to add to PATH. + 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. """ # First search in the SCons path path = env.WhereIs(key_program) - if (path): + if path: return path - # then the OS path: + + # Then in the OS path path = SCons.Util.WhereIs(key_program) - if (path): + if path: return path - # If that doesn't work try default location for mingw + # Finally, add the defaults and check again. Do not change + # ['ENV']['PATH'] permananetly, the caller can do that if needed. + 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) - if not path: - env['ENV']['PATH'] = save_path + env['ENV']['PATH'] = save_path return path # Local Variables: diff --git a/src/engine/SCons/Tool/aixc++.py b/src/engine/SCons/Tool/aixc++.py index be14abc..bafaea4 100644 --- a/src/engine/SCons/Tool/aixc++.py +++ b/src/engine/SCons/Tool/aixc++.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/aixc++.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/aixc++.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" #forward proxy to the preffered cxx version from SCons.Tool.aixcxx import * diff --git a/src/engine/SCons/Tool/aixcc.py b/src/engine/SCons/Tool/aixcc.py index a1fe216..349a40a 100644 --- a/src/engine/SCons/Tool/aixcc.py +++ b/src/engine/SCons/Tool/aixcc.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/aixcc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/aixcc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path diff --git a/src/engine/SCons/Tool/aixcxx.py b/src/engine/SCons/Tool/aixcxx.py index ec2af1a..0830e33 100644 --- a/src/engine/SCons/Tool/aixcxx.py +++ b/src/engine/SCons/Tool/aixcxx.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/aixcxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/aixcxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path diff --git a/src/engine/SCons/Tool/aixf77.py b/src/engine/SCons/Tool/aixf77.py index 6281b13..d2c619a 100644 --- a/src/engine/SCons/Tool/aixf77.py +++ b/src/engine/SCons/Tool/aixf77.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/aixf77.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/aixf77.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path diff --git a/src/engine/SCons/Tool/aixlink.py b/src/engine/SCons/Tool/aixlink.py index ddb4281..ea2949f 100644 --- a/src/engine/SCons/Tool/aixlink.py +++ b/src/engine/SCons/Tool/aixlink.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/aixlink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/aixlink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py index 5981d19..011b29d 100644 --- a/src/engine/SCons/Tool/applelink.py +++ b/src/engine/SCons/Tool/applelink.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/applelink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/applelink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/ar.py b/src/engine/SCons/Tool/ar.py index bef719f..459fb55 100644 --- a/src/engine/SCons/Tool/ar.py +++ b/src/engine/SCons/Tool/ar.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ar.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ar.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/as.py b/src/engine/SCons/Tool/as.py index 8deea59..02139c5 100644 --- a/src/engine/SCons/Tool/as.py +++ b/src/engine/SCons/Tool/as.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/as.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/as.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/bcc32.py b/src/engine/SCons/Tool/bcc32.py index 1cb0a38..e8ffbbf 100644 --- a/src/engine/SCons/Tool/bcc32.py +++ b/src/engine/SCons/Tool/bcc32.py @@ -27,7 +27,7 @@ XXX # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/bcc32.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/bcc32.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path diff --git a/src/engine/SCons/Tool/c++.py b/src/engine/SCons/Tool/c++.py index e670d0d..8819f82 100644 --- a/src/engine/SCons/Tool/c++.py +++ b/src/engine/SCons/Tool/c++.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/c++.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/c++.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" #forward proxy to the preffered cxx version diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py index 9b604c4..698d725 100644 --- a/src/engine/SCons/Tool/cc.py +++ b/src/engine/SCons/Tool/cc.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/cc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/cc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Tool import SCons.Defaults diff --git a/src/engine/SCons/Tool/clang.py b/src/engine/SCons/Tool/clang.py index 69cbbb6..f48afbf 100644 --- a/src/engine/SCons/Tool/clang.py +++ b/src/engine/SCons/Tool/clang.py @@ -33,7 +33,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -# __revision__ = "src/engine/SCons/Tool/clang.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +# __revision__ = "src/engine/SCons/Tool/clang.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" # Based on SCons/Tool/gcc.py by Paweł Tomulik 2014 as a separate tool. # Brought into the SCons mainline by Russel Winder 2017. @@ -77,7 +77,8 @@ def generate(env): stdout=subprocess.PIPE) if pipe.wait() != 0: return # clang -dumpversion is of no use - line = pipe.stdout.readline() + with pipe.stdout: + line = pipe.stdout.readline() if sys.version_info[0] > 2: line = line.decode() match = re.search(r'clang +version +([0-9]+(?:\.[0-9]+)+)', line) diff --git a/src/engine/SCons/Tool/clangxx.py b/src/engine/SCons/Tool/clangxx.py index 7899228..d88874e 100644 --- a/src/engine/SCons/Tool/clangxx.py +++ b/src/engine/SCons/Tool/clangxx.py @@ -33,7 +33,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -# __revision__ = "src/engine/SCons/Tool/clangxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +# __revision__ = "src/engine/SCons/Tool/clangxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" # Based on SCons/Tool/g++.py by Paweł Tomulik 2014 as a separate tool. # Brought into the SCons mainline by Russel Winder 2017. @@ -85,7 +85,8 @@ def generate(env): return # clang -dumpversion is of no use - line = pipe.stdout.readline() + with pipe.stdout: + line = pipe.stdout.readline() if sys.version_info[0] > 2: line = line.decode() match = re.search(r'clang +version +([0-9]+(?:\.[0-9]+)+)', line) diff --git a/src/engine/SCons/Tool/cvf.py b/src/engine/SCons/Tool/cvf.py index bc1e888..aa24d69 100644 --- a/src/engine/SCons/Tool/cvf.py +++ b/src/engine/SCons/Tool/cvf.py @@ -27,7 +27,7 @@ Tool-specific initialization for the Compaq Visual Fortran compiler. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/cvf.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/cvf.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import fortran diff --git a/src/engine/SCons/Tool/cxx.py b/src/engine/SCons/Tool/cxx.py index 81a67c7..2ac278e 100644 --- a/src/engine/SCons/Tool/cxx.py +++ b/src/engine/SCons/Tool/cxx.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/cxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/cxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path diff --git a/src/engine/SCons/Tool/cyglink.py b/src/engine/SCons/Tool/cyglink.py index f69b886..c3d78de 100644 --- a/src/engine/SCons/Tool/cyglink.py +++ b/src/engine/SCons/Tool/cyglink.py @@ -133,7 +133,7 @@ def _versioned_lib_suffix(env, suffix, version): if Verbose: print("_versioned_lib_suffix: suffix= ", suffix) print("_versioned_lib_suffix: version= ", version) - cygversion = re.sub('\.', '-', version) + cygversion = re.sub(r'\.', '-', version) if not suffix.startswith('-' + cygversion): suffix = '-' + cygversion + suffix if Verbose: diff --git a/src/engine/SCons/Tool/default.py b/src/engine/SCons/Tool/default.py index e8aa11b..866b823 100644 --- a/src/engine/SCons/Tool/default.py +++ b/src/engine/SCons/Tool/default.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/default.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/default.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Tool diff --git a/src/engine/SCons/Tool/dmd.py b/src/engine/SCons/Tool/dmd.py index b281e1c..befcd22 100644 --- a/src/engine/SCons/Tool/dmd.py +++ b/src/engine/SCons/Tool/dmd.py @@ -53,7 +53,7 @@ Lib tool variables: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/dmd.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/dmd.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import subprocess diff --git a/src/engine/SCons/Tool/docbook/__init__.py b/src/engine/SCons/Tool/docbook/__init__.py index d7d10c0..147556d 100644 --- a/src/engine/SCons/Tool/docbook/__init__.py +++ b/src/engine/SCons/Tool/docbook/__init__.py @@ -84,7 +84,7 @@ def __extend_targets_sources(target, source): source = [source] if len(target) < len(source): target.extend(source[len(target):]) - + return target, source def __init_xsl_stylesheet(kw, env, user_xsl_var, default_path): @@ -94,12 +94,12 @@ def __init_xsl_stylesheet(kw, env, user_xsl_var, default_path): path_args = [scriptpath, db_xsl_folder] + default_path xsl_style = os.path.join(*path_args) kw['DOCBOOK_XSL'] = xsl_style - + def __select_builder(lxml_builder, libxml2_builder, cmdline_builder): """ Selects a builder, based on which Python modules are present. """ if prefer_xsltproc: return cmdline_builder - + if not has_libxml2: # At the moment we prefer libxml2 over lxml, the latter can lead # to conflicts when installed together with libxml2. @@ -115,7 +115,7 @@ def __ensure_suffix(t, suffix): tpath = str(t) if not tpath.endswith(suffix): return tpath+suffix - + return t def __ensure_suffix_stem(t, suffix): @@ -124,11 +124,11 @@ def __ensure_suffix_stem(t, suffix): if not tpath.endswith(suffix): stem = tpath tpath += suffix - + return tpath, stem else: stem, ext = os.path.splitext(tpath) - + return t, stem def __get_xml_text(root): @@ -151,7 +151,7 @@ def __create_output_dir(base_dir): else: if base_dir.endswith('/'): dir = base_dir - + if dir and not os.path.isdir(dir): os.makedirs(dir) @@ -203,10 +203,10 @@ def _detect(env): the requested output formats. """ global prefer_xsltproc - + if env.get('DOCBOOK_PREFER_XSLTPROC',''): prefer_xsltproc = True - + if ((not has_libxml2 and not has_lxml) or (prefer_xsltproc)): # Try to find the XSLT processors __detect_cl_tool(env, 'DOCBOOK_XSLTPROC', xsltproc_com, xsltproc_com_priority) @@ -219,15 +219,15 @@ def _detect(env): # include_re = re.compile('fileref\\s*=\\s*["|\']([^\\n]*)["|\']') sentity_re = re.compile('') - + def __xml_scan(node, env, path, arg): """ Simple XML file scanner, detecting local images and XIncludes as implicit dependencies. """ # Does the node exist yet? if not os.path.isfile(str(node)): return [] - + if env.get('DOCBOOK_SCANENT',''): - # Use simple pattern matching for system entities..., no support + # Use simple pattern matching for system entities..., no support # for recursion yet. contents = node.get_text_contents() return sentity_re.findall(contents) @@ -235,9 +235,9 @@ def __xml_scan(node, env, path, arg): xsl_file = os.path.join(scriptpath,'utils','xmldepend.xsl') if not has_libxml2 or prefer_xsltproc: if has_lxml and not prefer_xsltproc: - + from lxml import etree - + xsl_tree = etree.parse(xsl_file) doc = etree.parse(str(node)) result = doc.xslt(xsl_tree) @@ -266,7 +266,7 @@ def __xml_scan(node, env, path, arg): for x in str(result).splitlines(): if x.strip() != "" and not x.startswith(" 1: env.Clean(outfiles[0], outfiles[1:]) - + return result def DocbookSlidesPdf(env, target, source=None, *args, **kw): @@ -750,7 +746,7 @@ def DocbookSlidesPdf(env, target, source=None, *args, **kw): for t,s in zip(target,source): t, stem = __ensure_suffix_stem(t, '.pdf') xsl = __builder.__call__(env, stem+'.fo', s, **kw) - env.Depends(xsl, kw['DOCBOOK_XSL']) + env.Depends(xsl, kw['DOCBOOK_XSL']) result.extend(xsl) result.extend(__fop_builder.__call__(env, t, xsl, **kw)) @@ -767,7 +763,7 @@ def DocbookSlidesHtml(env, target, source=None, *args, **kw): source = target target = ['index.html'] elif not SCons.Util.is_List(source): - source = [source] + source = [source] # Init XSL stylesheet __init_xsl_stylesheet(kw, env, '$DOCBOOK_DEFAULT_XSL_SLIDESHTML', ['slides','html','plain.xsl']) @@ -800,12 +796,12 @@ def DocbookXInclude(env, target, source, *args, **kw): # Setup builder __builder = __select_builder(__xinclude_lxml_builder,__xinclude_libxml2_builder,__xmllint_builder) - + # Create targets result = [] for t,s in zip(target,source): result.extend(__builder.__call__(env, t, s, **kw)) - + return result def DocbookXslt(env, target, source=None, *args, **kw): @@ -814,13 +810,13 @@ def DocbookXslt(env, target, source=None, *args, **kw): """ # Init list of targets/sources target, source = __extend_targets_sources(target, source) - + # Init XSL stylesheet kw['DOCBOOK_XSL'] = kw.get('xsl', 'transform.xsl') # Setup builder __builder = __select_builder(__lxml_builder, __libxml2_builder, __xsltproc_builder) - + # Create targets result = [] for t,s in zip(target,source): @@ -844,18 +840,18 @@ def generate(env): DOCBOOK_DEFAULT_XSL_MAN = '', DOCBOOK_DEFAULT_XSL_SLIDESPDF = '', DOCBOOK_DEFAULT_XSL_SLIDESHTML = '', - + # Paths to the detected executables DOCBOOK_XSLTPROC = '', DOCBOOK_XMLLINT = '', DOCBOOK_FOP = '', - + # Additional flags for the text processors DOCBOOK_XSLTPROCFLAGS = SCons.Util.CLVar(''), DOCBOOK_XMLLINTFLAGS = SCons.Util.CLVar(''), DOCBOOK_FOPFLAGS = SCons.Util.CLVar(''), DOCBOOK_XSLTPROCPARAMS = SCons.Util.CLVar(''), - + # Default command lines for the detected executables DOCBOOK_XSLTPROCCOM = xsltproc_com['xsltproc'], DOCBOOK_XMLLINTCOM = xmllint_com['xmllint'], @@ -865,7 +861,7 @@ def generate(env): DOCBOOK_XSLTPROCCOMSTR = None, DOCBOOK_XMLLINTCOMSTR = None, DOCBOOK_FOPCOMSTR = None, - + ) _detect(env) diff --git a/src/engine/SCons/Tool/dvi.py b/src/engine/SCons/Tool/dvi.py index 9844e37..5cd85ed 100644 --- a/src/engine/SCons/Tool/dvi.py +++ b/src/engine/SCons/Tool/dvi.py @@ -27,7 +27,7 @@ Common DVI Builder definition for various other Tool modules that use it. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/dvi.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/dvi.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Builder import SCons.Tool diff --git a/src/engine/SCons/Tool/dvipdf.py b/src/engine/SCons/Tool/dvipdf.py index a4d7d93..32f57e6 100644 --- a/src/engine/SCons/Tool/dvipdf.py +++ b/src/engine/SCons/Tool/dvipdf.py @@ -30,7 +30,7 @@ selection method. # 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__ = "src/engine/SCons/Tool/dvipdf.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/dvipdf.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Defaults diff --git a/src/engine/SCons/Tool/dvips.py b/src/engine/SCons/Tool/dvips.py index 30a79ba..988ecd3 100644 --- a/src/engine/SCons/Tool/dvips.py +++ b/src/engine/SCons/Tool/dvips.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/dvips.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/dvips.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Builder diff --git a/src/engine/SCons/Tool/f03.py b/src/engine/SCons/Tool/f03.py index 64b67bf..541ec88 100644 --- a/src/engine/SCons/Tool/f03.py +++ b/src/engine/SCons/Tool/f03.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/f03.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/f03.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/f08.py b/src/engine/SCons/Tool/f08.py index 3c317f5..5dc49da 100644 --- a/src/engine/SCons/Tool/f08.py +++ b/src/engine/SCons/Tool/f08.py @@ -33,7 +33,7 @@ from __future__ import absolute_import # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/f08.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/f08.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/f77.py b/src/engine/SCons/Tool/f77.py index 8ff5f5c..6d60edf 100644 --- a/src/engine/SCons/Tool/f77.py +++ b/src/engine/SCons/Tool/f77.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/f77.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/f77.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Scanner.Fortran diff --git a/src/engine/SCons/Tool/f90.py b/src/engine/SCons/Tool/f90.py index 6f80821..e0b5fae 100644 --- a/src/engine/SCons/Tool/f90.py +++ b/src/engine/SCons/Tool/f90.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/f90.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/f90.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Scanner.Fortran diff --git a/src/engine/SCons/Tool/f95.py b/src/engine/SCons/Tool/f95.py index 363245c..9a1afdf 100644 --- a/src/engine/SCons/Tool/f95.py +++ b/src/engine/SCons/Tool/f95.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/f95.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/f95.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/filesystem.py b/src/engine/SCons/Tool/filesystem.py index c3c79ce..a625714 100644 --- a/src/engine/SCons/Tool/filesystem.py +++ b/src/engine/SCons/Tool/filesystem.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/filesystem.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/filesystem.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons from SCons.Tool.install import copyFunc diff --git a/src/engine/SCons/Tool/fortran.py b/src/engine/SCons/Tool/fortran.py index c090b00..1b09880 100644 --- a/src/engine/SCons/Tool/fortran.py +++ b/src/engine/SCons/Tool/fortran.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/fortran.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/fortran.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import re diff --git a/src/engine/SCons/Tool/g++.py b/src/engine/SCons/Tool/g++.py index 7b3bc50..8cf881f 100644 --- a/src/engine/SCons/Tool/g++.py +++ b/src/engine/SCons/Tool/g++.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/g++.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/g++.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" #forward proxy to the preffered cxx version diff --git a/src/engine/SCons/Tool/g77.py b/src/engine/SCons/Tool/g77.py index fac9794..fc0bcba 100644 --- a/src/engine/SCons/Tool/g77.py +++ b/src/engine/SCons/Tool/g77.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/g77.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/g77.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util from SCons.Tool.FortranCommon import add_all_to_env, add_f77_to_env diff --git a/src/engine/SCons/Tool/gas.py b/src/engine/SCons/Tool/gas.py index d6b3886..c104e34 100644 --- a/src/engine/SCons/Tool/gas.py +++ b/src/engine/SCons/Tool/gas.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gas.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gas.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" try: as_module = __import__('as', globals(), locals(), []) diff --git a/src/engine/SCons/Tool/gcc.py b/src/engine/SCons/Tool/gcc.py index 3b4be6d..2532427 100644 --- a/src/engine/SCons/Tool/gcc.py +++ b/src/engine/SCons/Tool/gcc.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gcc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gcc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import cc import os diff --git a/src/engine/SCons/Tool/gdc.py b/src/engine/SCons/Tool/gdc.py index d12a7cf..46a910c 100644 --- a/src/engine/SCons/Tool/gdc.py +++ b/src/engine/SCons/Tool/gdc.py @@ -48,7 +48,7 @@ Lib tool variables: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gdc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gdc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Defaults diff --git a/src/engine/SCons/Tool/gettext_tool.py b/src/engine/SCons/Tool/gettext_tool.py index c33de28..5fc0158 100644 --- a/src/engine/SCons/Tool/gettext_tool.py +++ b/src/engine/SCons/Tool/gettext_tool.py @@ -23,7 +23,7 @@ # 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__ = "src/engine/SCons/Tool/gettext_tool.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gettext_tool.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" ############################################################################# def generate(env,**kw): diff --git a/src/engine/SCons/Tool/gfortran.py b/src/engine/SCons/Tool/gfortran.py index 37a697e..8d02daa 100644 --- a/src/engine/SCons/Tool/gfortran.py +++ b/src/engine/SCons/Tool/gfortran.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gfortran.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gfortran.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py index 7634772..53f5081 100644 --- a/src/engine/SCons/Tool/gnulink.py +++ b/src/engine/SCons/Tool/gnulink.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gnulink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gnulink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util import SCons.Tool diff --git a/src/engine/SCons/Tool/gs.py b/src/engine/SCons/Tool/gs.py index 8ff9065..371b810 100644 --- a/src/engine/SCons/Tool/gs.py +++ b/src/engine/SCons/Tool/gs.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gs.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gs.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Builder diff --git a/src/engine/SCons/Tool/gxx.py b/src/engine/SCons/Tool/gxx.py index 20445ba..d956c9f 100644 --- a/src/engine/SCons/Tool/gxx.py +++ b/src/engine/SCons/Tool/gxx.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/gxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/gxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import re diff --git a/src/engine/SCons/Tool/hpc++.py b/src/engine/SCons/Tool/hpc++.py index 1528066..4ec2fe3 100644 --- a/src/engine/SCons/Tool/hpc++.py +++ b/src/engine/SCons/Tool/hpc++.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/hpc++.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/hpc++.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" #forward proxy to the preffered cxx version diff --git a/src/engine/SCons/Tool/hpcc.py b/src/engine/SCons/Tool/hpcc.py index 2734006..6a564b9 100644 --- a/src/engine/SCons/Tool/hpcc.py +++ b/src/engine/SCons/Tool/hpcc.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/hpcc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/hpcc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/hpcxx.py b/src/engine/SCons/Tool/hpcxx.py index 196777a..8ee888f 100644 --- a/src/engine/SCons/Tool/hpcxx.py +++ b/src/engine/SCons/Tool/hpcxx.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/hpcxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/hpcxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path @@ -68,7 +68,8 @@ def generate(env): env['CXX'] = acc or 'aCC' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS +Z') # determine version of aCC - line = os.popen(acc + ' -V 2>&1').readline().rstrip() + with os.popen(acc + ' -V 2>&1') as p: + line = p.readline().rstrip() if line.find('aCC: HP ANSI C++') == 0: env['CXXVERSION'] = line.split()[-1] diff --git a/src/engine/SCons/Tool/hplink.py b/src/engine/SCons/Tool/hplink.py index 9f38a3b..1afa95e 100644 --- a/src/engine/SCons/Tool/hplink.py +++ b/src/engine/SCons/Tool/hplink.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/hplink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/hplink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path diff --git a/src/engine/SCons/Tool/icc.py b/src/engine/SCons/Tool/icc.py index 1923c60..8110a60 100644 --- a/src/engine/SCons/Tool/icc.py +++ b/src/engine/SCons/Tool/icc.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/icc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/icc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import cc diff --git a/src/engine/SCons/Tool/icl.py b/src/engine/SCons/Tool/icl.py index 26490f0..29d5862 100644 --- a/src/engine/SCons/Tool/icl.py +++ b/src/engine/SCons/Tool/icl.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/icl.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/icl.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Tool.intelc diff --git a/src/engine/SCons/Tool/ifl.py b/src/engine/SCons/Tool/ifl.py index e438c44..0eb7cda 100644 --- a/src/engine/SCons/Tool/ifl.py +++ b/src/engine/SCons/Tool/ifl.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ifl.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ifl.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults from SCons.Scanner.Fortran import FortranScan diff --git a/src/engine/SCons/Tool/ifort.py b/src/engine/SCons/Tool/ifort.py index d69bd14..730e778 100644 --- a/src/engine/SCons/Tool/ifort.py +++ b/src/engine/SCons/Tool/ifort.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ifort.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ifort.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults from SCons.Scanner.Fortran import FortranScan diff --git a/src/engine/SCons/Tool/ilink.py b/src/engine/SCons/Tool/ilink.py index 6c3a072..7c69e72 100644 --- a/src/engine/SCons/Tool/ilink.py +++ b/src/engine/SCons/Tool/ilink.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ilink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ilink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/ilink32.py b/src/engine/SCons/Tool/ilink32.py index 7c7e983..a1e12a6 100644 --- a/src/engine/SCons/Tool/ilink32.py +++ b/src/engine/SCons/Tool/ilink32.py @@ -27,7 +27,7 @@ XXX # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ilink32.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ilink32.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Tool import SCons.Tool.bcc32 diff --git a/src/engine/SCons/Tool/install.py b/src/engine/SCons/Tool/install.py index 6b5a741..c0a8917 100644 --- a/src/engine/SCons/Tool/install.py +++ b/src/engine/SCons/Tool/install.py @@ -31,7 +31,7 @@ selection method. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/install.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/install.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import re diff --git a/src/engine/SCons/Tool/intelc.py b/src/engine/SCons/Tool/intelc.py index 3429145..fdd9730 100644 --- a/src/engine/SCons/Tool/intelc.py +++ b/src/engine/SCons/Tool/intelc.py @@ -32,12 +32,12 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import division, print_function -__revision__ = "src/engine/SCons/Tool/intelc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/intelc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import math, sys, os.path, glob, string, re is_windows = sys.platform == 'win32' -is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or +is_win64 = is_windows and (os.environ['PROCESSOR_ARCHITECTURE'] == 'AMD64' or ('PROCESSOR_ARCHITEW6432' in os.environ and os.environ['PROCESSOR_ARCHITEW6432'] == 'AMD64')) is_linux = sys.platform.startswith('linux') @@ -113,11 +113,6 @@ def check_abi(abi): (abi, list(valid_abis.keys()))) return abi -def vercmp(a, b): - """Compare strings as floats, - but Intel changed Linux naming convention at 9.0""" - return cmp(linux_ver_normalize(b), linux_ver_normalize(a)) - def get_version_from_list(v, vlist): """See if we can match v (string) in vlist (list of strings) Linux has to match in a fuzzy way.""" @@ -221,7 +216,7 @@ def get_all_compiler_versions(): versions = [] try: while i < 100: - subkey = SCons.Util.RegEnumKey(k, i) # raises EnvironmentError + subkey = SCons.Util.RegEnumKey(k, i) # raises SConsEnvironmentError # Check that this refers to an existing dir. # This is not 100% perfect but should catch common # installation issues like when the compiler was installed @@ -293,7 +288,7 @@ def get_all_compiler_versions(): m = re.search(r'([0-9]{0,4})(?:_sp\d*)?\.([0-9][0-9.]*)$', d) if m: versions.append("%s.%s"%(m.group(1), m.group(2))) - + def keyfunc(str): """Given a dot-separated version string, return a tuple of ints representing it.""" return [int(x) for x in str.split('.')] @@ -383,7 +378,7 @@ def get_intel_compiler_top(version, abi): top = d break return top - + top = find_in_2016style_dir(version) or find_in_2011style_dir(version) or find_in_2010style_dir(version) or find_in_2008style_dir(version) # print "INTELC: top=",top if not top: diff --git a/src/engine/SCons/Tool/ipkg.py b/src/engine/SCons/Tool/ipkg.py index 5685abb..664c3a6 100644 --- a/src/engine/SCons/Tool/ipkg.py +++ b/src/engine/SCons/Tool/ipkg.py @@ -33,7 +33,7 @@ packages fake_root. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ipkg.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ipkg.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os @@ -55,8 +55,10 @@ def generate(env): env['IPKGCOM'] = '$IPKG $IPKGFLAGS ${SOURCE}' if env.WhereIs('id'): - env['IPKGUSER'] = os.popen('id -un').read().strip() - env['IPKGGROUP'] = os.popen('id -gn').read().strip() + with os.popen('id -un') as p: + env['IPKGUSER'] = p.read().strip() + with os.popen('id -gn') as p: + env['IPKGGROUP'] = p.read().strip() env['IPKGFLAGS'] = SCons.Util.CLVar('-o $IPKGUSER -g $IPKGGROUP') env['IPKGSUFFIX'] = '.ipk' diff --git a/src/engine/SCons/Tool/jar.py b/src/engine/SCons/Tool/jar.py index 5858b2c..98be08f 100644 --- a/src/engine/SCons/Tool/jar.py +++ b/src/engine/SCons/Tool/jar.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/jar.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/jar.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import SCons.Subst @@ -86,7 +86,7 @@ def jarFlags(target, source, env, for_signature): for src in source: contents = src.get_text_contents() if contents.startswith("Manifest-Version"): - if not 'm' in jarflags: + if 'm' not in jarflags: return jarflags + 'm' break return jarflags @@ -106,7 +106,7 @@ def Jar(env, target = None, source = [], *args, **kw): source = target target = None - # mutiple targets pass so build each target the same from the + # mutiple targets pass so build each target the same from the # same source #TODO Maybe this should only be done once, and the result copied # for each target since it should result in the same? @@ -184,7 +184,7 @@ def Jar(env, target = None, source = [], *args, **kw): continue except: pass - + try: # source is string try to covnert it to dir target_nodes.extend(dir_to_class(env.fs.Dir(s))) @@ -193,7 +193,7 @@ def Jar(env, target = None, source = [], *args, **kw): pass SCons.Warnings.Warning("File: " + str(s) + " could not be identified as File or Directory, skipping.") - + # at this point all our sources have been converted to classes or directories of class # so pass it to the Jar builder return env.JarFile(target = target, source = target_nodes, *args, **kw) @@ -209,9 +209,9 @@ def generate(env): env.AddMethod(Jar) if env['PLATFORM'] == 'win32': - # Ensure that we have a proper path for clang - jar = SCons.Tool.find_program_path(env, 'jar', - default_paths=get_java_install_dirs(env['PLATFORM'])) + # Ensure that we have a proper path for jar + paths = get_java_install_dirs('win32') + jar = SCons.Tool.find_program_path(env, 'jar', default_paths=paths) if jar: jar_bin_dir = os.path.dirname(jar) env.AppendENVPath('PATH', jar_bin_dir) diff --git a/src/engine/SCons/Tool/javac.py b/src/engine/SCons/Tool/javac.py index b5e4aaa..387afdb 100644 --- a/src/engine/SCons/Tool/javac.py +++ b/src/engine/SCons/Tool/javac.py @@ -30,7 +30,7 @@ selection method. # 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__ = "src/engine/SCons/Tool/javac.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/javac.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path @@ -210,15 +210,15 @@ def generate(env): version = env.get('JAVAVERSION', None) - javac = SCons.Tool.find_program_path(env, 'javac') if env['PLATFORM'] == 'win32': # Ensure that we have a proper path for javac - paths=get_java_install_dirs(env['PLATFORM'], version=version) - javac = SCons.Tool.find_program_path(env, 'javac', - default_paths=paths) + paths = get_java_install_dirs('win32', version=version) + javac = SCons.Tool.find_program_path(env, 'javac', default_paths=paths) if javac: javac_bin_dir = os.path.dirname(javac) env.AppendENVPath('PATH', javac_bin_dir) + else: + javac = SCons.Tool.find_program_path(env, 'javac') env['JAVAINCLUDES'] = get_java_include_paths(env, javac, version) diff --git a/src/engine/SCons/Tool/javah.py b/src/engine/SCons/Tool/javah.py index 84183cb..251778d 100644 --- a/src/engine/SCons/Tool/javah.py +++ b/src/engine/SCons/Tool/javah.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/javah.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/javah.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path @@ -123,9 +123,9 @@ def generate(env): java_javah.emitter = emit_java_headers if env['PLATFORM'] == 'win32': - # Ensure that we have a proper path for clang - javah = SCons.Tool.find_program_path(env, 'javah', - default_paths=get_java_install_dirs(env['PLATFORM'])) + # Ensure that we have a proper path for javah + paths = get_java_install_dirs('win32') + javah = SCons.Tool.find_program_path(env, 'javah', default_paths=paths) if javah: javah_bin_dir = os.path.dirname(javah) env.AppendENVPath('PATH', javah_bin_dir) diff --git a/src/engine/SCons/Tool/latex.py b/src/engine/SCons/Tool/latex.py index 9626693..3f1af08 100644 --- a/src/engine/SCons/Tool/latex.py +++ b/src/engine/SCons/Tool/latex.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/latex.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/latex.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Defaults diff --git a/src/engine/SCons/Tool/ldc.py b/src/engine/SCons/Tool/ldc.py index fe3df1f..dda1a5e 100644 --- a/src/engine/SCons/Tool/ldc.py +++ b/src/engine/SCons/Tool/ldc.py @@ -48,7 +48,7 @@ Lib tool variables: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/ldc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/ldc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import subprocess diff --git a/src/engine/SCons/Tool/lex.py b/src/engine/SCons/Tool/lex.py index affb52c..4831776 100644 --- a/src/engine/SCons/Tool/lex.py +++ b/src/engine/SCons/Tool/lex.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/lex.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/lex.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys @@ -45,6 +45,11 @@ from SCons.Platform.win32 import CHOCO_DEFAULT_PATH LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR") +if sys.platform == 'win32': + BINS = ['flex', 'lex', 'win_flex'] +else: + BINS = ["flex", "lex"] + def lexEmitter(target, source, env): sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0])) @@ -70,22 +75,22 @@ def lexEmitter(target, source, env): def get_lex_path(env, append_paths=False): """ - Find the a path containing the lex or flex binaries. If a construction - environment is passed in then append the path to the ENV PATH. - """ - # save existing path to reset if we don't want to append any paths - envPath = env['ENV']['PATH'] - bins = ['flex', 'lex', 'win_flex'] + Find the path to the lex tool, searching several possible names - for prog in bins: + Only called in the Windows case, so the default_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 + """ + for prog in BINS: bin_path = SCons.Tool.find_program_path( - env, - prog, + env, + prog, default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) if bin_path: - if not append_paths: - env['ENV']['PATH'] = envPath - else: + if append_paths: env.AppendENVPath('PATH', os.path.dirname(bin_path)) return bin_path SCons.Warnings.Warning('lex tool requested, but lex or flex binary not found in ENV PATH') @@ -113,20 +118,21 @@ def generate(env): env["LEXFLAGS"] = SCons.Util.CLVar("") if sys.platform == 'win32': - get_lex_path(env, append_paths=True) - env["LEX"] = env.Detect(['flex', 'lex', 'win_flex']) + # ignore the return - we do not need the full path here + _ = 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" else: - env["LEX"] = env.Detect(["flex", "lex"]) + env["LEX"] = env.Detect(BINS) env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET" def exists(env): if sys.platform == 'win32': return get_lex_path(env) else: - return env.Detect(["flex", "lex"]) + return env.Detect(BINS) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index 65fe013..dcaf91f 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -32,7 +32,7 @@ selection method. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/link.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/link.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import re diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py index cdcf25a..42d010d 100644 --- a/src/engine/SCons/Tool/linkloc.py +++ b/src/engine/SCons/Tool/linkloc.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/linkloc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/linkloc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import re diff --git a/src/engine/SCons/Tool/m4.py b/src/engine/SCons/Tool/m4.py index 30dc4c9..0ecc82a 100644 --- a/src/engine/SCons/Tool/m4.py +++ b/src/engine/SCons/Tool/m4.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/m4.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/m4.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Builder diff --git a/src/engine/SCons/Tool/masm.py b/src/engine/SCons/Tool/masm.py index 77e32c2..cf147a7 100644 --- a/src/engine/SCons/Tool/masm.py +++ b/src/engine/SCons/Tool/masm.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/masm.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/masm.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/midl.py b/src/engine/SCons/Tool/midl.py index 6879da1..f75204a 100644 --- a/src/engine/SCons/Tool/midl.py +++ b/src/engine/SCons/Tool/midl.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/midl.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/midl.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Builder diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py index ed3f1a3..8410d2f 100644 --- a/src/engine/SCons/Tool/mingw.py +++ b/src/engine/SCons/Tool/mingw.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/mingw.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/mingw.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path @@ -65,7 +65,7 @@ def shlib_generator(target, source, env, for_signature): def_target = env.FindIxes(target, 'WINDOWSDEFPREFIX', 'WINDOWSDEFSUFFIX') insert_def = env.subst("$WINDOWS_INSERT_DEF") - if not insert_def in ['', '0', 0] and def_target: \ + if insert_def not in ['', '0', 0] and def_target: \ cmd.append('-Wl,--output-def,' + def_target.get_string(for_signature)) return [cmd] @@ -123,7 +123,7 @@ key_program = 'mingw32-make' def find_version_specific_mingw_paths(): - """ + r""" One example of default mingw install paths is: C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev2\mingw64\bin diff --git a/src/engine/SCons/Tool/msgfmt.py b/src/engine/SCons/Tool/msgfmt.py index 87589ce..c8141a0 100644 --- a/src/engine/SCons/Tool/msgfmt.py +++ b/src/engine/SCons/Tool/msgfmt.py @@ -21,7 +21,7 @@ # 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__ = "src/engine/SCons/Tool/msgfmt.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/msgfmt.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Builder import BuilderBase ############################################################################# diff --git a/src/engine/SCons/Tool/msginit.py b/src/engine/SCons/Tool/msginit.py index e52c1b5..69e803a 100644 --- a/src/engine/SCons/Tool/msginit.py +++ b/src/engine/SCons/Tool/msginit.py @@ -24,7 +24,7 @@ Tool specific initialization of msginit tool. # 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__ = "src/engine/SCons/Tool/msginit.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/msginit.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Warnings import SCons.Builder diff --git a/src/engine/SCons/Tool/msgmerge.py b/src/engine/SCons/Tool/msgmerge.py index 849fe32..4254fa6 100644 --- a/src/engine/SCons/Tool/msgmerge.py +++ b/src/engine/SCons/Tool/msgmerge.py @@ -24,7 +24,7 @@ Tool specific initialization for `msgmerge` tool. # 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__ = "src/engine/SCons/Tool/msgmerge.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/msgmerge.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" ############################################################################# def _update_or_init_po_files(target, source, env): diff --git a/src/engine/SCons/Tool/mslib.py b/src/engine/SCons/Tool/mslib.py index 00a17d7..cecdbdc 100644 --- a/src/engine/SCons/Tool/mslib.py +++ b/src/engine/SCons/Tool/mslib.py @@ -31,7 +31,9 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/mslib.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/mslib.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" + +import os import SCons.Defaults import SCons.Tool @@ -54,6 +56,13 @@ def generate(env): env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' + # Issue #3350 + # Change tempfile argument joining character from a space to a newline + # mslink will fail if any single line is too long, but is fine with many lines + # in a tempfile + env['TEMPFILEARGJOIN'] = os.linesep + + def exists(env): return msvc_exists(env) diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 9df531b..9fbce4b 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -32,8 +32,9 @@ selection method. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/mslink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/mslink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" +import os import os.path import SCons.Action @@ -107,7 +108,7 @@ def _dllEmitter(target, source, env, paramtp): raise SCons.Errors.UserError('A shared library should have exactly one target with the suffix: %s' % env.subst('$%sSUFFIX' % paramtp)) insert_def = env.subst("$WINDOWS_INSERT_DEF") - if not insert_def in ['', '0', 0] and \ + if insert_def not in ['', '0', 0] and \ not env.FindIxes(source, "WINDOWSDEFPREFIX", "WINDOWSDEFSUFFIX"): # append a def file to the list of sources @@ -158,7 +159,7 @@ def windowsLibEmitter(target, source, env): def ldmodEmitter(target, source, env): """Emitter for loadable modules. - + Loadable modules are identical to shared libraries on Windows, but building them is subject to different parameters (LDMODULE*). """ @@ -219,7 +220,7 @@ def embedManifestDllCheck(target, source, env): if env.get('WINDOWS_EMBED_MANIFEST', 0): manifestSrc = target[0].get_abspath() + '.manifest' if os.path.exists(manifestSrc): - ret = (embedManifestDllAction) ([target[0]],None,env) + ret = (embedManifestDllAction) ([target[0]],None,env) if ret: raise SCons.Errors.UserError("Unable to embed manifest into %s" % (target[0])) return ret @@ -327,6 +328,12 @@ def generate(env): env['LDMODULEEMITTER'] = [ldmodEmitter] env['LDMODULECOM'] = compositeLdmodAction + # Issue #3350 + # Change tempfile argument joining character from a space to a newline + # mslink will fail if any single line is too long, but is fine with many lines + # in a tempfile + env['TEMPFILEARGJOIN'] = os.linesep + def exists(env): return msvc_exists(env) diff --git a/src/engine/SCons/Tool/mssdk.py b/src/engine/SCons/Tool/mssdk.py index d66c7fc..7a38c6a 100644 --- a/src/engine/SCons/Tool/mssdk.py +++ b/src/engine/SCons/Tool/mssdk.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/mssdk.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/mssdk.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" """engine.SCons.Tool.mssdk diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index a611f8e..052a0b0 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -31,9 +31,10 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/msvc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/msvc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path +import os import re import sys @@ -161,7 +162,7 @@ def msvc_batch_key(action, env, target, source): # Note we need to do the env.subst so $MSVC_BATCH can be a reference to # another construction variable, which is why we test for False and 0 # as strings. - if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): + if 'MSVC_BATCH' not in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): # We're not using batching; return no key. return None t = target[0] @@ -187,7 +188,7 @@ def msvc_output_flag(target, source, env, for_signature): # len(source)==1 as batch mode can compile only one file # (and it also fixed problem with compiling only one changed file # with batch mode enabled) - if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): + if 'MSVC_BATCH' not in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None): return '/Fo$TARGET' else: # The Visual C/C++ compiler requires a \ at the end of the /Fo @@ -283,6 +284,12 @@ def generate(env): msvc_set_PCHPDBFLAGS(env) + # Issue #3350 + # Change tempfile argument joining character from a space to a newline + # mslink will fail if any single line is too long, but is fine with many lines + # in a tempfile + env['TEMPFILEARGJOIN'] = os.linesep + env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' env['BUILDERS']['PCH'] = pch_builder diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml index 03d8ce3..b8b4f6d 100644 --- a/src/engine/SCons/Tool/msvc.xml +++ b/src/engine/SCons/Tool/msvc.xml @@ -353,6 +353,7 @@ constructor; setting it later has no effect. Valid values for Windows are +14.2, 14.1, 14.0, 14.0Exp, diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index ddf8608..38965e3 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -32,7 +32,7 @@ selection method. from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/msvs.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/msvs.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -70,10 +70,14 @@ def xmlify(s): s = s.replace('\n', ' ') return s -# Process a CPPPATH list in includes, given the env, target and source. -# Returns a tuple of nodes. def processIncludes(includes, env, target, source): - return SCons.PathList.PathList(includes).subst_path(env, target, source) + """ + Process a CPPPATH list in includes, given the env, target and source. + Returns a list of directory paths. These paths are absolute so we avoid + putting pound-prefixed paths in a Visual Studio project file. + """ + return [env.Dir(i).abspath for i in + SCons.PathList.PathList(includes).subst_path(env, target, source)] external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' @@ -348,10 +352,20 @@ V10DebugSettings = { } class _GenerateV10User(_UserGenerator): - """Generates a Project'user file for MSVS 2010""" + """Generates a Project'user file for MSVS 2010 or later""" def __init__(self, dspfile, source, env): - self.versionstr = '4.0' + version_num, suite = msvs_parse_version(env['MSVS_VERSION']) + if version_num >= 14.2: + # Visual Studio 2019 is considered to be version 16. + self.versionstr = '16.0' + elif version_num >= 14.1: + # Visual Studio 2017 is considered to be version 15. + self.versionstr = '15.0' + elif version_num == 14.0: + self.versionstr = '14.0' + else: + self.versionstr = '4.0' self.usrhead = V10UserHeader self.usrconf = V10UserConfiguration self.usrdebg = V10DebugSettings @@ -397,7 +411,7 @@ class _DSPGenerator(object): elif SCons.Util.is_List(env['variant']): variants = env['variant'] - if 'buildtarget' not in env or env['buildtarget'] == None: + if 'buildtarget' not in env or env['buildtarget'] is None: buildtarget = [''] elif SCons.Util.is_String(env['buildtarget']): buildtarget = [env['buildtarget']] @@ -418,7 +432,7 @@ class _DSPGenerator(object): for _ in variants: buildtarget.append(bt) - if 'outdir' not in env or env['outdir'] == None: + if 'outdir' not in env or env['outdir'] is None: outdir = [''] elif SCons.Util.is_String(env['outdir']): outdir = [env['outdir']] @@ -439,7 +453,7 @@ class _DSPGenerator(object): for v in variants: outdir.append(s) - if 'runfile' not in env or env['runfile'] == None: + if 'runfile' not in env or env['runfile'] is None: runfile = buildtarget[-1:] elif SCons.Util.is_String(env['runfile']): runfile = [env['runfile']] @@ -462,15 +476,41 @@ class _DSPGenerator(object): self.sconscript = env['MSVSSCONSCRIPT'] - if 'cmdargs' not in env or env['cmdargs'] == None: - cmdargs = [''] * len(variants) - elif SCons.Util.is_String(env['cmdargs']): - cmdargs = [env['cmdargs']] * len(variants) - elif SCons.Util.is_List(env['cmdargs']): - if len(env['cmdargs']) != len(variants): - raise SCons.Errors.InternalError("Sizes of 'cmdargs' and 'variant' lists must be the same.") + def GetKeyFromEnv(env, key, variants): + """ + Retrieves a specific key from the environment. If the key is + present, it is expected to either be a string or a list with length + equal to the number of variants. The function returns a list of + the desired value (e.g. cpp include paths) guaranteed to be of + length equal to the length of the variants list. + """ + if key not in env or env[key] is None: + return [''] * len(variants) + elif SCons.Util.is_String(env[key]): + return [env[key]] * len(variants) + elif SCons.Util.is_List(env[key]): + if len(env[key]) != len(variants): + raise SCons.Errors.InternalError("Sizes of '%s' and 'variant' lists must be the same." % key) + else: + return env[key] else: - cmdargs = env['cmdargs'] + raise SCons.Errors.InternalError("Unsupported type for key '%s' in environment: %s" % + (key, type(env[key]))) + + cmdargs = GetKeyFromEnv(env, 'cmdargs', variants) + + # The caller is allowed to put 'cppdefines' and/or 'cpppaths' in the + # environment, which is useful if they want to provide per-variant + # values for these. Otherwise, we fall back to using the global + # 'CPPDEFINES' and 'CPPPATH' functions. + if 'cppdefines' in env: + cppdefines = GetKeyFromEnv(env, 'cppdefines', variants) + else: + cppdefines = [env.get('CPPDEFINES', [])] * len(variants) + if 'cpppaths' in env: + cpppaths = GetKeyFromEnv(env, 'cpppaths', variants) + else: + cpppaths = [env.get('CPPPATH', [])] * len(variants) self.env = env @@ -513,13 +553,17 @@ class _DSPGenerator(object): for n in sourcenames: self.sources[n].sort(key=lambda a: a.lower()) - def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): + def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, cppdefines, cpppaths, dspfile=dspfile, env=env): config = Config() config.buildtarget = buildtarget config.outdir = outdir config.cmdargs = cmdargs + config.cppdefines = cppdefines config.runfile = runfile + # Dir objects can't be pickled, so we need an absolute path here. + config.cpppaths = processIncludes(cpppaths, env, None, None) + match = re.match(r'(.*)\|(.*)', variant) if match: config.variant = match.group(1) @@ -532,12 +576,12 @@ class _DSPGenerator(object): print("Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'") for i in range(len(variants)): - AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs[i]) + AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs[i], cppdefines[i], cpppaths[i]) self.platforms = [] for key in list(self.configs.keys()): platform = self.configs[key].platform - if not platform in self.platforms: + if platform not in self.platforms: self.platforms.append(platform) def Build(self): @@ -553,16 +597,16 @@ V6DSPHeader = """\ CFG=%(name)s - Win32 %(confkey)s !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "%(name)s.mak". -!MESSAGE +!MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" -!MESSAGE +!MESSAGE !MESSAGE Possible choices for configuration are: -!MESSAGE +!MESSAGE """ class _GenerateV6DSP(_DSPGenerator): @@ -580,7 +624,7 @@ class _GenerateV6DSP(_DSPGenerator): for kind in confkeys: self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) - self.file.write('!MESSAGE \n\n') + self.file.write('!MESSAGE\n\n') def PrintProject(self): name = self.name @@ -637,7 +681,7 @@ class _GenerateV6DSP(_DSPGenerator): first = 1 else: self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) - self.file.write('!ENDIF \n\n') + self.file.write('!ENDIF\n\n') self.PrintSourceFiles() self.file.write('# End Target\n' '# End Project\n') @@ -645,10 +689,10 @@ class _GenerateV6DSP(_DSPGenerator): if self.nokeep == 0: # now we pickle some data and add it to the file -- MSDEV will ignore it. pdata = pickle.dumps(self.configs,PICKLE_PROTOCOL) - pdata = base64.encodestring(pdata).decode() + pdata = base64.b64encode(pdata).decode() self.file.write(pdata + '\n') pdata = pickle.dumps(self.sources,PICKLE_PROTOCOL) - pdata = base64.encodestring(pdata).decode() + pdata = base64.b64encode(pdata).decode() self.file.write(pdata + '\n') def PrintSourceFiles(self): @@ -685,11 +729,13 @@ class _GenerateV6DSP(_DSPGenerator): return # doesn't exist yet, so can't add anything to configs. line = dspfile.readline() + # skip until marker while line: if line.find("# End Project") > -1: break line = dspfile.readline() + # read to get configs line = dspfile.readline() datas = line while line and line != '\n': @@ -707,12 +753,14 @@ class _GenerateV6DSP(_DSPGenerator): self.configs.update(data) + # keep reading to get sources data = None line = dspfile.readline() datas = line while line and line != '\n': line = dspfile.readline() datas = datas + line + dspfile.close() # OK, we've found our little pickled cache of data. # it has a "# " in front of it, so we strip that. @@ -878,6 +926,8 @@ class _GenerateV7DSP(_DSPGenerator, _GenerateV7User): buildtarget = self.configs[kind].buildtarget runfile = self.configs[kind].runfile cmdargs = self.configs[kind].cmdargs + cpppaths = self.configs[kind].cpppaths + cppdefines = self.configs[kind].cppdefines env_has_buildtarget = 'MSVSBUILDTARGET' in self.env if not env_has_buildtarget: @@ -895,9 +945,8 @@ class _GenerateV7DSP(_DSPGenerator, _GenerateV7User): # This isn't perfect; CPPDEFINES and CPPPATH can contain $TARGET and $SOURCE, # so they could vary depending on the command being generated. This code # assumes they don't. - preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', [])))) - includepath_Dirs = processIncludes(self.env.get('CPPPATH', []), self.env, None, None) - includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) + preprocdefs = xmlify(';'.join(processDefines(cppdefines))) + includepath = xmlify(';'.join(processIncludes(cpppaths, self.env, None, None))) if not env_has_buildtarget: del self.env['MSVSBUILDTARGET'] @@ -917,10 +966,10 @@ class _GenerateV7DSP(_DSPGenerator, _GenerateV7User): if self.nokeep == 0: # now we pickle some data and add it to the file -- MSDEV will ignore it. pdata = pickle.dumps(self.configs,PICKLE_PROTOCOL) - pdata = base64.encodestring(pdata).decode() + pdata = base64.b64encode(pdata).decode() self.file.write('\n') def printSources(self, hierarchy, commonprefix): @@ -998,11 +1047,13 @@ class _GenerateV7DSP(_DSPGenerator, _GenerateV7User): return # doesn't exist yet, so can't add anything to configs. line = dspfile.readline() + # skip until marker while line: if line.find('\n') def printFilters(self, hierarchy, name): @@ -1451,13 +1508,16 @@ class _GenerateV7DSW(_DSWGenerator): self.platforms = [] for key in list(self.configs.keys()): platform = self.configs[key].platform - if not platform in self.platforms: + if platform not in self.platforms: self.platforms.append(platform) def GenerateProjectFilesInfo(self): for dspfile in self.dspfiles: dsp_folder_path, name = os.path.split(dspfile) dsp_folder_path = os.path.abspath(dsp_folder_path) + if SCons.Util.splitext(name)[1] == '.filters': + # Ignore .filters project files + continue dsp_relative_folder_path = os.path.relpath(dsp_folder_path, self.dsw_folder_path) if dsp_relative_folder_path == os.curdir: dsp_relative_file_path = name @@ -1491,6 +1551,7 @@ class _GenerateV7DSW(_DSWGenerator): while line: line = dswfile.readline() datas = datas + line + dswfile.close() # OK, we've found our little pickled cache of data. try: @@ -1506,7 +1567,11 @@ class _GenerateV7DSW(_DSWGenerator): def PrintSolution(self): """Writes a solution file""" self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr) - if self.version_num > 14.0: + if self.version_num >= 14.2: + # Visual Studio 2019 is considered to be version 16. + self.file.write('# Visual Studio 16\n') + elif self.version_num > 14.0: + # Visual Studio 2015 and 2017 are both considered to be version 15. self.file.write('# Visual Studio 15\n') elif self.version_num >= 12.0: self.file.write('# Visual Studio 14\n') @@ -1617,7 +1682,7 @@ class _GenerateV7DSW(_DSWGenerator): self.file.write('EndGlobal\n') if self.nokeep == 0: pdata = pickle.dumps(self.configs,PICKLE_PROTOCOL) - pdata = base64.encodestring(pdata).decode() + pdata = base64.b64encode(pdata).decode() self.file.write(pdata) self.file.write('\n') @@ -1686,11 +1751,8 @@ def GenerateDSP(dspfile, source, env): version_num = 6.0 if 'MSVS_VERSION' in env: version_num, suite = msvs_parse_version(env['MSVS_VERSION']) - if version_num > 14.0: - g = _GenerateV10DSP(dspfile, V15DSPHeader, source, env) - g.Build() - elif version_num >= 10.0: - g = _GenerateV10DSP(dspfile, V10DSPHeader, source, env) + if version_num >= 10.0: + g = _GenerateV10DSP(dspfile, source, env) g.Build() elif version_num >= 7.0: g = _GenerateV7DSP(dspfile, source, env) @@ -1730,7 +1792,7 @@ def GenerateProject(target, source, env): dspfile = builddspfile.srcnode() # this detects whether or not we're using a VariantDir - if not dspfile is builddspfile: + if dspfile is not builddspfile: try: bdsp = open(str(builddspfile), "w+") except IOError as detail: @@ -1738,6 +1800,7 @@ def GenerateProject(target, source, env): raise bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) + bdsp.close() GenerateDSP(dspfile, source, env) @@ -1745,7 +1808,7 @@ def GenerateProject(target, source, env): builddswfile = target[1] dswfile = builddswfile.srcnode() - if not dswfile is builddswfile: + if dswfile is not builddswfile: try: bdsw = open(str(builddswfile), "w+") @@ -1754,6 +1817,7 @@ def GenerateProject(target, source, env): raise bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) + bdsw.close() GenerateDSW(dswfile, source, env) @@ -1781,11 +1845,10 @@ def projectEmitter(target, source, env): # Project file depends on CPPDEFINES and CPPPATH preprocdefs = xmlify(';'.join(processDefines(env.get('CPPDEFINES', [])))) - includepath_Dirs = processIncludes(env.get('CPPPATH', []), env, None, None) - includepath = xmlify(';'.join([str(x) for x in includepath_Dirs])) + includepath = xmlify(';'.join(processIncludes(env.get('CPPPATH', []), env, None, None))) source = source + "; ppdefs:%s incpath:%s"%(preprocdefs, includepath) - if 'buildtarget' in env and env['buildtarget'] != None: + if 'buildtarget' in env and env['buildtarget'] is not None: if SCons.Util.is_String(env['buildtarget']): source = source + ' "%s"' % env['buildtarget'] elif SCons.Util.is_List(env['buildtarget']): @@ -1799,7 +1862,7 @@ def projectEmitter(target, source, env): try: source = source + ' "%s"' % env['buildtarget'].get_abspath() except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None") - if 'outdir' in env and env['outdir'] != None: + if 'outdir' in env and env['outdir'] is not None: if SCons.Util.is_String(env['outdir']): source = source + ' "%s"' % env['outdir'] elif SCons.Util.is_List(env['outdir']): @@ -1962,8 +2025,14 @@ def generate(env): default_MSVS_SConscript = env.File('SConstruct') env['MSVSSCONSCRIPT'] = default_MSVS_SConscript - env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) - env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.get_abspath()}" -f ${MSVSSCONSCRIPT.name}' + # Allow consumers to provide their own versions of MSVSSCONS and + # MSVSSCONSFLAGS. This helps support consumers who use wrapper scripts to + # invoke scons. + if 'MSVSSCONS' not in env: + env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env)) + if 'MSVSSCONSFLAGS' not in env: + env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.get_abspath()}" -f ${MSVSSCONSCRIPT.name}' + env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS' env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"' diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml index 7943f22..9f2353a 100644 --- a/src/engine/SCons/Tool/msvs.xml +++ b/src/engine/SCons/Tool/msvs.xml @@ -48,14 +48,16 @@ See its __doc__ string for a discussion of the format. latest installed version, or the version specified by &cv-link-MSVS_VERSION; in the Environment constructor). For Visual Studio 6, it will generate a .dsp - file. For Visual Studio 7 (.NET) and later versions, it will - generate a .vcproj file. + file. For Visual Studio 7, 8, and 9, it will + generate a .vcproj file. For Visual + Studio 10 and later, it will generate a + .vcxproj file. By default, this also generates a solution file for the specified project, a .dsw file for Visual Studio 6 or a .sln file for - Visual Studio 7 (.NET). This behavior may be disabled by + Visual Studio 7 and later. This behavior may be disabled by specifying auto_build_solution=0 when you call &b-MSVSProject;, in which case you presumably want to build the solution file(s) by calling the &b-MSVSSolution; @@ -127,6 +129,36 @@ See its __doc__ string for a discussion of the format. + + cppdefines + + + Preprocessor definitions for the different variants. + The number of cppdefines entries + must match the number of variant + 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 + CPPDEFINES entry for all variants. + + + + + cpppaths + + + Compiler include paths for the different variants. + The number of cpppaths entries + must match the number of variant + 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 + CPPPATH entry for all variants. + + + buildtarget diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py index fd5e343..e0b5ced 100644 --- a/src/engine/SCons/Tool/msvsTests.py +++ b/src/engine/SCons/Tool/msvsTests.py @@ -22,7 +22,7 @@ # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/msvsTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/msvsTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys @@ -34,6 +34,7 @@ import TestUnit from SCons.Tool.msvs import * from SCons.Tool.MSCommon.vs import SupportedVSList +import SCons.Node.FS import SCons.Util import SCons.Warnings @@ -352,6 +353,36 @@ regdata_80 = r''' "VCXDCMakeTool"="*.xdc" '''.split('\n') +regdata_140 = r''' +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS] +"MSMDir"="C:\\Program Files (x86)\\Common Files\\Merge Modules\\" +"ProductDir"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\" +"VS7EnvironmentLocation"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\IDE\\devenv.exe" +"EnvironmentPath"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\IDE\\devenv.exe" +"EnvironmentDirectory"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\IDE\\" +"VS7CommonDir"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\" +"VS7CommonBinDir"="" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\BuildNumber] +"1033"="14.0" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\Community] +"ProductDir"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\JSLS_MSI] +"Version"="14.0.25527" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\JSPS_MSI] +"Version"="14.0.25527" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\Pro] +"ProductDir"="C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\professional] +"IsInstallInProgress"="0" +"CurrentOperation"="install" +"SetupFeedUri"="http://go.microsoft.com/fwlink/?LinkID=659004&clcid=0x409" +"SetupFeedLocalCache"="C:\\ProgramData\\Microsoft\\VisualStudioSecondaryInstaller\\14.0\\LastUsedFeed\\{68432bbb-c9a5-4a7b-bab3-ae5a49b28303}\\Feed.xml" +"InstallResult"="0" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\SecondaryInstaller] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\14.0\Setup\VS\SecondaryInstaller\AppInsightsTools] +"Version"="7.0.20620.1" +'''.split('\n') + regdata_cv = r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] "ProgramFilesDir"="C:\Program Files" "CommonFilesDir"="C:\Program Files\Common Files" @@ -363,6 +394,7 @@ regdata_none = [] class DummyEnv(object): def __init__(self, dict=None): + self.fs = SCons.Node.FS.FS() if dict: self.dict = dict else: @@ -385,6 +417,16 @@ class DummyEnv(object): def has_key(self,name): return name in self.dict + def get(self, name, value=None): + if self.has_key(name): + return self.dict[name] + else: + return value + + def Dir(self, name): + return self.fs.Dir(name) + + class RegKey(object): """key class for storing an 'open' registry key""" def __init__(self,key): @@ -554,6 +596,11 @@ class msvsTestCase(unittest.TestCase): from SCons.Tool.MSCommon.vs import reset_installed_visual_studios reset_installed_visual_studios() + self.test = TestCmd.TestCmd(workdir='') + # FS doesn't like the cwd to be something other than its root. + os.chdir(self.test.workpath("")) + self.fs = SCons.Node.FS.FS() + def test_get_default_version(self): """Test retrieval of the default visual studio version""" @@ -607,13 +654,19 @@ class msvsTestCase(unittest.TestCase): version_num, suite = msvs_parse_version(self.highest_version) if version_num >= 10.0: function_test = _GenerateV10DSP + suffix = '.vcxproj' elif version_num >= 7.0: function_test = _GenerateV7DSP + suffix = '.dsp' else: function_test = _GenerateV6DSP + suffix = '.dsp' + + # Avoid any race conditions between the test cases when we test + # actually writing the files. + dspfile = 'test%s%s' % (hash(self), suffix) str_function_test = str(function_test.__init__) - dspfile = 'test.dsp' source = 'test.cpp' # Create the cmdargs test list @@ -623,50 +676,95 @@ class msvsTestCase(unittest.TestCase): 'debug=False target_arch=32', 'debug=True target_arch=x64', 'debug=False target_arch=x64'] - - # Tuple list : (parameter, dictionary of expected result per variant) - tests_cmdargs = [(None, dict.fromkeys(list_variant, '')), - ('', dict.fromkeys(list_variant, '')), - (list_cmdargs[0], dict.fromkeys(list_variant, list_cmdargs[0])), - (list_cmdargs, dict(list(zip(list_variant, list_cmdargs))))] - + list_cppdefines = [['_A', '_B', 'C'], ['_B', '_C_'], ['D'], []] + list_cpppaths = [[r'C:\test1'], [r'C:\test1;C:\test2'], + [self.fs.Dir('subdir')], []] + + def TestParamsFromList(test_variant, test_list): + """ + Generates test data based on the parameters passed in. + + Returns tuple list: + 1. Parameter. + 2. Dictionary of expected result per variant. + """ + def normalizeParam(param): + """ + Converts the raw data based into the AddConfig function of + msvs.py to the expected result. + + Expects the following behavior: + 1. A value of None will be converted to an empty list. + 2. A File or Directory object will be converted to an + absolute path (because those objects can't be pickled). + 3. Otherwise, the parameter will be used. + """ + if param is None: + return [] + elif isinstance(param, list): + return [normalizeParam(p) for p in param] + elif hasattr(param, 'abspath'): + return param.abspath + else: + return param + + return [ + (None, dict.fromkeys(test_variant, '')), + ('', dict.fromkeys(test_variant, '')), + (test_list[0], dict.fromkeys(test_variant, normalizeParam(test_list[0]))), + (test_list, dict(list(zip(test_variant, [normalizeParam(x) for x in test_list])))) + ] + + tests_cmdargs = TestParamsFromList(list_variant, list_cmdargs) + tests_cppdefines = TestParamsFromList(list_variant, list_cppdefines) + tests_cpppaths = TestParamsFromList(list_variant, list_cpppaths) + # Run the test for each test case for param_cmdargs, expected_cmdargs in tests_cmdargs: - debug('Testing %s. with :\n variant = %s \n cmdargs = "%s"' % \ - (str_function_test, list_variant, param_cmdargs)) - param_configs = [] - expected_configs = {} - for platform in ['Win32', 'x64']: - for variant in ['Debug', 'Release']: - variant_platform = '%s|%s' % (variant, platform) - runfile = '%s\\%s\\test.exe' % (platform, variant) - buildtarget = '%s\\%s\\test.exe' % (platform, variant) - outdir = '%s\\%s' % (platform, variant) + for param_cppdefines, expected_cppdefines in tests_cppdefines: + for param_cpppaths, expected_cpppaths in tests_cpppaths: + debug('Testing %s. with :\n variant = %s \n cmdargs = "%s" \n cppdefines = "%s" \n cpppaths = "%s"' % \ + (str_function_test, list_variant, param_cmdargs, param_cppdefines, param_cpppaths)) + param_configs = [] + expected_configs = {} + for platform in ['Win32', 'x64']: + for variant in ['Debug', 'Release']: + variant_platform = '%s|%s' % (variant, platform) + runfile = '%s\\%s\\test.exe' % (platform, variant) + buildtarget = '%s\\%s\\test.exe' % (platform, variant) + outdir = '%s\\%s' % (platform, variant) - # Create parameter list for this variant_platform - param_configs.append([variant_platform, runfile, buildtarget, outdir]) - - # Create expected dictionary result for this variant_platform - expected_configs[variant_platform] = \ - {'variant': variant, 'platform': platform, - 'runfile': runfile, - 'buildtarget': buildtarget, - 'outdir': outdir, - 'cmdargs': expected_cmdargs[variant_platform]} + # Create parameter list for this variant_platform + param_configs.append([variant_platform, runfile, buildtarget, outdir]) + # Create expected dictionary result for this variant_platform + expected_configs[variant_platform] = { + 'variant': variant, + 'platform': platform, + 'runfile': runfile, + 'buildtarget': buildtarget, + 'outdir': outdir, + 'cmdargs': expected_cmdargs[variant_platform], + 'cppdefines': expected_cppdefines[variant_platform], + 'cpppaths': expected_cpppaths[variant_platform], + } + # Create parameter environment with final parameter dictionary param_dict = dict(list(zip(('variant', 'runfile', 'buildtarget', 'outdir'), [list(l) for l in zip(*param_configs)]))) param_dict['cmdargs'] = param_cmdargs + param_dict['cppdefines'] = param_cppdefines + param_dict['cpppaths'] = param_cpppaths # Hack to be able to run the test with a 'DummyEnv' class _DummyEnv(DummyEnv): - def subst(self, string) : + def subst(self, string, *args, **kwargs): return string env = _DummyEnv(param_dict) env['MSVSSCONSCRIPT'] = '' env['MSVS_VERSION'] = self.highest_version + env['MSVSBUILDTARGET'] = 'target' # Call function to test genDSP = function_test(dspfile, source, env) @@ -676,6 +774,16 @@ class msvsTestCase(unittest.TestCase): for key in list(genDSP.configs.keys()): self.assertDictEqual(genDSP.configs[key].__dict__, expected_configs[key]) + genDSP.Build() + + # Delete the resulting file so we don't leave anything behind. + for file in [dspfile, dspfile + '.filters']: + path = os.path.realpath(file) + try: + os.remove(path) + except OSError: + pass + class msvs6aTestCase(msvsTestCase): """Test MSVS 6 Registry""" registry = DummyRegistry(regdata_6a + regdata_cv) @@ -787,6 +895,18 @@ class msvs80TestCase(msvsTestCase): } default_install_loc = install_locs['8.0'] +class msvs140TestCase(msvsTestCase): + """Test MSVS 140 Registry""" + registry = DummyRegistry(regdata_140 + regdata_cv) + default_version = '14.0' + highest_version = '14.0' + number_of_versions = 2 + install_locs = { + '14.0' : {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 14.0', + 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio 14.0\\VC'}, + } + default_install_loc = install_locs['14.0'] + class msvsEmptyTestCase(msvsTestCase): """Test Empty Registry""" registry = DummyRegistry(regdata_none) @@ -829,6 +949,7 @@ if __name__ == "__main__": msvs71TestCase, msvs8ExpTestCase, msvs80TestCase, + msvs140TestCase, msvsEmptyTestCase, ] diff --git a/src/engine/SCons/Tool/mwcc.py b/src/engine/SCons/Tool/mwcc.py index 5cad127..b2b2f5b 100644 --- a/src/engine/SCons/Tool/mwcc.py +++ b/src/engine/SCons/Tool/mwcc.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/mwcc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/mwcc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path diff --git a/src/engine/SCons/Tool/mwld.py b/src/engine/SCons/Tool/mwld.py index 7d7341a..5049c55 100644 --- a/src/engine/SCons/Tool/mwld.py +++ b/src/engine/SCons/Tool/mwld.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/mwld.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/mwld.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Tool diff --git a/src/engine/SCons/Tool/nasm.py b/src/engine/SCons/Tool/nasm.py index 8871ab6..d4d0a1b 100644 --- a/src/engine/SCons/Tool/nasm.py +++ b/src/engine/SCons/Tool/nasm.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/nasm.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/nasm.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/packaging/__init__.py b/src/engine/SCons/Tool/packaging/__init__.py index 513e672..72b9eef 100644 --- a/src/engine/SCons/Tool/packaging/__init__.py +++ b/src/engine/SCons/Tool/packaging/__init__.py @@ -25,7 +25,7 @@ SCons Packaging Tool. # 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__ = "src/engine/SCons/Tool/packaging/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Environment @@ -35,11 +35,11 @@ from SCons.Util import is_List, make_path_relative from SCons.Warnings import warn, Warning import os -import imp +import importlib __all__ = [ - 'src_targz', 'src_tarbz2', 'src_xz', 'src_zip', - 'targz', 'tarbz2', 'xz', 'zip', + 'src_targz', 'src_tarbz2', 'src_tarxz', 'src_zip', + 'targz', 'tarbz2', 'tarxz', 'zip', 'rpm', 'msi', 'ipk', ] @@ -122,12 +122,12 @@ def Package(env, target=None, source=None, **kw): # load the needed packagers. def load_packager(type): try: - file,path,desc=imp.find_module(type, __path__) - return imp.load_module(type, file, path, desc) + # the specific packager is a relative import + return importlib.import_module("." + type, __name__) except ImportError as e: - raise EnvironmentError("packager %s not available: %s"%(type,str(e))) + raise SConsEnvironmentError("packager %s not available: %s" % (type, str(e))) - packagers=list(map(load_packager, PACKAGETYPE)) + packagers = list(map(load_packager, PACKAGETYPE)) # set up targets and the PACKAGEROOT try: diff --git a/src/engine/SCons/Tool/packaging/ipk.py b/src/engine/SCons/Tool/packaging/ipk.py index 60cd3cb..dbf5be8 100644 --- a/src/engine/SCons/Tool/packaging/ipk.py +++ b/src/engine/SCons/Tool/packaging/ipk.py @@ -3,7 +3,7 @@ # # Copyright (c) 2001 - 2019 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 @@ -24,7 +24,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/ipk.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/ipk.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os @@ -117,17 +117,17 @@ def build_specfiles(source, target, env): # # opened_files={} - def open_file(needle, haystack): + def open_file(needle, haystack=None): try: return opened_files[needle] except KeyError: files = filter(lambda x: x.get_path().rfind(needle) != -1, haystack) # Py3: filter returns an iterable, not a list file = list(files)[0] - opened_files[needle]=open(file.get_abspath(), 'w') + opened_files[needle] = open(file.get_abspath(), 'w') return opened_files[needle] - control_file=open_file('control', target) + control_file = open_file('control', target) if 'X_IPK_DESCRIPTION' not in env: env['X_IPK_DESCRIPTION']="%s\n %s"%(env['SUMMARY'], @@ -149,7 +149,7 @@ Description: $X_IPK_DESCRIPTION control_file.write(env.subst(content)) # - # now handle the various other files, which purpose it is to set post-, + # now handle the various other files, which purpose it is to set post-, # pre-scripts and mark files as config files. # # We do so by filtering the source files for files which are marked with @@ -161,14 +161,14 @@ Description: $X_IPK_DESCRIPTION # into the same named file. # for f in [x for x in source if 'PACKAGING_CONFIG' in dir(x)]: - config=open_file('conffiles') + config = open_file('conffiles') config.write(f.PACKAGING_INSTALL_LOCATION) config.write('\n') for str in 'POSTRM PRERM POSTINST PREINST'.split(): name="PACKAGING_X_IPK_%s"%str for f in [x for x in source if name in dir(x)]: - file=open_file(name) + file = open_file(name) file.write(env[str]) # diff --git a/src/engine/SCons/Tool/packaging/msi.py b/src/engine/SCons/Tool/packaging/msi.py index 4e8724b..4a48b47 100644 --- a/src/engine/SCons/Tool/packaging/msi.py +++ b/src/engine/SCons/Tool/packaging/msi.py @@ -25,7 +25,7 @@ The msi packager. # 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__ = "src/engine/SCons/Tool/packaging/msi.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/msi.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import SCons @@ -188,7 +188,7 @@ def build_wxsfile(target, source, env): """ Compiles a .wxs file from the keywords given in env['msi_spec'] and by analyzing the tree of source nodes and their tags. """ - file = open(target[0].get_abspath(), 'w') + f = open(target[0].get_abspath(), 'w') try: # Create a document with the Wix root tag @@ -209,7 +209,7 @@ def build_wxsfile(target, source, env): build_license_file(target[0].get_dir(), env) # write the xml to a file - file.write( doc.toprettyxml() ) + f.write( doc.toprettyxml() ) # call a user specified function if 'CHANGE_SPECFILE' in env: @@ -217,6 +217,8 @@ def build_wxsfile(target, source, env): except KeyError as e: raise SCons.Errors.UserError( '"%s" package field for MSI is missing.' % e.args[0] ) + finally: + f.close() # # setup function @@ -440,14 +442,13 @@ def build_license_file(directory, spec): pass # ignore this as X_MSI_LICENSE_TEXT is optional if name!='' or text!='': - file = open( os.path.join(directory.get_path(), 'License.rtf'), 'w' ) - file.write('{\\rtf') - if text!='': - file.write(text.replace('\n', '\\par ')) - else: - file.write(name+'\\par\\par') - file.write('}') - file.close() + with open(os.path.join(directory.get_path(), 'License.rtf'), 'w') as f: + f.write('{\\rtf') + if text!='': + f.write(text.replace('\n', '\\par ')) + else: + f.write(name+'\\par\\par') + f.write('}') # # mandatory and optional package tags diff --git a/src/engine/SCons/Tool/packaging/rpm.py b/src/engine/SCons/Tool/packaging/rpm.py index 48b9aab..a948e29 100644 --- a/src/engine/SCons/Tool/packaging/rpm.py +++ b/src/engine/SCons/Tool/packaging/rpm.py @@ -25,7 +25,7 @@ The rpm packager. # 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__ = "src/engine/SCons/Tool/packaging/rpm.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/rpm.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os @@ -125,11 +125,11 @@ def build_specfile(target, source, env): """ Builds a RPM specfile from a dictionary with string metadata and by analyzing a tree of nodes. """ - with open(target[0].get_abspath(), 'w') as file: + with open(target[0].get_abspath(), 'w') as ofp: try: - file.write(build_specfile_header(env)) - file.write(build_specfile_sections(env)) - file.write(build_specfile_filesection(env, source)) + ofp.write(build_specfile_header(env)) + ofp.write(build_specfile_sections(env)) + ofp.write(build_specfile_filesection(env, source)) # call a user specified function if 'CHANGE_SPECFILE' in env: diff --git a/src/engine/SCons/Tool/packaging/src_tarbz2.py b/src/engine/SCons/Tool/packaging/src_tarbz2.py index 473ce75..b628ccd 100644 --- a/src/engine/SCons/Tool/packaging/src_tarbz2.py +++ b/src/engine/SCons/Tool/packaging/src_tarbz2.py @@ -26,7 +26,7 @@ The tarbz2 SRC packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/src_tarbz2.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/src_targz.py b/src/engine/SCons/Tool/packaging/src_targz.py index 865b8dd..4c7469c 100644 --- a/src/engine/SCons/Tool/packaging/src_targz.py +++ b/src/engine/SCons/Tool/packaging/src_targz.py @@ -26,7 +26,7 @@ The targz SRC packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/src_targz.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/src_tarxz.py b/src/engine/SCons/Tool/packaging/src_tarxz.py index 07ffdb0..6688c46 100644 --- a/src/engine/SCons/Tool/packaging/src_tarxz.py +++ b/src/engine/SCons/Tool/packaging/src_tarxz.py @@ -26,7 +26,7 @@ The tarxz SRC packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/src_tarxz.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/src_tarxz.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/src_zip.py b/src/engine/SCons/Tool/packaging/src_zip.py index 819e52a..d56f3aa 100644 --- a/src/engine/SCons/Tool/packaging/src_zip.py +++ b/src/engine/SCons/Tool/packaging/src_zip.py @@ -26,7 +26,7 @@ The zip SRC packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/src_zip.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/tarbz2.py b/src/engine/SCons/Tool/packaging/tarbz2.py index 5658de3..e371f1b 100644 --- a/src/engine/SCons/Tool/packaging/tarbz2.py +++ b/src/engine/SCons/Tool/packaging/tarbz2.py @@ -26,7 +26,7 @@ The tarbz2 packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/tarbz2.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/targz.py b/src/engine/SCons/Tool/packaging/targz.py index f515f1e..121e4a4 100644 --- a/src/engine/SCons/Tool/packaging/targz.py +++ b/src/engine/SCons/Tool/packaging/targz.py @@ -26,7 +26,7 @@ The targz packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/targz.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/targz.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/tarxz.py b/src/engine/SCons/Tool/packaging/tarxz.py index 5f7a8d8..3ec9822 100644 --- a/src/engine/SCons/Tool/packaging/tarxz.py +++ b/src/engine/SCons/Tool/packaging/tarxz.py @@ -26,7 +26,7 @@ The tarxz packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/tarxz.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/tarxz.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot diff --git a/src/engine/SCons/Tool/packaging/zip.py b/src/engine/SCons/Tool/packaging/zip.py index 2ae496e..11ab3b5 100644 --- a/src/engine/SCons/Tool/packaging/zip.py +++ b/src/engine/SCons/Tool/packaging/zip.py @@ -26,7 +26,7 @@ The zip SRC packager. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/packaging/zip.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/packaging/zip.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Tool.packaging import stripinstallbuilder, putintopackageroot diff --git a/src/engine/SCons/Tool/pdf.py b/src/engine/SCons/Tool/pdf.py index da970ff..50988b6 100644 --- a/src/engine/SCons/Tool/pdf.py +++ b/src/engine/SCons/Tool/pdf.py @@ -28,7 +28,7 @@ Add an explicit action to run epstopdf to convert .eps files to .pdf # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/pdf.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/pdf.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Builder import SCons.Tool diff --git a/src/engine/SCons/Tool/pdflatex.py b/src/engine/SCons/Tool/pdflatex.py index 2a97d0f..2c559f7 100644 --- a/src/engine/SCons/Tool/pdflatex.py +++ b/src/engine/SCons/Tool/pdflatex.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/pdflatex.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/pdflatex.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Util diff --git a/src/engine/SCons/Tool/pdftex.py b/src/engine/SCons/Tool/pdftex.py index 80913ae..c658aa0 100644 --- a/src/engine/SCons/Tool/pdftex.py +++ b/src/engine/SCons/Tool/pdftex.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/pdftex.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/pdftex.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import SCons.Action diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py index e8b0325..a582e6f 100644 --- a/src/engine/SCons/Tool/qt.py +++ b/src/engine/SCons/Tool/qt.py @@ -33,7 +33,7 @@ selection method. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/qt.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/qt.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import re @@ -66,11 +66,17 @@ if SCons.Util.case_sensitive_suffixes('.h', '.H'): cxx_suffixes = cplusplus.CXXSuffixes -# def find_platform_specific_qt_paths(): """ - If the platform has non-standard paths which it installs QT in,return the likely default path - :return: + find non-standard QT paths + + If the platform does not put QT tools in standard search paths, + the path is expected to be set using QTDIR. SCons violates + the normal rule of not pulling from the user's environment + in this case. However, some test cases try to validate what + happens when QTDIR is unset, so we need to try to make a guess. + + :return: a guess at a path """ # qt_bin_dirs = [] @@ -83,6 +89,7 @@ def find_platform_specific_qt_paths(): # Centos installs QT under /usr/{lib,lib64}/qt{4,5,-3.3}/bin # so we need to handle this differently # qt_bin_dirs = glob.glob('/usr/lib64/qt*/bin') + # TODO: all current Fedoras do the same, need to look deeper here. qt_bin_dir = '/usr/lib64/qt-3.3/bin' return qt_bin_dir @@ -97,7 +104,7 @@ def checkMocIncluded(target, source, env): # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/ path = SCons.Defaults.CScan.path(env, moc.cwd) includes = SCons.Defaults.CScan(cpp, env, path) - if not moc in includes: + if moc not in includes: SCons.Warnings.warn( GeneratedMocFileNotIncluded, "Generated moc file '%s' is not included by '%s'" % @@ -118,7 +125,7 @@ class _Automoc(object): def __init__(self, objBuilderName): self.objBuilderName = objBuilderName - + def __call__(self, target, source, env): """ Smart autoscan function. Gets the list of objects for the Program @@ -133,25 +140,25 @@ class _Automoc(object): debug = int(env.subst('$QT_DEBUG')) except ValueError: debug = 0 - + # some shortcuts used in the scanner splitext = SCons.Util.splitext objBuilder = getattr(env, self.objBuilderName) - + # some regular expressions: # Q_OBJECT detection - q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') + q_object_search = re.compile(r'[^A-Za-z0-9]Q_OBJECT[^A-Za-z0-9]') # cxx and c comment 'eater' #comment = re.compile(r'(//.*)|(/\*(([^*])|(\*[^/]))*\*/)') # CW: something must be wrong with the regexp. See also bug #998222 # CURRENTLY THERE IS NO TEST CASE FOR THAT - + # The following is kind of hacky to get builders working properly (FIXME) objBuilderEnv = objBuilder.env objBuilder.env = env mocBuilderEnv = env.Moc.env env.Moc.env = env - + # make a deep copy for the result; MocH objects will be appended out_sources = source[:] @@ -164,7 +171,7 @@ class _Automoc(object): cpp = obj.sources[0] if not splitext(str(cpp))[1] in cxx_suffixes: if debug: - print("scons: qt: '%s' is no cxx file. Discarded." % str(cpp)) + print("scons: qt: '%s' is no cxx file. Discarded." % str(cpp)) # c or fortran source continue #cpp_contents = comment.sub('', cpp.get_text_contents()) @@ -260,7 +267,7 @@ def uicScannerFunc(node, env, path): return result uicScanner = SCons.Scanner.Base(uicScannerFunc, - name = "UicScanner", + name = "UicScanner", node_class = SCons.Node.FS.File, node_factory = SCons.Node.FS.File, recursive = 0) @@ -336,7 +343,7 @@ def generate(env): mocBld.prefix[cxx] = '$QT_MOCCXXPREFIX' mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' - # register the builders + # register the builders env['BUILDERS']['Uic'] = uicBld env['BUILDERS']['Moc'] = mocBld static_obj, shared_obj = SCons.Tool.createObjBuilders(env) diff --git a/src/engine/SCons/Tool/rmic.py b/src/engine/SCons/Tool/rmic.py index 1d401fd..d6f7b2b 100644 --- a/src/engine/SCons/Tool/rmic.py +++ b/src/engine/SCons/Tool/rmic.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/rmic.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/rmic.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path @@ -110,11 +110,9 @@ def generate(env): if env['PLATFORM'] == 'win32': version = env.get('JAVAVERSION', None) - default_paths=get_java_install_dirs(env['PLATFORM'], version=version) - # Ensure that we have a proper path for rmic - rmic = SCons.Tool.find_program_path(env, 'rmic', default_paths=default_paths) - + paths = get_java_install_dirs('win32', version=version) + rmic = SCons.Tool.find_program_path(env, 'rmic', default_paths=paths) # print("RMIC: %s"%rmic) if rmic: rmic_bin_dir = os.path.dirname(rmic) diff --git a/src/engine/SCons/Tool/rpcgen.py b/src/engine/SCons/Tool/rpcgen.py index 349af6a..b9f8ca6 100644 --- a/src/engine/SCons/Tool/rpcgen.py +++ b/src/engine/SCons/Tool/rpcgen.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/rpcgen.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/rpcgen.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from SCons.Builder import Builder import SCons.Util diff --git a/src/engine/SCons/Tool/rpm.py b/src/engine/SCons/Tool/rpm.py index 61e4185..0253365 100644 --- a/src/engine/SCons/Tool/rpm.py +++ b/src/engine/SCons/Tool/rpm.py @@ -33,7 +33,7 @@ tar.gz consisting of the source file and a specfile. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/rpm.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/rpm.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import re @@ -51,43 +51,44 @@ def get_cmd(source, env): if SCons.Util.is_List(source): tar_file_with_included_specfile = source[0] return "%s %s %s"%(env['RPM'], env['RPMFLAGS'], - tar_file_with_included_specfile.get_abspath() ) + tar_file_with_included_specfile.get_abspath()) def build_rpm(target, source, env): # create a temporary rpm build root. - tmpdir = os.path.join( os.path.dirname( target[0].get_abspath() ), 'rpmtemp' ) + tmpdir = os.path.join(os.path.dirname(target[0].get_abspath()), 'rpmtemp') if os.path.exists(tmpdir): shutil.rmtree(tmpdir) # now create the mandatory rpm directory structure. for d in ['RPMS', 'SRPMS', 'SPECS', 'BUILD']: - os.makedirs( os.path.join( tmpdir, d ) ) + os.makedirs(os.path.join(tmpdir, d)) # set the topdir as an rpmflag. - env.Prepend( RPMFLAGS = '--define \'_topdir %s\'' % tmpdir ) + env.Prepend(RPMFLAGS = '--define \'_topdir %s\'' % tmpdir) # now call rpmbuild to create the rpm package. handle = subprocess.Popen(get_cmd(source, env), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) - output = SCons.Util.to_str(handle.stdout.read()) + with handle.stdout: + output = SCons.Util.to_str(handle.stdout.read()) status = handle.wait() if status: - raise SCons.Errors.BuildError( node=target[0], - errstr=output, - filename=str(target[0]) ) + raise SCons.Errors.BuildError(node=target[0], + errstr=output, + filename=str(target[0])) else: # XXX: assume that LC_ALL=C is set while running rpmbuild - output_files = re.compile( 'Wrote: (.*)' ).findall( output ) + output_files = re.compile('Wrote: (.*)').findall(output) - for output, input in zip( output_files, target ): + for output, input in zip(output_files, target): rpm_output = os.path.basename(output) expected = os.path.basename(input.get_path()) assert expected == rpm_output, "got %s but expected %s" % (rpm_output, expected) - shutil.copy( output, input.get_abspath() ) + shutil.copy(output, input.get_abspath()) # cleanup before leaving. diff --git a/src/engine/SCons/Tool/rpmutils.py b/src/engine/SCons/Tool/rpmutils.py index 10bb28d..a2400ff 100644 --- a/src/engine/SCons/Tool/rpmutils.py +++ b/src/engine/SCons/Tool/rpmutils.py @@ -36,7 +36,7 @@ exact syntax. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/rpmutils.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/rpmutils.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import platform @@ -482,9 +482,11 @@ def updateRpmDicts(rpmrc, pyfile): """ try: # Read old rpmutils.py file - oldpy = open(pyfile,"r").readlines() + with open(pyfile,"r") as f: + oldpy = f.readlines() # Read current rpmrc.in file - rpm = open(rpmrc,"r").readlines() + with open(rpmrc,"r") as f: + rpm = f.readlines() # Parse for data data = {} # Allowed section names that get parsed @@ -511,24 +513,23 @@ def updateRpmDicts(rpmrc, pyfile): # Insert data data[key][tokens[1]] = tokens[2:] # Write new rpmutils.py file - out = open(pyfile,"w") - pm = 0 - for l in oldpy: - if pm: - if l.startswith('# End of rpmrc dictionaries'): - pm = 0 + with open(pyfile,"w") as out: + pm = 0 + for l in oldpy: + if pm: + if l.startswith('# End of rpmrc dictionaries'): + pm = 0 + out.write(l) + else: out.write(l) - else: - out.write(l) - if l.startswith('# Start of rpmrc dictionaries'): - pm = 1 - # Write data sections to single dictionaries - for key, entries in data.items(): - out.write("%s = {\n" % key) - for arch in sorted(entries.keys()): - out.write(" '%s' : ['%s'],\n" % (arch, "','".join(entries[arch]))) - out.write("}\n\n") - out.close() + if l.startswith('# Start of rpmrc dictionaries'): + pm = 1 + # Write data sections to single dictionaries + for key, entries in data.items(): + out.write("%s = {\n" % key) + for arch in sorted(entries.keys()): + out.write(" '%s' : ['%s'],\n" % (arch, "','".join(entries[arch]))) + out.write("}\n\n") except: pass diff --git a/src/engine/SCons/Tool/sgiar.py b/src/engine/SCons/Tool/sgiar.py index 152afd4..d82ec89 100644 --- a/src/engine/SCons/Tool/sgiar.py +++ b/src/engine/SCons/Tool/sgiar.py @@ -33,7 +33,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sgiar.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sgiar.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/sgic++.py b/src/engine/SCons/Tool/sgic++.py index 35f913f..84d775a 100644 --- a/src/engine/SCons/Tool/sgic++.py +++ b/src/engine/SCons/Tool/sgic++.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sgic++.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sgic++.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" #forward proxy to the preffered cxx version from SCons.Tool.sgicxx import * diff --git a/src/engine/SCons/Tool/sgicc.py b/src/engine/SCons/Tool/sgicc.py index c5461e3..537f1a3 100644 --- a/src/engine/SCons/Tool/sgicc.py +++ b/src/engine/SCons/Tool/sgicc.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sgicc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sgicc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" from . import cc diff --git a/src/engine/SCons/Tool/sgicxx.py b/src/engine/SCons/Tool/sgicxx.py index 7656939..d44ec26 100644 --- a/src/engine/SCons/Tool/sgicxx.py +++ b/src/engine/SCons/Tool/sgicxx.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sgicxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sgicxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/sgilink.py b/src/engine/SCons/Tool/sgilink.py index 366b227..ca9e69c 100644 --- a/src/engine/SCons/Tool/sgilink.py +++ b/src/engine/SCons/Tool/sgilink.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sgilink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sgilink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/sunar.py b/src/engine/SCons/Tool/sunar.py index 6745511..07d45b2 100644 --- a/src/engine/SCons/Tool/sunar.py +++ b/src/engine/SCons/Tool/sunar.py @@ -32,7 +32,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sunar.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sunar.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Defaults import SCons.Tool diff --git a/src/engine/SCons/Tool/sunc++.py b/src/engine/SCons/Tool/sunc++.py index f1bbf14..1611fe9 100644 --- a/src/engine/SCons/Tool/sunc++.py +++ b/src/engine/SCons/Tool/sunc++.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sunc++.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sunc++.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" #forward proxy to the preffered cxx version diff --git a/src/engine/SCons/Tool/suncc.py b/src/engine/SCons/Tool/suncc.py index 49cfa70..da6ca6e 100644 --- a/src/engine/SCons/Tool/suncc.py +++ b/src/engine/SCons/Tool/suncc.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/suncc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/suncc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/suncxx.py b/src/engine/SCons/Tool/suncxx.py index 1fa5331..1ee1c6a 100644 --- a/src/engine/SCons/Tool/suncxx.py +++ b/src/engine/SCons/Tool/suncxx.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/suncxx.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/suncxx.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons @@ -52,11 +52,17 @@ def get_package_info(package_name, pkginfo, pkgchk): version = None pathname = None try: - sadm_contents = open('/var/sadm/install/contents', 'r').read() + from subprocess import DEVNULL # py3k + except ImportError: + DEVNULL = open(os.devnull, 'wb') + + try: + with open('/var/sadm/install/contents', 'r') as f: + sadm_contents = f.read() except EnvironmentError: pass else: - sadm_re = re.compile('^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M) + sadm_re = re.compile(r'^(\S*/bin/CC)(=\S*)? %s$' % package_name, re.M) sadm_match = sadm_re.search(sadm_contents) if sadm_match: pathname = os.path.dirname(sadm_match.group(1)) @@ -64,12 +70,12 @@ def get_package_info(package_name, pkginfo, pkgchk): try: p = subprocess.Popen([pkginfo, '-l', package_name], stdout=subprocess.PIPE, - stderr=open('/dev/null', 'w')) + stderr=DEVNULL) except EnvironmentError: pass else: pkginfo_contents = p.communicate()[0] - version_re = re.compile('^ *VERSION:\s*(.*)$', re.M) + version_re = re.compile(r'^ *VERSION:\s*(.*)$', re.M) version_match = version_re.search(pkginfo_contents) if version_match: version = version_match.group(1) @@ -78,7 +84,7 @@ def get_package_info(package_name, pkginfo, pkgchk): try: p = subprocess.Popen([pkgchk, '-l', package_name], stdout=subprocess.PIPE, - stderr=open('/dev/null', 'w')) + stderr=DEVNULL) except EnvironmentError: pass else: diff --git a/src/engine/SCons/Tool/sunf77.py b/src/engine/SCons/Tool/sunf77.py index 1d9487d..c1c216f 100644 --- a/src/engine/SCons/Tool/sunf77.py +++ b/src/engine/SCons/Tool/sunf77.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sunf77.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sunf77.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/sunf90.py b/src/engine/SCons/Tool/sunf90.py index 4de62c3..4cabee8 100644 --- a/src/engine/SCons/Tool/sunf90.py +++ b/src/engine/SCons/Tool/sunf90.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sunf90.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sunf90.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/sunf95.py b/src/engine/SCons/Tool/sunf95.py index 6d1ccf6..077b1bc 100644 --- a/src/engine/SCons/Tool/sunf95.py +++ b/src/engine/SCons/Tool/sunf95.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sunf95.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sunf95.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Util diff --git a/src/engine/SCons/Tool/sunlink.py b/src/engine/SCons/Tool/sunlink.py index 1eca1b2..8d965fa 100644 --- a/src/engine/SCons/Tool/sunlink.py +++ b/src/engine/SCons/Tool/sunlink.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/sunlink.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/sunlink.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path diff --git a/src/engine/SCons/Tool/swig.py b/src/engine/SCons/Tool/swig.py index a207a89..2e61b91 100644 --- a/src/engine/SCons/Tool/swig.py +++ b/src/engine/SCons/Tool/swig.py @@ -32,7 +32,7 @@ from __future__ import print_function # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/swig.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/swig.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys @@ -184,9 +184,10 @@ def generate(env): from SCons.Platform.mingw import MINGW_DEFAULT_PATHS from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS + from SCons.Platform.win32 import CHOCO_DEFAULT_PATH if sys.platform == 'win32': - swig = SCons.Tool.find_program_path(env, 'swig', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS + [r'C:\ProgramData\chocolatey\bin'] ) + swig = SCons.Tool.find_program_path(env, 'swig', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS + CHOCO_DEFAULT_PATH) if swig: swig_bin_dir = os.path.dirname(swig) env.AppendENVPath('PATH', swig_bin_dir) diff --git a/src/engine/SCons/Tool/tar.py b/src/engine/SCons/Tool/tar.py index ac4f7f7..08c80f2 100644 --- a/src/engine/SCons/Tool/tar.py +++ b/src/engine/SCons/Tool/tar.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/tar.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/tar.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Action import SCons.Builder diff --git a/src/engine/SCons/Tool/tex.py b/src/engine/SCons/Tool/tex.py index 2fecd1d..9beb557 100644 --- a/src/engine/SCons/Tool/tex.py +++ b/src/engine/SCons/Tool/tex.py @@ -33,7 +33,7 @@ selection method. # from __future__ import print_function -__revision__ = "src/engine/SCons/Tool/tex.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/tex.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import re @@ -781,7 +781,7 @@ def tex_emitter_core(target, source, env, graphics_extensions): # add side effects if feature is present.If file is to be generated,add all side effects if Verbose and theSearch: print("check side effects for ",suffix_list[-1]) - if (theSearch != None) or (not source[0].exists() ): + if theSearch is not None or not source[0].exists(): file_list = [targetbase,] # for bibunit we need a list of files if suffix_list[-1] == 'bibunit': diff --git a/src/engine/SCons/Tool/textfile.py b/src/engine/SCons/Tool/textfile.py index bbbde9f..58a5007 100644 --- a/src/engine/SCons/Tool/textfile.py +++ b/src/engine/SCons/Tool/textfile.py @@ -44,7 +44,7 @@ Textfile/Substfile builder for SCons. is unpredictable whether the expansion will occur. """ -__revision__ = "src/engine/SCons/Tool/textfile.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/textfile.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons diff --git a/src/engine/SCons/Tool/tlib.py b/src/engine/SCons/Tool/tlib.py index e1483a2..4d8101b 100644 --- a/src/engine/SCons/Tool/tlib.py +++ b/src/engine/SCons/Tool/tlib.py @@ -27,7 +27,7 @@ XXX # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/tlib.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/tlib.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Tool import SCons.Tool.bcc32 diff --git a/src/engine/SCons/Tool/wix.py b/src/engine/SCons/Tool/wix.py index bba033e..e79e4a1 100644 --- a/src/engine/SCons/Tool/wix.py +++ b/src/engine/SCons/Tool/wix.py @@ -30,7 +30,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/wix.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/wix.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.Builder import SCons.Action diff --git a/src/engine/SCons/Tool/wixTests.py b/src/engine/SCons/Tool/wixTests.py index c1812a2..f2649a3 100644 --- a/src/engine/SCons/Tool/wixTests.py +++ b/src/engine/SCons/Tool/wixTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/wixTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/wixTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import unittest import os.path diff --git a/src/engine/SCons/Tool/xgettext.py b/src/engine/SCons/Tool/xgettext.py index 3f11357..185558e 100644 --- a/src/engine/SCons/Tool/xgettext.py +++ b/src/engine/SCons/Tool/xgettext.py @@ -24,7 +24,24 @@ Tool specific initialization of `xgettext` tool. # 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__ = "src/engine/SCons/Tool/xgettext.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/xgettext.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" + +import os +import re +import subprocess +import sys + +import SCons.Action +import SCons.Node.FS +import SCons.Tool +import SCons.Util +from SCons.Builder import BuilderBase +from SCons.Environment import _null +from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS +from SCons.Platform.mingw import MINGW_DEFAULT_PATHS +from SCons.Tool.GettextCommon import _POTargetFactory +from SCons.Tool.GettextCommon import RPaths, _detect_xgettext +from SCons.Tool.GettextCommon import _xgettext_exists ############################################################################# @@ -41,10 +58,6 @@ class _CmdRunner(object): self.commandstr = commandstr def __call__(self, target, source, env): - import SCons.Action - import subprocess - import os - import sys kw = { 'stdin': 'devnull', 'stdout': subprocess.PIPE, @@ -57,11 +70,10 @@ class _CmdRunner(object): self.out, self.err = proc.communicate() self.status = proc.wait() if self.err: - sys.stderr.write(unicode(self.err)) + sys.stderr.write(SCons.Util.UnicodeType(self.err)) return self.status def strfunction(self, target, source, env): - import os comstr = self.commandstr if env.subst(comstr, target=target, source=source) == "": comstr = self.command @@ -74,9 +86,6 @@ class _CmdRunner(object): ############################################################################# def _update_pot_file(target, source, env): """ Action function for `POTUpdate` builder """ - import re - import os - import SCons.Action nop = lambda target, source, env: 0 # Save scons cwd and os cwd (NOTE: they may be different. After the job, we @@ -153,10 +162,6 @@ def _update_pot_file(target, source, env): ############################################################################# -############################################################################# -from SCons.Builder import BuilderBase - - ############################################################################# class _POTBuilder(BuilderBase): def _execute(self, env, target, source, *args): @@ -175,10 +180,6 @@ class _POTBuilder(BuilderBase): def _scan_xgettext_from_files(target, source, env, files=None, path=None): """ Parses `POTFILES.in`-like file and returns list of extracted file names. """ - import re - import SCons.Util - import SCons.Node.FS - if files is None: return 0 if not SCons.Util.is_List(files): @@ -230,10 +231,6 @@ def _scan_xgettext_from_files(target, source, env, files=None, path=None): ############################################################################# def _pot_update_emitter(target, source, env): """ Emitter function for `POTUpdate` builder """ - from SCons.Tool.GettextCommon import _POTargetFactory - import SCons.Util - import SCons.Node.FS - if 'XGETTEXTFROM' in env: xfrom = env['XGETTEXTFROM'] else: @@ -260,10 +257,6 @@ def _pot_update_emitter(target, source, env): ############################################################################# -############################################################################# -from SCons.Environment import _null - - ############################################################################# def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw): return env._POTUpdateBuilder(target, source, **kw) @@ -274,8 +267,6 @@ def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw): ############################################################################# def _POTUpdateBuilder(env, **kw): """ Creates `POTUpdate` builder object """ - import SCons.Action - from SCons.Tool.GettextCommon import _POTargetFactory kw['action'] = SCons.Action.Action(_update_pot_file, None) kw['suffix'] = '$POTSUFFIX' kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File @@ -288,13 +279,6 @@ def _POTUpdateBuilder(env, **kw): ############################################################################# def generate(env, **kw): """ Generate `xgettext` tool """ - import sys - import os - import SCons.Util - import SCons.Tool - from SCons.Tool.GettextCommon import RPaths, _detect_xgettext - from SCons.Platform.mingw import MINGW_DEFAULT_PATHS - from SCons.Platform.cygwin import CYGWIN_DEFAULT_PATHS if sys.platform == 'win32': xgettext = SCons.Tool.find_program_path(env, 'xgettext', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) @@ -359,7 +343,6 @@ def generate(env, **kw): ############################################################################# def exists(env): """ Check, whether the tool exists """ - from SCons.Tool.GettextCommon import _xgettext_exists try: return _xgettext_exists(env) except: diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py index d397de1..6857574 100644 --- a/src/engine/SCons/Tool/yacc.py +++ b/src/engine/SCons/Tool/yacc.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/yacc.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/yacc.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys @@ -45,6 +45,11 @@ from SCons.Platform.win32 import CHOCO_DEFAULT_PATH YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR") +if sys.platform == 'win32': + BINS = ['bison', 'yacc', 'win_bison'] +else: + BINS = ["bison", "yacc"] + def _yaccEmitter(target, source, env, ysuf, hsuf): yaccflags = env.subst("$YACCFLAGS", target=target, source=source) flags = SCons.Util.CLVar(yaccflags) @@ -100,25 +105,26 @@ def yyEmitter(target, source, env): def get_yacc_path(env, append_paths=False): """ - Find the a path containing the lex or flex binaries. If a construction - environment is passed in then append the path to the ENV PATH. - """ - # save existing path to reset if we don't want to append any paths - envPath = env['ENV']['PATH'] - bins = ['bison', 'yacc', 'win_bison'] + Find the path to the yacc tool, searching several possible names + + Only called in the Windows case, so the default_path + can be Windows-specific - for prog in bins: + :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 + """ + for prog in BINS: bin_path = SCons.Tool.find_program_path( - env, - prog, + env, + prog, default_paths=CHOCO_DEFAULT_PATH + MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) if bin_path: - if not append_paths: - env['ENV']['PATH'] = envPath - else: + if append_paths: env.AppendENVPath('PATH', os.path.dirname(bin_path)) return bin_path - SCons.Warnings.Warning('lex tool requested, but lex or flex binary not found in ENV PATH') + SCons.Warnings.Warning('yacc tool requested, but yacc or bison binary not found in ENV PATH') + def generate(env): """Add Builders and construction variables for yacc to an Environment.""" @@ -140,29 +146,21 @@ def generate(env): cxx_file.add_emitter('.yy', yyEmitter) if sys.platform == 'win32': - bison = SCons.Tool.find_program_path(env, 'bison', default_paths=MINGW_DEFAULT_PATHS + CYGWIN_DEFAULT_PATHS ) - if bison: - bison_bin_dir = os.path.dirname(bison) - env.AppendENVPath('PATH', bison_bin_dir) - else: - SCons.Warnings.Warning('yacc tool requested, but bison binary not found in ENV PATH') + # ignore the return, all we need is for the path to be added + _ = get_yacc_path(env, append_paths=True) - if sys.platform == 'win32': - get_yacc_path(env, append_paths=True) - env["YACC"] = env.Detect(['bison', 'yacc', 'win_bison']) - else: - env["YACC"] = env.Detect(["bison", "yacc"]) - + env["YACC"] = env.Detect(BINS) env['YACCFLAGS'] = SCons.Util.CLVar('') env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES' env['YACCHFILESUFFIX'] = '.h' - env['YACCHXXFILESUFFIX'] = '.hpp' - env['YACCVCGFILESUFFIX'] = '.vcg' def exists(env): - return env.Detect(['bison', 'yacc']) + if sys.platform == 'win32': + return get_yacc_path(env) + else: + return env.Detect(BINS) # Local Variables: # tab-width:4 diff --git a/src/engine/SCons/Tool/zip.py b/src/engine/SCons/Tool/zip.py index 55792c2..b7efdc7 100644 --- a/src/engine/SCons/Tool/zip.py +++ b/src/engine/SCons/Tool/zip.py @@ -31,7 +31,7 @@ selection method. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Tool/zip.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Tool/zip.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index f2498e9..5b4c225 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -24,7 +24,7 @@ Various utility functions go here. # 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__ = "src/engine/SCons/Util.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Util.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys @@ -33,6 +33,7 @@ import re import types import codecs import pprint +import hashlib PY3 = sys.version_info[0] == 3 @@ -656,13 +657,15 @@ except ImportError: pass RegError = _NoError -WinError = None + # Make sure we have a definition of WindowsError so we can # run platform-independent tests of Windows functionality on # platforms other than Windows. (WindowsError is, in fact, an # OSError subclass on Windows.) + class PlainWindowsError(OSError): pass + try: WinError = WindowsError except NameError: @@ -676,7 +679,7 @@ if can_read_reg: HKEY_USERS = hkey_mod.HKEY_USERS def RegGetValue(root, key): - """This utility function returns a value in the registry + r"""This utility function returns a value in the registry without having to open the key first. Only available on Windows platforms with a version of Python that can read the registry. Returns the same thing as @@ -883,7 +886,7 @@ def PrependPath(oldpath, newpath, sep = os.pathsep, # now we add them only if they are unique for path in newpaths: normpath = os.path.normpath(os.path.normcase(path)) - if path and not normpath in normpaths: + if path and normpath not in normpaths: paths.append(path) normpaths.append(normpath) @@ -963,7 +966,7 @@ def AppendPath(oldpath, newpath, sep = os.pathsep, # now we add them only if they are unique for path in newpaths: normpath = os.path.normpath(os.path.normcase(path)) - if path and not normpath in normpaths: + if path and normpath not in normpaths: paths.append(path) normpaths.append(normpath) paths.reverse() @@ -999,7 +1002,9 @@ if sys.platform == 'cygwin': def get_native_path(path): """Transforms an absolute path into a native path for the system. In Cygwin, this converts from a Cygwin path to a Windows one.""" - return os.popen('cygpath -w ' + path).read().replace('\n', '') + with os.popen('cygpath -w ' + path) as p: + npath = p.read().replace('\n', '') + return npath else: def get_native_path(path): """Transforms an absolute path into a native path for the system. @@ -1176,10 +1181,13 @@ def unique(s): # 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): - if idfun is None: - def idfun(x): return x + def default_idfun(x): + return x + if not idfun: + idfun = default_idfun seen = {} result = [] for item in seq: @@ -1444,56 +1452,53 @@ def RenameFunction(function, name): function.__defaults__) -md5 = False - +if hasattr(hashlib, 'md5'): + md5 = True -def MD5signature(s): - return str(s) + def MD5signature(s): + """ + Generate md5 signature of a string + :param s: either string or bytes. Normally should be bytes + :return: String of hex digits representing the signature + """ + m = hashlib.md5() -def MD5filesignature(fname, chunksize=65536): - with open(fname, "rb") as f: - result = f.read() - return result + try: + m.update(to_bytes(s)) + except TypeError as e: + m.update(to_bytes(str(s))) -try: - import hashlib -except ImportError: - pass -else: - if hasattr(hashlib, 'md5'): - md5 = True + return m.hexdigest() - def MD5signature(s): - """ - Generate a String of Hex digits representing the md5 signature of the string - :param s: either string or bytes. Normally should be bytes - :return: String of hex digits - """ - m = hashlib.md5() + def MD5filesignature(fname, chunksize=65536): + """ + Generate the md5 signature of a file - try: - m.update(to_bytes(s)) - except TypeError as e: - m.update(to_bytes(str(s))) - - return m.hexdigest() - - def MD5filesignature(fname, chunksize=65536): - """ - :param fname: - :param chunksize: - :return: String of Hex digits - """ - m = hashlib.md5() - f = open(fname, "rb") + :param fname: file to hash + :param chunksize: chunk size to read + :return: String of Hex digits representing the signature + """ + m = hashlib.md5() + with open(fname, "rb") as f: while True: blck = f.read(chunksize) if not blck: break m.update(to_bytes(blck)) - f.close() - return m.hexdigest() + return m.hexdigest() +else: + # if md5 algorithm not available, just return data unmodified + # could add alternative signature scheme here + md5 = False + + def MD5signature(s): + return str(s) + + def MD5filesignature(fname, chunksize=65536): + with open(fname, "rb") as f: + result = f.read() + return result def MD5collect(signatures): @@ -1509,7 +1514,6 @@ def MD5collect(signatures): return MD5signature(', '.join(signatures)) - def silent_intern(x): """ Perform sys.intern() on the passed argument and return the result. @@ -1533,7 +1537,7 @@ def silent_intern(x): class Null(object): """ Null objects always and reliably "do nothing." """ def __new__(cls, *args, **kwargs): - if not '_instance' in vars(cls): + if '_instance' not in vars(cls): cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) return cls._instance def __init__(self, *args, **kwargs): diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index c33a1fe..41a1647 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/UtilTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/UtilTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import SCons.compat @@ -275,7 +275,7 @@ class UtilTestCase(unittest.TestCase): assert not is_Dict(()) assert not is_Dict("") if HasUnicode: - exec("assert not is_Dict(u'')") + exec ("assert not is_Dict(u'')") def test_is_List(self): assert is_List([]) @@ -291,12 +291,12 @@ class UtilTestCase(unittest.TestCase): assert not is_List({}) assert not is_List("") if HasUnicode: - exec("assert not is_List(u'')") + exec ("assert not is_List(u'')") def test_is_String(self): assert is_String("") if HasUnicode: - exec("assert is_String(u'')") + exec ("assert is_String(u'')") assert is_String(UserString('')) try: class mystr(str): @@ -322,7 +322,7 @@ class UtilTestCase(unittest.TestCase): assert not is_Tuple({}) assert not is_Tuple("") if HasUnicode: - exec("assert not is_Tuple(u'')") + exec ("assert not is_Tuple(u'')") def test_to_Bytes(self): """ Test the to_Bytes method""" @@ -768,12 +768,12 @@ class UtilTestCase(unittest.TestCase): def test_LogicalLines(self): """Test the LogicalLines class""" content = u""" -foo \ -bar \ +foo \\ +bar \\ baz foo -bling \ -bling \ bling +bling \\ +bling \\ bling bling """ fobj = io.StringIO(content) @@ -875,17 +875,18 @@ class flattenTestCase(unittest.TestCase): def test_scalar(self): """Test flattening a scalar""" result = flatten('xyz') - self.assertEqual(result,['xyz'], result) + self.assertEqual(result, ['xyz'], result) def test_dictionary_values(self): """Test flattening the dictionary values""" items = {"a": 1, "b": 2, "c": 3} result = flatten(items.values()) - self.assertEqual(sorted(result),[1,2,3]) + self.assertEqual(sorted(result), [1, 2, 3]) class OsEnviron(object): """Used to temporarily mock os.environ""" + def __init__(self, environ): self._environ = environ @@ -915,21 +916,21 @@ class get_env_boolTestCase(unittest.TestCase): assert var is False, "var should be False, not %s" % repr(var) def test_true(self): - for foo in [ 'TRUE', 'True', 'true', - 'YES', 'Yes', 'yes', - 'Y', 'y', - 'ON', 'On', 'on', - '1', '20', '-1']: + for foo in ['TRUE', 'True', 'true', + 'YES', 'Yes', 'yes', + 'Y', 'y', + 'ON', 'On', 'on', + '1', '20', '-1']: env = {'FOO': foo} var = get_env_bool(env, 'FOO') assert var is True, 'var should be True, not %s' % repr(var) def test_false(self): - for foo in [ 'FALSE', 'False', 'false', - 'NO', 'No', 'no', - 'N', 'n', - 'OFF', 'Off', 'off', - '0']: + for foo in ['FALSE', 'False', 'false', + 'NO', 'No', 'no', + 'N', 'n', + 'OFF', 'Off', 'off', + '0']: env = {'FOO': foo} var = get_env_bool(env, 'FOO', True) assert var is False, 'var should be True, not %s' % repr(var) @@ -952,21 +953,21 @@ class get_os_env_boolTestCase(unittest.TestCase): assert var is False, "var should be False, not %s" % repr(var) def test_true(self): - for foo in [ 'TRUE', 'True', 'true', - 'YES', 'Yes', 'yes', - 'Y', 'y', - 'ON', 'On', 'on', - '1', '20', '-1']: + for foo in ['TRUE', 'True', 'true', + 'YES', 'Yes', 'yes', + 'Y', 'y', + 'ON', 'On', 'on', + '1', '20', '-1']: with OsEnviron({'FOO': foo}): var = get_os_env_bool('FOO') assert var is True, 'var should be True, not %s' % repr(var) def test_false(self): - for foo in [ 'FALSE', 'False', 'false', - 'NO', 'No', 'no', - 'N', 'n', - 'OFF', 'Off', 'off', - '0']: + for foo in ['FALSE', 'False', 'false', + 'NO', 'No', 'no', + 'N', 'n', + 'OFF', 'Off', 'off', + '0']: with OsEnviron({'FOO': foo}): var = get_os_env_bool('FOO', True) assert var is False, 'var should be True, not %s' % repr(var) diff --git a/src/engine/SCons/Variables/BoolVariable.py b/src/engine/SCons/Variables/BoolVariable.py index 5af3a5f..4cb9f35 100644 --- a/src/engine/SCons/Variables/BoolVariable.py +++ b/src/engine/SCons/Variables/BoolVariable.py @@ -34,7 +34,7 @@ Usage example:: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/BoolVariable.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/BoolVariable.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __all__ = ['BoolVariable',] diff --git a/src/engine/SCons/Variables/BoolVariableTests.py b/src/engine/SCons/Variables/BoolVariableTests.py index 62519bc..a184378 100644 --- a/src/engine/SCons/Variables/BoolVariableTests.py +++ b/src/engine/SCons/Variables/BoolVariableTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/BoolVariableTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/BoolVariableTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Variables/EnumVariable.py b/src/engine/SCons/Variables/EnumVariable.py index 1a5f40c..ba9f972 100644 --- a/src/engine/SCons/Variables/EnumVariable.py +++ b/src/engine/SCons/Variables/EnumVariable.py @@ -37,7 +37,7 @@ Usage example:: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/EnumVariable.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/EnumVariable.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __all__ = ['EnumVariable',] @@ -45,7 +45,7 @@ __all__ = ['EnumVariable',] import SCons.Errors def _validator(key, val, env, vals): - if not val in vals: + if val not in vals: raise SCons.Errors.UserError( 'Invalid value for option %s: %s. Valid values are: %s' % (key, val, vals)) diff --git a/src/engine/SCons/Variables/EnumVariableTests.py b/src/engine/SCons/Variables/EnumVariableTests.py index d6e4761..28b9d68 100644 --- a/src/engine/SCons/Variables/EnumVariableTests.py +++ b/src/engine/SCons/Variables/EnumVariableTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/EnumVariableTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/EnumVariableTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Variables/ListVariable.py b/src/engine/SCons/Variables/ListVariable.py index d0f99f5..a30f265 100644 --- a/src/engine/SCons/Variables/ListVariable.py +++ b/src/engine/SCons/Variables/ListVariable.py @@ -46,7 +46,7 @@ Usage example:: # 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__ = "src/engine/SCons/Variables/ListVariable.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/ListVariable.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" # Known Bug: This should behave like a Set-Type, but does not really, # since elements can occur twice. @@ -96,7 +96,7 @@ def _converter(val, allowedElems, mapdict): else: val = [_f for _f in val.split(',') if _f] val = [mapdict.get(v, v) for v in val] - notAllowed = [v for v in val if not v in allowedElems] + notAllowed = [v for v in val if v not in allowedElems] if notAllowed: raise ValueError("Invalid value(s) for option: %s" % ','.join(notAllowed)) diff --git a/src/engine/SCons/Variables/ListVariableTests.py b/src/engine/SCons/Variables/ListVariableTests.py index ff933d0..86e2c9e 100644 --- a/src/engine/SCons/Variables/ListVariableTests.py +++ b/src/engine/SCons/Variables/ListVariableTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/ListVariableTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/ListVariableTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import copy import sys diff --git a/src/engine/SCons/Variables/PackageVariable.py b/src/engine/SCons/Variables/PackageVariable.py index e2b3394..7c23cca 100644 --- a/src/engine/SCons/Variables/PackageVariable.py +++ b/src/engine/SCons/Variables/PackageVariable.py @@ -50,7 +50,7 @@ Usage example: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/PackageVariable.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/PackageVariable.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __all__ = ['PackageVariable',] diff --git a/src/engine/SCons/Variables/PackageVariableTests.py b/src/engine/SCons/Variables/PackageVariableTests.py index 636e2c1..2e48536 100644 --- a/src/engine/SCons/Variables/PackageVariableTests.py +++ b/src/engine/SCons/Variables/PackageVariableTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/PackageVariableTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/PackageVariableTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Variables/PathVariable.py b/src/engine/SCons/Variables/PathVariable.py index 52df0af..bf123fb 100644 --- a/src/engine/SCons/Variables/PathVariable.py +++ b/src/engine/SCons/Variables/PathVariable.py @@ -67,7 +67,7 @@ Usage example:: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/PathVariable.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/PathVariable.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __all__ = ['PathVariable',] diff --git a/src/engine/SCons/Variables/PathVariableTests.py b/src/engine/SCons/Variables/PathVariableTests.py index 1cd35f6..d862887 100644 --- a/src/engine/SCons/Variables/PathVariableTests.py +++ b/src/engine/SCons/Variables/PathVariableTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/PathVariableTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/PathVariableTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys diff --git a/src/engine/SCons/Variables/VariablesTests.py b/src/engine/SCons/Variables/VariablesTests.py index 7712bb4..ed2edab 100644 --- a/src/engine/SCons/Variables/VariablesTests.py +++ b/src/engine/SCons/Variables/VariablesTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/Variables/VariablesTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/VariablesTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/Variables/__init__.py b/src/engine/SCons/Variables/__init__.py index bde5b88..5b4b3af 100644 --- a/src/engine/SCons/Variables/__init__.py +++ b/src/engine/SCons/Variables/__init__.py @@ -26,7 +26,7 @@ customizable variables to an SCons build. # 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__ = "src/engine/SCons/Variables/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Variables/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os.path import sys @@ -99,7 +99,7 @@ class Variables(object): option.converter = converter self.options.append(option) - + # options might be added after the 'unknown' dict has been set up, # so we remove the key and all its aliases from that dict for alias in list(option.aliases) + [ option.key ]: @@ -168,7 +168,7 @@ class Variables(object): # first set the defaults: for option in self.options: - if not option.default is None: + if option.default is not None: values[option.key] = option.default # next set the value specified in the options file @@ -288,7 +288,7 @@ class Variables(object): env - an environment that is used to get the current values of the options. - cmp - Either a function as follows: The specific sort function should take two arguments and return -1, 0 or 1 + cmp - Either a function as follows: The specific sort function should take two arguments and return -1, 0 or 1 or a boolean to indicate if it should be sorted. """ diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py index 4c6031a..1c84d57 100644 --- a/src/engine/SCons/Warnings.py +++ b/src/engine/SCons/Warnings.py @@ -27,7 +27,7 @@ This file implements the warnings framework for SCons. """ -__revision__ = "src/engine/SCons/Warnings.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/Warnings.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys @@ -193,9 +193,10 @@ def warn(clazz, *args): break def process_warn_strings(arguments): - """Process string specifications of enabling/disabling warnings, - as passed to the --warn option or the SetOption('warn') function. - + """Process requests to enable/disable warnings. + + The requests are strings passed to the --warn option or the + SetOption('warn') function. An argument to this option should be of the form or no-. The warning class is munged in order @@ -210,7 +211,6 @@ def process_warn_strings(arguments): As a special case, --warn=all and --warn=no-all will enable or disable (respectively) the base Warning class of all warnings. - """ def _capitalize(s): diff --git a/src/engine/SCons/WarningsTests.py b/src/engine/SCons/WarningsTests.py index 5257050..2b36eb2 100644 --- a/src/engine/SCons/WarningsTests.py +++ b/src/engine/SCons/WarningsTests.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/WarningsTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/WarningsTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import sys import unittest diff --git a/src/engine/SCons/__init__.py b/src/engine/SCons/__init__.py index 6b22009..c915441 100644 --- a/src/engine/SCons/__init__.py +++ b/src/engine/SCons/__init__.py @@ -27,17 +27,17 @@ The main package for the SCons software construction utility. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" -__version__ = "3.0.5" +__version__ = "3.1.0" -__build__ = "103260fce95bf5db1c35fb2371983087d85dd611" +__build__ = "e724ae812eb96f4858a132f5b8c769724744faf6" -__buildsys__ = "Williams-MBP-2" +__buildsys__ = "kufra" -__date__ = "2019-07-13 18:25:30" +__date__ = "2019-07-21 00:04:47" -__developer__ = "bdbaddog" +__developer__ = "bdeegan" # make sure compatibility is always in place import SCons.compat diff --git a/src/engine/SCons/compat/__init__.py b/src/engine/SCons/compat/__init__.py index 1e744e4..dcbd8e8 100644 --- a/src/engine/SCons/compat/__init__.py +++ b/src/engine/SCons/compat/__init__.py @@ -57,31 +57,22 @@ function defined below loads the module as the "real" name (without the rest of our code will find our pre-loaded compatibility module. """ -__revision__ = "src/engine/SCons/compat/__init__.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/compat/__init__.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys -import imp # Use the "imp" module to protect imports from fixers. +import importlib PYPY = hasattr(sys, 'pypy_translation_info') -def import_as(module, name): - """ - Imports the specified module (from our local directory) as the - specified name, returning the loaded module object. - """ - dir = os.path.split(__file__)[0] - return imp.load_module(name, *imp.find_module(module, [dir])) - - def rename_module(new, old): """ - Attempts to import the old module and load it under the new name. + Attempt to import the old module and load it under the new name. Used for purely cosmetic name changes in Python 3.x. """ try: - sys.modules[new] = imp.load_module(old, *imp.find_module(old)) + sys.modules[new] = importlib.import_module(old) return True except ImportError: return False @@ -122,28 +113,28 @@ except AttributeError: # intern into the sys package sys.intern = intern -# Preparing for 3.x. UserDict, UserList, UserString are in -# collections for 3.x, but standalone in 2.7.x +# UserDict, UserList, UserString are in # collections for 3.x, +# but standalone in 2.7.x. Monkey-patch into collections for 2.7. import collections try: collections.UserDict except AttributeError: - exec ('from UserDict import UserDict as _UserDict') + from UserDict import UserDict as _UserDict collections.UserDict = _UserDict del _UserDict try: collections.UserList except AttributeError: - exec ('from UserList import UserList as _UserList') + from UserList import UserList as _UserList collections.UserList = _UserList del _UserList try: collections.UserString except AttributeError: - exec ('from UserString import UserString as _UserString') + from UserString import UserString as _UserString collections.UserString = _UserString del _UserString diff --git a/src/engine/SCons/compat/_scons_dbm.py b/src/engine/SCons/compat/_scons_dbm.py index 31d6803..ec5e261 100644 --- a/src/engine/SCons/compat/_scons_dbm.py +++ b/src/engine/SCons/compat/_scons_dbm.py @@ -30,7 +30,7 @@ that the whichdb.whichdb() implementstation in the various 2.X versions of Python won't blow up even if dbm wasn't compiled in. """ -__revision__ = "src/engine/SCons/compat/_scons_dbm.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/compat/_scons_dbm.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" class error(Exception): pass diff --git a/src/engine/SCons/cpp.py b/src/engine/SCons/cpp.py index e500711..7c4ac92 100644 --- a/src/engine/SCons/cpp.py +++ b/src/engine/SCons/cpp.py @@ -21,7 +21,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/cpp.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/cpp.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ SCons C Pre-Processor module @@ -165,7 +165,7 @@ def CPP_to_Python(s): """ s = CPP_to_Python_Ops_Expression.sub(CPP_to_Python_Ops_Sub, s) for expr, repl in CPP_to_Python_Eval_List: - s = expr.sub(repl, s) + s = re.sub(expr, repl, s) return s @@ -210,7 +210,7 @@ class FunctionEvaluator(object): parts = [] for s in self.expansion: - if not s in self.args: + if s not in self.args: s = repr(s) parts.append(s) statement = ' + '.join(parts) diff --git a/src/engine/SCons/cppTests.py b/src/engine/SCons/cppTests.py index 7ebdf99..6d41ab0 100644 --- a/src/engine/SCons/cppTests.py +++ b/src/engine/SCons/cppTests.py @@ -23,7 +23,7 @@ from __future__ import absolute_import -__revision__ = "src/engine/SCons/cppTests.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/cppTests.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import atexit import sys diff --git a/src/engine/SCons/exitfuncs.py b/src/engine/SCons/exitfuncs.py index 3f4c1ac..c5414a2 100644 --- a/src/engine/SCons/exitfuncs.py +++ b/src/engine/SCons/exitfuncs.py @@ -27,7 +27,7 @@ Register functions which are executed when SCons exits for any reason. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -__revision__ = "src/engine/SCons/exitfuncs.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/engine/SCons/exitfuncs.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import atexit diff --git a/src/script/scons-configure-cache.py b/src/script/scons-configure-cache.py index 7c5d3b6..757a79f 100644 --- a/src/script/scons-configure-cache.py +++ b/src/script/scons-configure-cache.py @@ -37,17 +37,17 @@ import glob import json import os -__revision__ = "src/script/scons-configure-cache.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/script/scons-configure-cache.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" -__version__ = "3.0.5" +__version__ = "3.1.0" -__build__ = "103260fce95bf5db1c35fb2371983087d85dd611" +__build__ = "e724ae812eb96f4858a132f5b8c769724744faf6" -__buildsys__ = "Williams-MBP-2" +__buildsys__ = "kufra" -__date__ = "2019-07-13 18:25:30" +__date__ = "2019-07-21 00:04:47" -__developer__ = "bdbaddog" +__developer__ = "bdeegan" def rearrange_cache_entries(current_prefix_len, new_prefix_len): diff --git a/src/script/scons-time.py b/src/script/scons-time.py index b2ae22b..975ff64 100644 --- a/src/script/scons-time.py +++ b/src/script/scons-time.py @@ -31,7 +31,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import division, print_function -__revision__ = "src/script/scons-time.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/script/scons-time.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import getopt import glob @@ -41,21 +41,7 @@ import shutil import sys import tempfile import time - -def make_temp_file(**kw): - try: - result = tempfile.mktemp(**kw) - result = os.path.realpath(result) - except TypeError: - try: - save_template = tempfile.template - prefix = kw['prefix'] - del kw['prefix'] - tempfile.template = prefix - result = tempfile.mktemp(**kw) - finally: - tempfile.template = save_template - return result +import subprocess def HACK_for_exec(cmd, *args): """ @@ -121,7 +107,7 @@ class Line(object): # in the line's index number. We might want to represent # this some way rather than just drawing the line straight # between the two points on either side. - if not y is None: + if y is not None: print(fmt % (x, y)) print('e') @@ -155,13 +141,13 @@ class Gnuplotter(Plotter): result = [] for line in self.lines: result.extend(line.get_x_values()) - return [r for r in result if not r is None] + return [r for r in result if r is not None] def get_all_y_values(self): result = [] for line in self.lines: result.extend(line.get_y_values()) - return [r for r in result if not r is None] + return [r for r in result if r is not None] def get_min_x(self): try: @@ -248,14 +234,16 @@ def unzip(fname): os.makedirs(dir) except: pass - open(name, 'wb').write(zf.read(name)) + with open(name, 'wb') as f: + f.write(zf.read(name)) def read_tree(dir): for dirpath, dirnames, filenames in os.walk(dir): for fn in filenames: fn = os.path.join(dirpath, fn) if os.path.isfile(fn): - open(fn, 'rb').read() + with open(fn, 'rb') as f: + f.read() def redirect_to_file(command, log): return '%s > %s 2>&1' % (command, log) @@ -264,7 +252,7 @@ def tee_to_file(command, log): return '%s 2>&1 | tee %s' % (command, log) - + class SConsTimer(object): """ Usage: scons-time SUBCOMMAND [ARGUMENTS] @@ -360,7 +348,7 @@ class SConsTimer(object): 'SCons' : 'Total SCons execution time', 'commands' : 'Total command execution time', } - + time_string_all = 'Total .* time' # @@ -456,14 +444,20 @@ class SConsTimer(object): def log_execute(self, command, log): command = self.subst(command, self.__dict__) - output = os.popen(command).read() + p = os.popen(command) + output = p.read() + p.close() + #TODO: convert to subrocess, os.popen is obsolete. This didn't work: + #process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) + #output = process.stdout.read() + #process.stdout.close() + #process.wait() if self.verbose: sys.stdout.write(output) # TODO: Figure out # Not sure we need to write binary here - open(log, 'w').write(output) - - # + with open(log, 'w') as f: + f.write(str(output)) def archive_splitext(self, path): """ @@ -628,13 +622,14 @@ class SConsTimer(object): search_string = self.time_string_all else: search_string = time_string - contents = open(file).read() + with open(file) as f: + contents = f.read() if not contents: sys.stderr.write('file %s has no contents!\n' % repr(file)) return None result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] result = [ float(r) for r in result ] - if not time_string is None: + if time_string is not None: try: result = result[0] except IndexError: @@ -673,7 +668,8 @@ class SConsTimer(object): search_string = self.memory_string_all else: search_string = memory_string - lines = open(file).readlines() + with open(file) as f: + lines = f.readlines() lines = [ l for l in lines if l.startswith(search_string) ][-4:] result = [ int(l.split()[-1]) for l in lines[-4:] ] if len(result) == 1: @@ -685,14 +681,14 @@ class SConsTimer(object): Returns the counts of the specified object_name. """ object_string = ' ' + object_name + '\n' - lines = open(file).readlines() + with open(file) as f: + lines = f.readlines() line = [ l for l in lines if l.endswith(object_string) ][0] result = [ int(field) for field in line.split()[:4] ] if index is not None: result = result[index] return result - # command_alias = {} @@ -925,7 +921,7 @@ class SConsTimer(object): elif o in ('-p', '--prefix'): self.prefix = a elif o in ('--stage',): - if not a in self.stages: + if a not in self.stages: sys.stderr.write('%s: mem: Unrecognized stage "%s".\n' % (self.name, a)) sys.exit(1) stage = a @@ -1039,7 +1035,7 @@ class SConsTimer(object): elif o in ('-p', '--prefix'): self.prefix = a elif o in ('--stage',): - if not a in self.stages: + if a not in self.stages: sys.stderr.write('%s: obj: Unrecognized stage "%s".\n' % (self.name, a)) sys.stderr.write('%s Type "%s help obj" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) @@ -1239,7 +1235,7 @@ class SConsTimer(object): return os.path.join(dir, 'src', 'engine') def prep_aegis_run(self, commands, removals): - self.aegis_tmpdir = make_temp_file(prefix = self.name + '-aegis-') + self.aegis_tmpdir = tempfile.mkdtemp(prefix=self.name + '-aegis-') removals.append((shutil.rmtree, 'rm -rf %%s', self.aegis_tmpdir)) self.aegis_parent_project = os.path.splitext(self.aegis_project)[0] @@ -1247,21 +1243,19 @@ class SConsTimer(object): self.scons_lib_dir = self.scons_lib_dir_path(self.aegis_tmpdir) commands.extend([ - 'mkdir %(aegis_tmpdir)s', (lambda: os.chdir(self.aegis_tmpdir), 'cd %(aegis_tmpdir)s'), '%(aegis)s -cp -ind -p %(aegis_parent_project)s .', '%(aegis)s -cp -ind -p %(aegis_project)s -delta %(run_number)s .', ]) def prep_subversion_run(self, commands, removals): - self.svn_tmpdir = make_temp_file(prefix = self.name + '-svn-') + self.svn_tmpdir = tempfile.mkdtemp(prefix=self.name + '-svn-') removals.append((shutil.rmtree, 'rm -rf %%s', self.svn_tmpdir)) self.scons = self.scons_path(self.svn_tmpdir) self.scons_lib_dir = self.scons_lib_dir_path(self.svn_tmpdir) commands.extend([ - 'mkdir %(svn_tmpdir)s', '%(svn)s co %(svn_co_flag)s -r %(run_number)s %(subversion_url)s %(svn_tmpdir)s', ]) @@ -1308,11 +1302,9 @@ class SConsTimer(object): if self.targets2 is None: self.targets2 = self.targets - self.tmpdir = make_temp_file(prefix = self.name + '-') + self.tmpdir = tempfile.mkdtemp(prefix=self.name + '-') commands.extend([ - 'mkdir %(tmpdir)s', - (os.chdir, 'cd %%s', self.tmpdir), ]) @@ -1365,7 +1357,6 @@ class SConsTimer(object): if not os.environ.get('PRESERVE'): commands.extend(removals) - commands.append((shutil.rmtree, 'rm -rf %%s', self.tmpdir)) self.run_command_list(commands, self.__dict__) @@ -1432,14 +1423,16 @@ class SConsTimer(object): elif o in ('--title',): self.title = a elif o in ('--which',): - if not a in list(self.time_strings.keys()): + if a not in list(self.time_strings.keys()): sys.stderr.write('%s: time: Unrecognized timer "%s".\n' % (self.name, a)) sys.stderr.write('%s Type "%s help time" for help.\n' % (self.name_spaces, self.name)) sys.exit(1) which = a if self.config_file: - HACK_for_exec(open(self.config_file, 'r').read(), self.__dict__) + with open(self.config_file, 'r') as f: + config = f.read() + HACK_for_exec(config, self.__dict__) if self.chdir: os.chdir(self.chdir) diff --git a/src/script/scons.bat b/src/script/scons.bat index 156aa9c..75e2a49 100644 --- a/src/script/scons.bat +++ b/src/script/scons.bat @@ -1,11 +1,11 @@ @REM Copyright (c) 2001 - 2019 The SCons Foundation -@REM src/script/scons.bat 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog +@REM src/script/scons.bat e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan @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-3.0.5'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-3.0.5'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %1 %2 %3 %4 %5 %6 %7 %8 %9 +python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-3.1.0'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-3.1.0'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %1 %2 %3 %4 %5 %6 %7 %8 %9 @REM no way to set exit status of this script for 9x/Me goto endscons diff --git a/src/script/scons.py b/src/script/scons.py index e58d8e2..b3172b1 100755 --- a/src/script/scons.py +++ b/src/script/scons.py @@ -25,17 +25,17 @@ from __future__ import print_function -__revision__ = "src/script/scons.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/script/scons.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" -__version__ = "3.0.5" +__version__ = "3.1.0" -__build__ = "103260fce95bf5db1c35fb2371983087d85dd611" +__build__ = "e724ae812eb96f4858a132f5b8c769724744faf6" -__buildsys__ = "Williams-MBP-2" +__buildsys__ = "kufra" -__date__ = "2019-07-13 18:25:30" +__date__ = "2019-07-21 00:04:47" -__developer__ = "bdbaddog" +__developer__ = "bdeegan" # This is the entry point to the SCons program. # The only job of this script is to work out where the guts of the program diff --git a/src/script/sconsign.py b/src/script/sconsign.py index 217193a..a0d7b7e 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -25,17 +25,17 @@ from __future__ import print_function -__revision__ = "src/script/sconsign.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/script/sconsign.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" -__version__ = "3.0.5" +__version__ = "3.1.0" -__build__ = "103260fce95bf5db1c35fb2371983087d85dd611" +__build__ = "e724ae812eb96f4858a132f5b8c769724744faf6" -__buildsys__ = "Williams-MBP-2" +__buildsys__ = "kufra" -__date__ = "2019-07-13 18:25:30" +__date__ = "2019-07-21 00:04:47" -__developer__ = "bdbaddog" +__developer__ = "bdeegan" import os import sys @@ -117,7 +117,7 @@ else: # check `pwd`/lib/scons*. temp.append(os.getcwd()) else: - if script_dir == '.' or script_dir == '': + if script_dir in ('.', ''): script_dir = os.getcwd() head, tail = os.path.split(script_dir) if tail == "bin": @@ -185,13 +185,13 @@ import SCons.compat try: import whichdb + whichdb = whichdb.whichdb except ImportError as e: from dbm import whichdb import time import pickle -import imp import SCons.SConsign @@ -200,9 +200,8 @@ def my_whichdb(filename): if filename[-7:] == ".dblite": return "SCons.dblite" try: - f = open(filename + ".dblite", "rb") - f.close() - return "SCons.dblite" + with open(filename + ".dblite", "rb"): + return "SCons.dblite" except IOError: pass return _orig_whichdb(filename) @@ -217,6 +216,8 @@ whichdb = my_whichdb #dbm.whichdb = my_whichdb def my_import(mname): + import imp + if '.' in mname: i = mname.rfind('.') parent = my_import(mname[:i]) @@ -247,9 +248,8 @@ Readable = 0 Warns = 0 - def default_mapper(entry, name): - ''' + """ Stringify an entry that doesn't have an explicit mapping. Args: @@ -258,10 +258,10 @@ def default_mapper(entry, name): Returns: str - ''' + """ try: val = eval("entry." + name) - except: + except AttributeError: val = None if sys.version_info.major >= 3 and isinstance(val, bytes): # This is a dirty hack for py 2/3 compatibility. csig is a bytes object @@ -272,7 +272,7 @@ def default_mapper(entry, name): def map_action(entry, _): - ''' + """ Stringify an action entry and signature. Args: @@ -281,7 +281,7 @@ def map_action(entry, _): Returns: str - ''' + """ try: bact = entry.bact bactsig = entry.bactsig @@ -289,8 +289,9 @@ def map_action(entry, _): return None return '%s [%s]' % (bactsig, bact) + def map_timestamp(entry, _): - ''' + """ Stringify a timestamp entry. Args: @@ -299,7 +300,7 @@ def map_timestamp(entry, _): Returns: str - ''' + """ try: timestamp = entry.timestamp except AttributeError: @@ -309,8 +310,9 @@ def map_timestamp(entry, _): else: return str(timestamp) + def map_bkids(entry, _): - ''' + """ Stringify an implicit entry. Args: @@ -319,7 +321,7 @@ def map_bkids(entry, _): Returns: str - ''' + """ try: bkids = entry.bsources + entry.bdepends + entry.bimplicit bkidsigs = entry.bsourcesigs + entry.bdependsigs + entry.bimplicitsigs @@ -448,8 +450,8 @@ class Do_SConsignDB(object): def __call__(self, fname): # The *dbm modules stick their own file suffixes on the names - # that are passed in. This is causes us to jump through some - # hoops here to be able to allow the user + # that are passed in. This causes us to jump through some + # hoops here. try: # Try opening the specified file name. Example: # SPECIFIED OPENED BY self.dbm.open() @@ -461,16 +463,17 @@ class Do_SConsignDB(object): print_e = e try: # That didn't work, so try opening the base name, - # so that if the actually passed in 'sconsign.dblite' + # so that if they actually passed in 'sconsign.dblite' # (for example), the dbm module will put the suffix back # on for us and open it anyway. db = self.dbm.open(os.path.splitext(fname)[0], "r") except (IOError, OSError): # That didn't work either. See if the file name - # they specified just exists (independent of the dbm + # they specified even exists (independent of the dbm # suffix-mangling). try: - open(fname, "r") + with open(fname, "rb"): + pass # this is a touch only, we don't use it here. except (IOError, OSError) as e: # Nope, that file doesn't even exist, so report that # fact back. @@ -486,6 +489,9 @@ class Do_SConsignDB(object): except Exception as e: sys.stderr.write("sconsign: ignoring invalid `%s' file `%s': %s\n" % (self.dbm_name, fname, e)) + exc_type, _, _ = sys.exc_info() + if exc_type.__name__ == "ValueError" and sys.version_info < (3,0,0): + sys.stderr.write("Python 2 only supports pickle protocols 0-2.\n") return if Print_Directories: @@ -512,23 +518,23 @@ class Do_SConsignDB(object): def Do_SConsignDir(name): try: - fp = open(name, 'rb') + with open(name, 'rb') as fp: + try: + sconsign = SCons.SConsign.Dir(fp) + except KeyboardInterrupt: + raise + except pickle.UnpicklingError: + err = "sconsign: ignoring invalid .sconsign file `%s'\n" % (name) + sys.stderr.write(err) + return + except Exception as e: + err = "sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e) + sys.stderr.write(err) + return + printentries(sconsign.entries, args[0]) except (IOError, OSError) as e: sys.stderr.write("sconsign: %s\n" % e) return - try: - sconsign = SCons.SConsign.Dir(fp) - except KeyboardInterrupt: - raise - except pickle.UnpicklingError: - err = "sconsign: ignoring invalid .sconsign file `%s'\n" % (name) - sys.stderr.write(err) - return - except Exception as e: - err = "sconsign: ignoring invalid .sconsign file `%s': %s\n" % (name, e) - sys.stderr.write(err) - return - printentries(sconsign.entries, args[0]) ############################################################################## @@ -536,7 +542,7 @@ def Do_SConsignDir(name): import getopt helpstr = """\ -Usage: sconsign [OPTIONS] FILE [...] +Usage: sconsign [OPTIONS] [FILE ...] Options: -a, --act, --action Print build action information. -c, --csig Print content signature information. @@ -552,13 +558,17 @@ Options: -v, --verbose Verbose, describe each field. """ -opts, args = getopt.getopt(sys.argv[1:], "acd:e:f:hirstv", - ['act', 'action', - 'csig', 'dir=', 'entry=', - 'format=', 'help', 'implicit', - 'raw', 'readable', - 'size', 'timestamp', 'verbose']) - +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']) +except getopt.GetoptError as err: + sys.stderr.write(str(err) + '\n') + print(helpstr) + sys.exit(2) for o, a in opts: if o in ('-a', '--act', '--action'): @@ -586,7 +596,7 @@ for o, a in opts: # this was handled by calling my_import('SCons.dblite') # again in earlier versions... SCons.dblite.ignore_corrupt_dbfiles = 0 - except: + except ImportError: sys.stderr.write("sconsign: illegal file format `%s'\n" % a) print(helpstr) sys.exit(2) @@ -613,6 +623,8 @@ if Do_Call: for a in args: Do_Call(a) else: + if not args: + args = [".sconsign.dblite"] for a in args: dbm_name = whichdb(a) if dbm_name: diff --git a/src/setup.py b/src/setup.py index 2d021c2..790b1ea 100755 --- a/src/setup.py +++ b/src/setup.py @@ -44,13 +44,13 @@ NOTE: Installed SCons is not importable like usual Python packages. It is """ -__revision__ = "src/setup.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/setup.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import stat import sys -Version = "3.0.5" +Version = "3.1.0" man_pages = [ 'scons.1', diff --git a/src/test_files.py b/src/test_files.py index cab1045..42fb397 100644 --- a/src/test_files.py +++ b/src/test_files.py @@ -23,7 +23,7 @@ # from __future__ import print_function -__revision__ = "src/test_files.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/test_files.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" """ Verify that we have certain important files in our distribution diff --git a/src/test_interrupts.py b/src/test_interrupts.py index 6b8c7c3..4ae0969 100644 --- a/src/test_interrupts.py +++ b/src/test_interrupts.py @@ -23,7 +23,7 @@ from __future__ import print_function -__revision__ = "src/test_interrupts.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/test_interrupts.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" """ Verify that the SCons source code contains only correct handling of @@ -87,7 +87,8 @@ exceptall_pat = re.compile(r' *except(?: *| +Exception *, *[^: ]+):[^\n]*') uncaughtKeyboardInterrupt = 0 for f in files: - contents = open(os.path.join(scons_lib_dir, f)).read() + with open(os.path.join(scons_lib_dir, f)) as ifp: + contents = ifp.read() try_except_lines = {} lastend = 0 while True: diff --git a/src/test_pychecker.py b/src/test_pychecker.py index 058189e..a93033d 100644 --- a/src/test_pychecker.py +++ b/src/test_pychecker.py @@ -22,7 +22,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from __future__ import print_function -__revision__ = "src/test_pychecker.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/test_pychecker.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" """ Use pychecker to catch various Python coding errors. @@ -63,7 +63,8 @@ else: src_engine_ = os.path.join(src_engine, '') MANIFEST = os.path.join(src_engine, 'MANIFEST.in') -files = open(MANIFEST).read().split() +with open(MANIFEST) as f: + files = f.read().split() files = [f for f in files if f[-3:] == '.py'] diff --git a/src/test_setup.py b/src/test_setup.py index 2726465..b540ab2 100644 --- a/src/test_setup.py +++ b/src/test_setup.py @@ -23,7 +23,7 @@ # from __future__ import print_function -__revision__ = "src/test_setup.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/test_setup.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" """ Test how the setup.py script installs SCons. @@ -176,21 +176,26 @@ tar_gz = os.path.join(cwd, 'build', 'dist', '%s.tar.gz' % scons_version) zip = os.path.join(cwd, 'build', 'dist', '%s.zip' % scons_version) if os.path.isfile(zip): - try: import zipfile - except ImportError: pass + try: + import zipfile + except ImportError: + pass else: - zf = zipfile.ZipFile(zip, 'r') - - for name in zf.namelist(): - dir = os.path.dirname(name) - try: os.makedirs(dir) - except: pass - # 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(name) or os.path.islink(name): - os.unlink(name) - if not os.path.isdir(name): - open(name, 'w').write(zf.read(name)) + with zipfile.ZipFile(zip, 'r') as zf: + + for name in zf.namelist(): + dname = os.path.dirname(name) + try: + os.makedirs(dname) + except FileExistsError: + pass + # 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(name) or os.path.islink(name): + os.unlink(name) + if not os.path.isdir(name): + with open(name, 'w') as ofp: + ofp.write(zf.read(name)) if not os.path.isdir(scons_version) and os.path.isfile(tar_gz): # Unpack the .tar.gz file. This should create the scons_version/ diff --git a/src/test_strings.py b/src/test_strings.py index 59ac613..ad2c755 100644 --- a/src/test_strings.py +++ b/src/test_strings.py @@ -23,7 +23,7 @@ # from __future__ import print_function -__revision__ = "src/test_strings.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "src/test_strings.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" """ Verify that we have proper strings like Copyright notices on all the @@ -98,7 +98,8 @@ class Checker(object): for fname in filenames: fpath = os.path.join(dirpath, fname) if self.search_this(fpath) and not self.remove_this(fname, fpath): - body = open(fpath, 'r').read() + with open(fpath, 'r') as f: + body = f.read() for expr in self.expressions: if not expr.search(body): msg = '%s: missing %s' % (fpath, repr(expr.pattern)) @@ -108,7 +109,7 @@ class Checker(object): class CheckUnexpandedStrings(Checker): expressions = [ re.compile('Copyright (c) 2001 - 2019 The SCons Foundation'), - re.compile('src/test_strings.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog'), + re.compile('src/test_strings.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan'), ] def must_be_built(self): return None diff --git a/testing/framework/README.txt b/testing/framework/README.txt index 0e0f2bb..817cb3f 100644 --- a/testing/framework/README.txt +++ b/testing/framework/README.txt @@ -47,4 +47,4 @@ the pieces here are local to SCons. Test infrastructure for the sconsign.py script. Copyright (c) 2001 - 2019 The SCons Foundation -testing/framework/README.txt 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog +testing/framework/README.txt e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan diff --git a/testing/framework/SConscript b/testing/framework/SConscript index 535f187..f79a254 100644 --- a/testing/framework/SConscript +++ b/testing/framework/SConscript @@ -41,12 +41,13 @@ files = [ def copy(target, source, env): t = str(target[0]) s = str(source[0]) - c = open(s, 'r').read() - # Note: We construct the __ VERSION __ substitution string at - # run-time so it doesn't get replaced when this file gets copied - # into the tree for packaging. - c = c.replace('__' + 'VERSION' + '__', env['VERSION']) - open(t, 'w').write(c) + with open(s, 'r') as i, open(t, 'w') as o: + c = i.read() + # Note: We construct the __ VERSION __ substitution string at + # run-time so it doesn't get replaced when this file gets copied + # into the tree for packaging. + c = c.replace('__' + 'VERSION' + '__', env['VERSION']) + o.write(c) for file in files: # Guarantee that real copies of these files always exist in diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index a6a8045..81e03f3 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -366,13 +366,11 @@ else: def is_String(e): return isinstance(e, (str, unicode, UserString)) -tempfile.template = 'testcmd.' +testprefix = 'testcmd.' if os.name in ('posix', 'nt'): - tempfile.template = 'testcmd.' + str(os.getpid()) + '.' -else: - tempfile.template = 'testcmd.' + testprefix += "%s." % str(os.getpid()) -re_space = re.compile('\s') +re_space = re.compile(r'\s') def _caller(tblist, skip): @@ -470,6 +468,14 @@ def pass_test(self=None, condition=1, function=None): def match_exact(lines=None, matches=None, newline=os.sep): """ + Match function using exact match. + + :param lines: data lines + :type lines: str or list[str] + :param matches: expected lines to match + :type matches: str or list[str] + :param newline: line separator + :returns: an object (1) on match, else None, like re.match """ if isinstance(lines, bytes) or bytes is str: @@ -480,30 +486,51 @@ def match_exact(lines=None, matches=None, newline=os.sep): if not is_List(matches): matches = matches.split(newline) if len(lines) != len(matches): - return - for i in range(len(lines)): - if lines[i] != matches[i]: - return + return None + for line, match in zip(lines, matches): + if line != match: + return None return 1 def match_caseinsensitive(lines=None, matches=None): """ + Match function using case-insensitive matching. + + Only a simplistic comparison is done, based on lowercasing the + strings. This has plenty of holes for unicode data using + non-English languages. + + TODO: casefold() is better than lower() if we don't need Py2 support. + + :param lines: data lines + :type lines: str or list[str] + :param matches: expected lines to match + :type matches: str or list[str] + :returns: True or False + :returns: an object (1) on match, else None, like re.match """ if not is_List(lines): lines = lines.split("\n") if not is_List(matches): matches = matches.split("\n") if len(lines) != len(matches): - return - for i in range(len(lines)): - if lines[i].lower() != matches[i].lower(): - return + return None + for line, match in zip(lines, matches): + if line.lower() != match.lower(): + return None return 1 def match_re(lines=None, res=None): """ + Match function using line-by-line regular expression match. + + :param lines: data lines + :type lines: str or list[str] + :param res: regular expression(s) for matching + :type res: str or list[str] + :returns: an object (1) on match, else None, like re.match """ if not is_List(lines): # CRs mess up matching (Windows) so split carefully @@ -512,29 +539,39 @@ def match_re(lines=None, res=None): res = res.split("\n") if len(lines) != len(res): print("match_re: expected %d lines, found %d" % (len(res), len(lines))) - return - for i in range(len(lines)): - s = "^" + res[i] + "$" + return None + for i, (line, regex) in enumerate(zip(lines, res)): + s = r"^{}$".format(regex) try: expr = re.compile(s) except re.error as e: msg = "Regular expression error in %s: %s" raise re.error(msg % (repr(s), e.args[0])) - if not expr.search(lines[i]): - print("match_re: mismatch at line %d:\n search re='%s'\n line='%s'" % ( - i, s, lines[i])) - return + if not expr.search(line): + miss_tmpl = "match_re: mismatch at line {}:\n search re='{}'\n line='{}'" + print(miss_tmpl.format(i, s, line)) + return None return 1 def match_re_dotall(lines=None, res=None): """ + Match function using regular expression match. + + Unlike match_re, the arguments are converted to strings (if necessary) + and must match exactly. + + :param lines: data lines + :type lines: str or list[str] + :param res: regular expression(s) for matching + :type res: str or list[str] + :returns: a match object, or None as for re.match """ if not isinstance(lines, str): lines = "\n".join(lines) if not isinstance(res, str): res = "\n".join(res) - s = "^" + res + "$" + s = r"^{}$".format(res) try: expr = re.compile(s, re.DOTALL) except re.error as e: @@ -544,11 +581,32 @@ def match_re_dotall(lines=None, res=None): def simple_diff(a, b, fromfile='', tofile='', - fromfiledate='', tofiledate='', n=3, lineterm='\n'): - """ - A function with the same calling signature as difflib.context_diff - (diff -c) and difflib.unified_diff (diff -u) but which prints - output like the simple, unadorned 'diff" command. + fromfiledate='', tofiledate='', n=0, lineterm=''): + r""" + Compare two sequences of lines; generate the delta as a simple diff. + + Similar to difflib.context_diff and difflib.unified_diff but + output is like from the 'diff" command without arguments. The function + keeps the same signature as the difflib ones so they will be + interchangeable, but except for lineterm, the arguments beyond the + two sequences are ignored in this version. By default, the + diff is not created with trailing newlines, set the lineterm + argument to '\n' to do so. + + :raises re.error: if a regex fails to compile + + Example: + + >>> print(''.join(simple_diff('one\ntwo\nthree\nfour\n'.splitlines(True), + ... 'zero\none\ntree\nfour\n'.splitlines(True), lineterm='\n'))) + 0a1 + > zero + 2,3c3 + < two + < three + --- + > tree + """ a = [to_str(q) for q in a] b = [to_str(q) for q in b] @@ -556,25 +614,30 @@ def simple_diff(a, b, fromfile='', tofile='', def comma(x1, x2): return x1 + 1 == x2 and str(x2) or '%s,%s' % (x1 + 1, x2) - result = [] + for op, a1, a2, b1, b2 in sm.get_opcodes(): if op == 'delete': - result.append("%sd%d" % (comma(a1, a2), b1)) - result.extend(['< ' + l for l in a[a1:a2]]) + yield "{}d{}{}".format(comma(a1, a2), b1, lineterm) + for l in a[a1:a2]: + yield '< ' + l elif op == 'insert': - result.append("%da%s" % (a1, comma(b1, b2))) - result.extend(['> ' + l for l in b[b1:b2]]) + yield "{}a{}{}".format(a1, comma(b1, b2), lineterm) + for l in b[b1:b2]: + yield '> ' + l elif op == 'replace': - result.append("%sc%s" % (comma(a1, a2), comma(b1, b2))) - result.extend(['< ' + l for l in a[a1:a2]]) - result.append('---') - result.extend(['> ' + l for l in b[b1:b2]]) - return result + yield "{}c{}{}".format(comma(a1, a2), comma(b1, b2), lineterm) + for l in a[a1:a2]: + yield '< ' + l + yield '---{}'.format(lineterm) + for l in b[b1:b2]: + yield '> ' + l def diff_re(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n'): """ + Compare a and b (lists of strings) where a are regexes. + A simple "diff" of two sets of lines when the expected lines are regular expressions. This is a really dumb thing that just compares each line in turn, so it doesn't look for @@ -587,9 +650,8 @@ def diff_re(a, b, fromfile='', tofile='', a = a + [''] * (-diff) elif diff > 0: b = b + [''] * diff - i = 0 - for aline, bline in zip(a, b): - s = "^" + aline + "$" + for i, (aline, bline) in enumerate(zip(a, b)): + s = r"^{}$".format(aline) try: expr = re.compile(s) except re.error as e: @@ -600,7 +662,6 @@ def diff_re(a, b, fromfile='', tofile='', result.append('< ' + repr(a[i])) result.append('---') result.append('> ' + repr(b[i])) - i = i + 1 return result @@ -974,6 +1035,12 @@ class TestCmd(object): self.subdir(subdir) self.fixture_dirs = [] + try: + self.fixture_dirs = (os.environ['FIXTURE_DIRS']).split(os.pathsep) + except KeyError: + pass + + def __del__(self): self.cleanup() @@ -1026,7 +1093,7 @@ class TestCmd(object): condition = self.condition if self._preserve[condition]: for dir in self._dirlist: - print(u"Preserved directory " + dir + "\n") + print(u"Preserved directory " + dir) else: list = self._dirlist[:] list.reverse() @@ -1242,6 +1309,7 @@ class TestCmd(object): def read(self, file, mode='rb', newline=None): """Reads and returns the contents of the specified file name. + The file name may be a list, in which case the elements are concatenated with the os.path.join() method. The file is assumed to be under the temporary working directory unless it @@ -1261,6 +1329,7 @@ class TestCmd(object): def rmdir(self, dir): """Removes the specified dir name. + The dir name may be a list, in which case the elements are concatenated with the os.path.join() method. The dir is assumed to be under the temporary working directory unless it @@ -1302,6 +1371,7 @@ class TestCmd(object): """Copies the contents of the specified folder srcdir from the directory of the called script, to the current working directory. + The srcdir name may be a list, in which case the elements are concatenated with the os.path.join() method. The dstdir is assumed to be under the temporary working directory, it gets @@ -1343,6 +1413,7 @@ class TestCmd(object): def file_fixture(self, srcfile, dstfile=None): """Copies the file srcfile from the directory of the called script, to the current working directory. + The dstfile is assumed to be under the temporary working directory unless it is an absolute path name. If dstfile is specified its target directory gets created @@ -1439,6 +1510,7 @@ class TestCmd(object): def fix_binary_stream(stream): """ Handle stdout/stderr from popen when we specify universal_newlines = False. + This will read from the pipes in binary mode, not decode the output, and not convert line endings to \n. We do this because in py3 (3.5) with universal_newlines=True, it will @@ -1455,7 +1527,7 @@ class TestCmd(object): return stream # TODO: Run full tests on both platforms and see if this fixes failures # It seems that py3.6 still sets text mode if you set encoding. - elif sys.version_info[0] == 3:# TODO and sys.version_info[1] < 6: + elif sys.version_info[0] == 3: # TODO and sys.version_info[1] < 6: stream = stream.decode('utf-8') stream = stream.replace('\r\n', '\n') elif sys.version_info[0] == 2: @@ -1629,7 +1701,7 @@ class TestCmd(object): try: os.mkdir(new) except OSError as e: - print("Got error :%s"%e) + print("Got error creating dir: %s :%s" % (sub, e)) pass else: count = count + 1 @@ -1662,10 +1734,11 @@ class TestCmd(object): """ if path is None: try: - path = tempfile.mktemp(prefix=tempfile.template) + path = tempfile.mkdtemp(prefix=testprefix) except TypeError: - path = tempfile.mktemp() - os.mkdir(path) + path = tempfile.mkdtemp() + else: + os.mkdir(path) # Symlinks in the path will report things # differently from os.getcwd(), so chdir there diff --git a/testing/framework/TestCmdTests.py b/testing/framework/TestCmdTests.py index b9226fd..ef76228 100644 --- a/testing/framework/TestCmdTests.py +++ b/testing/framework/TestCmdTests.py @@ -26,13 +26,22 @@ import os import shutil import signal import stat -from StringIO import StringIO +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO +from contextlib import closing import sys import tempfile import time import types import unittest -from UserList import UserList +try: + from collections import UserList +except ImportError: + from UserList import UserList + +from SCons.Util import to_bytes, to_str # Strip the current directory so we get the right TestCmd.py module. @@ -55,7 +64,7 @@ def _is_executable(path): def _clear_dict(dict, *keys): for key in keys: try: - dict[key] = '' # del dict[key] + del dict[key] except KeyError: pass @@ -156,9 +165,9 @@ class TestCmdTestCase(unittest.TestCase): stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = p.communicate(input) - stdout = self.translate_newlines(stdout) - stderr = self.translate_newlines(stderr) + stdout, stderr = p.communicate(to_bytes(input)) + stdout = self.translate_newlines(to_str(stdout)) + stderr = self.translate_newlines(to_str(stderr)) return stdout, stderr, p.returncode def popen_python(self, input, status=0, stdout="", stderr="", python=None): @@ -181,7 +190,7 @@ class TestCmdTestCase(unittest.TestCase): def run_match(self, content, *args): expect = "%s: %s: %s: %s\n" % args - content = self.translate_newlines(content) + content = self.translate_newlines(to_str(content)) assert content == expect, \ "Expected %s ==========\n" % args[1] + expect + \ "Actual %s ============\n" % args[1] + content @@ -257,6 +266,7 @@ result = TestCmd.TestCmd(workdir = '') sys.exit(0) """ % self.orig_cwd, stdout='my_exitfunc()\n') + @unittest.skipIf(TestCmd.IS_PY3, "No sys.exitfunc in Python 3") def test_exitfunc(self): """Test cleanup() when sys.exitfunc is set""" self.popen_python("""from __future__ import print_function @@ -271,7 +281,6 @@ sys.exit(0) """ % self.orig_cwd, stdout='my_exitfunc()\n') - class chmod_TestCase(TestCmdTestCase): def test_chmod(self): """Test chmod()""" @@ -280,8 +289,10 @@ class chmod_TestCase(TestCmdTestCase): wdir_file1 = os.path.join(test.workdir, 'file1') wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') - open(wdir_file1, 'w').write("") - open(wdir_sub_file2, 'w').write("") + with open(wdir_file1, 'w') as f: + f.write("") + with open(wdir_sub_file2, 'w') as f: + f.write("") if sys.platform == 'win32': @@ -349,11 +360,8 @@ sys.stderr.write("run2 STDERR third line\\n") test = TestCmd.TestCmd(interpreter = 'python', workdir = '', combine = 1) - try: - output = test.stdout() - except IndexError: - pass - else: + output = test.stdout() + if output is not None: raise IndexError("got unexpected output:\n\t`%s'\n" % output) # The underlying system subprocess implementations can combine @@ -422,10 +430,13 @@ class diff_TestCase(TestCmdTestCase): def test_diff_re(self): """Test diff_re()""" result = TestCmd.diff_re(["abcde"], ["abcde"]) + result = list(result) assert result == [], result result = TestCmd.diff_re(["a.*e"], ["abcde"]) + result = list(result) assert result == [], result result = TestCmd.diff_re(["a.*e"], ["xxx"]) + result = list(result) assert result == ['1c1', "< 'a.*e'", '---', "> 'xxx'"], result def test_diff_custom_function(self): @@ -475,13 +486,13 @@ STDOUT========================================================================== script_input = """import sys sys.path = ['%s'] + sys.path import TestCmd -assert TestCmd.diff_re(["a.*(e"], ["abcde"]) +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 )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" + expect1 = "Regular expression error in '^a.*(e$': missing )" + expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis" assert (stderr.find(expect1) != -1 or stderr.find(expect2) != -1), repr(stderr) @@ -492,6 +503,7 @@ sys.path = ['%s'] + sys.path import TestCmd result = TestCmd.TestCmd.simple_diff(['a', 'b', 'c', 'e', 'f1'], ['a', 'c', 'd', 'e', 'f2']) +result = list(result) expect = ['2d1', '< b', '3a3', '> d', '5c5', '< f1', '---', '> f2'] assert result == expect, result sys.exit(0) @@ -557,6 +569,7 @@ sys.path = ['%s'] + sys.path import TestCmd result = TestCmd.TestCmd.diff_re(['a', 'b', 'c', '.', 'f1'], ['a', 'c', 'd', 'e', 'f2']) +result = list(result) expect = [ '2c2', "< 'b'", @@ -844,22 +857,22 @@ test.%s() _test_it(cwd, 'dir04', 'pass_test', 1) _test_it(cwd, 'dir05', 'fail_test', 1) _test_it(cwd, 'dir06', 'no_result', 1) - os.environ['PRESERVE'] = '' # del os.environ['PRESERVE'] + del os.environ['PRESERVE'] os.environ['PRESERVE_PASS'] = '1' _test_it(cwd, 'dir07', 'pass_test', 1) _test_it(cwd, 'dir08', 'fail_test', 0) _test_it(cwd, 'dir09', 'no_result', 0) - os.environ['PRESERVE_PASS'] = '' # del os.environ['PRESERVE_PASS'] + del os.environ['PRESERVE_PASS'] os.environ['PRESERVE_FAIL'] = '1' _test_it(cwd, 'dir10', 'pass_test', 0) _test_it(cwd, 'dir11', 'fail_test', 1) _test_it(cwd, 'dir12', 'no_result', 0) - os.environ['PRESERVE_FAIL'] = '' # del os.environ['PRESERVE_FAIL'] + del os.environ['PRESERVE_FAIL'] os.environ['PRESERVE_NO_RESULT'] = '1' _test_it(cwd, 'dir13', 'pass_test', 0) _test_it(cwd, 'dir14', 'fail_test', 0) _test_it(cwd, 'dir15', 'no_result', 1) - os.environ['PRESERVE_NO_RESULT'] = '' # del os.environ['PRESERVE_NO_RESULT'] + del os.environ['PRESERVE_NO_RESULT'] finally: _clear_dict(os.environ, 'PRESERVE', 'PRESERVE_PASS', 'PRESERVE_FAIL', 'PRESERVE_NO_RESULT') @@ -980,10 +993,10 @@ class match_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match = TestCmd.match_exact) assert not test.match("abcde\n", "a.*e\n") assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match(lines, regexes) assert test.match(lines, lines) @@ -992,10 +1005,10 @@ class match_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match=TestCmd.TestCmd.match_exact) assert not test.match("abcde\n", "a.*e\n") assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match(lines, regexes) assert test.match(lines, lines) @@ -1004,10 +1017,10 @@ class match_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match='match_exact') assert not test.match("abcde\n", "a.*e\n") assert test.match("abcde\n", "abcde\n") - assert not test.match("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match(lines, regexes) assert test.match(lines, lines) @@ -1057,16 +1070,16 @@ class match_exact_TestCase(TestCmdTestCase): class match_re_dotall_TestCase(TestCmdTestCase): def test_match_re_dotall_function(self): """Test calling the TestCmd.match_re_dotall() function""" - assert TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") + assert TestCmd.match_re_dotall("abcde\nfghij\n", r"a.*j\n") def test_match_re_dotall_instance_method(self): """Test calling the TestCmd.TestCmd().match_re_dotall() instance method""" test = TestCmd.TestCmd() - test.match_re_dotall("abcde\\nfghij\\n", "a.*j\\n") + test.match_re_dotall("abcde\\nfghij\\n", r"a.*j\\n") def test_match_re_dotall_static_method(self): """Test calling the TestCmd.TestCmd.match_re_dotall() static method""" - assert TestCmd.TestCmd.match_re_dotall("abcde\nfghij\n", "a.*j\n") + assert TestCmd.TestCmd.match_re_dotall("abcde\nfghij\n", r"a.*j\n") def test_error(self): """Test handling a compilation error in TestCmd.match_re_dotall()""" @@ -1079,13 +1092,13 @@ class match_re_dotall_TestCase(TestCmdTestCase): script_input = """import sys sys.path = ['%s'] + sys.path import TestCmd -assert TestCmd.match_re_dotall("abcde", "a.*(e") +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 )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" + expect1 = "Regular expression error in '^a.*(e$': missing )" + expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis" assert (stderr.find(expect1) != -1 or stderr.find(expect2) != -1), repr(stderr) finally: @@ -1094,44 +1107,34 @@ sys.exit(0) def test_evaluation(self): """Test match_re_dotall() evaluation""" test = TestCmd.TestCmd() - assert test.match_re_dotall("abcde\nfghij\n", "a.*e\nf.*j\n") - assert test.match_re_dotall("abcde\nfghij\n", "a[^j]*j\n") - assert test.match_re_dotall("abcde\nfghij\n", "abcde\nfghij\n") + assert test.match_re_dotall("abcde\nfghij\n", r"a.*e\nf.*j\n") + assert test.match_re_dotall("abcde\nfghij\n", r"a[^j]*j\n") + assert test.match_re_dotall("abcde\nfghij\n", r"abcde\nfghij\n") assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) + [r"1[0-9]*5\n", r"a.*e\n", r"f.*j\n"]) assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["1.*j\n"]) + [r"1.*j\n"]) assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - ["12345\n", "abcde\n", "fghij\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["1[0-9]*5\n", "a.*e\n", "f.*j\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["1.*j\n"]) - assert test.match_re_dotall(UserList(["12345\n", - "abcde\n", - "fghij\n"]), - ["12345\n", "abcde\n", "fghij\n"]) + [r"12345\n", r"abcde\n", r"fghij\n"]) + assert test.match_re_dotall(UserList(["12345\n", "abcde\n", "fghij\n"]), + [r"1[0-9]*5\n", r"a.*e\n", r"f.*j\n"]) + assert test.match_re_dotall(UserList(["12345\n", "abcde\n", "fghij\n"]), + [r"1.*j\n"]) + assert test.match_re_dotall(UserList(["12345\n", "abcde\n", "fghij\n"]), + [r"12345\n", r"abcde\n", r"fghij\n"]) assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["1[0-9]*5\n", - "a.*e\n", - "f.*j\n"])) + UserList([r"1[0-9]*5\n", r"a.*e\n", r"f.*j\n"])) assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["1.*j\n"])) + UserList([r"1.*j\n"])) assert test.match_re_dotall(["12345\n", "abcde\n", "fghij\n"], - UserList(["12345\n", - "abcde\n", - "fghij\n"])) + UserList([r"12345\n", r"abcde\n", r"fghij\n"])) assert test.match_re_dotall("12345\nabcde\nfghij\n", - "1[0-9]*5\na.*e\nf.*j\n") - assert test.match_re_dotall("12345\nabcde\nfghij\n", "1.*j\n") + r"1[0-9]*5\na.*e\nf.*j\n") + assert test.match_re_dotall("12345\nabcde\nfghij\n", r"1.*j\n") assert test.match_re_dotall("12345\nabcde\nfghij\n", - "12345\nabcde\nfghij\n") + r"12345\nabcde\nfghij\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert test.match_re_dotall(lines, regexes) assert test.match_re_dotall(lines, lines) @@ -1167,8 +1170,8 @@ sys.exit(0) """ % cwd stdout, stderr, status = self.call_python(script_input) assert status == 1, status - expect1 = "Regular expression error in '^a.*(e$': missing )\n" - expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis\n" + expect1 = "Regular expression error in '^a.*(e$': missing )" + expect2 = "Regular expression error in '^a.*(e$': unbalanced parenthesis" assert (stderr.find(expect1) != -1 or stderr.find(expect2) != -1), repr(stderr) finally: @@ -1192,7 +1195,7 @@ sys.exit(0) assert test.match_re("12345\nabcde\n", "1[0-9]*5\na.*e\n") assert test.match_re("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert test.match_re(lines, regexes) assert test.match_re(lines, lines) @@ -1205,7 +1208,7 @@ class match_stderr_TestCase(TestCmdTestCase): assert test.match_stderr("abcde\n", "a.*e\n") assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert test.match_stderr(lines, regexes) def test_match_stderr_not_affecting_match_stdout(self): @@ -1217,14 +1220,14 @@ class match_stderr_TestCase(TestCmdTestCase): assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stderr(lines, regexes) assert test.match_stderr(lines, lines) assert test.match_stdout("abcde\n", "a.*e\n") assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert test.match_stdout(lines, regexes) def test_match_stderr_custom_function(self): @@ -1237,7 +1240,7 @@ class match_stderr_TestCase(TestCmdTestCase): assert not test.match_stderr("123\n123\n", "1\n1\n") assert test.match_stderr("123\n123\n", "111\n111\n") lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] + regexes = [r"1\n", r"1\n"] assert test.match_stderr(lines, regexes) # equal numbers of lines def test_match_stderr_TestCmd_function(self): @@ -1245,10 +1248,10 @@ class match_stderr_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match_stderr = TestCmd.match_exact) assert not test.match_stderr("abcde\n", "a.*e\n") assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stderr(lines, regexes) assert test.match_stderr(lines, lines) @@ -1257,10 +1260,10 @@ class match_stderr_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match_stderr=TestCmd.TestCmd.match_exact) assert not test.match_stderr("abcde\n", "a.*e\n") assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stderr(lines, regexes) assert test.match_stderr(lines, lines) @@ -1269,10 +1272,10 @@ class match_stderr_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match_stderr='match_exact') assert not test.match_stderr("abcde\n", "a.*e\n") assert test.match_stderr("abcde\n", "abcde\n") - assert not test.match_stderr("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stderr("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stderr(lines, regexes) assert test.match_stderr(lines, lines) @@ -1285,7 +1288,7 @@ class match_stdout_TestCase(TestCmdTestCase): assert test.match_stdout("abcde\n", "a.*e\n") assert test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert test.match_stdout(lines, regexes) def test_match_stdout_not_affecting_match_stderr(self): @@ -1297,14 +1300,14 @@ class match_stdout_TestCase(TestCmdTestCase): assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stdout(lines, regexes) assert test.match_stdout(lines, lines) assert test.match_stderr("abcde\n", "a.*e\n") assert test.match_stderr("12345\nabcde\n", "1\\d+5\na.*e\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert test.match_stderr(lines, regexes) def test_match_stdout_custom_function(self): @@ -1317,7 +1320,7 @@ class match_stdout_TestCase(TestCmdTestCase): assert not test.match_stdout("123\n123\n", "1\n1\n") assert test.match_stdout("123\n123\n", "111\n111\n") lines = ["123\n", "123\n"] - regexes = ["1\n", "1\n"] + regexes = [r"1\n", r"1\n"] assert test.match_stdout(lines, regexes) # equal numbers of lines def test_match_stdout_TestCmd_function(self): @@ -1325,10 +1328,10 @@ class match_stdout_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match_stdout = TestCmd.match_exact) assert not test.match_stdout("abcde\n", "a.*e\n") assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stdout(lines, regexes) assert test.match_stdout(lines, lines) @@ -1337,10 +1340,10 @@ class match_stdout_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match_stdout=TestCmd.TestCmd.match_exact) assert not test.match_stdout("abcde\n", "a.*e\n") assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stdout(lines, regexes) assert test.match_stdout(lines, lines) @@ -1349,10 +1352,10 @@ class match_stdout_TestCase(TestCmdTestCase): test = TestCmd.TestCmd(match_stdout='match_exact') assert not test.match_stdout("abcde\n", "a.*e\n") assert test.match_stdout("abcde\n", "abcde\n") - assert not test.match_stdout("12345\nabcde\n", "1\d+5\na.*e\n") + assert not test.match_stdout("12345\nabcde\n", "1\\d+5\na.*e\n") assert test.match_stdout("12345\nabcde\n", "12345\nabcde\n") lines = ["vwxyz\n", "67890\n"] - regexes = ["v[^a-u]*z\n", "6[^ ]+0\n"] + regexes = [r"v[^a-u]*z\n", r"6[^ ]+0\n"] assert not test.match_stdout(lines, regexes) assert test.match_stdout(lines, lines) @@ -1468,20 +1471,20 @@ class preserve_TestCase(TestCmdTestCase): def test_preserve(self): """Test preserve()""" def cleanup_test(test, cond=None, stdout=""): - io = StringIO() save = sys.stdout - sys.stdout = io - try: - if cond: - test.cleanup(cond) - else: - test.cleanup() - o = io.getvalue() - assert o == stdout, "o = `%s', stdout = `%s'" % (o, stdout) - finally: - sys.stdout = save - - test = TestCmd.TestCmd(workdir = '') + with closing(StringIO()) as io: + sys.stdout = io + try: + if cond: + test.cleanup(cond) + else: + test.cleanup() + o = io.getvalue() + assert o == stdout, "o = `%s', stdout = `%s'" % (o, stdout) + finally: + sys.stdout = save + + test = TestCmd.TestCmd(workdir='') wdir = test.workdir try: test.write('file1', "Test file #1\n") @@ -1490,10 +1493,10 @@ class preserve_TestCase(TestCmdTestCase): assert not os.path.exists(wdir) finally: if os.path.exists(wdir): - shutil.rmtree(wdir, ignore_errors = 1) + shutil.rmtree(wdir, ignore_errors=1) test._dirlist.remove(wdir) - test = TestCmd.TestCmd(workdir = '') + test = TestCmd.TestCmd(workdir='') wdir = test.workdir try: test.write('file2', "Test file #2\n") @@ -1576,11 +1579,16 @@ class read_TestCase(TestCmdTestCase): wdir_file4 = os.path.join(test.workdir, 'file4') wdir_file5 = os.path.join(test.workdir, 'file5') - open(wdir_file1, 'wb').write("") - open(wdir_file2, 'wb').write("Test\nfile\n#2.\n") - open(wdir_foo_file3, 'wb').write("Test\nfile\n#3.\n") - open(wdir_file4, 'wb').write("Test\nfile\n#4.\n") - open(wdir_file5, 'wb').write("Test\r\nfile\r\n#5.\r\n") + with open(wdir_file1, 'wb') as f: + f.write(to_bytes("")) + with open(wdir_file2, 'wb') as f: + f.write(to_bytes("Test\nfile\n#2.\n")) + with open(wdir_foo_file3, 'wb') as f: + f.write(to_bytes("Test\nfile\n#3.\n")) + with open(wdir_file4, 'wb') as f: + f.write(to_bytes("Test\nfile\n#4.\n")) + with open(wdir_file5, 'wb') as f: + f.write(to_bytes("Test\r\nfile\r\n#5.\r\n")) try: contents = test.read('no_file') @@ -1597,6 +1605,7 @@ class read_TestCase(TestCmdTestCase): raise def _file_matches(file, contents, expected): + contents = to_str(contents) assert contents == expected, \ "Expected contents of " + str(file) + "==========\n" + \ expected + \ @@ -1627,7 +1636,7 @@ class rmdir_TestCase(TestCmdTestCase): except EnvironmentError: pass else: - raise Exception("did not catch expected EnvironmentError") + raise Exception("did not catch expected SConsEnvironmentError") test.subdir(['sub'], ['sub', 'dir'], @@ -1642,7 +1651,7 @@ class rmdir_TestCase(TestCmdTestCase): except EnvironmentError: pass else: - raise Exception("did not catch expected EnvironmentError") + raise Exception("did not catch expected SConsEnvironmentError") assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o @@ -1651,7 +1660,7 @@ class rmdir_TestCase(TestCmdTestCase): except EnvironmentError: pass else: - raise Exception("did not catch expected EnvironmentError") + raise Exception("did not catch expected SConsEnvironmentError") assert os.path.isdir(s_d_o), "%s is gone?" % s_d_o @@ -1867,29 +1876,25 @@ class run_verbose_TestCase(TestCmdTestCase): workdir = '', verbose = 1) - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - assert expect == e, (expect, e) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + test.run(arguments = ['arg1 arg2']) + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + assert expect == e, (expect, e) testx = TestCmd.TestCmd(program = t.scriptx, workdir = '', verbose = 1) - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - assert expect == e, (expect, e) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + testx.run(arguments = ['arg1 arg2']) + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + assert expect == e, (expect, e) # Test calling TestCmd() with an explicit verbose = 2. @@ -1918,43 +1923,39 @@ class run_verbose_TestCase(TestCmdTestCase): workdir = '', verbose = 2) - sys.stdout = StringIO() - sys.stderr = StringIO() + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + test.run(arguments = ['arg1 arg2']) - test.run(arguments = ['arg1 arg2']) + line_fmt = "script: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) - line_fmt = "script: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) testx = TestCmd.TestCmd(program = t.scriptx, workdir = '', verbose = 2) - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + testx.run(arguments = ['arg1 arg2']) - line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) + line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) # Test calling TestCmd() with an explicit verbose = 3. @@ -1963,41 +1964,37 @@ class run_verbose_TestCase(TestCmdTestCase): workdir = '', verbose = 2) - sys.stdout = StringIO() - sys.stderr = StringIO() + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + test.run(arguments = ['arg1 arg2']) - test.run(arguments = ['arg1 arg2']) + line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + expect = out_fmt % (len(stdout_line), stdout_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) - line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - expect = out_fmt % (len(stdout_line), stdout_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) - assert e == expect, (e, expect) + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) + assert e == expect, (e, expect) test = TestCmd.TestCmd(program = t.scriptout, interpreter = 'python', workdir = '', verbose = 3) - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + test.run(arguments = ['arg1 arg2']) - line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - '0', '') - o = sys.stdout.getvalue() - assert expect == o, (expect, o) + line_fmt = "scriptout: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + '0', '') + o = sys.stdout.getvalue() + assert expect == o, (expect, o) - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) - assert e == expect, (e, expect) + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % (t.scriptout_path) + assert e == expect, (e, expect) # Test letting TestCmd() pick up verbose = 2 from the environment. @@ -2007,42 +2004,38 @@ class run_verbose_TestCase(TestCmdTestCase): interpreter = 'python', workdir = '') - sys.stdout = StringIO() - sys.stderr = StringIO() + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + test.run(arguments = ['arg1 arg2']) - test.run(arguments = ['arg1 arg2']) + line_fmt = "script: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) - line_fmt = "script: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) - - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) testx = TestCmd.TestCmd(program = t.scriptx, workdir = '') - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + testx.run(arguments = ['arg1 arg2']) - line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" - stdout_line = line_fmt % ('STDOUT', t.sub_dir) - stderr_line = line_fmt % ('STDERR', t.sub_dir) - expect = outerr_fmt % (len(stdout_line), stdout_line, - len(stderr_line), stderr_line) - o = sys.stdout.getvalue() - assert expect == o, (expect, o) + line_fmt = "scriptx.bat: %s: %s: ['arg1 arg2']\n" + stdout_line = line_fmt % ('STDOUT', t.sub_dir) + stderr_line = line_fmt % ('STDERR', t.sub_dir) + expect = outerr_fmt % (len(stdout_line), stdout_line, + len(stderr_line), stderr_line) + o = sys.stdout.getvalue() + assert expect == o, (expect, o) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - e = sys.stderr.getvalue() - assert e == expect, (e, expect) + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + e = sys.stderr.getvalue() + assert e == expect, (e, expect) # Test letting TestCmd() pick up verbose = 1 from the environment. @@ -2053,29 +2046,25 @@ class run_verbose_TestCase(TestCmdTestCase): workdir = '', verbose = 1) - sys.stdout = StringIO() - sys.stderr = StringIO() - - test.run(arguments = ['arg1 arg2']) - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - expect = 'python "%s" "arg1 arg2"\n' % t.script_path - assert expect == e, (expect, e) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + test.run(arguments = ['arg1 arg2']) + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + expect = 'python "%s" "arg1 arg2"\n' % t.script_path + assert expect == e, (expect, e) testx = TestCmd.TestCmd(program = t.scriptx, workdir = '', verbose = 1) - sys.stdout = StringIO() - sys.stderr = StringIO() - - testx.run(arguments = ['arg1 arg2']) - expect = '"%s" "arg1 arg2"\n' % t.scriptx_path - o = sys.stdout.getvalue() - assert o == '', o - e = sys.stderr.getvalue() - assert expect == e, (expect, e) + with closing(StringIO()) as sys.stdout, closing(StringIO()) as sys.stderr: + testx.run(arguments = ['arg1 arg2']) + expect = '"%s" "arg1 arg2"\n' % t.scriptx_path + o = sys.stdout.getvalue() + assert o == '', o + e = sys.stderr.getvalue() + assert expect == e, (expect, e) finally: sys.stdout = save_stdout @@ -2343,16 +2332,15 @@ sys.stderr = Unbuffered(sys.stderr) sys.stdout.write('script_recv: STDOUT\\n') sys.stderr.write('script_recv: STDERR\\n') -logfp = open(r'%s', 'wb') -while 1: - 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) -logfp.close() - """ % t.recv_out_path +with open(r'%s', 'wb') as logfp: + while 1: + 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 @@ -2618,10 +2606,11 @@ script_recv: STDERR: input p = test.start(stdin=1) input = 'stdin.write() input to the receive script\n' - p.stdin.write(input) + p.stdin.write(to_bytes(input)) p.stdin.close() p.wait() - result = open(t.recv_out_path, 'rb').read() + with open(t.recv_out_path, 'rb') as f: + result = to_str(f.read()) expect = 'script_recv: ' + input assert result == expect, repr(result) @@ -2630,7 +2619,8 @@ script_recv: STDERR: input p.send(input) p.stdin.close() p.wait() - result = open(t.recv_out_path, 'rb').read() + with open(t.recv_out_path, 'rb') as f: + result = to_str(f.read()) expect = 'script_recv: ' + input assert result == expect, repr(result) @@ -2689,7 +2679,8 @@ script_recv: STDERR: input to the receive script """ assert stdout == expect_stdout, stdout assert stderr == expect_stderr, stderr - result = open(t.recv_out_path, 'rb').read() + with open(t.recv_out_path, 'rb') as f: + result = f.read() expect = ('script_recv: ' + input) * 2 assert result == expect, (result, stdout, stderr) @@ -2745,11 +2736,8 @@ sys.stderr.write("run2 STDERR second line\\n") # Everything before this prepared our "source directory." # Now do the real test. test = TestCmd.TestCmd(interpreter = 'python', workdir = '') - try: - output = test.stdout() - except IndexError: - pass - else: + output = test.stdout() + if output is not None: raise IndexError("got unexpected output:\n\t`%s'\n" % output) test.program_set('run1') test.run(arguments = 'foo bar') @@ -2807,15 +2795,18 @@ class symlink_TestCase(TestCmdTestCase): test.symlink('target1', 'file1') assert os.path.islink(wdir_file1) assert not os.path.exists(wdir_file1) - open(wdir_target1, 'w').write("") + with open(wdir_target1, 'w') as f: + f.write("") assert os.path.exists(wdir_file1) test.symlink('target2', ['foo', 'file2']) assert os.path.islink(wdir_foo_file2) assert not os.path.exists(wdir_foo_file2) - open(wdir_target2, 'w').write("") + with open(wdir_target2, 'w') as f: + f.write("") assert not os.path.exists(wdir_foo_file2) - open(wdir_foo_target2, 'w').write("") + with open(wdir_foo_target2, 'w') as f: + f.write("") assert os.path.exists(wdir_foo_file2) @@ -2947,12 +2938,18 @@ class unlink_TestCase(TestCmdTestCase): wdir_foo_file4 = os.path.join(test.workdir, 'foo', 'file4') wdir_file5 = os.path.join(test.workdir, 'file5') - open(wdir_file1, 'w').write("") - open(wdir_file2, 'w').write("") - open(wdir_foo_file3a, 'w').write("") - open(wdir_foo_file3b, 'w').write("") - open(wdir_foo_file4, 'w').write("") - open(wdir_file5, 'w').write("") + with open(wdir_file1, 'w') as f: + f.write("") + with open(wdir_file2, 'w') as f: + f.write("") + with open(wdir_foo_file3a, 'w') as f: + f.write("") + with open(wdir_foo_file3b, 'w') as f: + f.write("") + with open(wdir_foo_file4, 'w') as f: + f.write("") + with open(wdir_file5, 'w') as f: + f.write("") try: contents = test.unlink('no_file') @@ -2981,20 +2978,17 @@ class unlink_TestCase(TestCmdTestCase): # For Windows, open the file. os.chmod(test.workdir, 0o500) os.chmod(wdir_file5, 0o400) - f = open(wdir_file5, 'r') - - try: + with open(wdir_file5, 'r'): try: - test.unlink('file5') - except OSError: # expect "Permission denied" - pass - except: - raise - finally: - os.chmod(test.workdir, 0o700) - os.chmod(wdir_file5, 0o600) - f.close() - + try: + test.unlink('file5') + except OSError: # expect "Permission denied" + pass + except: + raise + finally: + os.chmod(test.workdir, 0o700) + os.chmod(wdir_file5, 0o600) class touch_TestCase(TestCmdTestCase): @@ -3005,8 +2999,10 @@ class touch_TestCase(TestCmdTestCase): wdir_file1 = os.path.join(test.workdir, 'file1') wdir_sub_file2 = os.path.join(test.workdir, 'sub', 'file2') - open(wdir_file1, 'w').write("") - open(wdir_sub_file2, 'w').write("") + with open(wdir_file1, 'w') as f: + f.write("") + with open(wdir_sub_file2, 'w') as f: + f.write("") file1_old_time = os.path.getmtime(wdir_file1) file2_old_time = os.path.getmtime(wdir_sub_file2) @@ -3312,9 +3308,12 @@ class write_TestCase(TestCmdTestCase): if os.name != "nt": assert not os.path.exists(test.workpath('file10')) - assert open(test.workpath('file8'), 'r').read() == "Test file #8.\n" - assert open(test.workpath('file9'), 'rb').read() == "Test file #9.\r\n" - + with open(test.workpath('file8'), 'r') as f: + res = f.read() + assert res == "Test file #8.\n", res + with open(test.workpath('file9'), 'rb') as f: + res = to_str(f.read()) + assert res == "Test file #9.\r\n", res class variables_TestCase(TestCmdTestCase): diff --git a/testing/framework/TestRuntest.py b/testing/framework/TestRuntest.py index 64dcf17..e9ca524 100644 --- a/testing/framework/TestRuntest.py +++ b/testing/framework/TestRuntest.py @@ -14,7 +14,7 @@ attributes defined in this subclass. # Copyright (c) 2001 - 2019 The SCons Foundation -__revision__ = "testing/framework/TestRuntest.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "testing/framework/TestRuntest.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path @@ -30,7 +30,7 @@ __all__.extend([ 'TestRuntest', 'pythonflags', ]) -if re.search('\s', python): +if re.search(r'\s', python): pythonstring = _python_ else: pythonstring = python diff --git a/testing/framework/TestSCons.py b/testing/framework/TestSCons.py index e415291..2228423 100644 --- a/testing/framework/TestSCons.py +++ b/testing/framework/TestSCons.py @@ -15,7 +15,7 @@ attributes defined in this subclass. # Copyright (c) 2001 - 2019 The SCons Foundation from __future__ import division, print_function -__revision__ = "testing/framework/TestSCons.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "testing/framework/TestSCons.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import re @@ -35,7 +35,7 @@ from TestCmd import PIPE # here provides some independent verification that what we packaged # conforms to what we expect. -default_version = '3.0.5' +default_version = '3.1.0' python_version_unsupported = (2, 6, 0) python_version_deprecated = (2, 7, 0) @@ -44,7 +44,7 @@ python_version_deprecated = (2, 7, 0) # 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 = '3.0.5' +SConsVersion = '3.1.0' if SConsVersion == '__' + 'VERSION' + '__': SConsVersion = default_version @@ -682,6 +682,9 @@ class TestSCons(TestCommon): """ Initialize with a default external environment that uses a local Java SDK in preference to whatever's found in the default PATH. + + :param version: if set, match only that version + :return: the new env. """ if not self.external: try: @@ -698,11 +701,11 @@ class TestSCons(TestCommon): if version: if sys.platform == 'win32': patterns = [ - 'C:/Program Files*/Java/jdk%s*/bin'%version, + 'C:/Program Files*/Java/jdk*%s*/bin' % version, ] else: patterns = [ - '/usr/java/jdk%s*/bin' % version, + '/usr/java/jdk%s*/bin' % version, '/usr/lib/jvm/*-%s*/bin' % version, '/usr/local/j2sdk%s*/bin' % version, ] @@ -727,7 +730,10 @@ class TestSCons(TestCommon): def java_where_includes(self,version=None): """ - Return java include paths compiling java jni code + Find include path needed for compiling java jni code. + + :param version: if set, match only that version + :return: path to java headers """ import sys @@ -761,6 +767,14 @@ class TestSCons(TestCommon): return result def java_where_java_home(self, version=None): + """ + Find path to what would be JAVA_HOME. + + SCons does not read JAVA_HOME from the environment, so deduce it. + + :param version: if set, match only that version + :return: path where JDK components live + """ if sys.platform[:6] == 'darwin': # osx 10.11, 10.12 home_tool = '/usr/libexec/java_home' @@ -807,6 +821,12 @@ class TestSCons(TestCommon): self.skip_test("Could not find Java " + java_bin_name + ", skipping test(s).\n") def java_where_jar(self, version=None): + """ + Find java archiver jar. + + :param version: if set, match only that version + :return: path to jar + """ ENV = self.java_ENV(version) if self.detect_tool('jar', ENV=ENV): where_jar = self.detect('JAR', 'jar', ENV=ENV) @@ -821,7 +841,10 @@ class TestSCons(TestCommon): def java_where_java(self, version=None): """ - Return a path to the java executable. + Find java executable. + + :param version: if set, match only that version + :return: path to the java rutime """ ENV = self.java_ENV(version) where_java = self.where_is('java', ENV['PATH']) @@ -835,7 +858,10 @@ class TestSCons(TestCommon): def java_where_javac(self, version=None): """ - Return a path to the javac compiler. + Find java compiler. + + :param version: if set, match only that version + :return: path to javac """ ENV = self.java_ENV(version) if self.detect_tool('javac'): @@ -851,15 +877,17 @@ class TestSCons(TestCommon): arguments = '-version', stderr=None, status=None) + # Note recent versions output version info to stdout instead of stderr if version: - if self.stderr().find('javac %s' % version) == -1: + verf = 'javac %s' % 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) else: - m = re.search(r'javac (\d\.*\d)', self.stderr()) - # Java 11 outputs this to stdout + version_re = r'javac (\d*\.*\d)' + m = re.search(version_re, self.stderr()) if not m: - m = re.search(r'javac (\d\.*\d)', self.stdout()) + m = re.search(version_re, self.stdout()) if m: version = m.group(1) @@ -873,6 +901,16 @@ class TestSCons(TestCommon): return where_javac, version def java_where_javah(self, version=None): + """ + Find java header generation tool. + + TODO issue #3347 since JDK10, there is no separate javah command, + 'javac -h' is used. We should not return a javah from a different + installed JDK - how to detect and what to return in this case? + + :param version: if set, match only that version + :return: path to javah + """ ENV = self.java_ENV(version) if self.detect_tool('javah'): where_javah = self.detect('JAVAH', 'javah', ENV=ENV) @@ -883,6 +921,12 @@ class TestSCons(TestCommon): return where_javah def java_where_rmic(self, version=None): + """ + Find java rmic tool. + + :param version: if set, match only that version + :return: path to rmic + """ ENV = self.java_ENV(version) if self.detect_tool('rmic'): where_rmic = self.detect('RMIC', 'rmic', ENV=ENV) @@ -905,7 +949,7 @@ class TestSCons(TestCommon): def Qt_dummy_installation(self, dir='qt'): # create a dummy qt installation - self.subdir( dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib'] ) + self.subdir(dir, [dir, 'bin'], [dir, 'include'], [dir, 'lib']) self.write([dir, 'bin', 'mymoc.py'], """\ import getopt @@ -913,23 +957,23 @@ import sys import re # -w and -z are fake options used in test/QT/QTFLAGS.py cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:wz', []) -output = None impl = 0 opt_string = '' for opt, arg in cmd_opts: - if opt == '-o': output = open(arg, 'w') + if opt == '-o': outfile = arg elif opt == '-i': impl = 1 else: opt_string = opt_string + ' ' + opt -output.write("/* mymoc.py%s */\\n" % opt_string) -for a in args: - with open(a, 'r') as f: - contents = f.read() - a = a.replace('\\\\', '\\\\\\\\') - subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }' - if impl: - contents = re.sub( r'#include.*', '', contents ) - output.write(contents.replace('Q_OBJECT', subst)) -output.close() + +with open(outfile, 'w') as ofp: + ofp.write("/* mymoc.py%s */\\n" % opt_string) + for a in args: + with open(a, 'r') as ifp: + contents = ifp.read() + a = a.replace('\\\\', '\\\\\\\\') + subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }' + if impl: + contents = re.sub(r'#include.*', '', contents) + ofp.write(contents.replace('Q_OBJECT', subst)) sys.exit(0) """) @@ -944,7 +988,7 @@ source = None opt_string = '' for arg in sys.argv[1:]: if output_arg: - output = open(arg, 'w') + outfile = arg output_arg = 0 elif impl_arg: impl = arg @@ -958,19 +1002,19 @@ for arg in sys.argv[1:]: else: if source: sys.exit(1) - source = open(arg, 'r') - sourceFile = arg -output.write("/* myuic.py%s */\\n" % opt_string) -if impl: - output.write( '#include "' + impl + '"\\n' ) - includes = re.findall('(.*?)', source.read()) - for incFile in includes: - # this is valid for ui.h files, at least - if os.path.exists(incFile): - output.write('#include "' + incFile + '"\\n') -else: - output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" ) -output.close() + source = sourceFile = arg + +with open(outfile, 'w') as ofp, open(source, 'r') as ifp: + ofp.write("/* myuic.py%s */\\n" % opt_string) + if impl: + ofp.write('#include "' + impl + '"\\n') + includes = re.findall('(.*?)', ifp.read()) + for incFile in includes: + # this is valid for ui.h files, at least + if os.path.exists(incFile): + ofp.write('#include "' + incFile + '"\\n') + else: + ofp.write('#include "my_qobject.h"\\n' + ifp.read() + " Q_OBJECT \\n") sys.exit(0) """ ) @@ -983,7 +1027,7 @@ void my_qt_symbol(const char *arg); #include "../include/my_qobject.h" #include void my_qt_symbol(const char *arg) { - fputs( arg, stdout ); + fputs(arg, stdout); } """) @@ -991,9 +1035,9 @@ void my_qt_symbol(const char *arg) { env = Environment() import sys if sys.platform == 'win32': - env.StaticLibrary( 'myqt', 'my_qobject.cpp' ) + env.StaticLibrary('myqt', 'my_qobject.cpp') else: - env.SharedLibrary( 'myqt', 'my_qobject.cpp' ) + env.SharedLibrary('myqt', 'my_qobject.cpp') """) self.run(chdir = self.workpath(dir, 'lib'), @@ -1036,7 +1080,7 @@ if ARGUMENTS.get('variant_dir', 0): else: sconscript = File('SConscript') Export("env dup") -SConscript( sconscript ) +SConscript(sconscript) """ % (self.QT, self.QT_LIB, self.QT_MOC, self.QT_UIC)) diff --git a/testing/framework/TestSConsMSVS.py b/testing/framework/TestSConsMSVS.py index 09cb5ee..86935fd 100644 --- a/testing/framework/TestSConsMSVS.py +++ b/testing/framework/TestSConsMSVS.py @@ -15,7 +15,7 @@ in this subclass. # Copyright (c) 2001 - 2019 The SCons Foundation -__revision__ = "testing/framework/TestSConsMSVS.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "testing/framework/TestSConsMSVS.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import sys @@ -23,6 +23,7 @@ import platform import traceback from xml.etree import ElementTree +import SCons.Errors from TestSCons import * from TestSCons import __all__ @@ -38,18 +39,18 @@ expected_dspfile_6_0 = '''\ CFG=Test - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "Test.mak". -!MESSAGE +!MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Release" -!MESSAGE +!MESSAGE !MESSAGE Possible choices for configuration are: -!MESSAGE +!MESSAGE !MESSAGE "Test - Win32 Release" (based on "Win32 (x86) External Target") -!MESSAGE +!MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 @@ -85,7 +86,7 @@ CFG=Test - Win32 Release !IF "$(CFG)" == "Test - Win32 Release" -!ENDIF +!ENDIF # Begin Group "Header Files" @@ -435,31 +436,10 @@ env.MSVSProject(target = 'Test.vcproj', """ - -expected_slnfile_8_0 = """\ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t.Release|Win32.ActiveCfg = Release|Win32 -\t\t.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_9_0 = """\ -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "" +expected_slnfile_fmt = """\ +Microsoft Visual Studio Solution File, Format Version %(FORMAT_VERSION)s +# Visual Studio %(VS_NUMBER)s +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%(PROJECT_NAME)s", "%(PROJECT_FILE)s", "" EndProject Global @@ -476,91 +456,11 @@ Global EndGlobal """ -expected_slnfile_10_0 = """\ -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_11_0 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 11 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_14_0 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_slnfile_14_1 = """\ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test.vcxproj", "Test.vcxproj", "{39A97E1F-1A52-8954-A0B1-A10A8487545E}" -EndProject -Global - -\tGlobalSection(SolutionConfigurationPlatforms) = preSolution -\t\tRelease|Win32 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(ProjectConfigurationPlatforms) = postSolution -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.ActiveCfg = Release|Win32 -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E}.Release|Win32.Build.0 = Release|Win32 -\tEndGlobalSection -\tGlobalSection(SolutionProperties) = preSolution -\t\tHideSolutionNode = FALSE -\tEndGlobalSection -EndGlobal -""" - -expected_vcprojfile_8_0 = """\ +expected_vcprojfile_fmt = """\ \t\t\t +\t\t\t\tRelativePath="sdk_dir\\sdk.h"> \t\t\t \t\t \t\t """ -expected_vcprojfile_9_0 = """\ - - -\tKeyword="MakeFileProj"> -\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t -\t -\t -\t -\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t\t -\t\t -\t\t -\t\t -\t -\t -\t - -""" - -expected_vcprojfile_10_0 = """\ +expected_vcxprojfile_fmt = """\ - + \t \t\t \t\t\tRelease @@ -746,6 +558,7 @@ expected_vcprojfile_10_0 = """\ \t\tTest \t\tMakeFileProj +\t\tNoUpgrade \t \t \t @@ -767,13 +580,13 @@ expected_vcprojfile_10_0 = """\ \t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" \t\tTest.exe \t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 +\t\t%(INCLUDE_DIRS)s \t\t$(NMakeForcedIncludes) \t\t$(NMakeAssemblySearchPath) \t\t$(NMakeForcedUsingAssemblies) \t \t -\t\t +\t\t \t \t \t\t @@ -797,237 +610,19 @@ expected_vcprojfile_10_0 = """\ """ -expected_vcprojfile_11_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv110 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -expected_vcprojfile_14_0 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv140 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -expected_vcprojfile_14_1 = """\ - - -\t -\t\t -\t\t\tRelease -\t\t\tWin32 -\t\t -\t -\t -\t\t{39A97E1F-1A52-8954-A0B1-A10A8487545E} - -\t\tTest -\t\tMakeFileProj -\t -\t -\t -\t\tMakefile -\t\tfalse -\t\tv141 -\t -\t -\t -\t -\t -\t\t -\t -\t -\t -\t<_ProjectFileVersion>10.0.30319.1 -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct "Test.exe" -\t\techo Starting SCons && "" -c "" -C "" -f SConstruct -c "Test.exe" -\t\tTest.exe -\t\tDEF1;DEF2;DEF3=1234 -\t\tinc1;inc2 -\t\t$(NMakeForcedIncludes) -\t\t$(NMakeAssemblySearchPath) -\t\t$(NMakeForcedUsingAssemblies) -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t -\t -\t\t -\t\t -\t -\t -\t\t -\t -\t -\t -\t - -""" - -SConscript_contents_8_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_9_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='9.0', +SConscript_contents_fmt = """\ +env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='%(MSVS_VERSION)s', CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], CPPPATH=['inc1', 'inc2'], HOST_ARCH='%(HOST_ARCH)s') testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk.h'] +testincs = [r'sdk_dir\\sdk.h'] testlocalincs = ['test.h'] testresources = ['test.rc'] testmisc = ['readme.txt'] -env.MSVSProject(target = 'Test.vcproj', +env.MSVSProject(target = '%(PROJECT_FILE)s', slnguid = '{SLNGUID}', srcs = testsrc, incs = testincs, @@ -1038,97 +633,13 @@ env.MSVSProject(target = 'Test.vcproj', variant = 'Release') """ -SConscript_contents_10_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='10.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] +def get_tested_proj_file_vc_versions(): + """ + Returns all MSVC versions that we want to test project file creation for. + """ + return ['8.0', '9.0', '10.0', '11.0', '12.0', '14.0', '14.1', '14.2'] -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_11_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='11.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_14_0 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.0', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" - -SConscript_contents_14_1 = """\ -env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='14.1', - CPPDEFINES=['DEF1', 'DEF2',('DEF3','1234')], - CPPPATH=['inc1', 'inc2'], - HOST_ARCH='%(HOST_ARCH)s') - -testsrc = ['test1.cpp', 'test2.cpp'] -testincs = ['sdk_dir\sdk.h'] -testlocalincs = ['test.h'] -testresources = ['test.rc'] -testmisc = ['readme.txt'] - -env.MSVSProject(target = 'Test.vcxproj', - slnguid = '{SLNGUID}', - srcs = testsrc, - incs = testincs, - localincs = testlocalincs, - resources = testresources, - misc = testmisc, - buildtarget = 'Test.exe', - variant = 'Release') -""" class TestSConsMSVS(TestSCons): """Subclass for testing MSVS-specific portions of SCons.""" @@ -1150,7 +661,7 @@ import SCons.Tool.MSCommon print("self.scons_version =%%s"%%repr(SCons.__%s__)) print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) """ % 'version' - + self.run(arguments = '-n -q -Q -f -', stdin = input) exec(self.stdout()) @@ -1233,11 +744,11 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) finally: os.environ['SCONSFLAGS'] = save_sconsflags or '' return result - + def get_vs_host_arch(self): """ Get an MSVS, SDK , and/or MSVS acceptable platform arch """ - + # Dict to 'canonalize' the arch _ARCH_TO_CANONICAL = { "x86": "x86", @@ -1255,21 +766,19 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) # PROCESSOR_ARCHITECTURE. if not host_platform: host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') - - + try: host = _ARCH_TO_CANONICAL[host_platform] except KeyError as e: # Default to x86 for all other platforms host = 'x86' - - + return host def validate_msvs_file(self, file): try: x = ElementTree.parse(file) - except: + except: print("--------------------------------------------------------------") print("--------------------------------------------------------------") print(traceback.format_exc()) @@ -1278,6 +787,119 @@ print("self._msvs_versions =%%s"%%str(SCons.Tool.MSCommon.query_versions())) print("--------------------------------------------------------------") print("--------------------------------------------------------------") self.fail_test() + + def parse_vc_version(self, vc_version): + """ + Parses the string vc_version to determine the major and minor version + included. + """ + components = vc_version.split('.') + major = int(components[0]) + minor = 0 if len(components) < 2 else int(components[1]) + return major, minor + + def _get_solution_file_format_version(self, vc_version): + """ + Returns the Visual Studio format version expected in the .sln file. + """ + major, _ = self.parse_vc_version(vc_version) + if major == 8: + return '9.00' + elif major == 9: + return '10.00' + elif major == 10: + return '11.00' + elif major > 10: + return '12.00' + else: + raise SCons.Errors.UserError('Received unexpected VC version %s' % vc_version) + + def _get_solution_file_vs_number(self, vc_version): + """ + Returns the Visual Studio number expected in the .sln file. + """ + major, minor = self.parse_vc_version(vc_version) + if major == 8: + return '2005' + elif major == 9: + return '2008' + if major == 10: + return '2010' + elif major == 11: + return '11' + elif major == 12: + return '14' + elif major == 14 and (minor == 0 or minor == 1): + # Visual Studio 2015 and 2017 both use 15 in this entry. + return '15' + elif major == 14 and minor == 2: + return '16' + else: + raise SCons.Errors.UserError('Received unexpected VC version %s' % vc_version) + + def _get_vcxproj_file_tools_version(self, vc_version): + """ + Returns the version entry expected in the project file. + For .vcxproj files, this goes is ToolsVersion. + For .vcproj files, this goes in Version. + """ + major, minor = self.parse_vc_version(vc_version) + if major == 8: + # Version="8.00" + return '8.00' + elif major == 9: + # Version="9.00" + return '9.00' + elif major < 14: + # ToolsVersion='4.0' + return '4.0' + elif major == 14 and minor == 0: + # ToolsVersion='14.0' + return '14.0' + elif major == 14 and minor == 1: + # ToolsVersion='15.0' + return '15.0' + elif vc_version == '14.2': + # ToolsVersion='16' + return '16.0' + else: + raise SCons.Errors.UserError('Received unexpected VC version %s' % vc_version) + + def _get_vcxproj_file_cpp_path(self, dirs): + """Returns the include paths expected in the .vcxproj file""" + return ';'.join([self.workpath(dir) for dir in dirs]) + + def get_expected_sln_file_contents(self, vc_version, project_file): + """ + Returns the expected .sln file contents. + Currently this function only supports the newer VC versions that use + the .vcxproj file format. + """ + return expected_slnfile_fmt % { + 'FORMAT_VERSION': self._get_solution_file_format_version(vc_version), + 'VS_NUMBER': self._get_solution_file_vs_number(vc_version), + 'PROJECT_NAME': project_file.split('.')[0], + 'PROJECT_FILE': project_file, + } + + def get_expected_proj_file_contents(self, vc_version, dirs, project_file): + """Returns the expected .vcxproj file contents""" + if project_file.endswith('.vcxproj'): + fmt = expected_vcxprojfile_fmt + else: + fmt = expected_vcprojfile_fmt + return fmt % { + 'TOOLS_VERSION': self._get_vcxproj_file_tools_version(vc_version), + 'INCLUDE_DIRS': self._get_vcxproj_file_cpp_path(dirs), + } + + def get_expected_sconscript_file_contents(self, vc_version, project_file): + return SConscript_contents_fmt % { + 'HOST_ARCH': self.get_vs_host_arch(), + 'MSVS_VERSION': vc_version, + 'PROJECT_FILE': project_file, + } + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/testing/framework/TestSCons_time.py b/testing/framework/TestSCons_time.py index 624eb4c..547e264 100644 --- a/testing/framework/TestSCons_time.py +++ b/testing/framework/TestSCons_time.py @@ -13,7 +13,7 @@ attributes defined in this subclass. # Copyright (c) 2001 - 2019 The SCons Foundation -__revision__ = "testing/framework/TestSCons_time.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "testing/framework/TestSCons_time.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" import os import os.path @@ -272,7 +272,8 @@ class TestSCons_time(TestCommon): d, f = os.path.split(path) if not os.path.isdir(d): os.makedirs(d) - open(path, 'w').write(content) + with open(path, 'w') as f: + f.write(content) return dir def write_sample_tarfile(self, archive, dir, files): diff --git a/testing/framework/TestSConsign.py b/testing/framework/TestSConsign.py index fc72aa4..8f992ef 100644 --- a/testing/framework/TestSConsign.py +++ b/testing/framework/TestSConsign.py @@ -1,7 +1,7 @@ # Copyright (c) 2001 - 2019 The SCons Foundation from __future__ import print_function -__revision__ = "testing/framework/TestSConsign.py 103260fce95bf5db1c35fb2371983087d85dd611 2019-07-13 18:25:30 bdbaddog" +__revision__ = "testing/framework/TestSConsign.py e724ae812eb96f4858a132f5b8c769724744faf6 2019-07-21 00:04:47 bdeegan" __doc__ = """ TestSConsign.py: a testing framework for the "sconsign" script -- cgit v1.2.3 From 6e228c305122f0564eda1e67d56651f8386d24d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Tue, 23 Jul 2019 17:05:43 +0200 Subject: New upstream release --- debian/changelog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 1e66256..2327c63 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -scons-doc (3.0.5+repack-1) unstable; urgency=medium +scons-doc (3.1.0+repack-1) unstable; urgency=medium * New upstream release. - Rewrite Files-Excluded. @@ -6,9 +6,9 @@ scons-doc (3.0.5+repack-1) unstable; urgency=medium - Change debian/compat to 12. - Bump minimum debhelper version in debian/control to >= 12. * Declare compliance with Debian Policy 4.4.0. (No changes needed). - * debian/copyright: Add year 2019.. + * debian/copyright: Add year 2019. - -- Jörg Frings-Fürst Sun, 14 Jul 2019 09:37:41 +0200 + -- Jörg Frings-Fürst Tue, 23 Jul 2019 17:04:59 +0200 scons-doc (3.0.0+repack-2) unstable; urgency=medium -- cgit v1.2.3