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 @@