summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/build/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/boost/tools/build/test
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/tools/build/test')
-rw-r--r--src/boost/tools/build/test/BoostBuild.py1381
-rw-r--r--src/boost/tools/build/test/Jamfile.jam29
-rwxr-xr-xsrc/boost/tools/build/test/MockToolset.py267
-rw-r--r--src/boost/tools/build/test/TestCmd.py609
-rw-r--r--src/boost/tools/build/test/TestToolset.py134
-rw-r--r--src/boost/tools/build/test/abs_workdir.py39
-rw-r--r--src/boost/tools/build/test/absolute_sources.py73
-rw-r--r--src/boost/tools/build/test/alias.py116
-rw-r--r--src/boost/tools/build/test/alternatives.py129
-rw-r--r--src/boost/tools/build/test/always.py34
-rw-r--r--src/boost/tools/build/test/bad_dirname.py22
-rw-r--r--src/boost/tools/build/test/boost-build.jam14
-rw-r--r--src/boost/tools/build/test/boostbook.py23
-rw-r--r--src/boost/tools/build/test/boostbook/a.hpp16
-rw-r--r--src/boost/tools/build/test/boostbook/docs.xml36
-rw-r--r--src/boost/tools/build/test/boostbook/jamroot.jam3
-rw-r--r--src/boost/tools/build/test/build_dir.py107
-rw-r--r--src/boost/tools/build/test/build_file.py170
-rw-r--r--src/boost/tools/build/test/build_hooks.py39
-rw-r--r--src/boost/tools/build/test/build_no.py23
-rwxr-xr-xsrc/boost/tools/build/test/builtin_echo.py30
-rwxr-xr-xsrc/boost/tools/build/test/builtin_exit.py42
-rwxr-xr-xsrc/boost/tools/build/test/builtin_glob.py87
-rw-r--r--src/boost/tools/build/test/builtin_glob_archive.py217
-rwxr-xr-xsrc/boost/tools/build/test/builtin_readlink.py31
-rwxr-xr-xsrc/boost/tools/build/test/builtin_split_by_characters.py57
-rwxr-xr-xsrc/boost/tools/build/test/bzip2.py119
-rw-r--r--src/boost/tools/build/test/c_file.py36
-rw-r--r--src/boost/tools/build/test/chain.py56
-rw-r--r--src/boost/tools/build/test/clean.py104
-rw-r--r--src/boost/tools/build/test/cli_property_expansion.py41
-rwxr-xr-xsrc/boost/tools/build/test/collect_debug_info.py341
-rw-r--r--src/boost/tools/build/test/command_line_properties.py166
-rw-r--r--src/boost/tools/build/test/composite.py25
-rw-r--r--src/boost/tools/build/test/conditionals.py48
-rw-r--r--src/boost/tools/build/test/conditionals2.py43
-rw-r--r--src/boost/tools/build/test/conditionals3.py30
-rw-r--r--src/boost/tools/build/test/conditionals4.py45
-rwxr-xr-xsrc/boost/tools/build/test/conditionals_multiple.py312
-rwxr-xr-xsrc/boost/tools/build/test/configuration.py397
-rw-r--r--src/boost/tools/build/test/configure.py267
-rwxr-xr-xsrc/boost/tools/build/test/copy_time.py69
-rw-r--r--src/boost/tools/build/test/core-language/test.jam1571
-rwxr-xr-xsrc/boost/tools/build/test/core_action_output.py62
-rwxr-xr-xsrc/boost/tools/build/test/core_action_status.py27
-rwxr-xr-xsrc/boost/tools/build/test/core_actions_quietly.py61
-rwxr-xr-xsrc/boost/tools/build/test/core_arguments.py105
-rwxr-xr-xsrc/boost/tools/build/test/core_at_file.py64
-rwxr-xr-xsrc/boost/tools/build/test/core_bindrule.py45
-rw-r--r--src/boost/tools/build/test/core_d12.py32
-rw-r--r--src/boost/tools/build/test/core_delete_module.py51
-rw-r--r--src/boost/tools/build/test/core_dependencies.py157
-rw-r--r--src/boost/tools/build/test/core_fail_expected.py139
-rw-r--r--src/boost/tools/build/test/core_import_module.py82
-rw-r--r--src/boost/tools/build/test/core_jamshell.py55
-rwxr-xr-xsrc/boost/tools/build/test/core_language.py12
-rw-r--r--src/boost/tools/build/test/core_modifiers.py50
-rwxr-xr-xsrc/boost/tools/build/test/core_multifile_actions.py202
-rwxr-xr-xsrc/boost/tools/build/test/core_nt_cmd_line.py266
-rwxr-xr-xsrc/boost/tools/build/test/core_option_d2.py55
-rwxr-xr-xsrc/boost/tools/build/test/core_option_l.py44
-rwxr-xr-xsrc/boost/tools/build/test/core_option_n.py51
-rwxr-xr-xsrc/boost/tools/build/test/core_parallel_actions.py103
-rwxr-xr-xsrc/boost/tools/build/test/core_parallel_multifile_actions_1.py78
-rwxr-xr-xsrc/boost/tools/build/test/core_parallel_multifile_actions_2.py71
-rw-r--r--src/boost/tools/build/test/core_scanner.py36
-rwxr-xr-xsrc/boost/tools/build/test/core_source_line_tracking.py74
-rw-r--r--src/boost/tools/build/test/core_syntax_error_exit_status.py23
-rw-r--r--src/boost/tools/build/test/core_typecheck.py47
-rwxr-xr-xsrc/boost/tools/build/test/core_update_now.py377
-rwxr-xr-xsrc/boost/tools/build/test/core_variables_in_actions.py39
-rw-r--r--src/boost/tools/build/test/core_varnames.py38
-rw-r--r--src/boost/tools/build/test/custom_generator.py66
-rw-r--r--src/boost/tools/build/test/debugger-mi.py326
-rw-r--r--src/boost/tools/build/test/debugger.py674
-rw-r--r--src/boost/tools/build/test/default_build.py81
-rw-r--r--src/boost/tools/build/test/default_features.py50
-rwxr-xr-xsrc/boost/tools/build/test/default_toolset.py215
-rw-r--r--src/boost/tools/build/test/dependency_property.py38
-rw-r--r--src/boost/tools/build/test/dependency_test.py243
-rw-r--r--src/boost/tools/build/test/disambiguation.py32
-rw-r--r--src/boost/tools/build/test/dll_path.py163
-rw-r--r--src/boost/tools/build/test/double_loading.py31
-rw-r--r--src/boost/tools/build/test/duplicate.py38
-rw-r--r--src/boost/tools/build/test/example_customization.py21
-rw-r--r--src/boost/tools/build/test/example_gettext.py30
-rw-r--r--src/boost/tools/build/test/example_libraries.py21
-rw-r--r--src/boost/tools/build/test/example_make.py17
-rw-r--r--src/boost/tools/build/test/example_qt4.py26
-rwxr-xr-xsrc/boost/tools/build/test/exit_status.py26
-rw-r--r--src/boost/tools/build/test/expansion.py140
-rw-r--r--src/boost/tools/build/test/explicit.py59
-rwxr-xr-xsrc/boost/tools/build/test/feature_cxxflags.py37
-rw-r--r--src/boost/tools/build/test/feature_force_include.py41
-rw-r--r--src/boost/tools/build/test/feature_implicit_dependency.py113
-rw-r--r--src/boost/tools/build/test/feature_relevant.py142
-rw-r--r--src/boost/tools/build/test/feature_suppress_import_lib.py33
-rw-r--r--src/boost/tools/build/test/file_types.py44
-rw-r--r--src/boost/tools/build/test/flags.py74
-rw-r--r--src/boost/tools/build/test/gcc_runtime.py27
-rwxr-xr-xsrc/boost/tools/build/test/generator_selection.py158
-rw-r--r--src/boost/tools/build/test/generators_test.py433
-rw-r--r--src/boost/tools/build/test/implicit_dependency.py81
-rw-r--r--src/boost/tools/build/test/indirect_conditional.py150
-rw-r--r--src/boost/tools/build/test/inherit_toolset.py100
-rwxr-xr-xsrc/boost/tools/build/test/inherited_dependency.py237
-rw-r--r--src/boost/tools/build/test/inline.py62
-rwxr-xr-xsrc/boost/tools/build/test/install_build_no.py26
-rw-r--r--src/boost/tools/build/test/lang_objc.py33
-rw-r--r--src/boost/tools/build/test/lib_source_property.py45
-rwxr-xr-xsrc/boost/tools/build/test/lib_zlib.py184
-rwxr-xr-xsrc/boost/tools/build/test/libjpeg.py117
-rwxr-xr-xsrc/boost/tools/build/test/liblzma.py118
-rwxr-xr-xsrc/boost/tools/build/test/libpng.py119
-rw-r--r--src/boost/tools/build/test/library_chain.py152
-rw-r--r--src/boost/tools/build/test/library_order.py94
-rw-r--r--src/boost/tools/build/test/library_property.py56
-rwxr-xr-xsrc/boost/tools/build/test/libtiff.py119
-rwxr-xr-xsrc/boost/tools/build/test/libzstd.py118
-rwxr-xr-xsrc/boost/tools/build/test/link.py350
-rw-r--r--src/boost/tools/build/test/load_dir.py84
-rw-r--r--src/boost/tools/build/test/load_order.py71
-rw-r--r--src/boost/tools/build/test/loop.py24
-rw-r--r--src/boost/tools/build/test/make_rule.py54
-rwxr-xr-xsrc/boost/tools/build/test/message.py38
-rw-r--r--src/boost/tools/build/test/module_actions.py105
-rw-r--r--src/boost/tools/build/test/ndebug.py33
-rw-r--r--src/boost/tools/build/test/no_type.py19
-rw-r--r--src/boost/tools/build/test/notfile.py36
-rw-r--r--src/boost/tools/build/test/ordered_include.py251
-rw-r--r--src/boost/tools/build/test/ordered_properties.py33
-rw-r--r--src/boost/tools/build/test/out_of_tree.py29
-rw-r--r--src/boost/tools/build/test/package.py231
-rw-r--r--src/boost/tools/build/test/param.py61
-rw-r--r--src/boost/tools/build/test/path_features.py163
-rw-r--r--src/boost/tools/build/test/pch.py69
-rw-r--r--src/boost/tools/build/test/prebuilt.py43
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/a.cpp14
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/debug/a.h10
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/jamfile.jam13
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/jamfile2.jam41
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/jamfile3.jam48
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/jamroot.jam5
-rw-r--r--src/boost/tools/build/test/prebuilt/ext/release/a.h10
-rw-r--r--src/boost/tools/build/test/prebuilt/hello.cpp17
-rw-r--r--src/boost/tools/build/test/prebuilt/jamfile.jam13
-rw-r--r--src/boost/tools/build/test/prebuilt/jamroot.jam4
-rwxr-xr-xsrc/boost/tools/build/test/preprocessor.py53
-rw-r--r--src/boost/tools/build/test/print.py48
-rw-r--r--src/boost/tools/build/test/project-test3/a.cpp5
-rw-r--r--src/boost/tools/build/test/project-test3/jamfile.jam13
-rw-r--r--src/boost/tools/build/test/project-test3/jamroot.jam67
-rw-r--r--src/boost/tools/build/test/project-test3/lib/b.cpp5
-rw-r--r--src/boost/tools/build/test/project-test3/lib/jamfile.jam9
-rw-r--r--src/boost/tools/build/test/project-test3/lib2/c.cpp5
-rw-r--r--src/boost/tools/build/test/project-test3/lib2/d.cpp5
-rw-r--r--src/boost/tools/build/test/project-test3/lib2/helper/e.cpp5
-rw-r--r--src/boost/tools/build/test/project-test3/lib2/helper/jamfile.jam9
-rw-r--r--src/boost/tools/build/test/project-test3/lib2/jamfile.jam11
-rw-r--r--src/boost/tools/build/test/project-test3/lib3/f.cpp5
-rw-r--r--src/boost/tools/build/test/project-test3/lib3/jamfile.jam47
-rw-r--r--src/boost/tools/build/test/project-test3/lib3/jamroot.jam5
-rw-r--r--src/boost/tools/build/test/project-test3/readme.txt7
-rw-r--r--src/boost/tools/build/test/project-test4/a.cpp5
-rw-r--r--src/boost/tools/build/test/project-test4/a_gcc.cpp5
-rw-r--r--src/boost/tools/build/test/project-test4/jamfile.jam11
-rw-r--r--src/boost/tools/build/test/project-test4/jamfile3.jam5
-rw-r--r--src/boost/tools/build/test/project-test4/jamfile4.jam4
-rw-r--r--src/boost/tools/build/test/project-test4/jamfile5.jam6
-rw-r--r--src/boost/tools/build/test/project-test4/jamroot.jam68
-rw-r--r--src/boost/tools/build/test/project-test4/lib/b.cpp5
-rw-r--r--src/boost/tools/build/test/project-test4/lib/jamfile.jam6
-rw-r--r--src/boost/tools/build/test/project-test4/lib/jamfile1.jam2
-rw-r--r--src/boost/tools/build/test/project-test4/lib/jamfile2.jam4
-rw-r--r--src/boost/tools/build/test/project-test4/lib/jamfile3.jam2
-rw-r--r--src/boost/tools/build/test/project-test4/lib2/jamfile.jam8
-rw-r--r--src/boost/tools/build/test/project-test4/lib2/jamfile2.jam4
-rw-r--r--src/boost/tools/build/test/project-test4/readme.txt6
-rw-r--r--src/boost/tools/build/test/project_dependencies.py51
-rw-r--r--src/boost/tools/build/test/project_glob.py212
-rwxr-xr-xsrc/boost/tools/build/test/project_id.py414
-rw-r--r--src/boost/tools/build/test/project_root_constants.py62
-rw-r--r--src/boost/tools/build/test/project_root_rule.py34
-rw-r--r--src/boost/tools/build/test/project_test3.py135
-rw-r--r--src/boost/tools/build/test/project_test4.py65
-rw-r--r--src/boost/tools/build/test/property_expansion.py28
-rwxr-xr-xsrc/boost/tools/build/test/qt4.py19
-rw-r--r--src/boost/tools/build/test/qt4/jamroot.jam82
-rw-r--r--src/boost/tools/build/test/qt4/mock.cpp26
-rw-r--r--src/boost/tools/build/test/qt4/mock.h21
-rw-r--r--src/boost/tools/build/test/qt4/phonon.cpp23
-rw-r--r--src/boost/tools/build/test/qt4/qt3support.cpp29
-rw-r--r--src/boost/tools/build/test/qt4/qtassistant.cpp21
-rw-r--r--src/boost/tools/build/test/qt4/qtcore.cpp22
-rw-r--r--src/boost/tools/build/test/qt4/qtcorefail.cpp23
-rw-r--r--src/boost/tools/build/test/qt4/qtdeclarative.cpp27
-rw-r--r--src/boost/tools/build/test/qt4/qtgui.cpp42
-rw-r--r--src/boost/tools/build/test/qt4/qthelp.cpp22
-rw-r--r--src/boost/tools/build/test/qt4/qtmultimedia.cpp25
-rw-r--r--src/boost/tools/build/test/qt4/qtnetwork.cpp33
-rw-r--r--src/boost/tools/build/test/qt4/qtscript.cpp37
-rw-r--r--src/boost/tools/build/test/qt4/qtscripttools.cpp47
-rw-r--r--src/boost/tools/build/test/qt4/qtsql.cpp37
-rw-r--r--src/boost/tools/build/test/qt4/qtsvg.cpp21
-rw-r--r--src/boost/tools/build/test/qt4/qttest.cpp30
-rw-r--r--src/boost/tools/build/test/qt4/qtwebkit.cpp24
-rw-r--r--src/boost/tools/build/test/qt4/qtxml.cpp29
-rw-r--r--src/boost/tools/build/test/qt4/qtxmlpatterns.cpp76
-rw-r--r--src/boost/tools/build/test/qt4/rcc.cpp20
-rw-r--r--src/boost/tools/build/test/qt4/rcc.qrc5
-rwxr-xr-xsrc/boost/tools/build/test/qt5.py20
-rw-r--r--src/boost/tools/build/test/qt5/initialization.cpp7
-rw-r--r--src/boost/tools/build/test/qt5/jamroot.jam118
-rw-r--r--src/boost/tools/build/test/qt5/mock.cpp26
-rw-r--r--src/boost/tools/build/test/qt5/mock.h21
-rw-r--r--src/boost/tools/build/test/qt5/qt3dcore.cpp21
-rw-r--r--src/boost/tools/build/test/qt5/qt3dinput.cpp24
-rw-r--r--src/boost/tools/build/test/qt5/qt3dlogic.cpp20
-rw-r--r--src/boost/tools/build/test/qt5/qt3drender.cpp21
-rw-r--r--src/boost/tools/build/test/qt5/qtassistant.cpp21
-rw-r--r--src/boost/tools/build/test/qt5/qtbluetooth.cpp34
-rw-r--r--src/boost/tools/build/test/qt5/qtcharts.cpp15
-rw-r--r--src/boost/tools/build/test/qt5/qtcore.cpp22
-rw-r--r--src/boost/tools/build/test/qt5/qtcorefail.cpp23
-rw-r--r--src/boost/tools/build/test/qt5/qtdatavisualization.cpp31
-rw-r--r--src/boost/tools/build/test/qt5/qtdeclarative.cpp26
-rw-r--r--src/boost/tools/build/test/qt5/qtgamepad.cpp29
-rw-r--r--src/boost/tools/build/test/qt5/qthelp.cpp22
-rw-r--r--src/boost/tools/build/test/qt5/qtlocation.cpp30
-rw-r--r--src/boost/tools/build/test/qt5/qtmultimedia.cpp25
-rw-r--r--src/boost/tools/build/test/qt5/qtnetwork.cpp33
-rw-r--r--src/boost/tools/build/test/qt5/qtnfc.cpp28
-rw-r--r--src/boost/tools/build/test/qt5/qtpositioning.cpp23
-rw-r--r--src/boost/tools/build/test/qt5/qtpurchasing.cpp44
-rw-r--r--src/boost/tools/build/test/qt5/qtquick.cpp43
-rw-r--r--src/boost/tools/build/test/qt5/qtquick.qml20
-rw-r--r--src/boost/tools/build/test/qt5/qtscript.cpp37
-rw-r--r--src/boost/tools/build/test/qt5/qtscripttools.cpp47
-rw-r--r--src/boost/tools/build/test/qt5/qtscxml.cpp33
-rw-r--r--src/boost/tools/build/test/qt5/qtserialbus.cpp25
-rw-r--r--src/boost/tools/build/test/qt5/qtserialport.cpp22
-rw-r--r--src/boost/tools/build/test/qt5/qtsql.cpp37
-rw-r--r--src/boost/tools/build/test/qt5/qtsvg.cpp21
-rw-r--r--src/boost/tools/build/test/qt5/qttest.cpp30
-rw-r--r--src/boost/tools/build/test/qt5/qtwebchannel.cpp29
-rw-r--r--src/boost/tools/build/test/qt5/qtwebengine.cpp30
-rw-r--r--src/boost/tools/build/test/qt5/qtwebenginewidgets.cpp40
-rw-r--r--src/boost/tools/build/test/qt5/qtwebkit.cpp22
-rw-r--r--src/boost/tools/build/test/qt5/qtwebkitwidgets.cpp23
-rw-r--r--src/boost/tools/build/test/qt5/qtwebsocket.cpp26
-rw-r--r--src/boost/tools/build/test/qt5/qtwebsockets.cpp24
-rw-r--r--src/boost/tools/build/test/qt5/qtwebview.cpp31
-rw-r--r--src/boost/tools/build/test/qt5/qtwidgets.cpp43
-rw-r--r--src/boost/tools/build/test/qt5/qtxml.cpp29
-rw-r--r--src/boost/tools/build/test/qt5/qtxmlpatterns.cpp76
-rw-r--r--src/boost/tools/build/test/qt5/rcc.cpp20
-rw-r--r--src/boost/tools/build/test/qt5/rcc.qrc5
-rw-r--r--src/boost/tools/build/test/readme.txt6
-rw-r--r--src/boost/tools/build/test/rebuilds.py68
-rw-r--r--src/boost/tools/build/test/relative_sources.py38
-rw-r--r--src/boost/tools/build/test/remove_requirement.py91
-rwxr-xr-xsrc/boost/tools/build/test/rescan_header.py268
-rw-r--r--src/boost/tools/build/test/resolution.py35
-rw-r--r--src/boost/tools/build/test/results-python.txt132
-rw-r--r--src/boost/tools/build/test/rootless.py36
-rw-r--r--src/boost/tools/build/test/rootless/test1/sub_root/a.cpp6
-rw-r--r--src/boost/tools/build/test/rootless/test1/sub_root/jamfile.jam10
-rw-r--r--src/boost/tools/build/test/rootless/test2/sub_root/a.cpp6
-rw-r--r--src/boost/tools/build/test/rootless/test2/sub_root/jamfile.jam13
-rw-r--r--src/boost/tools/build/test/rootless/test3/jamfile.jam6
-rw-r--r--src/boost/tools/build/test/rootless/test3/sub/inner/a.cpp6
-rw-r--r--src/boost/tools/build/test/rootless/test3/sub/inner/jamfile.jam11
-rwxr-xr-xsrc/boost/tools/build/test/scanner_causing_rebuilds.py132
-rw-r--r--src/boost/tools/build/test/searched_lib.py202
-rw-r--r--src/boost/tools/build/test/skipping.py27
-rwxr-xr-xsrc/boost/tools/build/test/sort_rule.py98
-rw-r--r--src/boost/tools/build/test/source_locations.py42
-rwxr-xr-xsrc/boost/tools/build/test/source_order.py84
-rwxr-xr-xsrc/boost/tools/build/test/space_in_path.py51
-rw-r--r--src/boost/tools/build/test/stage.py207
-rw-r--r--src/boost/tools/build/test/standalone.py53
-rw-r--r--src/boost/tools/build/test/startup/boost-root/boost-build.jam7
-rw-r--r--src/boost/tools/build/test/startup/boost-root/build/boost-build.jam6
-rw-r--r--src/boost/tools/build/test/startup/boost-root/build/bootstrap.jam7
-rw-r--r--src/boost/tools/build/test/startup/bootstrap-env/boost-build.jam5
-rw-r--r--src/boost/tools/build/test/startup/bootstrap-explicit/boost-build.jam6
-rw-r--r--src/boost/tools/build/test/startup/bootstrap-implicit/readme.txt5
-rw-r--r--src/boost/tools/build/test/startup/no-bootstrap1/boost-build.jam6
-rw-r--r--src/boost/tools/build/test/startup/no-bootstrap1/subdir/readme.txt5
-rw-r--r--src/boost/tools/build/test/startup/no-bootstrap2/boost-build.jam6
-rw-r--r--src/boost/tools/build/test/startup/no-bootstrap3/boost-build.jam5
-rw-r--r--src/boost/tools/build/test/startup_v2.py96
-rwxr-xr-xsrc/boost/tools/build/test/static_and_shared_library.py36
-rw-r--r--src/boost/tools/build/test/suffix.py78
-rw-r--r--src/boost/tools/build/test/symlink.py43
-rw-r--r--src/boost/tools/build/test/tag.py122
-rw-r--r--src/boost/tools/build/test/template.py42
-rw-r--r--src/boost/tools/build/test/test-config-example.jam19
-rw-r--r--src/boost/tools/build/test/test.jam39
-rw-r--r--src/boost/tools/build/test/test1.py18
-rw-r--r--src/boost/tools/build/test/test2.py25
-rw-r--r--src/boost/tools/build/test/test2/foo.cpp7
-rw-r--r--src/boost/tools/build/test/test2/jamroot.jam5
-rw-r--r--src/boost/tools/build/test/test_all.py373
-rwxr-xr-xsrc/boost/tools/build/test/test_rc.py148
-rw-r--r--src/boost/tools/build/test/test_system.html623
-rwxr-xr-xsrc/boost/tools/build/test/testing.py556
-rw-r--r--src/boost/tools/build/test/timedata.py178
-rw-r--r--src/boost/tools/build/test/toolset-mock/Jamroot.jam8
-rw-r--r--src/boost/tools/build/test/toolset-mock/lib.cpp7
-rw-r--r--src/boost/tools/build/test/toolset-mock/main.cpp7
-rw-r--r--src/boost/tools/build/test/toolset-mock/project-config.jam48
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/Jamroot.jam57
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/MockProgram.py287
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/ar.py27
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/clang-3.9.0-darwin.py48
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/clang-linux-3.9.0.py87
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/clang-vxworks-4.0.1.py41
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/darwin-4.2.1.py38
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/gcc-4.2.1-darwin.py36
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/gcc-4.8.3-linux.py49
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/intel-darwin-10.2.py42
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/ld.py33
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/libtool.py14
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/linkx.py33
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/mock-program.cpp42
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/msvc-14.3.py33
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/project-config.jam5
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/strip.py13
-rw-r--r--src/boost/tools/build/test/toolset-mock/src/verify.py9
-rw-r--r--src/boost/tools/build/test/toolset_clang_darwin.py20
-rw-r--r--src/boost/tools/build/test/toolset_clang_linux.py29
-rw-r--r--src/boost/tools/build/test/toolset_clang_vxworks.py20
-rw-r--r--src/boost/tools/build/test/toolset_darwin.py21
-rw-r--r--src/boost/tools/build/test/toolset_defaults.py60
-rw-r--r--src/boost/tools/build/test/toolset_gcc.py26
-rw-r--r--src/boost/tools/build/test/toolset_intel_darwin.py19
-rw-r--r--src/boost/tools/build/test/toolset_msvc.py19
-rw-r--r--src/boost/tools/build/test/toolset_requirements.py44
-rw-r--r--src/boost/tools/build/test/transitive_skip.py30
-rw-r--r--src/boost/tools/build/test/tree.py245
-rw-r--r--src/boost/tools/build/test/unit_test.py36
-rw-r--r--src/boost/tools/build/test/unit_tests.py11
-rw-r--r--src/boost/tools/build/test/unused.py81
-rw-r--r--src/boost/tools/build/test/use_requirements.py283
-rw-r--r--src/boost/tools/build/test/using.py32
-rw-r--r--src/boost/tools/build/test/wrapper.py38
-rw-r--r--src/boost/tools/build/test/wrong_project.py39
348 files changed, 26613 insertions, 0 deletions
diff --git a/src/boost/tools/build/test/BoostBuild.py b/src/boost/tools/build/test/BoostBuild.py
new file mode 100644
index 000000000..e7dee3063
--- /dev/null
+++ b/src/boost/tools/build/test/BoostBuild.py
@@ -0,0 +1,1381 @@
+# Copyright 2002-2005 Vladimir Prus.
+# Copyright 2002-2003 Dave Abrahams.
+# Copyright 2006 Rene Ferdinand Rivera Morell.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from __future__ import print_function
+
+import TestCmd
+
+import copy
+import fnmatch
+import glob
+import math
+import os
+import os.path
+import re
+import shutil
+try:
+ from StringIO import StringIO
+except:
+ from io import StringIO
+import subprocess
+import sys
+import tempfile
+import time
+import traceback
+import tree
+import types
+
+from xml.sax.saxutils import escape
+
+try:
+ from functools import reduce
+except:
+ pass
+
+
+def isstr(data):
+ return isinstance(data, (type(''), type(u'')))
+
+
+class TestEnvironmentError(Exception):
+ pass
+
+
+annotations = []
+
+
+def print_annotation(name, value, xml):
+ """Writes some named bits of information about the current test run."""
+ if xml:
+ print(escape(name) + " {{{")
+ print(escape(value))
+ print("}}}")
+ else:
+ print(name + " {{{")
+ print(value)
+ print("}}}")
+
+
+def flush_annotations(xml=0):
+ global annotations
+ for ann in annotations:
+ print_annotation(ann[0], ann[1], xml)
+ annotations = []
+
+
+def clear_annotations():
+ global annotations
+ annotations = []
+
+
+defer_annotations = 0
+
+def set_defer_annotations(n):
+ global defer_annotations
+ defer_annotations = n
+
+
+def annotate_stack_trace(tb=None):
+ if tb:
+ trace = TestCmd.caller(traceback.extract_tb(tb), 0)
+ else:
+ trace = TestCmd.caller(traceback.extract_stack(), 1)
+ annotation("stacktrace", trace)
+
+
+def annotation(name, value):
+ """Records an annotation about the test run."""
+ annotations.append((name, value))
+ if not defer_annotations:
+ flush_annotations()
+
+
+def get_toolset():
+ toolset = None
+ for arg in sys.argv[1:]:
+ if not arg.startswith("-"):
+ toolset = arg
+ return toolset or "gcc"
+
+
+# Detect the host OS.
+cygwin = hasattr(os, "uname") and os.uname()[0].lower().startswith("cygwin")
+windows = cygwin or os.environ.get("OS", "").lower().startswith("windows")
+
+if cygwin:
+ default_os = "cygwin"
+elif windows:
+ default_os = "windows"
+elif hasattr(os, "uname"):
+ default_os = os.uname()[0].lower()
+
+
+def expand_toolset(toolset, target_os=default_os):
+ match = re.match(r'^(clang|intel)(-[\d\.]+|)$', toolset)
+ if match:
+ if match.group(1) == "intel" and target_os == "windows":
+ return match.expand(r'\1-win\2')
+ elif target_os == "darwin":
+ return match.expand(r'\1-darwin\2')
+ else:
+ return match.expand(r'\1-linux\2')
+
+ return toolset
+
+
+def prepare_prefixes_and_suffixes(toolset, target_os=default_os):
+ ind = toolset.find('-')
+ if ind == -1:
+ rtoolset = toolset
+ else:
+ rtoolset = toolset[:ind]
+ prepare_suffix_map(rtoolset, target_os)
+ prepare_library_prefix(rtoolset, target_os)
+
+
+def prepare_suffix_map(toolset, target_os=default_os):
+ """
+ Set up suffix translation performed by the Boost Build testing framework
+ to accommodate different toolsets generating targets of the same type using
+ different filename extensions (suffixes).
+
+ """
+ global suffixes
+ suffixes = {}
+ if target_os == "cygwin":
+ suffixes[".lib"] = ".a"
+ suffixes[".obj"] = ".o"
+ suffixes[".implib"] = ".lib.a"
+ elif target_os == "windows":
+ if toolset == "gcc":
+ # MinGW
+ suffixes[".lib"] = ".a"
+ suffixes[".obj"] = ".o"
+ suffixes[".implib"] = ".dll.a"
+ else:
+ # Everything else Windows
+ suffixes[".implib"] = ".lib"
+ else:
+ suffixes[".exe"] = ""
+ suffixes[".dll"] = ".so"
+ suffixes[".lib"] = ".a"
+ suffixes[".obj"] = ".o"
+ suffixes[".implib"] = ".no_implib_files_on_this_platform"
+
+ if target_os == "darwin":
+ suffixes[".dll"] = ".dylib"
+
+
+def prepare_library_prefix(toolset, target_os=default_os):
+ """
+ Setup whether Boost Build is expected to automatically prepend prefixes
+ to its built library targets.
+
+ """
+ global lib_prefix
+ lib_prefix = "lib"
+
+ global dll_prefix
+ if target_os == "cygwin":
+ dll_prefix = "cyg"
+ elif target_os == "windows" and toolset != "gcc":
+ dll_prefix = None
+ else:
+ dll_prefix = "lib"
+
+
+def re_remove(sequence, regex):
+ me = re.compile(regex)
+ result = list(filter(lambda x: me.match(x), sequence))
+ if not result:
+ raise ValueError()
+ for r in result:
+ sequence.remove(r)
+
+
+def glob_remove(sequence, pattern):
+ result = list(fnmatch.filter(sequence, pattern))
+ if not result:
+ raise ValueError()
+ for r in result:
+ sequence.remove(r)
+
+
+class Tester(TestCmd.TestCmd):
+ """Main tester class for Boost Build.
+
+ Optional arguments:
+
+ `arguments` - Arguments passed to the run executable.
+ `executable` - Name of the executable to invoke.
+ `match` - Function to use for compating actual and
+ expected file contents.
+ `boost_build_path` - Boost build path to be passed to the run
+ executable.
+ `translate_suffixes` - Whether to update suffixes on the the file
+ names passed from the test script so they
+ match those actually created by the current
+ toolset. For example, static library files
+ are specified by using the .lib suffix but
+ when the "gcc" toolset is used it actually
+ creates them using the .a suffix.
+ `pass_toolset` - Whether the test system should pass the
+ specified toolset to the run executable.
+ `use_test_config` - Whether the test system should tell the run
+ executable to read in the test_config.jam
+ configuration file.
+ `ignore_toolset_requirements` - Whether the test system should tell the run
+ executable to ignore toolset requirements.
+ `workdir` - Absolute directory where the test will be
+ run from.
+ `pass_d0` - If set, when tests are not explicitly run
+ in verbose mode, they are run as silent
+ (-d0 & --quiet Boost Jam options).
+
+ Optional arguments inherited from the base class:
+
+ `description` - Test description string displayed in case
+ of a failed test.
+ `subdir` - List of subdirectories to automatically
+ create under the working directory. Each
+ subdirectory needs to be specified
+ separately, parent coming before its child.
+ `verbose` - Flag that may be used to enable more
+ verbose test system output. Note that it
+ does not also enable more verbose build
+ system output like the --verbose command
+ line option does.
+ """
+ def __init__(self, arguments=None, executable=None,
+ match=TestCmd.match_exact, boost_build_path=None,
+ translate_suffixes=True, pass_toolset=True, use_test_config=True,
+ ignore_toolset_requirements=False, workdir="", pass_d0=False,
+ **keywords):
+
+ if not executable:
+ executable = os.getenv('B2')
+ if not executable:
+ executable = 'b2'
+
+ assert arguments.__class__ is not str
+ self.original_workdir = os.path.dirname(__file__)
+ if workdir and not os.path.isabs(workdir):
+ raise ("Parameter workdir <%s> must point to an absolute "
+ "directory: " % workdir)
+
+ self.last_build_timestamp = 0
+ self.translate_suffixes = translate_suffixes
+ self.use_test_config = use_test_config
+
+ self.toolset = get_toolset()
+ self.expanded_toolset = expand_toolset(self.toolset)
+ self.pass_toolset = pass_toolset
+ self.ignore_toolset_requirements = ignore_toolset_requirements
+
+ prepare_prefixes_and_suffixes(pass_toolset and self.toolset or "gcc")
+
+ use_default_bjam = "--default-bjam" in sys.argv
+
+ if not use_default_bjam:
+ jam_build_dir = ""
+
+ # Find where jam_src is located. Try for the debug version if it is
+ # lying around.
+ srcdir = os.path.join(os.path.dirname(__file__), "..", "src")
+ dirs = [os.path.join(srcdir, "engine", jam_build_dir + ".debug"),
+ os.path.join(srcdir, "engine", jam_build_dir)]
+ for d in dirs:
+ if os.path.exists(d):
+ jam_build_dir = d
+ break
+ else:
+ print("Cannot find built Boost.Jam")
+ sys.exit(1)
+
+ verbosity = ["-d0", "--quiet"]
+ if not pass_d0:
+ verbosity = []
+ if "--verbose" in sys.argv:
+ keywords["verbose"] = True
+ verbosity = ["-d2"]
+ self.verbosity = verbosity
+
+ if boost_build_path is None:
+ boost_build_path = self.original_workdir + "/.."
+
+ program_list = []
+ if use_default_bjam:
+ program_list.append(executable)
+ else:
+ program_list.append(os.path.join(jam_build_dir, executable))
+ program_list.append('-sBOOST_BUILD_PATH="' + boost_build_path + '"')
+ if arguments:
+ program_list += arguments
+
+ TestCmd.TestCmd.__init__(self, program=program_list, match=match,
+ workdir=workdir, inpath=use_default_bjam, **keywords)
+
+ os.chdir(self.workdir)
+
+ def cleanup(self):
+ try:
+ TestCmd.TestCmd.cleanup(self)
+ os.chdir(self.original_workdir)
+ except AttributeError:
+ # When this is called during TestCmd.TestCmd.__del__ we can have
+ # both 'TestCmd' and 'os' unavailable in our scope. Do nothing in
+ # this case.
+ pass
+
+ def set_toolset(self, toolset, target_os=default_os):
+ self.toolset = toolset
+ self.expanded_toolset = expand_toolset(toolset, target_os)
+ self.pass_toolset = True
+ prepare_prefixes_and_suffixes(toolset, target_os)
+
+
+ #
+ # Methods that change the working directory's content.
+ #
+ def set_tree(self, tree_location):
+ # It is not possible to remove the current directory.
+ d = os.getcwd()
+ os.chdir(os.path.dirname(self.workdir))
+ shutil.rmtree(self.workdir, ignore_errors=False)
+
+ if not os.path.isabs(tree_location):
+ tree_location = os.path.join(self.original_workdir, tree_location)
+ shutil.copytree(tree_location, self.workdir)
+
+ os.chdir(d)
+ def make_writable(unused, dir, entries):
+ for e in entries:
+ name = os.path.join(dir, e)
+ os.chmod(name, os.stat(name).st_mode | 0o222)
+ for root, _, files in os.walk("."):
+ make_writable(None, root, files)
+
+ def write(self, file, content, wait=True):
+ nfile = self.native_file_name(file)
+ self.__makedirs(os.path.dirname(nfile), wait)
+ if not type(content) == bytes:
+ content = content.encode()
+ f = open(nfile, "wb")
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ self.__ensure_newer_than_last_build(nfile)
+
+ def rename(self, src, dst):
+ src_name = self.native_file_name(src)
+ dst_name = self.native_file_name(dst)
+ os.rename(src_name, dst_name)
+
+ def copy(self, src, dst):
+ try:
+ self.write(dst, self.read(src, binary=True))
+ except:
+ self.fail_test(1)
+
+ def copy_timestamp(self, src, dst):
+ src_name = self.native_file_name(src)
+ dst_name = self.native_file_name(dst)
+ shutil.copystat(src_name, dst_name)
+
+ def copy_preserving_timestamp(self, src, dst):
+ src_name = self.native_file_name(src)
+ dst_name = self.native_file_name(dst)
+ shutil.copy2(src_name, dst_name)
+
+ def touch(self, names, wait=True):
+ if isstr(names):
+ names = [names]
+ for name in names:
+ path = self.native_file_name(name)
+ if wait:
+ self.__ensure_newer_than_last_build(path)
+ else:
+ os.utime(path, None)
+
+ def rm(self, names):
+ if not type(names) == list:
+ names = [names]
+
+ if names == ["."]:
+ # If we are deleting the entire workspace, there is no need to wait
+ # for a clock tick.
+ self.last_build_timestamp = 0
+
+ # Avoid attempts to remove the current directory.
+ os.chdir(self.original_workdir)
+ for name in names:
+ n = glob.glob(self.native_file_name(name))
+ if n: n = n[0]
+ if not n:
+ n = self.glob_file(name.replace("$toolset", self.expanded_toolset + "*")
+ )
+ if n:
+ if os.path.isdir(n):
+ shutil.rmtree(n, ignore_errors=False)
+ else:
+ os.unlink(n)
+
+ # Create working dir root again in case we removed it.
+ if not os.path.exists(self.workdir):
+ os.mkdir(self.workdir)
+ os.chdir(self.workdir)
+
+ def expand_toolset(self, name):
+ """
+ Expands $toolset placeholder in the given file to the name of the
+ toolset currently being tested.
+
+ """
+ self.write(name, self.read(name).replace("$toolset", self.expanded_toolset))
+
+ def dump_stdio(self):
+ annotation("STDOUT", self.stdout())
+ annotation("STDERR", self.stderr())
+
+ def run_build_system(self, extra_args=None, subdir="", stdout=None,
+ stderr="", status=0, match=None, pass_toolset=None,
+ use_test_config=None, ignore_toolset_requirements=None,
+ expected_duration=None, **kw):
+
+ assert extra_args.__class__ is not str
+
+ if os.path.isabs(subdir):
+ raise ValueError(
+ "You must pass a relative directory to subdir <%s>." % subdir)
+
+ self.previous_tree, dummy = tree.build_tree(self.workdir)
+ self.wait_for_time_change_since_last_build()
+
+ if match is None:
+ match = self.match
+
+ if pass_toolset is None:
+ pass_toolset = self.pass_toolset
+
+ if use_test_config is None:
+ use_test_config = self.use_test_config
+
+ if ignore_toolset_requirements is None:
+ ignore_toolset_requirements = self.ignore_toolset_requirements
+
+ try:
+ kw["program"] = []
+ kw["program"] += self.program
+ if extra_args:
+ kw["program"] += extra_args
+ if not extra_args or not any(a.startswith("-j") for a in extra_args):
+ kw["program"] += ["-j1"]
+ if stdout is None and not any(a.startswith("-d") for a in kw["program"]):
+ kw["program"] += self.verbosity
+ if pass_toolset:
+ kw["program"].append("toolset=" + self.toolset)
+ if use_test_config:
+ kw["program"].append('--test-config="%s"' % os.path.join(
+ self.original_workdir, "test-config.jam"))
+ if ignore_toolset_requirements:
+ kw["program"].append("--ignore-toolset-requirements")
+ if "--python" in sys.argv:
+ # -z disables Python optimization mode.
+ # this enables type checking (all assert
+ # and if __debug__ statements).
+ kw["program"].extend(["--python", "-z"])
+ if "--stacktrace" in sys.argv:
+ kw["program"].append("--stacktrace")
+ kw["chdir"] = subdir
+ self.last_program_invocation = kw["program"]
+ build_time_start = time.time()
+ TestCmd.TestCmd.run(self, **kw)
+ build_time_finish = time.time()
+ except:
+ self.dump_stdio()
+ raise
+
+ old_last_build_timestamp = self.last_build_timestamp
+ self.tree, self.last_build_timestamp = tree.build_tree(self.workdir)
+ self.difference = tree.tree_difference(self.previous_tree, self.tree)
+ if self.difference.empty():
+ # If nothing has been changed by this build and sufficient time has
+ # passed since the last build that actually changed something,
+ # there is no need to wait for touched or newly created files to
+ # start getting newer timestamps than the currently existing ones.
+ self.last_build_timestamp = old_last_build_timestamp
+
+ self.difference.ignore_directories()
+ self.unexpected_difference = copy.deepcopy(self.difference)
+
+ if (status and self.status) is not None and self.status != status:
+ expect = ""
+ if status != 0:
+ expect = " (expected %d)" % status
+
+ annotation("failure", '"%s" returned %d%s' % (kw["program"],
+ self.status, expect))
+
+ annotation("reason", "unexpected status returned by bjam")
+ self.fail_test(1)
+
+ if stdout is not None and not match(self.stdout(), stdout):
+ stdout_test = match(self.stdout(), stdout)
+ annotation("failure", "Unexpected stdout")
+ annotation("Expected STDOUT", stdout)
+ annotation("Actual STDOUT", self.stdout())
+ stderr = self.stderr()
+ if stderr:
+ annotation("STDERR", stderr)
+ self.maybe_do_diff(self.stdout(), stdout, stdout_test)
+ self.fail_test(1, dump_stdio=False)
+
+ # Intel tends to produce some messages to stderr which make tests fail.
+ intel_workaround = re.compile("^xi(link|lib): executing.*\n", re.M)
+ actual_stderr = re.sub(intel_workaround, "", self.stderr())
+
+ if stderr is not None and not match(actual_stderr, stderr):
+ stderr_test = match(actual_stderr, stderr)
+ annotation("failure", "Unexpected stderr")
+ annotation("Expected STDERR", stderr)
+ annotation("Actual STDERR", self.stderr())
+ annotation("STDOUT", self.stdout())
+ self.maybe_do_diff(actual_stderr, stderr, stderr_test)
+ self.fail_test(1, dump_stdio=False)
+
+ if expected_duration is not None:
+ actual_duration = build_time_finish - build_time_start
+ if actual_duration > expected_duration:
+ print("Test run lasted %f seconds while it was expected to "
+ "finish in under %f seconds." % (actual_duration,
+ expected_duration))
+ self.fail_test(1, dump_stdio=False)
+
+ self.__ignore_junk()
+
+ def glob_file(self, name):
+ name = self.adjust_name(name)
+ result = None
+ if hasattr(self, "difference"):
+ for f in (self.difference.added_files +
+ self.difference.modified_files +
+ self.difference.touched_files):
+ if fnmatch.fnmatch(f, name):
+ result = self.__native_file_name(f)
+ break
+ if not result:
+ result = glob.glob(self.__native_file_name(name))
+ if result:
+ result = result[0]
+ return result
+
+ def __read(self, name, binary=False):
+ try:
+ openMode = "r"
+ if binary:
+ openMode += "b"
+ else:
+ openMode += "U"
+ f = open(name, openMode)
+ result = f.read()
+ f.close()
+ return result
+ except:
+ annotation("failure", "Could not open '%s'" % name)
+ self.fail_test(1)
+ return ""
+
+ def read(self, name, binary=False):
+ name = self.glob_file(name)
+ return self.__read(name, binary=binary)
+
+ def read_and_strip(self, name):
+ if not self.glob_file(name):
+ return ""
+ f = open(self.glob_file(name), "rb")
+ lines = f.readlines()
+ f.close()
+ result = "\n".join(x.decode().rstrip() for x in lines)
+ if lines and lines[-1][-1] != "\n":
+ return result + "\n"
+ return result
+
+ def fail_test(self, condition, dump_difference=True, dump_stdio=True,
+ dump_stack=True):
+ if not condition:
+ return
+
+ if dump_difference and hasattr(self, "difference"):
+ f = StringIO()
+ self.difference.pprint(f)
+ annotation("changes caused by the last build command",
+ f.getvalue())
+
+ if dump_stdio:
+ self.dump_stdio()
+
+ if "--preserve" in sys.argv:
+ print()
+ print("*** Copying the state of working dir into 'failed_test' ***")
+ print()
+ path = os.path.join(self.original_workdir, "failed_test")
+ if os.path.isdir(path):
+ shutil.rmtree(path, ignore_errors=False)
+ elif os.path.exists(path):
+ raise "Path " + path + " already exists and is not a directory"
+ shutil.copytree(self.workdir, path)
+ print("The failed command was:")
+ print(" ".join(self.last_program_invocation))
+
+ if dump_stack:
+ annotate_stack_trace()
+ sys.exit(1)
+
+ # A number of methods below check expectations with actual difference
+ # between directory trees before and after a build. All the 'expect*'
+ # methods require exact names to be passed. All the 'ignore*' methods allow
+ # wildcards.
+
+ # All names can be either a string or a list of strings.
+ def expect_addition(self, names):
+ for name in self.adjust_names(names):
+ try:
+ glob_remove(self.unexpected_difference.added_files, name)
+ except:
+ annotation("failure", "File %s not added as expected" % name)
+ self.fail_test(1)
+
+ def ignore_addition(self, wildcard):
+ self.__ignore_elements(self.unexpected_difference.added_files,
+ wildcard)
+
+ def expect_removal(self, names):
+ for name in self.adjust_names(names):
+ try:
+ glob_remove(self.unexpected_difference.removed_files, name)
+ except:
+ annotation("failure", "File %s not removed as expected" % name)
+ self.fail_test(1)
+
+ def ignore_removal(self, wildcard):
+ self.__ignore_elements(self.unexpected_difference.removed_files,
+ wildcard)
+
+ def expect_modification(self, names):
+ for name in self.adjust_names(names):
+ try:
+ glob_remove(self.unexpected_difference.modified_files, name)
+ except:
+ annotation("failure", "File %s not modified as expected" %
+ name)
+ self.fail_test(1)
+
+ def ignore_modification(self, wildcard):
+ self.__ignore_elements(self.unexpected_difference.modified_files,
+ wildcard)
+
+ def expect_touch(self, names):
+ d = self.unexpected_difference
+ for name in self.adjust_names(names):
+ # We need to check both touched and modified files. The reason is
+ # that:
+ # (1) Windows binaries such as obj, exe or dll files have slight
+ # differences even with identical inputs due to Windows PE
+ # format headers containing an internal timestamp.
+ # (2) Intel's compiler for Linux has the same behaviour.
+ filesets = [d.modified_files, d.touched_files]
+
+ while filesets:
+ try:
+ glob_remove(filesets[-1], name)
+ break
+ except ValueError:
+ filesets.pop()
+
+ if not filesets:
+ annotation("failure", "File %s not touched as expected" % name)
+ self.fail_test(1)
+
+ def ignore_touch(self, wildcard):
+ self.__ignore_elements(self.unexpected_difference.touched_files,
+ wildcard)
+
+ def ignore(self, wildcard):
+ self.ignore_addition(wildcard)
+ self.ignore_removal(wildcard)
+ self.ignore_modification(wildcard)
+ self.ignore_touch(wildcard)
+
+ def expect_nothing(self, names):
+ for name in self.adjust_names(names):
+ if name in self.difference.added_files:
+ annotation("failure",
+ "File %s added, but no action was expected" % name)
+ self.fail_test(1)
+ if name in self.difference.removed_files:
+ annotation("failure",
+ "File %s removed, but no action was expected" % name)
+ self.fail_test(1)
+ pass
+ if name in self.difference.modified_files:
+ annotation("failure",
+ "File %s modified, but no action was expected" % name)
+ self.fail_test(1)
+ if name in self.difference.touched_files:
+ annotation("failure",
+ "File %s touched, but no action was expected" % name)
+ self.fail_test(1)
+
+ def __ignore_junk(self):
+ # Not totally sure about this change, but I do not see a good
+ # alternative.
+ if windows:
+ self.ignore("*.ilk") # MSVC incremental linking files.
+ self.ignore("*.pdb") # MSVC program database files.
+ self.ignore("*.rsp") # Response files.
+ self.ignore("*.tds") # Borland debug symbols.
+ self.ignore("*.manifest") # MSVC DLL manifests.
+ self.ignore("bin/standalone/msvc/*/msvc-setup.bat")
+
+ # Debug builds of bjam built with gcc produce this profiling data.
+ self.ignore("gmon.out")
+ self.ignore("*/gmon.out")
+
+ # Boost Build's 'configure' functionality (unfinished at the time)
+ # produces this file.
+ self.ignore("bin/config.log")
+ self.ignore("bin/project-cache.jam")
+
+ # Compiled Python files created when running Python based Boost Build.
+ self.ignore("*.pyc")
+
+ # OSX/Darwin files and dirs.
+ self.ignore("*.dSYM/*")
+
+ def expect_nothing_more(self):
+ if not self.unexpected_difference.empty():
+ annotation("failure", "Unexpected changes found")
+ output = StringIO()
+ self.unexpected_difference.pprint(output)
+ annotation("unexpected changes", output.getvalue())
+ self.fail_test(1)
+
+ def expect_output_lines(self, lines, expected=True):
+ self.__expect_lines(self.stdout(), lines, expected)
+
+ def expect_content_lines(self, filename, line, expected=True):
+ self.__expect_lines(self.read_and_strip(filename), line, expected)
+
+ def expect_content(self, name, content, exact=False):
+ actual = self.read(name)
+ content = content.replace("$toolset", self.expanded_toolset + "*")
+
+ matched = False
+ if exact:
+ matched = fnmatch.fnmatch(actual, content)
+ else:
+ def sorted_(z):
+ z.sort(key=lambda x: x.lower().replace("\\", "/"))
+ return z
+ actual_ = list(map(lambda x: sorted_(x.split()), actual.splitlines()))
+ content_ = list(map(lambda x: sorted_(x.split()), content.splitlines()))
+ if len(actual_) == len(content_):
+ matched = map(
+ lambda x, y: map(lambda n, p: fnmatch.fnmatch(n, p), x, y),
+ actual_, content_)
+ matched = reduce(
+ lambda x, y: x and reduce(
+ lambda a, b: a and b,
+ y, True),
+ matched, True)
+
+ if not matched:
+ print("Expected:\n")
+ print(content)
+ print("Got:\n")
+ print(actual)
+ self.fail_test(1)
+
+ def maybe_do_diff(self, actual, expected, result=None):
+ if os.environ.get("DO_DIFF"):
+ e = tempfile.mktemp("expected")
+ a = tempfile.mktemp("actual")
+ f = open(e, "w")
+ f.write(expected)
+ f.close()
+ f = open(a, "w")
+ f.write(actual)
+ f.close()
+ print("DIFFERENCE")
+ # Current diff should return 1 to indicate 'different input files'
+ # but some older diff versions may return 0 and depending on the
+ # exact Python/OS platform version, os.system() call may gobble up
+ # the external process's return code and return 0 itself.
+ if os.system('diff -u "%s" "%s"' % (e, a)) not in [0, 1]:
+ print('Unable to compute difference: diff -u "%s" "%s"' % (e, a
+ ))
+ os.unlink(e)
+ os.unlink(a)
+ elif type(result) is TestCmd.MatchError:
+ print(result.message)
+ else:
+ print("Set environmental variable 'DO_DIFF' to examine the "
+ "difference.")
+
+ # Internal methods.
+ def adjust_lib_name(self, name):
+ global lib_prefix
+ global dll_prefix
+ result = name
+
+ pos = name.rfind(".")
+ if pos != -1:
+ suffix = name[pos:]
+ if suffix == ".lib":
+ (head, tail) = os.path.split(name)
+ if lib_prefix:
+ tail = lib_prefix + tail
+ result = os.path.join(head, tail)
+ elif suffix == ".dll" or suffix == ".implib":
+ (head, tail) = os.path.split(name)
+ if dll_prefix:
+ tail = dll_prefix + tail
+ result = os.path.join(head, tail)
+ # If we want to use this name in a Jamfile, we better convert \ to /,
+ # as otherwise we would have to quote \.
+ result = result.replace("\\", "/")
+ return result
+
+ def adjust_suffix(self, name):
+ if not self.translate_suffixes:
+ return name
+ pos = name.rfind(".")
+ if pos == -1:
+ return name
+ suffix = name[pos:]
+ return name[:pos] + suffixes.get(suffix, suffix)
+
+ # Acceps either a string or a list of strings and returns a list of
+ # strings. Adjusts suffixes on all names.
+ def adjust_names(self, names):
+ if isstr(names):
+ names = [names]
+ r = map(self.adjust_lib_name, names)
+ r = map(self.adjust_suffix, r)
+ r = map(lambda x, t=self.expanded_toolset: x.replace("$toolset", t + "*"), r)
+ return list(r)
+
+ def adjust_name(self, name):
+ return self.adjust_names(name)[0]
+
+ def __native_file_name(self, name):
+ return os.path.normpath(os.path.join(self.workdir, *name.split("/")))
+
+ def native_file_name(self, name):
+ return self.__native_file_name(self.adjust_name(name))
+
+ def wait_for_time_change(self, path, touch):
+ """
+ Wait for newly assigned file system modification timestamps for the
+ given path to become large enough for the timestamp difference to be
+ correctly recognized by both this Python based testing framework and
+ the Boost Jam executable being tested. May optionally touch the given
+ path to set its modification timestamp to the new value.
+
+ """
+ self.__wait_for_time_change(path, touch, last_build_time=False)
+
+ def wait_for_time_change_since_last_build(self):
+ """
+ Wait for newly assigned file system modification timestamps to
+ become large enough for the timestamp difference to be
+ correctly recognized by the Python based testing framework.
+ Does not care about Jam's timestamp resolution, since we
+ only need this to detect touched files.
+ """
+ if self.last_build_timestamp:
+ timestamp_file = "timestamp-3df2f2317e15e4a9"
+ open(timestamp_file, "wb").close()
+ self.__wait_for_time_change_impl(timestamp_file,
+ self.last_build_timestamp,
+ self.__python_timestamp_resolution(timestamp_file, 0), 0)
+ os.unlink(timestamp_file)
+
+ def __build_timestamp_resolution(self):
+ """
+ Returns the minimum path modification timestamp resolution supported
+ by the used Boost Jam executable.
+
+ """
+ dir = tempfile.mkdtemp("bjam_version_info")
+ try:
+ jam_script = "timestamp_resolution.jam"
+ f = open(os.path.join(dir, jam_script), "w")
+ try:
+ f.write("EXIT $(JAM_TIMESTAMP_RESOLUTION) : 0 ;")
+ finally:
+ f.close()
+ p = subprocess.Popen([self.program[0], "-d0", "-f%s" % jam_script],
+ stdout=subprocess.PIPE, cwd=dir, universal_newlines=True)
+ out, err = p.communicate()
+ finally:
+ shutil.rmtree(dir, ignore_errors=False)
+
+ if p.returncode != 0:
+ raise TestEnvironmentError("Unexpected return code (%s) when "
+ "detecting Boost Jam's minimum supported path modification "
+ "timestamp resolution version information." % p.returncode)
+ if err:
+ raise TestEnvironmentError("Unexpected error output (%s) when "
+ "detecting Boost Jam's minimum supported path modification "
+ "timestamp resolution version information." % err)
+
+ r = re.match("([0-9]{2}):([0-9]{2}):([0-9]{2}\\.[0-9]{9})$", out)
+ if not r:
+ # Older Boost Jam versions did not report their minimum supported
+ # path modification timestamp resolution and did not actually
+ # support path modification timestamp resolutions finer than 1
+ # second.
+ # TODO: Phase this support out to avoid such fallback code from
+ # possibly covering up other problems.
+ return 1
+ if r.group(1) != "00" or r.group(2) != "00": # hours, minutes
+ raise TestEnvironmentError("Boost Jam with too coarse minimum "
+ "supported path modification timestamp resolution (%s:%s:%s)."
+ % (r.group(1), r.group(2), r.group(3)))
+ return float(r.group(3)) # seconds.nanoseconds
+
+ def __ensure_newer_than_last_build(self, path):
+ """
+ Updates the given path's modification timestamp after waiting for the
+ newly assigned file system modification timestamp to become large
+ enough for the timestamp difference between it and the last build
+ timestamp to be correctly recognized by both this Python based testing
+ framework and the Boost Jam executable being tested. Does nothing if
+ there is no 'last build' information available.
+
+ """
+ if self.last_build_timestamp:
+ self.__wait_for_time_change(path, touch=True, last_build_time=True)
+
+ def __expect_lines(self, data, lines, expected):
+ """
+ Checks whether the given data contains the given lines.
+
+ Data may be specified as a single string containing text lines
+ separated by newline characters.
+
+ Lines may be specified in any of the following forms:
+ * Single string containing text lines separated by newlines - the
+ given lines are searched for in the given data without any extra
+ data lines between them.
+ * Container of strings containing text lines separated by newlines
+ - the given lines are searched for in the given data with extra
+ data lines allowed between lines belonging to different strings.
+ * Container of strings containing text lines separated by newlines
+ and containers containing strings - the same as above with the
+ internal containers containing strings being interpreted as if
+ all their content was joined together into a single string
+ separated by newlines.
+
+ A newline at the end of any multi-line lines string is interpreted as
+ an expected extra trailig empty line.
+ """
+ # str.splitlines() trims at most one trailing newline while we want the
+ # trailing newline to indicate that there should be an extra empty line
+ # at the end.
+ def splitlines(x):
+ return (x + "\n").splitlines()
+
+ if data is None:
+ data = []
+ elif isstr(data):
+ data = splitlines(data)
+
+ if isstr(lines):
+ lines = [splitlines(lines)]
+ else:
+ expanded = []
+ for x in lines:
+ if isstr(x):
+ x = splitlines(x)
+ expanded.append(x)
+ lines = expanded
+
+ if _contains_lines(data, lines) != bool(expected):
+ output = []
+ if expected:
+ output = ["Did not find expected lines:"]
+ else:
+ output = ["Found unexpected lines:"]
+ first = True
+ for line_sequence in lines:
+ if line_sequence:
+ if first:
+ first = False
+ else:
+ output.append("...")
+ output.extend(" > " + line for line in line_sequence)
+ output.append("in output:")
+ output.extend(" > " + line for line in data)
+ annotation("failure", "\n".join(output))
+ self.fail_test(1)
+
+ def __ignore_elements(self, things, wildcard):
+ """Removes in-place 'things' elements matching the given 'wildcard'."""
+ things[:] = list(filter(lambda x: not fnmatch.fnmatch(x, wildcard), things))
+
+ def __makedirs(self, path, wait):
+ """
+ Creates a folder with the given path, together with any missing
+ parent folders. If WAIT is set, makes sure any newly created folders
+ have modification timestamps newer than the ones left behind by the
+ last build run.
+
+ """
+ try:
+ if wait:
+ stack = []
+ while path and path not in stack and not os.path.isdir(path):
+ stack.append(path)
+ path = os.path.dirname(path)
+ while stack:
+ path = stack.pop()
+ os.mkdir(path)
+ self.__ensure_newer_than_last_build(path)
+ else:
+ os.makedirs(path)
+ except Exception:
+ pass
+
+ def __python_timestamp_resolution(self, path, minimum_resolution):
+ """
+ Returns the modification timestamp resolution for the given path
+ supported by the used Python interpreter/OS/filesystem combination.
+ Will not check for resolutions less than the given minimum value. Will
+ change the path's modification timestamp in the process.
+
+ Return values:
+ 0 - nanosecond resolution supported
+ positive decimal - timestamp resolution in seconds
+
+ """
+ # Note on Python's floating point timestamp support:
+ # Python interpreter versions prior to Python 2.3 did not support
+ # floating point timestamps. Versions 2.3 through 3.3 may or may not
+ # support it depending on the configuration (may be toggled by calling
+ # os.stat_float_times(True/False) at program startup, disabled by
+ # default prior to Python 2.5 and enabled by default since). Python 3.3
+ # deprecated this configuration and 3.4 removed support for it after
+ # which floating point timestamps are always supported.
+ ver = sys.version_info[0:2]
+ python_nanosecond_support = ver >= (3, 4) or (ver >= (2, 3) and
+ os.stat_float_times())
+
+ # Minimal expected floating point difference used to account for
+ # possible imprecise floating point number representations. We want
+ # this number to be small (at least smaller than 0.0001) but still
+ # large enough that we can be sure that increasing a floating point
+ # value by 2 * eta guarantees the value read back will be increased by
+ # at least eta.
+ eta = 0.00005
+
+ stats_orig = os.stat(path)
+ def test_time(diff):
+ """Returns whether a timestamp difference is detectable."""
+ os.utime(path, (stats_orig.st_atime, stats_orig.st_mtime + diff))
+ return os.stat(path).st_mtime > stats_orig.st_mtime + eta
+
+ # Test for nanosecond timestamp resolution support.
+ if not minimum_resolution and python_nanosecond_support:
+ if test_time(2 * eta):
+ return 0
+
+ # Detect the filesystem timestamp resolution. Note that there is no
+ # need to make this code 'as fast as possible' as, this function gets
+ # called before having to sleep until the next detectable modification
+ # timestamp value and that, since we already know nanosecond resolution
+ # is not supported, will surely take longer than whatever we do here to
+ # detect this minimal detectable modification timestamp resolution.
+ step = 0.1
+ if not python_nanosecond_support:
+ # If Python does not support nanosecond timestamp resolution we
+ # know the minimum possible supported timestamp resolution is 1
+ # second.
+ minimum_resolution = max(1, minimum_resolution)
+ index = max(1, int(minimum_resolution / step))
+ while step * index < minimum_resolution:
+ # Floating point number representation errors may cause our
+ # initially calculated start index to be too small if calculated
+ # directly.
+ index += 1
+ while True:
+ # Do not simply add up the steps to avoid cumulative floating point
+ # number representation errors.
+ next = step * index
+ if next > 10:
+ raise TestEnvironmentError("File systems with too coarse "
+ "modification timestamp resolutions not supported.")
+ if test_time(next):
+ return next
+ index += 1
+
+ def __wait_for_time_change(self, path, touch, last_build_time):
+ """
+ Wait until a newly assigned file system modification timestamp for
+ the given path is large enough for the timestamp difference between it
+ and the last build timestamp or the path's original file system
+ modification timestamp (depending on the last_build_time flag) to be
+ correctly recognized by both this Python based testing framework and
+ the Boost Jam executable being tested. May optionally touch the given
+ path to set its modification timestamp to the new value.
+
+ """
+ assert self.last_build_timestamp or not last_build_time
+ stats_orig = os.stat(path)
+
+ if last_build_time:
+ start_time = self.last_build_timestamp
+ else:
+ start_time = stats_orig.st_mtime
+
+ build_resolution = self.__build_timestamp_resolution()
+ assert build_resolution >= 0
+
+ # Check whether the current timestamp is already new enough.
+ if stats_orig.st_mtime > start_time and (not build_resolution or
+ stats_orig.st_mtime >= start_time + build_resolution):
+ return
+
+ resolution = self.__python_timestamp_resolution(path, build_resolution)
+ assert resolution >= build_resolution
+ self.__wait_for_time_change_impl(path, start_time, resolution, build_resolution)
+
+ if not touch:
+ os.utime(path, (stats_orig.st_atime, stats_orig.st_mtime))
+
+ def __wait_for_time_change_impl(self, path, start_time, resolution, build_resolution):
+ # Implementation notes:
+ # * Theoretically time.sleep() API might get interrupted too soon
+ # (never actually encountered).
+ # * We encountered cases where we sleep just long enough for the
+ # filesystem's modifiction timestamp to change to the desired value,
+ # but after waking up, the read timestamp is still just a tiny bit
+ # too small (encountered on Windows). This is most likely caused by
+ # imprecise floating point timestamp & sleep interval representation
+ # used by Python. Note though that we never encountered a case where
+ # more than one additional tiny sleep() call was needed to remedy
+ # the situation.
+ # * We try to wait long enough for the timestamp to change, but do not
+ # want to waste processing time by waiting too long. The main
+ # problem is that when we have a coarse resolution, the actual times
+ # get rounded and we do not know the exact sleep time needed for the
+ # difference between two such times to pass. E.g. if we have a 1
+ # second resolution and the original and the current file timestamps
+ # are both 10 seconds then it could be that the current time is
+ # 10.99 seconds and that we can wait for just one hundredth of a
+ # second for the current file timestamp to reach its next value, and
+ # using a longer sleep interval than that would just be wasting
+ # time.
+ while True:
+ os.utime(path, None)
+ c = os.stat(path).st_mtime
+ if resolution:
+ if c > start_time and (not build_resolution or c >= start_time
+ + build_resolution):
+ break
+ if c <= start_time - resolution:
+ # Move close to the desired timestamp in one sleep, but not
+ # close enough for timestamp rounding to potentially cause
+ # us to wait too long.
+ if start_time - c > 5:
+ if last_build_time:
+ error_message = ("Last build time recorded as "
+ "being a future event, causing a too long "
+ "wait period. Something must have played "
+ "around with the system clock.")
+ else:
+ error_message = ("Original path modification "
+ "timestamp set to far into the future or "
+ "something must have played around with the "
+ "system clock, causing a too long wait "
+ "period.\nPath: '%s'" % path)
+ raise TestEnvironmentError(message)
+ _sleep(start_time - c)
+ else:
+ # We are close to the desired timestamp so take baby sleeps
+ # to avoid sleeping too long.
+ _sleep(max(0.01, resolution / 10))
+ else:
+ if c > start_time:
+ break
+ _sleep(max(0.01, start_time - c))
+
+
+class List:
+ def __init__(self, s=""):
+ elements = []
+ if isstr(s):
+ # Have to handle escaped spaces correctly.
+ elements = s.replace("\ ", "\001").split()
+ else:
+ elements = s
+ self.l = [e.replace("\001", " ") for e in elements]
+
+ def __len__(self):
+ return len(self.l)
+
+ def __getitem__(self, key):
+ return self.l[key]
+
+ def __setitem__(self, key, value):
+ self.l[key] = value
+
+ def __delitem__(self, key):
+ del self.l[key]
+
+ def __str__(self):
+ return str(self.l)
+
+ def __repr__(self):
+ return "%s.List(%r)" % (self.__module__, " ".join(self.l))
+
+ def __mul__(self, other):
+ result = List()
+ if not isinstance(other, List):
+ other = List(other)
+ for f in self:
+ for s in other:
+ result.l.append(f + s)
+ return result
+
+ def __rmul__(self, other):
+ if not isinstance(other, List):
+ other = List(other)
+ return List.__mul__(other, self)
+
+ def __add__(self, other):
+ result = List()
+ result.l = self.l[:] + other.l[:]
+ return result
+
+
+def _contains_lines(data, lines):
+ data_line_count = len(data)
+ expected_line_count = reduce(lambda x, y: x + len(y), lines, 0)
+ index = 0
+ for expected in lines:
+ if expected_line_count > data_line_count - index:
+ return False
+ expected_line_count -= len(expected)
+ index = _match_line_sequence(data, index, data_line_count -
+ expected_line_count, expected)
+ if index < 0:
+ return False
+ return True
+
+
+def _match_line_sequence(data, start, end, lines):
+ if not lines:
+ return start
+ for index in range(start, end - len(lines) + 1):
+ data_index = index
+ for expected in lines:
+ if not fnmatch.fnmatch(data[data_index], expected):
+ break
+ data_index += 1
+ else:
+ return data_index
+ return -1
+
+
+def _sleep(delay):
+ if delay > 5:
+ raise TestEnvironmentError("Test environment error: sleep period of "
+ "more than 5 seconds requested. Most likely caused by a file with "
+ "its modification timestamp set to sometime in the future.")
+ time.sleep(delay)
+
+
+###############################################################################
+#
+# Initialization.
+#
+###############################################################################
+
+# Make os.stat() return file modification times as floats instead of integers
+# to get the best possible file timestamp resolution available. The exact
+# resolution depends on the underlying file system and the Python os.stat()
+# implementation. The better the resolution we achieve, the shorter we need to
+# wait for files we create to start getting new timestamps.
+#
+# Additional notes:
+# * os.stat_float_times() function first introduced in Python 2.3. and
+# suggested for deprecation in Python 3.3.
+# * On Python versions 2.5+ we do not need to do this as there os.stat()
+# returns floating point file modification times by default.
+# * Windows CPython implementations prior to version 2.5 do not support file
+# modification timestamp resolutions of less than 1 second no matter whether
+# these timestamps are returned as integer or floating point values.
+# * Python documentation states that this should be set in a program's
+# __main__ module to avoid affecting other libraries that might not be ready
+# to support floating point timestamps. Since we use no such external
+# libraries, we ignore this warning to make it easier to enable this feature
+# in both our single & multiple-test scripts.
+if (2, 3) <= sys.version_info < (2, 5) and not os.stat_float_times():
+ os.stat_float_times(True)
+
+
+# Quickie tests. Should use doctest instead.
+if __name__ == "__main__":
+ assert str(List("foo bar") * "/baz") == "['foo/baz', 'bar/baz']"
+ assert repr("foo/" * List("bar baz")) == "__main__.List('foo/bar foo/baz')"
+
+ assert _contains_lines([], [])
+ assert _contains_lines([], [[]])
+ assert _contains_lines([], [[], []])
+ assert _contains_lines([], [[], [], []])
+ assert not _contains_lines([], [[""]])
+ assert not _contains_lines([], [["a"]])
+
+ assert _contains_lines([""], [])
+ assert _contains_lines(["a"], [])
+ assert _contains_lines(["a", "b"], [])
+ assert _contains_lines(["a", "b"], [[], [], []])
+
+ assert _contains_lines([""], [[""]])
+ assert not _contains_lines([""], [["a"]])
+ assert not _contains_lines(["a"], [[""]])
+ assert _contains_lines(["a", "", "b", ""], [["a"]])
+ assert _contains_lines(["a", "", "b", ""], [[""]])
+ assert _contains_lines(["a", "", "b"], [["b"]])
+ assert not _contains_lines(["a", "b"], [[""]])
+ assert not _contains_lines(["a", "", "b", ""], [["c"]])
+ assert _contains_lines(["a", "", "b", "x"], [["x"]])
+
+ data = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
+ assert _contains_lines(data, [["1", "2"]])
+ assert not _contains_lines(data, [["2", "1"]])
+ assert not _contains_lines(data, [["1", "3"]])
+ assert not _contains_lines(data, [["1", "3"]])
+ assert _contains_lines(data, [["1"], ["2"]])
+ assert _contains_lines(data, [["1"], [], [], [], ["2"]])
+ assert _contains_lines(data, [["1"], ["3"]])
+ assert not _contains_lines(data, [["3"], ["1"]])
+ assert _contains_lines(data, [["3"], ["7"], ["8"]])
+ assert not _contains_lines(data, [["1"], ["3", "5"]])
+ assert not _contains_lines(data, [["1"], [""], ["5"]])
+ assert not _contains_lines(data, [["1"], ["5"], ["3"]])
+ assert not _contains_lines(data, [["1"], ["5", "3"]])
+
+ assert not _contains_lines(data, [[" 3"]])
+ assert not _contains_lines(data, [["3 "]])
+ assert not _contains_lines(data, [["3", ""]])
+ assert not _contains_lines(data, [["", "3"]])
+
+ print("tests passed")
diff --git a/src/boost/tools/build/test/Jamfile.jam b/src/boost/tools/build/test/Jamfile.jam
new file mode 100644
index 000000000..944a0f73f
--- /dev/null
+++ b/src/boost/tools/build/test/Jamfile.jam
@@ -0,0 +1,29 @@
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import python ;
+import testing ;
+
+if ! [ python.configured ]
+{
+ using python ;
+}
+
+# Not quite perfect, but good enough for most purposes
+local test-files = [ glob *.py ] ;
+
+local boost-build-files = [ glob
+ ../src/tools/*.jam
+ ../src/tools/*/*.jam
+ ../src/build/*.jam
+ ../src/util/*.jam
+ ../src/kernel/*.jam
+ ../src/options/*.jam
+ ../src/*.jam ] ;
+
+testing.make-test run-pyd : test_all.py :
+ <dependency>$(test-files)
+ <dependency>$(boost-build-files)
+ ;
diff --git a/src/boost/tools/build/test/MockToolset.py b/src/boost/tools/build/test/MockToolset.py
new file mode 100755
index 000000000..834a56b0f
--- /dev/null
+++ b/src/boost/tools/build/test/MockToolset.py
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import sys
+
+def create(t):
+ t.write('''mockinfo.py''', '''
+from __future__ import print_function
+import re
+import optparse
+import os
+
+parser = optparse.OptionParser()
+parser.add_option('-o', dest="output_file")
+parser.add_option('-x', dest="language")
+parser.add_option('-c', dest="compile", action="store_true")
+parser.add_option('-I', dest="includes", action="append")
+parser.add_option('-D', dest="defines", action="append")
+parser.add_option('-L', dest="library_path", action="append")
+parser.add_option('--dll', dest="dll", action="store_true")
+parser.add_option('--archive', dest="archive", action="store_true")
+parser.add_option('--static-lib', dest="static_libraries", action="append")
+parser.add_option('--shared-lib', dest="shared_libraries", action="append")
+
+cwd = os.environ["JAM_CWD"]
+
+class MockInfo(object):
+ def __init__(self, verbose=False):
+ self.files = dict()
+ self.commands = list()
+ self.verbose = verbose
+ def source_file(self, name, pattern):
+ self.files[name] = pattern
+ def action(self, command, status=0):
+ if isinstance(command, str):
+ command = command.split()
+ self.commands.append((command, status))
+ def check(self, command):
+ print("Testing command", command)
+ for (raw, status) in self.commands:
+ if self.matches(raw, command):
+ return status
+ def matches(self, raw, command):
+ (expected_options, expected_args) = parser.parse_args(raw)
+ options = command[0]
+ input_files = list(command[1])
+ if self.verbose:
+ print(" - matching against", (expected_options, expected_args))
+ if len(expected_args) != len(input_files):
+ if self.verbose:
+ print(" argument list sizes differ")
+ return False
+ for arg in expected_args:
+ if arg.startswith('$'):
+ fileid = arg[1:]
+ pattern = self.files[fileid] if fileid in self.files else fileid
+ matching_file = None
+ for input_file in input_files:
+ with open(input_file, 'r') as f:
+ contents = f.read()
+ if pattern == contents:
+ matching_file = input_file
+ break
+ if matching_file is not None:
+ input_files.remove(matching_file)
+ else:
+ if self.verbose:
+ print(" Failed to match input file contents: %s" % arg)
+ return False
+ else:
+ if arg in input_files:
+ input_files.remove(arg)
+ else:
+ if self.verbose:
+ print(" Failed to match input file: %s" % arg)
+ return False
+
+ if options.language != expected_options.language:
+ if self.verbose:
+ print(" Failed to match -c")
+ return False
+
+ if options.compile != expected_options.compile:
+ if self.verbose:
+ print(" Failed to match -x")
+ return False
+
+ # Normalize a path for comparison purposes
+ def adjust_path(p):
+ return os.path.normcase(os.path.normpath(os.path.join(cwd, p)))
+
+ # order matters
+ if options.includes is None:
+ options.includes = []
+ if expected_options.includes is None:
+ expected_options.includes = []
+ if list(map(adjust_path, options.includes)) != \
+ list(map(adjust_path, expected_options.includes)):
+ if self.verbose:
+ print(" Failed to match -I ", list(map(adjust_path, options.includes)), \
+ " != ", list(map(adjust_path, expected_options.includes)))
+ return False
+
+ if options.defines is None:
+ options.defines = []
+ if expected_options.defines is None:
+ expected_options.defines = []
+ if options.defines != expected_options.defines:
+ if self.verbose:
+ print(" Failed to match -I ", options.defines, \
+ " != ", expected_options.defines)
+ return False
+
+ if options.library_path is None:
+ options.library_path = []
+ if expected_options.library_path is None:
+ expected_options.library_path = []
+ if list(map(adjust_path, options.library_path)) != \
+ list(map(adjust_path, expected_options.library_path)):
+ if self.verbose:
+ print(" Failed to match -L ", list(map(adjust_path, options.library_path)), \
+ " != ", list(map(adjust_path, expected_options.library_path)))
+ return False
+
+ if options.static_libraries != expected_options.static_libraries:
+ if self.verbose:
+ print(" Failed to match --static-lib")
+ return False
+
+ if options.shared_libraries != expected_options.shared_libraries:
+ if self.verbose:
+ print(" Failed to match --shared-lib")
+ return False
+
+ if options.dll != expected_options.dll:
+ if self.verbose:
+ print(" Failed to match --dll")
+ return False
+
+ if options.archive != expected_options.archive:
+ if self.verbose:
+ print(" Failed to match --archive")
+ return False
+
+ # The output must be handled after everything else
+ # is validated
+ if expected_options.output_file is not None:
+ if options.output_file is not None:
+ if expected_options.output_file.startswith('$'):
+ fileid = expected_options.output_file[1:]
+ if fileid not in self.files:
+ self.files[fileid] = fileid
+ else:
+ assert(self.files[fileid] == fileid)
+ with open(options.output_file, 'w') as output:
+ output.write(fileid)
+ else:
+ if self.verbose:
+ print("Failed to match -o")
+ return False
+ elif options.output_file is not None:
+ if self.verbose:
+ print("Failed to match -o")
+ return False
+
+ # if we've gotten here, then everything matched
+ if self.verbose:
+ print(" Matched")
+ return True
+''')
+
+ t.write('mock.py', '''
+from __future__ import print_function
+import mockinfo
+import markup
+import sys
+
+status = markup.info.check(mockinfo.parser.parse_args())
+if status is not None:
+ exit(status)
+else:
+ print("Unrecognized command: " + ' '.join(sys.argv))
+ exit(1)
+''')
+
+ t.write('mock.jam', '''
+import feature ;
+import toolset ;
+import path ;
+import modules ;
+import common ;
+import type ;
+
+.python-cmd = "\"%s\"" ;
+
+# Behave the same as gcc on Windows, because that's what
+# the test system expects
+type.set-generated-target-prefix SHARED_LIB : <toolset>mock <target-os>windows : lib ;
+type.set-generated-target-suffix STATIC_LIB : <toolset>mock <target-os>windows : a ;
+
+rule init ( )
+{
+ local here = [ path.make [ modules.binding $(__name__) ] ] ;
+ here = [ path.native [ path.root [ path.parent $(here) ] [ path.pwd ] ] ] ;
+ .config-cmd = [ common.variable-setting-command JAM_CWD : $(here) ] $(.python-cmd) -B ;
+}
+
+feature.extend toolset : mock ;
+
+generators.register-c-compiler mock.compile.c++ : CPP : OBJ : <toolset>mock ;
+generators.register-c-compiler mock.compile.c : C : OBJ : <toolset>mock ;
+
+generators.register-linker mock.link : LIB OBJ : EXE : <toolset>mock ;
+generators.register-linker mock.link.dll : LIB OBJ : SHARED_LIB : <toolset>mock ;
+generators.register-archiver mock.archive : OBJ : STATIC_LIB : <toolset>mock ;
+
+toolset.flags mock.compile OPTIONS <link>shared : -fPIC ;
+toolset.flags mock.compile INCLUDES : <include> ;
+toolset.flags mock.compile DEFINES : <define> ;
+
+actions compile.c
+{
+ $(.config-cmd) mock.py -c -x c -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)"
+}
+
+actions compile.c++
+{
+ $(.config-cmd) mock.py -c -x c++ -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)"
+}
+
+toolset.flags mock.link USER_OPTIONS <linkflags> ;
+toolset.flags mock.link FINDLIBS-STATIC <find-static-library> ;
+toolset.flags mock.link FINDLIBS-SHARED <find-shared-library> ;
+toolset.flags mock.link LINK_PATH <library-path> ;
+toolset.flags mock.link LIBRARIES <library-file> ;
+
+actions link
+{
+ $(.config-cmd) mock.py "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED)
+}
+
+actions archive
+{
+ $(.config-cmd) mock.py --archive "$(>)" -o "$(<)" $(USER_OPTIONS)
+}
+
+actions link.dll
+{
+ $(.config-cmd) mock.py --dll "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED)
+}
+
+''' % sys.executable.replace('\\', '\\\\'))
+
+def set_expected(t, markup):
+ verbose = "True" if t.verbose else "False"
+ t.write('markup.py', '''
+import mockinfo
+info = mockinfo.MockInfo(%s)
+def source_file(name, contents):
+ info.source_file(name, contents)
+def action(command, status=0):
+ info.action(command, status)
+''' % (verbose) + markup)
diff --git a/src/boost/tools/build/test/TestCmd.py b/src/boost/tools/build/test/TestCmd.py
new file mode 100644
index 000000000..d3b13e29c
--- /dev/null
+++ b/src/boost/tools/build/test/TestCmd.py
@@ -0,0 +1,609 @@
+"""
+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:
+
+ test = TestCmd()
+
+The TestCmd module provides pass_test(), fail_test(), and no_result() unbound
+methods 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).
+
+"""
+
+# Copyright 2000 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.
+
+# Copyright 2002-2003 Vladimir Prus.
+# Copyright 2002-2003 Dave Abrahams.
+# Copyright 2006 Rene Rivera.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from __future__ import print_function
+
+__author__ = "Steven Knight <knight@baldmt.com>"
+__revision__ = "TestCmd.py 0.D002 2001/08/31 14:56:12 software"
+__version__ = "0.02"
+
+from types import *
+
+import os
+import os.path
+import re
+import shutil
+import stat
+import subprocess
+import sys
+import tempfile
+import traceback
+
+
+tempfile.template = 'testcmd.'
+
+_Cleanup = []
+
+def _clean():
+ global _Cleanup
+ list = _Cleanup[:]
+ _Cleanup = []
+ list.reverse()
+ for test in list:
+ test.cleanup()
+
+sys.exitfunc = _clean
+
+
+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 == "?":
+ 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=True, function=None, skip=0):
+ """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 " + " ".join(self.program)
+ sep = "\n\t"
+ if self.description:
+ desc = " [" + self.description + "]"
+ sep = "\n\t"
+
+ at = caller(traceback.extract_stack(), skip)
+
+ sys.stderr.write("FAILED test" + of + desc + sep + at + """
+in directory: """ + os.getcwd() )
+ sys.exit(1)
+
+
+def no_result(self=None, condition=True, 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=True, 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)
+
+class MatchError(object):
+ def __init__(self, message):
+ self.message = message
+ def __nonzero__(self):
+ return False
+ def __bool__(self):
+ return False
+
+def match_exact(lines=None, matches=None):
+ """
+ Returns whether the given lists or strings containing lines separated
+ using newline characters contain exactly the same data.
+
+ """
+ if not type(lines) is list:
+ lines = lines.split("\n")
+ if not type(matches) is list:
+ matches = matches.split("\n")
+ if len(lines) != len(matches):
+ return
+ for i in range(len(lines)):
+ if lines[i] != matches[i]:
+ return MatchError("Mismatch at line %d\n- %s\n+ %s\n" %
+ (i+1, matches[i], lines[i]))
+ if len(lines) < len(matches):
+ return MatchError("Missing lines at line %d\n- %s" %
+ (len(lines), "\n- ".join(matches[len(lines):])))
+ if len(lines) > len(matches):
+ return MatchError("Extra lines at line %d\n+ %s" %
+ (len(matches), "\n+ ".join(lines[len(matches):])))
+ return 1
+
+
+def match_re(lines=None, res=None):
+ """
+ Given lists or strings contain lines separated using newline characters.
+ This function matches those lines one by one, interpreting the lines in the
+ res parameter as regular expressions.
+
+ """
+ if not type(lines) is list:
+ lines = lines.split("\n")
+ if not type(res) is list:
+ res = res.split("\n")
+ for i in range(min(len(lines), len(res))):
+ if not re.compile("^" + res[i] + "$").search(lines[i]):
+ return MatchError("Mismatch at line %d\n- %s\n+ %s\n" %
+ (i+1, res[i], lines[i]))
+ if len(lines) < len(res):
+ return MatchError("Missing lines at line %d\n- %s" %
+ (len(lines), "\n- ".join(res[len(lines):])))
+ if len(lines) > len(res):
+ return MatchError("Extra lines at line %d\n+ %s" %
+ (len(res), "\n+ ".join(lines[len(res):])))
+ return 1
+
+
+class TestCmd:
+ def __init__(self, description=None, program=None, workdir=None,
+ subdir=None, verbose=False, match=None, inpath=None):
+
+ self._cwd = os.getcwd()
+ self.description_set(description)
+ self.program_set(program, inpath)
+ self.verbose_set(verbose)
+ if match is None:
+ self.match_func = match_re
+ else:
+ self.match_func = match
+ self._dirlist = []
+ self._preserve = {'pass_test': 0, 'fail_test': 0, 'no_result': 0}
+ env = os.environ.get('PRESERVE')
+ if env:
+ self._preserve['pass_test'] = env
+ self._preserve['fail_test'] = env
+ self._preserve['no_result'] = env
+ else:
+ env = os.environ.get('PRESERVE_PASS')
+ if env is not None:
+ self._preserve['pass_test'] = env
+ env = os.environ.get('PRESERVE_FAIL')
+ if env is not None:
+ self._preserve['fail_test'] = env
+ env = os.environ.get('PRESERVE_PASS')
+ if env is not None:
+ self._preserve['PRESERVE_NO_RESULT'] = env
+ self._stdout = []
+ self._stderr = []
+ self.status = None
+ self.condition = 'no_result'
+ self.workdir_set(workdir)
+ self.subdir(subdir)
+
+ def __del__(self):
+ self.cleanup()
+
+ def __repr__(self):
+ return "%x" % id(self)
+
+ 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
+ if condition is None:
+ condition = self.condition
+ if self._preserve[condition]:
+ for dir in self._dirlist:
+ print("Preserved directory %s" % dir)
+ else:
+ list = self._dirlist[:]
+ list.reverse()
+ for dir in list:
+ self.writable(dir, 1)
+ shutil.rmtree(dir, ignore_errors=1)
+
+ self._dirlist = []
+ self.workdir = None
+ os.chdir(self._cwd)
+ try:
+ global _Cleanup
+ _Cleanup.remove(self)
+ except (AttributeError, ValueError):
+ pass
+
+ def description_set(self, description):
+ """Set the description of the functionality being tested."""
+ self.description = description
+
+ def fail_test(self, condition=True, function=None, skip=0):
+ """Cause the test to fail."""
+ if not condition:
+ return
+ self.condition = 'fail_test'
+ fail_test(self = self,
+ condition = condition,
+ function = function,
+ skip = skip)
+
+ def match(self, lines, matches):
+ """Compare actual and expected file contents."""
+ return self.match_func(lines, matches)
+
+ def match_exact(self, lines, matches):
+ """Compare actual and expected file content exactly."""
+ return match_exact(lines, matches)
+
+ def match_re(self, lines, res):
+ """Compare file content with a regular expression."""
+ return match_re(lines, res)
+
+ def no_result(self, condition=True, 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=True, function=None):
+ """Cause the test to pass."""
+ if not condition:
+ return
+ self.condition = 'pass_test'
+ pass_test(self, condition, 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 == ():
+ conditions = ('pass_test', 'fail_test', 'no_result')
+ for cond in conditions:
+ self._preserve[cond] = 1
+
+ def program_set(self, program, inpath):
+ """Set the executable program or script to be tested."""
+ if not inpath and program and not os.path.isabs(program[0]):
+ program[0] = os.path.join(self._cwd, program[0])
+ self.program = program
+
+ def read(self, file, mode='rb'):
+ """
+ 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 and must begin with an 'r'. The
+ default is 'rb' (binary read).
+
+ """
+ if type(file) is list:
+ file = os.path.join(*file)
+ if not os.path.isabs(file):
+ file = os.path.join(self.workdir, file)
+ if mode[0] != 'r':
+ raise ValueError("mode must begin with 'r'")
+ return open(file, mode).read()
+
+ def run(self, program=None, arguments=None, chdir=None, stdin=None,
+ universal_newlines=True):
+ """
+ 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.
+
+ 'universal_newlines' parameter controls how the child process
+ input/output streams are opened as defined for the same named Python
+ subprocess.POpen constructor parameter.
+
+ """
+ if chdir:
+ if not os.path.isabs(chdir):
+ chdir = os.path.join(self.workpath(chdir))
+ if self.verbose:
+ sys.stderr.write("chdir(" + chdir + ")\n")
+ else:
+ chdir = self.workdir
+
+ cmd = []
+ if program and program[0]:
+ if program[0] != self.program[0] and not os.path.isabs(program[0]):
+ program[0] = os.path.join(self._cwd, program[0])
+ cmd += program
+ else:
+ cmd += self.program
+ if arguments:
+ cmd += arguments.split(" ")
+ if self.verbose:
+ sys.stderr.write("run(" + " ".join(cmd) + ")\n")
+ p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=chdir,
+ universal_newlines=universal_newlines)
+
+ if stdin:
+ if type(stdin) is list:
+ stdin = "".join(stdin)
+ out, err = p.communicate(stdin)
+ if not type(out) is str:
+ out = out.decode()
+ if not type(err) is str:
+ err = err.decode()
+ self._stdout.append(out)
+ self._stderr.append(err)
+ self.status = p.returncode
+
+ if self.verbose:
+ sys.stdout.write(self._stdout[-1])
+ sys.stderr.write(self._stderr[-1])
+
+ 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 -= 1
+ if run < 0:
+ return ''
+ 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 -= 1
+ if run < 0:
+ return ''
+ 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 type(sub) is list:
+ sub = os.path.join(*tuple(sub))
+ new = os.path.join(self.workdir, sub)
+ try:
+ os.mkdir(new)
+ except:
+ pass
+ else:
+ count += 1
+ return count
+
+ def unlink(self, file):
+ """
+ Unlinks the specified file name. The file name may be a list, in
+ which case the elements are concatenated using the os.path.join()
+ method. The file is assumed to be under the temporary working directory
+ unless it is an absolute path name.
+
+ """
+ if type(file) is list:
+ file = os.path.join(*tuple(file))
+ if not os.path.isabs(file):
+ file = os.path.join(self.workdir, file)
+ os.unlink(file)
+
+ def verbose_set(self, verbose):
+ """Set the verbose level."""
+ self.verbose = verbose
+
+ 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 os.path.isabs(path):
+ self.workdir = path
+ else:
+ if path != None:
+ if path == '':
+ path = tempfile.mktemp()
+ if path != None:
+ os.mkdir(path)
+ self._dirlist.append(path)
+ global _Cleanup
+ try:
+ _Cleanup.index(self)
+ except ValueError:
+ _Cleanup.append(self)
+ # We would like to set self.workdir like this:
+ # self.workdir = path
+ # But symlinks in the path will report things differently from
+ # os.getcwd(), so chdir there and back to fetch the canonical
+ # path.
+ cwd = os.getcwd()
+ os.chdir(path)
+ self.workdir = os.getcwd()
+ os.chdir(cwd)
+ else:
+ self.workdir = None
+
+ 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 os.path.join().
+
+ """
+ return os.path.join(self.workdir, *tuple(args))
+
+ def writable(self, top, write):
+ """
+ Make the specified directory tree writable (write == 1) or not
+ (write == None).
+
+ """
+ def _walk_chmod(arg, dirname, names):
+ st = os.stat(dirname)
+ os.chmod(dirname, arg(st[stat.ST_MODE]))
+ for name in names:
+ fullname = os.path.join(dirname, name)
+ st = os.stat(fullname)
+ os.chmod(fullname, arg(st[stat.ST_MODE]))
+
+ _mode_writable = lambda mode: stat.S_IMODE(mode|0o200)
+ _mode_non_writable = lambda mode: stat.S_IMODE(mode&~0o200)
+
+ if write:
+ f = _mode_writable
+ else:
+ f = _mode_non_writable
+ try:
+ for root, _, files in os.walk(top):
+ _walk_chmod(f, root, files)
+ except:
+ pass # Ignore any problems changing modes.
+
+ 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 using 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
+ and must begin with a 'w'. The default is 'wb' (binary write).
+
+ """
+ if type(file) is list:
+ file = os.path.join(*tuple(file))
+ if not os.path.isabs(file):
+ file = os.path.join(self.workdir, file)
+ if mode[0] != 'w':
+ raise ValueError("mode must begin with 'w'")
+ open(file, mode).write(content)
diff --git a/src/boost/tools/build/test/TestToolset.py b/src/boost/tools/build/test/TestToolset.py
new file mode 100644
index 000000000..7fef16b02
--- /dev/null
+++ b/src/boost/tools/build/test/TestToolset.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates a toolset using a mock of the compiler
+
+import BoostBuild
+import os
+import re
+import sys
+
+renames = {"debug": "variant=debug", "release": "variant=release"}
+
+def set_default_target_os(os):
+ global removed
+ global default_target_os
+ default_target_os = os
+ removed = set()
+ removed.add("target-os=" + default_target_os)
+
+def adjust_property(property):
+ global renames
+ if property in renames:
+ return renames[property]
+ else:
+ return property
+
+def adjust_properties(properties):
+ global removed
+ return [adjust_property(p) for p in properties if p not in removed]
+
+def has_property(name, properties):
+ return name in [re.sub("=.*", "", p) for p in properties]
+
+def get_property(name, properties):
+ for m in [re.match("(.*)=(.*)", p) for p in properties]:
+ if m and m.group(1) == name:
+ return m.group(2)
+
+def get_target_os(properties):
+ return get_property("target-os", properties) or default_target_os
+
+def expand_properties(properties, toolset):
+ result = properties[:]
+ if not has_property("variant", properties):
+ result += ["variant=debug"]
+ if not has_property("threading", properties):
+ result += ["threading=single"]
+ if not has_property("exception-handling", properties):
+ result += ["exception-handling=on"]
+ if not has_property("link", properties):
+ result += ["link=shared"]
+ if not has_property("rtti", properties):
+ result += ["rtti=on"]
+ if not has_property("runtime-link", properties):
+ result += ["runtime-link=shared"]
+ if toolset == "msvc" and "runtime-link=shared" in result:
+ result.remove("threading=" + get_property("threading", result))
+ result += ["threading=multi"]
+ if not has_property("strip", properties):
+ result += ["strip=off"]
+ if not has_property("target-os", properties):
+ result += ["target-os=" + default_target_os]
+ if not has_property("windows-api", properties):
+ result += ["windows-api=desktop"]
+ return result
+
+def compute_path(properties, target_type):
+ path = ""
+ if "variant=release" in properties:
+ path += "/release"
+ else:
+ path += "/debug"
+ if has_property("address-model", properties):
+ path += "/address-model-" + get_property("address-model", properties)
+ if has_property("architecture", properties):
+ path += "/architecture-" + get_property("architecture", properties)
+ if "cxxstd=latest" in properties:
+ path += "/cxxstd-latest-iso"
+ if "exception-handling=off" in properties:
+ path += "/exception-handling-off"
+ if "link=static" in properties:
+ path += "/link-static"
+ if "rtti=off" in properties:
+ path += "/rtti-off"
+ if "runtime-link=static" in properties: # and target_type in ["exe"]:
+ path += "/runtime-link-static"
+ if "strip=on" in properties and target_type in ["dll", "exe", "obj2"]:
+ path += "/strip-on"
+ if get_target_os(properties) != default_target_os:
+ path += "/target-os-" + get_target_os(properties)
+ if "threading=multi" in properties:
+ if "runtime-link=static" not in properties: # TODO: I don't think this it's intended to work this way though
+ path += "/threading-multi"
+ if not "windows-api=desktop" in properties:
+ path += "/windows-api-" + get_property("windows-api", properties)
+ return path
+
+def test_toolset(toolset, version, property_sets):
+ t = BoostBuild.Tester()
+
+ t.set_tree("toolset-mock")
+
+ # Build necessary tools
+ t.run_build_system(["-sPYTHON_CMD=%s" % sys.executable], subdir="src")
+ set_default_target_os(t.read("src/bin/target-os.txt").strip())
+
+ for properties in property_sets:
+ t.set_toolset(toolset + "-" + version, get_target_os(properties))
+ properties = adjust_properties(properties)
+ expanded_properties = expand_properties(properties, toolset)
+ def path(t):
+ return toolset.split("-")[0] + "-*" + version + compute_path(expanded_properties, t)
+ os.environ["B2_PROPERTIES"] = " ".join(expanded_properties)
+ t.run_build_system(["--user-config=", "-sPYTHON_CMD=%s" % sys.executable] + properties)
+ t.expect_addition("bin/%s/lib.obj" % (path("obj")))
+ if "link=static" not in properties:
+ if get_target_os(properties) in ["cygwin", "windows"] and toolset != "clang-linux":
+ t.expect_addition("bin/%s/l1.implib" % (path("dll")))
+ t.expect_addition("bin/%s/l1.dll" % (path("dll")))
+ t.ignore_addition("bin/%s/*l1.*.rsp" % (path("dll")))
+ else:
+ t.expect_addition("bin/%s/l1.lib" % (path("lib")))
+ t.expect_addition("bin/%s/main.obj" % (path("obj2")))
+ t.expect_addition("bin/%s/test.exe" % (path("exe")))
+ t.ignore_addition("bin/%s/test.rsp" % (path("exe")))
+ t.expect_nothing_more()
+ t.rm("bin")
+
+ t.cleanup()
diff --git a/src/boost/tools/build/test/abs_workdir.py b/src/boost/tools/build/test/abs_workdir.py
new file mode 100644
index 000000000..02b460fc5
--- /dev/null
+++ b/src/boost/tools/build/test/abs_workdir.py
@@ -0,0 +1,39 @@
+# Niklaus Giger, 2005-03-15
+# Testing whether we may run a test in absolute directories. There are no tests
+# for temporary directories as this is implictly tested in a lot of other cases.
+
+# TODO: Move to a separate testing-system test group.
+
+import BoostBuild
+import os
+import tempfile
+
+# Python 2.7 does not implement os.path.samefile on Windows
+import ntpath
+if not hasattr(ntpath, "samefile"):
+ def samefile(f1, f2):
+ try:
+ from os.path.nt import _getfinalpathname
+ return os.path._getfinalpathname(f1) == os.path._getfinalpathname(f2)
+ except (NotImplementedError, ImportError):
+ return os.path.abspath(f1) == os.path.abspath(f2)
+
+ ntpath.samefile = samefile
+
+t = BoostBuild.Tester(["-ffile.jam"], workdir=os.getcwd(), pass_d0=False,
+ pass_toolset=False)
+
+t.write("file.jam", "EXIT [ PWD ] : 0 ;")
+
+t.run_build_system()
+t.fail_test(not os.path.samefile(t.stdout().rstrip("\n"), os.getcwd()))
+
+try:
+ t.run_build_system(status=123, subdir="/must/fail/with/absolute/path",
+ stderr=None)
+except ValueError as e:
+ assert "subdir" in str(e), e
+else:
+ raise ValueError("exception expected")
+finally:
+ t.cleanup()
diff --git a/src/boost/tools/build/test/absolute_sources.py b/src/boost/tools/build/test/absolute_sources.py
new file mode 100644
index 000000000..93fdf49fc
--- /dev/null
+++ b/src/boost/tools/build/test/absolute_sources.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that sources with absolute names are handled OK.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "path-constant TOP : . ;")
+t.write("jamfile.jam", """\
+local pwd = [ PWD ] ;
+ECHO $(pwd) XXXXX ;
+exe hello : $(pwd)/hello.cpp $(TOP)/empty.cpp ;
+""")
+t.write("hello.cpp", "int main() {}\n")
+t.write("empty.cpp", "\n")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+t.rm(".")
+
+# Test a contrived case in which an absolute name is used in a standalone
+# project (not Jamfile). Moreover, the target with an absolute name is returned
+# via an 'alias' and used from another project.
+t.write("a.cpp", "int main() {}\n")
+t.write("jamfile.jam", "exe a : /standalone//a ;")
+t.write("jamroot.jam", "import standalone ;")
+t.write("standalone.jam", """\
+import project ;
+project.initialize $(__name__) ;
+project standalone ;
+local pwd = [ PWD ] ;
+alias a : $(pwd)/a.cpp ;
+""")
+
+t.write("standalone.py", """
+from b2.manager import get_manager
+
+# FIXME: this is ugly as death
+get_manager().projects().initialize(__name__)
+
+import os ;
+
+# This use of list as parameter is also ugly.
+project(['standalone'])
+
+pwd = os.getcwd()
+alias('a', [os.path.join(pwd, 'a.cpp')])
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+
+# Test absolute path in target ids.
+t.rm(".")
+
+t.write("d1/jamroot.jam", "")
+t.write("d1/jamfile.jam", "exe a : a.cpp ;")
+t.write("d1/a.cpp", "int main() {}\n")
+t.write("d2/jamroot.jam", "")
+t.write("d2/jamfile.jam", """\
+local pwd = [ PWD ] ;
+alias x : $(pwd)/../d1//a ;
+""")
+
+t.run_build_system(subdir="d2")
+t.expect_addition("d1/bin/$toolset/debug*/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/alias.py b/src/boost/tools/build/test/alias.py
new file mode 100644
index 000000000..ef89df96e
--- /dev/null
+++ b/src/boost/tools/build/test/alias.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+
+###############################################################################
+#
+# test_alias_rule()
+# -----------------
+#
+###############################################################################
+
+def test_alias_rule(t):
+ """Basic alias rule test."""
+
+ t.write("jamroot.jam", """\
+exe a : a.cpp ;
+exe b : b.cpp ;
+exe c : c.cpp ;
+
+alias bin1 : a ;
+alias bin2 : a b ;
+
+alias src : s.cpp ;
+exe hello : hello.cpp src ;
+""")
+
+ t.write("a.cpp", "int main() {}\n")
+ t.copy("a.cpp", "b.cpp")
+ t.copy("a.cpp", "c.cpp")
+ t.copy("a.cpp", "hello.cpp")
+ t.write("s.cpp", "")
+
+ # Check that targets to which "bin1" refers are updated, and only those.
+ t.run_build_system(["bin1"])
+ t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * "a.exe a.obj")
+ t.ignore_addition('bin/*/a.rsp')
+ t.ignore_addition('bin/*/a.*.rsp')
+ t.expect_nothing_more()
+
+ # Try again with "bin2"
+ t.run_build_system(["bin2"])
+ t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * "b.exe b.obj")
+ t.ignore_addition('bin/*/b.rsp')
+ t.ignore_addition('bin/*/b.*.rsp')
+ t.expect_nothing_more()
+
+ # Try building everything, making sure 'hello' target is created.
+ t.run_build_system()
+ t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * \
+ "hello.exe hello.obj")
+ t.ignore_addition('bin/*/hello.rsp')
+ t.ignore_addition('bin/*/hello.*.rsp')
+ t.expect_addition("bin/$toolset/debug*/s.obj")
+ t.ignore_addition('bin/*/s.*.rsp')
+ t.expect_addition(BoostBuild.List("bin/$toolset/debug*/") * "c.exe c.obj")
+ t.ignore_addition('bin/*/c.rsp')
+ t.ignore_addition('bin/*/c.*.rsp')
+ t.expect_nothing_more()
+
+
+###############################################################################
+#
+# test_alias_source_usage_requirements()
+# --------------------------------------
+#
+###############################################################################
+
+def test_alias_source_usage_requirements(t):
+ """
+ Check whether usage requirements are propagated via "alias". In case they
+ are not, linking will fail as there will be no main() function defined
+ anywhere in the source.
+
+ """
+ t.write("jamroot.jam", """\
+lib l : l.cpp : : : <define>WANT_MAIN ;
+alias la : l ;
+exe main : main.cpp la ;
+""")
+
+ t.write("l.cpp", """\
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+ t.write("main.cpp", """\
+#ifdef WANT_MAIN
+int main() {}
+#endif
+""")
+
+ t.run_build_system()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+t = BoostBuild.Tester(use_test_config=False)
+
+test_alias_rule(t)
+test_alias_source_usage_requirements(t)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/alternatives.py b/src/boost/tools/build/test/alternatives.py
new file mode 100644
index 000000000..3da322515
--- /dev/null
+++ b/src/boost/tools/build/test/alternatives.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2003, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test main target alternatives.
+
+import BoostBuild
+import string
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Test that basic alternatives selection works.
+t.write("jamroot.jam", "")
+
+t.write("jamfile.jam", """
+exe a : a_empty.cpp ;
+exe a : a.cpp : <variant>release ;
+""")
+
+t.write("a_empty.cpp", "")
+
+t.write("a.cpp", "int main() {}\n")
+
+t.run_build_system(["release"])
+
+t.expect_addition("bin/$toolset/release*/a.exe")
+
+# Test that alternative selection works for ordinary properties, in particular
+# user-defined.
+t.write("jamroot.jam", "")
+
+t.write("jamfile.jam", """
+import feature ;
+feature.feature X : off on : propagated ;
+exe a : b.cpp ;
+exe a : a.cpp : <X>on ;
+""")
+t.write("b.cpp", "int main() {}\n")
+
+t.rm("bin")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/b.obj")
+
+t.run_build_system(["X=on"])
+t.expect_addition("bin/$toolset/debug/X-on*/a.obj")
+
+t.rm("bin")
+
+# Test that everything works ok even with the default build.
+t.write("jamfile.jam", """\
+exe a : a_empty.cpp : <variant>release ;
+exe a : a.cpp : <variant>debug ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+
+# Test that only properties which are in the build request matter for
+# alternative selection. IOW, alternative with <variant>release is better than
+# one with <variant>debug when building the release variant.
+t.write("jamfile.jam", """\
+exe a : a_empty.cpp : <variant>debug ;
+exe a : a.cpp : <variant>release ;
+""")
+
+t.run_build_system(["release"])
+t.expect_addition("bin/$toolset/release*/a.exe")
+
+# Test that free properties do not matter. We really do not want <cxxflags>
+# property in build request to affect alternative selection.
+t.write("jamfile.jam", """
+exe a : a_empty.cpp : <variant>debug <define>FOO <include>BAR ;
+exe a : a.cpp : <variant>release ;
+""")
+
+t.rm("bin/$toolset/release/a.exe")
+t.rm("bin/$toolset/release/*/a.exe")
+t.run_build_system(["release", "define=FOO"])
+t.expect_addition("bin/$toolset/release*/a.exe")
+
+# Test that ambiguity is reported correctly.
+t.write("jamfile.jam", """\
+exe a : a_empty.cpp ;
+exe a : a.cpp ;
+""")
+t.run_build_system(["--no-error-backtrace"], status=None)
+t.expect_output_lines("error: No best alternative for ./a")
+
+# Another ambiguity test: two matches properties in one alternative are neither
+# better nor worse than a single one in another alternative.
+t.write("jamfile.jam", """\
+exe a : a_empty.cpp : <optimization>off <profiling>off ;
+exe a : a.cpp : <debug-symbols>on ;
+""")
+
+t.run_build_system(["--no-error-backtrace"], status=None)
+t.expect_output_lines("error: No best alternative for ./a")
+t.rm("bin")
+
+# Test that we can have alternative without sources.
+t.write("jamfile.jam", """\
+alias specific-sources ;
+import feature ;
+feature.extend os : MAGIC ;
+alias specific-sources : b.cpp : <os>MAGIC ;
+exe a : a.cpp specific-sources ;
+""")
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+t.rm("bin")
+
+# Test that subfeatures are expanded in alternatives
+# and that unknown subfeatures fail to match instead of
+# causing errors.
+t.write("jamfile.jam", """\
+import feature : feature subfeature ;
+feature X : off on : propagated ;
+subfeature X on : version : 1 : propagated ;
+exe a : a.cpp : <X>on-1 ;
+exe a : a_empty.cpp ;
+exe a : a_empty.cpp : <X>on-2 ;
+""")
+t.run_build_system(["X=on-1"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/always.py b/src/boost/tools/build/test/always.py
new file mode 100644
index 000000000..5ebe4d57b
--- /dev/null
+++ b/src/boost/tools/build/test/always.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+# Copyright 2016 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("main.cpp", """\
+int main() {}
+""")
+
+t.write("Jamroot", """\
+exe test : main.cpp ;
+always test ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/main.obj")
+t.ignore_addition('bin/*/main.*.rsp')
+t.expect_addition("bin/$toolset/debug*/test.exe")
+t.ignore_addition('bin/*/test.rsp')
+t.expect_nothing_more()
+
+t.run_build_system()
+t.expect_touch("bin/$toolset/debug*/main.obj")
+t.ignore_touch('bin/*/main.*.rsp')
+t.expect_touch("bin/$toolset/debug*/test.exe")
+t.ignore_touch('bin/*/test.rsp')
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/bad_dirname.py b/src/boost/tools/build/test/bad_dirname.py
new file mode 100644
index 000000000..44af30a66
--- /dev/null
+++ b/src/boost/tools/build/test/bad_dirname.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Regression test: when directory of project root contained regex
+# metacharacters, B2 failed to work. Bug reported by Michael Stevens.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("bad[abc]dirname/jamfile.jam", """
+""")
+
+t.write("bad[abc]dirname/jamroot.jam", """
+""")
+
+t.run_build_system(subdir="bad[abc]dirname")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/boost-build.jam b/src/boost/tools/build/test/boost-build.jam
new file mode 100644
index 000000000..b79ec530c
--- /dev/null
+++ b/src/boost/tools/build/test/boost-build.jam
@@ -0,0 +1,14 @@
+# Copyright 2002, 2003 Dave Abrahams
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Assume BOOST_BUILD_PATH point to the 'test' directory.
+# We need to leave 'test' there, so that 'test-config.jam'
+# can be found, but also add parent directory, to find
+# all the other modules.
+
+BOOST_BUILD_PATH = $(BOOST_BUILD_PATH)/.. $(BOOST_BUILD_PATH) ;
+
+# Find the boost build system in the ../kernel directory.
+boost-build ../src/kernel ;
diff --git a/src/boost/tools/build/test/boostbook.py b/src/boost/tools/build/test/boostbook.py
new file mode 100644
index 000000000..a5e6a0f11
--- /dev/null
+++ b/src/boost/tools/build/test/boostbook.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+# Copyright 2004, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import string
+
+t = BoostBuild.Tester()
+
+t.set_tree("boostbook")
+
+# For some reason, the messages are sent to stderr.
+t.run_build_system()
+t.fail_test(t.stdout().find("""Writing boost/A.html for refentry(boost.A)
+Writing library/reference.html for section(library.reference)
+Writing index.html for chapter(library)
+Writing docs_HTML.manifest
+""") == -1)
+t.expect_addition(["html/boost/A.html", "html/index.html"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/boostbook/a.hpp b/src/boost/tools/build/test/boostbook/a.hpp
new file mode 100644
index 000000000..5fab129a9
--- /dev/null
+++ b/src/boost/tools/build/test/boostbook/a.hpp
@@ -0,0 +1,16 @@
+/* Copyright 2004, 2006 Vladimir Prus */
+/* Distributed under the Boost Software License, Version 1.0. */
+/* (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) */
+
+
+// Seems like Boostbook does like classes outside of namespaces,
+// and won't generate anything for them.
+namespace boost {
+
+/// A class
+class A {
+public:
+ /// A constructor
+ A();
+};
+}
diff --git a/src/boost/tools/build/test/boostbook/docs.xml b/src/boost/tools/build/test/boostbook/docs.xml
new file mode 100644
index 000000000..c2d9b1f88
--- /dev/null
+++ b/src/boost/tools/build/test/boostbook/docs.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
+ "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
+
+<!-- Copyright 2004 Vladimir Prus -->
+<!-- Distributed under the Boost Software License, Version 1.0. -->
+<!-- (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) -->
+
+<library
+ name="library"
+ dirname="librarys" id="library"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <libraryinfo>
+ <author>
+ <firstname>Joe</firstname>
+ <surname>Hacker</surname>
+ </author>
+
+ <copyright>
+ <year>7002</year>
+ <holder>Joe Hacker</holder>
+ </copyright>
+
+ </libraryinfo>
+
+ <title>Documentation</title>
+
+ <section>
+ <title>Introduction</title>
+
+ <para>This is introduction</para>
+
+ </section>
+
+ <xi:include href="autodoc.xml"/>
+</library>
diff --git a/src/boost/tools/build/test/boostbook/jamroot.jam b/src/boost/tools/build/test/boostbook/jamroot.jam
new file mode 100644
index 000000000..94564ca29
--- /dev/null
+++ b/src/boost/tools/build/test/boostbook/jamroot.jam
@@ -0,0 +1,3 @@
+
+boostbook docs : docs.xml autodoc ;
+doxygen autodoc : [ glob *.hpp ] ;
diff --git a/src/boost/tools/build/test/build_dir.py b/src/boost/tools/build/test/build_dir.py
new file mode 100644
index 000000000..f9175c984
--- /dev/null
+++ b/src/boost/tools/build/test/build_dir.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that we can change build directory using the 'build-dir' project
+# attribute.
+
+import BoostBuild
+import string
+import os
+
+t = BoostBuild.Tester(use_test_config=False)
+
+
+# Test that top-level project can affect build dir.
+t.write("jamroot.jam", "import gcc ;")
+t.write("jamfile.jam", """\
+project : build-dir build ;
+exe a : a.cpp ;
+build-project src ;
+""")
+
+t.write("a.cpp", "int main() {}\n")
+
+t.write("src/jamfile.jam", "exe b : b.cpp ; ")
+
+t.write("src/b.cpp", "int main() {}\n")
+
+t.run_build_system()
+
+t.expect_addition(["build/$toolset/debug*/a.exe",
+ "build/src/$toolset/debug*/b.exe"])
+
+# Test that building from child projects work.
+t.run_build_system(subdir='src')
+t.ignore("build/config.log")
+t.ignore("build/project-cache.jam")
+t.expect_nothing_more()
+
+# Test that project can override build dir.
+t.write("jamfile.jam", """\
+exe a : a.cpp ;
+build-project src ;
+""")
+
+t.write("src/jamfile.jam", """\
+project : build-dir build ;
+exe b : b.cpp ;
+""")
+
+t.run_build_system()
+t.expect_addition(["bin/$toolset/debug*/a.exe",
+ "src/build/$toolset/debug*/b.exe"])
+
+# Now test the '--build-dir' option.
+t.rm(".")
+t.write("jamroot.jam", "")
+
+# Test that we get an error when no project id is specified.
+t.run_build_system(["--build-dir=foo"])
+t.fail_test(t.stdout().find(
+ "warning: the --build-dir option will be ignored") == -1)
+
+t.write("jamroot.jam", """\
+project foo ;
+exe a : a.cpp ;
+build-project sub ;
+""")
+t.write("a.cpp", "int main() {}\n")
+t.write("sub/jamfile.jam", "exe b : b.cpp ;\n")
+t.write("sub/b.cpp", "int main() {}\n")
+
+t.run_build_system(["--build-dir=build"])
+t.expect_addition(["build/foo/$toolset/debug*/a.exe",
+ "build/foo/sub/$toolset/debug*/b.exe"])
+
+t.write("jamroot.jam", """\
+project foo : build-dir bin.v2 ;
+exe a : a.cpp ;
+build-project sub ;
+""")
+
+t.run_build_system(["--build-dir=build"])
+t.expect_addition(["build/foo/bin.v2/$toolset/debug*/a.exe",
+ "build/foo/bin.v2/sub/$toolset/debug*/b.exe"])
+
+# Try building in subdir. We expect that the entire build tree with be in
+# 'sub/build'. Today, I am not sure if this is what the user expects, but let
+# it be.
+t.rm('build')
+t.run_build_system(["--build-dir=build"], subdir="sub")
+t.expect_addition(["sub/build/foo/bin.v2/sub/$toolset/debug*/b.exe"])
+
+t.write("jamroot.jam", """\
+project foo : build-dir %s ;
+exe a : a.cpp ;
+build-project sub ;
+""" % os.getcwd().replace('\\', '\\\\'))
+
+t.run_build_system(["--build-dir=build"], status=1)
+t.fail_test(t.stdout().find(
+ "Absolute directory specified via 'build-dir' project attribute") == -1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/build_file.py b/src/boost/tools/build/test/build_file.py
new file mode 100644
index 000000000..fcfe2378f
--- /dev/null
+++ b/src/boost/tools/build/test/build_file.py
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+
+# Copyright (C) 2006. Vladimir Prus
+# Copyright (C) 2008. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that we explicitly request a file (not target) to be built by
+# specifying its name on the command line.
+
+import BoostBuild
+
+
+###############################################################################
+#
+# test_building_file_from_specific_project()
+# ------------------------------------------
+#
+###############################################################################
+
+def test_building_file_from_specific_project():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+exe hello : hello.cpp ;
+exe hello2 : hello.cpp ;
+build-project sub ;
+""")
+ t.write("hello.cpp", "int main() {}\n")
+ t.write("sub/jamfile.jam", """
+exe hello : hello.cpp ;
+exe hello2 : hello.cpp ;
+exe sub : hello.cpp ;
+""")
+ t.write("sub/hello.cpp", "int main() {}\n")
+
+ t.run_build_system(["sub", t.adjust_suffix("hello.obj")])
+ t.expect_output_lines("*depends on itself*", False)
+ t.expect_addition("sub/bin/$toolset/debug*/hello.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_building_file_from_specific_target()
+# -----------------------------------------
+#
+###############################################################################
+
+def test_building_file_from_specific_target():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+exe hello1 : hello1.cpp ;
+exe hello2 : hello2.cpp ;
+exe hello3 : hello3.cpp ;
+""")
+ t.write("hello1.cpp", "int main() {}\n")
+ t.write("hello2.cpp", "int main() {}\n")
+ t.write("hello3.cpp", "int main() {}\n")
+
+ t.run_build_system(["hello1", t.adjust_suffix("hello1.obj")])
+ t.expect_addition("bin/$toolset/debug*/hello1.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_building_missing_file_from_specific_target()
+# -------------------------------------------------
+#
+###############################################################################
+
+def test_building_missing_file_from_specific_target():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+exe hello1 : hello1.cpp ;
+exe hello2 : hello2.cpp ;
+exe hello3 : hello3.cpp ;
+""")
+ t.write("hello1.cpp", "int main() {}\n")
+ t.write("hello2.cpp", "int main() {}\n")
+ t.write("hello3.cpp", "int main() {}\n")
+
+ obj = t.adjust_suffix("hello2.obj")
+ t.run_build_system(["hello1", obj], status=1)
+ t.expect_output_lines("don't know how to make*" + obj)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_building_multiple_files_with_different_names()
+# ---------------------------------------------------
+#
+###############################################################################
+
+def test_building_multiple_files_with_different_names():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+exe hello1 : hello1.cpp ;
+exe hello2 : hello2.cpp ;
+exe hello3 : hello3.cpp ;
+""")
+ t.write("hello1.cpp", "int main() {}\n")
+ t.write("hello2.cpp", "int main() {}\n")
+ t.write("hello3.cpp", "int main() {}\n")
+
+ t.run_build_system([t.adjust_suffix("hello1.obj"), t.adjust_suffix(
+ "hello2.obj")])
+ t.expect_addition("bin/$toolset/debug*/hello1.obj")
+ t.expect_addition("bin/$toolset/debug*/hello2.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_building_multiple_files_with_the_same_name()
+# -------------------------------------------------
+#
+###############################################################################
+
+def test_building_multiple_files_with_the_same_name():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+exe hello : hello.cpp ;
+exe hello2 : hello.cpp ;
+build-project sub ;
+""")
+ t.write("hello.cpp", "int main() {}\n")
+ t.write("sub/jamfile.jam", """
+exe hello : hello.cpp ;
+exe hello2 : hello.cpp ;
+exe sub : hello.cpp ;
+""")
+ t.write("sub/hello.cpp", "int main() {}\n")
+
+ t.run_build_system([t.adjust_suffix("hello.obj")])
+ t.expect_output_lines("*depends on itself*", False)
+ t.expect_addition("bin/$toolset/debug*/hello.obj")
+ t.expect_addition("sub/bin/$toolset/debug*/hello.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+test_building_file_from_specific_project()
+test_building_file_from_specific_target()
+test_building_missing_file_from_specific_target()
+test_building_multiple_files_with_different_names()
+test_building_multiple_files_with_the_same_name()
diff --git a/src/boost/tools/build/test/build_hooks.py b/src/boost/tools/build/test/build_hooks.py
new file mode 100644
index 000000000..167331341
--- /dev/null
+++ b/src/boost/tools/build/test/build_hooks.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests add-pre-build-hook and add-post-build-hook
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("Jamroot.jam", """
+import build-system ;
+build-system.add-pre-build-hook pre-build ;
+build-system.add-post-build-hook post-build ;
+
+rule pre-build ( )
+{
+ ECHO "in" pre-build hook ;
+}
+
+rule post-build ( okay ? )
+{
+ ECHO "in" post-build hook $(okay) ;
+}
+
+message show : building main targets ;
+""")
+
+t.run_build_system(stdout="""\
+building main targets
+in pre-build hook
+...found 1 target...
+in post-build hook ok
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/build_no.py b/src/boost/tools/build/test/build_no.py
new file mode 100644
index 000000000..54c1e9dbb
--- /dev/null
+++ b/src/boost/tools/build/test/build_no.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that <build>no property prevents a target from being built.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "exe hello : hello.cpp : <variant>debug:<build>no ;")
+t.write("hello.cpp", "int main() {}\n")
+
+t.run_build_system()
+t.expect_nothing_more()
+
+t.run_build_system(["release"])
+t.expect_addition("bin/$toolset/release*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/builtin_echo.py b/src/boost/tools/build/test/builtin_echo.py
new file mode 100755
index 000000000..9597aca1b
--- /dev/null
+++ b/src/boost/tools/build/test/builtin_echo.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+# Copyright 2012 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the ECHO rule.
+
+import BoostBuild
+
+def test_echo(name):
+ t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
+
+ t.write("file.jam", """\
+%s ;
+UPDATE ;
+""" % name)
+ t.run_build_system(stdout="\n")
+
+ t.write("file.jam", """\
+%s a message ;
+UPDATE ;
+""" % name)
+ t.run_build_system(stdout="a message\n")
+
+ t.cleanup()
+
+test_echo("ECHO")
+test_echo("Echo")
+test_echo("echo")
diff --git a/src/boost/tools/build/test/builtin_exit.py b/src/boost/tools/build/test/builtin_exit.py
new file mode 100755
index 000000000..82f69dc7a
--- /dev/null
+++ b/src/boost/tools/build/test/builtin_exit.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+# Copyright 2012 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the EXIT rule.
+
+import BoostBuild
+
+def test_exit(name):
+ t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
+
+ t.write("file.jam", "%s ;" % name)
+ t.run_build_system(status=1, stdout="\n")
+ t.rm(".")
+
+ t.write("file.jam", "%s : 0 ;" % name)
+ t.run_build_system(stdout="\n")
+ t.rm(".")
+
+ t.write("file.jam", "%s : 1 ;" % name)
+ t.run_build_system(status=1, stdout="\n")
+ t.rm(".")
+
+ t.write("file.jam", "%s : 2 ;" % name)
+ t.run_build_system(status=2, stdout="\n")
+ t.rm(".")
+
+ t.write("file.jam", "%s a message ;" % name)
+ t.run_build_system(status=1, stdout="a message\n")
+ t.rm(".")
+
+ t.write("file.jam", "%s a message : 0 ;" % name)
+ t.run_build_system(stdout="a message\n")
+ t.rm(".")
+
+ t.cleanup()
+
+test_exit("EXIT")
+test_exit("Exit")
+test_exit("exit")
diff --git a/src/boost/tools/build/test/builtin_glob.py b/src/boost/tools/build/test/builtin_glob.py
new file mode 100755
index 000000000..bb6153f5d
--- /dev/null
+++ b/src/boost/tools/build/test/builtin_glob.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+# Copyright 2014 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the GLOB rule.
+
+import os
+import BoostBuild
+
+def test_glob(files, glob, expected, setup=""):
+ t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
+ t.write("file.jam", setup + """
+ for local p in [ SORT %s ]
+ {
+ ECHO $(p) ;
+ }
+ UPDATE ;
+ """ % glob)
+ for f in files:
+ t.write(f, "")
+ # convert / into \ on windows
+ expected = [os.path.join(*p.split("/")) for p in expected]
+ expected.sort()
+ t.run_build_system(stdout="\n".join(expected + [""]))
+ t.cleanup()
+
+# one or both arguments empty
+test_glob([], "[ GLOB : ]", [])
+test_glob([], "[ GLOB . : ]", [])
+test_glob([], "[ GLOB : * ]", [])
+
+# a single result
+test_glob([], "[ GLOB . : * ]", ["./file.jam"])
+
+# * can match any number of characters
+test_glob([], "[ GLOB . : file*.jam ]", ["./file.jam"])
+test_glob([], "[ GLOB . : f*am ]", ["./file.jam"])
+# ? should match a single character, but not more than one
+test_glob([], "[ GLOB . : fi?e.?am ]", ["./file.jam"])
+test_glob([], "[ GLOB . : fi?.jam ]", [])
+# [abc-fh-j] matches a set of characters
+test_glob([], '[ GLOB . : "[f][i][l][e].jam" ]', ["./file.jam"])
+test_glob([], '[ GLOB . : "[fghau][^usdrwe][k-o][^f-s].jam" ]', ["./file.jam"])
+# \x matches x
+test_glob([], "[ GLOB . : \\f\\i\\l\\e.jam ]", ["./file.jam"])
+
+# multiple results
+test_glob(["test.txt"], "[ GLOB . : * ]", ["./file.jam", "./test.txt"])
+
+# directories
+test_glob(["dir1/dir2/test.txt"], "[ GLOB dir1 : * ]", ["dir1/dir2"]);
+
+# non-existent directory
+test_glob([], "[ GLOB dir1 : * ] ", [])
+
+# multiple directories and patterns
+test_glob(["dir1/file1.txt", "dir2/file1.txt",
+ "dir2/file2.txt"],
+ "[ GLOB dir1 dir2 : file1* file2* ]",
+ ["dir1/file1.txt", "dir2/file1.txt",
+ "dir2/file2.txt"])
+
+# The directory can contain . and ..
+test_glob(["dir/test.txt"], "[ GLOB dir/. : test.txt ]", ["dir/./test.txt"])
+test_glob(["dir/test.txt"], "[ GLOB dir/.. : file.jam ]", ["dir/../file.jam"])
+
+# On case insensitive filesystems, the result should
+# be normalized. It should NOT be downcased.
+test_glob(["TEST.TXT"], "[ GLOB . : TEST.TXT ]", ["./TEST.TXT"])
+
+case_insensitive = (os.path.normcase("FILE") == "file")
+
+if case_insensitive:
+ test_glob(["TEST.TXT"], "[ GLOB . : test.txt ]", ["./TEST.TXT"])
+ # This used to fail because the caching routines incorrectly
+ # reported that . and .. do not exist.
+ test_glob(["D1/D2/TEST.TXT"], "[ GLOB D1/./D2 : test.txt ]",
+ ["D1/./D2/TEST.TXT"])
+ test_glob(["D1/TEST.TXT", "TEST.TXT"], "[ GLOB D1/../D1 : test.txt ]",
+ ["D1/../D1/TEST.TXT"])
+ # This also failed because directories that were first found
+ # by GLOB were recorded as non-existent.
+ test_glob(["D1/D2/TEST.TXT"], "[ GLOB d1/d2 : test.txt ]",
+ ["D1/D2/TEST.TXT"],
+ "GLOB . : * ;")
diff --git a/src/boost/tools/build/test/builtin_glob_archive.py b/src/boost/tools/build/test/builtin_glob_archive.py
new file mode 100644
index 000000000..38c97e22e
--- /dev/null
+++ b/src/boost/tools/build/test/builtin_glob_archive.py
@@ -0,0 +1,217 @@
+#!/usr/bin/python
+
+# Copyright 2014 Steven Watanabe
+# Copyright 2015 Artur Shepilko
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the GLOB_ARCHIVE rule.
+
+import os
+import sys
+try:
+ from StringIO import StringIO
+except:
+ from io import StringIO
+import BoostBuild
+
+vms = ( os.name == 'posix' and sys.platform == 'OpenVMS')
+
+t = BoostBuild.Tester()
+
+## Setup test archive sources and symbols they contain.
+sources = {
+ "a.cpp" : ["a"],
+ "b.cpp" : ["b"],
+ "b_match.cpp" : ["b_match"],
+ "c/nopath_check.cpp" : ["nopath_check"],
+ "CaseCheck.cpp" : ["CaseCheck"],
+ "seq_check1.cpp" : ["seq_check1"],
+ "seq_check2.cpp" : ["seq_check2"],
+ "seq_check3.cpp" : ["seq_check3"],
+ "symbols_check.c" : ["symbol", "symbol_match"],
+ "members_and_symbols_check.c" : ["member_and_symbol_match"],
+ "symbol_case_check.c" : ["SymbolCaseCheck"],
+ "main_check.cpp" : ["main"]
+}
+
+
+def create_sources(path, sources):
+ for s in sources :
+ f = os.path.join(path, s)
+ t.write(f, "")
+ output = StringIO()
+ for sym in sources[s] :
+ output.write("int %s() { return 0; }\n" % sym)
+ t.write(f, output.getvalue())
+
+
+def setup_archive(name, sources):
+ global archive
+ global obj_suffix
+ archive = t.adjust_names(name)[0]
+ obj_suffix = t.adjust_names(".obj")[0]
+ output = StringIO()
+ t.write("jamroot.jam","")
+ output.write("""\
+static-lib %s :
+""" % name.split(".")[0])
+ ## sort the sources, so we can test order of the globbed members
+ for s in sorted(sources) :
+ output.write("""\
+ %s
+""" % s)
+ output.write("""\
+ ;
+""")
+ t.write("lib/jamfile.jam", output.getvalue())
+ create_sources("lib", sources)
+ t.run_build_system(subdir="lib")
+ built_archive = "lib/bin/$toolset/debug*/%s" % name
+ t.expect_addition(built_archive)
+ t.copy(built_archive, name)
+ t.rm("lib")
+
+
+def test_glob_archive(archives, glob, expected, sort_results = False):
+ output = StringIO()
+ ## replace placeholders
+ glob = glob.replace("$archive1", archives[0]).replace("$obj", obj_suffix)
+ expected = [ m.replace("$archive1",
+ archives[0]).replace("$obj", obj_suffix) for m in expected ]
+ if len(archives) > 1 :
+ glob = glob.replace("$archive2", archives[1]).replace("$obj", obj_suffix)
+ expected = [ m.replace("$archive2",
+ archives[1]).replace("$obj", obj_suffix) for m in expected ]
+ ## create test jamfile
+ if sort_results : glob = "[ SORT %s ]" % glob
+ output.write("""\
+ for local p in %s
+ {
+ ECHO $(p) ;
+ }
+ UPDATE ;
+ """ % glob)
+ t.write("file.jam", output.getvalue())
+ ## run test jamfile and match against expected results
+ if sort_results : expected.sort()
+ t.run_build_system(["-ffile.jam"], stdout="\n".join(expected + [""]))
+ t.rm("file.jam")
+
+
+## RUN TESTS
+setup_archive("auxilliary1.lib", sources)
+archive1 = archive
+setup_archive("auxilliary2.lib", sources)
+archive2 = archive
+
+## all arguments empty
+test_glob_archive([archive1], "[ GLOB_ARCHIVE ]", [])
+
+## empty query
+test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : ]", [])
+
+## no-match
+test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : a ]", [])
+
+## match exact
+test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : a$obj ]",
+ ["$archive1(a$obj)"])
+
+## glob wildcards:1
+test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : b.* ]",
+ ["$archive1(b$obj)"])
+
+## glob wildcards:2
+test_glob_archive([archive1], '[ GLOB_ARCHIVE $archive1 : "\\b?match[\.]*" ]',
+ ["$archive1(b_match$obj)"])
+
+## glob wildcards:3
+test_glob_archive([archive1], "[ SORT [ GLOB_ARCHIVE $archive1 : b* ] ]",
+ ["$archive1(b$obj)", "$archive1(b_match$obj)"])
+
+## glob multiple patterns with multiple results.
+test_glob_archive([archive1], "[ SORT [ GLOB_ARCHIVE $archive1 : b.* b_* ] ]",
+ ["$archive1(b$obj)", "$archive1(b_match$obj)"])
+
+## glob multiple archives and patterns.
+test_glob_archive([archive1, archive2],
+ "[ SORT [ GLOB_ARCHIVE $archive1 $archive2 : b.* b_* ] ]",
+ ["$archive1(b$obj)", "$archive1(b_match$obj)",
+ "$archive2(b$obj)", "$archive2(b_match$obj)"])
+
+## glob same archive multiple times.
+test_glob_archive([archive1, archive1],
+ "[ GLOB_ARCHIVE $archive1 $archive2 $archive1 : b.* ]",
+ ["$archive1(b$obj)", "$archive2(b$obj)", "$archive1(b$obj)"])
+
+## returned archive member has no path, even though its source object-file did.
+## this is rather NT-specific, where members also store their object-file's path.
+test_glob_archive([archive1], "[ GLOB_ARCHIVE $archive1 : nopath_check$obj ]",
+ ["$archive1(nopath_check$obj)"])
+
+## case insensitive matching, when archives support case sensitive member names.
+## VMS implementation forces case-insensitive matching and downcased member names.
+
+case_sensitive_members = ( not vms )
+
+if case_sensitive_members:
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : casecheck$obj : true ]",
+ ["$archive1(CaseCheck$obj)"])
+elif vms:
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : CaseCheck$obj : false ]",
+ ["$archive1(casecheck$obj)"])
+
+
+## test the order of matched members, in general it should match the
+## insertion sequence.
+test_glob_archive([archive1], "[ SORT [ GLOB_ARCHIVE $archive1 : seq_check*$obj ] ]",
+ ["$archive1(seq_check1$obj)", "$archive1(seq_check2$obj)",
+ "$archive1(seq_check3$obj)"])
+
+
+## glob members by symbols they contain.
+## Currently supported only on VMS.
+symbol_glob_supported = ( vms )
+
+if symbol_glob_supported :
+ ## NOTE: generated symbols are compiler-dependent and may be specifically
+ ## mangled (as in C++ case), so globbing by exact symbol is non-trivial.
+ ## However, C-generated symbols are likely to have more portable names,
+ ## so for the glob-by-symbol tests we glob C-generated archive members.
+
+ ## glob members by exact symbol.
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : : : symbol ]",
+ ["$archive1(symbols_check$obj)"])
+
+ ## glob members by symbol wildcard.
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : : : symbol_* ]",
+ ["$archive1(symbols_check$obj)"])
+
+ ## glob members by member pattern AND symbol pattern.
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : *symbol* : : *member* ]",
+ ["$archive1(members_and_symbols_check$obj)"])
+
+ ## case insensitive symbol glob.
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : : true : symbolcasecheck ]",
+ ["$archive1(symbol_case_check$obj)"])
+
+ ## glob member that contains main symbol.
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : : : main _main ]",
+ ["$archive1(main_check$obj)"])
+
+else:
+ test_glob_archive([archive1],
+ "[ GLOB_ARCHIVE $archive1 : : : symbol ]",
+ [])
+
+
+t.cleanup()
+
diff --git a/src/boost/tools/build/test/builtin_readlink.py b/src/boost/tools/build/test/builtin_readlink.py
new file mode 100755
index 000000000..dafd3d7bc
--- /dev/null
+++ b/src/boost/tools/build/test/builtin_readlink.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+# Copyright 2012 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+import sys
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("link-target", "")
+try:
+ os.symlink("link-target", "link")
+except (AttributeError, OSError) as e:
+ # Either OS does not support symlinks or not enough privilege
+ print("XFAIL: %s" % e)
+ t.cleanup()
+ sys.exit()
+
+t.write("file.jam", """
+ECHO [ READLINK link ] ;
+EXIT [ READLINK link-target ] : 0 ;
+""")
+
+t.run_build_system(["-ffile.jam"], stdout="""link-target
+
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/builtin_split_by_characters.py b/src/boost/tools/build/test/builtin_split_by_characters.py
new file mode 100755
index 000000000..d10015caa
--- /dev/null
+++ b/src/boost/tools/build/test/builtin_split_by_characters.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+# Copyright 2012. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the SPLIT_BY_CHARACTERS rule.
+
+import BoostBuild
+
+def test_invalid(params, expected_error_line):
+ t = BoostBuild.Tester(pass_toolset=0)
+ t.write("file.jam", "SPLIT_BY_CHARACTERS %s ;" % params)
+ t.run_build_system(["-ffile.jam"], status=1)
+ t.expect_output_lines("[*] %s" % expected_error_line)
+ t.cleanup()
+
+def test_valid():
+ t = BoostBuild.Tester(pass_toolset=0)
+ t.write("jamroot.jam", """\
+import assert ;
+
+assert.result FooBarBaz : SPLIT_BY_CHARACTERS FooBarBaz : "" ;
+assert.result FooBarBaz : SPLIT_BY_CHARACTERS FooBarBaz : x ;
+assert.result FooBa Baz : SPLIT_BY_CHARACTERS FooBarBaz : r ;
+assert.result FooBa Baz : SPLIT_BY_CHARACTERS FooBarBaz : rr ;
+assert.result FooBa Baz : SPLIT_BY_CHARACTERS FooBarBaz : rrr ;
+assert.result FooB rB z : SPLIT_BY_CHARACTERS FooBarBaz : a ;
+assert.result FooB B z : SPLIT_BY_CHARACTERS FooBarBaz : ar ;
+assert.result ooBarBaz : SPLIT_BY_CHARACTERS FooBarBaz : F ;
+assert.result FooBarBa : SPLIT_BY_CHARACTERS FooBarBaz : z ;
+assert.result ooBarBa : SPLIT_BY_CHARACTERS FooBarBaz : Fz ;
+assert.result F B rB z : SPLIT_BY_CHARACTERS FooBarBaz : oa ;
+assert.result Alib b : SPLIT_BY_CHARACTERS Alibaba : oa ;
+assert.result libaba : SPLIT_BY_CHARACTERS Alibaba : oA ;
+assert.result : SPLIT_BY_CHARACTERS FooBarBaz : FooBarBaz ;
+assert.result : SPLIT_BY_CHARACTERS FooBarBaz : FoBarz ;
+
+# Questionable results - should they return an empty string or an empty list?
+assert.result : SPLIT_BY_CHARACTERS "" : "" ;
+assert.result : SPLIT_BY_CHARACTERS "" : x ;
+assert.result : SPLIT_BY_CHARACTERS "" : r ;
+assert.result : SPLIT_BY_CHARACTERS "" : rr ;
+assert.result : SPLIT_BY_CHARACTERS "" : rrr ;
+assert.result : SPLIT_BY_CHARACTERS "" : oa ;
+""")
+ t.run_build_system()
+ t.cleanup()
+
+test_invalid("", "missing argument string")
+test_invalid("Foo", "missing argument delimiters")
+test_invalid(": Bar", "missing argument string")
+test_invalid("a : b : c", "extra argument c")
+test_invalid("a b : c", "extra argument b")
+test_invalid("a : b c", "extra argument c")
+test_valid()
diff --git a/src/boost/tools/build/test/bzip2.py b/src/boost/tools/build/test/bzip2.py
new file mode 100755
index 000000000..0525abd29
--- /dev/null
+++ b/src/boost/tools/build/test/bzip2.py
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Build from source
+t.write("bzip2/bzlib.h", 'bzip2')
+t.write("bzip2/blocksort.c", 'blocksort')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using bzip2 : : <source>$(here)/bzip2 ;
+alias bzip2 : /bzip2//bzip2 : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, '''
+source_file('blocksort.c', 'blocksort')
+action('-c -x c -I./bzip2 -o $blocksort.o $blocksort.c')
+action('--dll $blocksort.o -o $bz2.so')
+action('--archive $blocksort.o -o $bz2.a')
+''')
+
+t.run_build_system()
+t.expect_addition('bin/standalone/bzip2/mock/debug/bz2.dll')
+t.expect_addition('bin/standalone/bzip2/mock/debug/link-static/bz2.lib')
+
+t.rm('bzip2')
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('bzlib.h.cpp', '#include <bzlib.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+# Default initialization - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using bzip2 ;
+exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=bz2 -o $config.exe')
+action('-c -x c++ $bzlib.h.cpp -o $bzlib.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=bz2 -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using bzip2 ;
+exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=bz2 -o $config.exe')
+action('-c -x c++ $bzlib.h.cpp -o $bzlib.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=bz2 -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using bzip2 : : <name>mybzlib <include>$(here)/bzip2 <search>$(here)/bzip2 ;
+exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
+""")
+
+t.write('bzip2/bzlib.h', 'bzip2')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./bzip2 --static-lib=mybzlib -o $config.exe')
+action('-c -x c++ $test.cpp -I./bzip2 -o $test.o')
+action('$test.o -L./bzip2 --static-lib=mybzlib -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using bzip2 : : <name>mybzlib <include>$(here)/bzip2 <search>$(here)/bzip2 ;
+exe test : test.cpp /bzip2//bzip2 : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./bzip2 --shared-lib=mybzlib -o $config.exe')
+action('-c -x c++ $test.cpp -I./bzip2 -o $test.o')
+action('$test.o -L./bzip2 --shared-lib=mybzlib -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/c_file.py b/src/boost/tools/build/test/c_file.py
new file mode 100644
index 000000000..b4dbaf4be
--- /dev/null
+++ b/src/boost/tools/build/test/c_file.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that C files are compiled by a C compiler.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+project ;
+exe hello : hello.cpp a.c ;
+""")
+
+t.write("hello.cpp", """
+extern "C" int foo();
+int main() { return foo(); }
+""")
+
+t.write("a.c", """
+// This will not compile unless in C mode.
+int foo()
+{
+ int new = 0;
+ new = (new+1)*7;
+ return new;
+}
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/chain.py b/src/boost/tools/build/test/chain.py
new file mode 100644
index 000000000..ede2bbcb3
--- /dev/null
+++ b/src/boost/tools/build/test/chain.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests that :
+# 1) the 'make' correctly assigns types to produced targets
+# 2) if 'make' creates targets of type CPP, they are correctly used.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# In order to correctly link this app, 'b.cpp', created by a 'make' rule, should
+# be compiled.
+
+t.write("jamroot.jam", "import gcc ;")
+
+t.write("jamfile.jam", r'''
+import os ;
+if [ os.name ] = NT
+{
+ actions create
+ {
+ echo int main() {} > $(<)
+ }
+}
+else
+{
+ actions create
+ {
+ echo "int main() {}" > $(<)
+ }
+}
+
+IMPORT $(__name__) : create : : create ;
+
+exe a : l dummy.cpp ;
+
+# Needs to be a static lib for Windows - main() cannot appear in DLL.
+static-lib l : a.cpp b.cpp ;
+
+make b.cpp : : create ;
+''')
+
+t.write("a.cpp", "")
+
+t.write("dummy.cpp", "// msvc needs at least one object file\n")
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/clean.py b/src/boost/tools/build/test/clean.py
new file mode 100644
index 000000000..0d17f370c
--- /dev/null
+++ b/src/boost/tools/build/test/clean.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("a.cpp", "int main() {}\n")
+t.write("jamroot.jam", "exe a : a.cpp sub1//sub1 sub2//sub2 sub3//sub3 ;")
+t.write("sub1/jamfile.jam", """\
+lib sub1 : sub1.cpp sub1_2 ../sub2//sub2 ;
+lib sub1_2 : sub1_2.cpp ;
+""")
+
+t.write("sub1/sub1.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void sub1() {}
+""")
+
+t.write("sub1/sub1_2.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void sub1() {}
+""")
+
+t.write("sub2/jamfile.jam", "lib sub2 : sub2.cpp ;")
+t.write("sub2/sub2.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void sub2() {}
+""")
+
+t.write("sub3/jamroot.jam", "lib sub3 : sub3.cpp ;")
+t.write("sub3/sub3.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void sub3() {}
+""")
+
+# 'clean' should not remove files under separate jamroot.jam.
+t.run_build_system()
+t.run_build_system(["--clean"])
+t.expect_removal("bin/$toolset/debug*/a.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
+t.expect_removal("sub2/bin/$toolset/debug*/sub2.obj")
+t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
+
+# 'clean-all' removes everything it can reach.
+t.run_build_system()
+t.run_build_system(["--clean-all"])
+t.expect_removal("bin/$toolset/debug*/a.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
+t.expect_removal("sub2/bin/$toolset/debug*/sub2.obj")
+t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
+
+# 'clean' together with project target removes only under that project.
+t.run_build_system()
+t.run_build_system(["sub1", "--clean"])
+t.expect_nothing("bin/$toolset/debug*/a.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
+t.expect_nothing("sub2/bin/$toolset/debug*/sub2.obj")
+t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
+
+# 'clean-all' removes everything.
+t.run_build_system()
+t.run_build_system(["sub1", "--clean-all"])
+t.expect_nothing("bin/$toolset/debug*/a.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
+t.expect_removal("sub1/bin/$toolset/debug*/sub1_2.obj")
+t.expect_removal("sub2/bin/$toolset/debug*/sub2.obj")
+t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
+
+# If main target is explicitly named, we should not remove files from other
+# targets.
+t.run_build_system()
+t.run_build_system(["sub1//sub1", "--clean"])
+t.expect_removal("sub1/bin/$toolset/debug*/sub1.obj")
+t.expect_nothing("sub1/bin/$toolset/debug*/sub1_2.obj")
+t.expect_nothing("sub2/bin/$toolset/debug*/sub2.obj")
+t.expect_nothing("sub3/bin/$toolset/debug*/sub3.obj")
+
+# Regression test: sources of the 'cast' rule were mistakenly deleted.
+t.rm(".")
+t.write("jamroot.jam", """\
+import cast ;
+cast a cpp : a.h ;
+""")
+t.write("a.h", "")
+t.run_build_system(["--clean"])
+t.expect_nothing("a.h")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/cli_property_expansion.py b/src/boost/tools/build/test/cli_property_expansion.py
new file mode 100644
index 000000000..4d143358d
--- /dev/null
+++ b/src/boost/tools/build/test/cli_property_expansion.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+
+# Copyright 2015 Aaron Boman
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that free property inside.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "")
+t.write(
+ "subdir/build.jam",
+ """
+ import feature ;
+ feature.feature my-feature : : free ;
+ """
+)
+t.write(
+ "subdir/subsubdir/build.jam",
+ """
+ exe hello : hello.c ;
+ """
+)
+t.write(
+ "subdir/subsubdir/hello.c",
+ r"""
+ #include <stdio.h>
+
+ int main(int argc, char **argv){
+ printf("%s\n", "Hello, World!");
+ }
+ """
+)
+
+# run from the root directory
+t.run_build_system(['subdir/subsubdir', 'my-feature="some value"'])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/collect_debug_info.py b/src/boost/tools/build/test/collect_debug_info.py
new file mode 100755
index 000000000..fe9ef7ec9
--- /dev/null
+++ b/src/boost/tools/build/test/collect_debug_info.py
@@ -0,0 +1,341 @@
+#!/usr/bin/python
+
+# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Temporarily enabled dummy test that always fails and is used to collect
+# extra debugging information from Boost Build test runner sites.
+
+import BoostBuild
+
+import os
+import re
+import sys
+
+
+###############################################################################
+#
+# Public interface.
+#
+###############################################################################
+
+def collectDebugInfo():
+ t = _init()
+
+ global tag
+
+ tag = "Python version"
+ try:
+ _info(sys.version)
+ except:
+ _info_exc()
+
+ tag = "Python platform"
+ try:
+ _info(sys.platform)
+ except:
+ _info_exc()
+
+ tag = "Boost Jam/Build version"
+ try:
+ _infoX(_getJamVersionInfo(t))
+ except:
+ _info_exc()
+
+ #_collectDebugInfo_environ()
+
+ # Report prepared annotations.
+ t.fail_test(1, dump_difference=False, dump_stdio=False, dump_stack=False)
+
+
+###############################################################################
+#
+# Private interface.
+#
+###############################################################################
+
+varSeparator = "###$^%~~~"
+
+
+def _collect(results, prefix, name, t):
+ results.append("%s - %s - os.getenv(): %r" % (prefix, name, os.getenv(
+ name)))
+ results.append("%s - %s - os.environ.get(): %r" % (prefix, name,
+ os.environ.get(name)))
+ external_values = _getExternalValues(t, name)
+ results.append("%s - %s - external: %r" % (prefix, name,
+ external_values[name]))
+
+
+def _collectDebugInfo_environ(t):
+ dummyVars = ["WOOF_WOOFIE_%d" % x for x in range(4)]
+ global tag
+
+ tag = "XXX in os.environ"
+ try:
+ def f(name):
+ return "%s: %s" % (name, name in os.environ)
+ _infoX(f(x) for x in dummyVars)
+ except:
+ _info_exc()
+
+ tag = "os.environ[XXX]"
+ try:
+ def f(name):
+ try:
+ result = os.environ[name]
+ except:
+ result = _str_exc()
+ return "%s: %r" % (name, result)
+ _infoX(f(x) for x in dummyVars)
+ except:
+ _info_exc()
+
+ tag = "os.environ.get(XXX)"
+ try:
+ def f(name):
+ return "%s: %r" % (name, os.environ.get(name))
+ _infoX(f(x) for x in dummyVars)
+ except:
+ _info_exc()
+
+ tag = "os.getenv(XXX)"
+ try:
+ def f(name):
+ return "%s: %r" % (name, os.getenv(name))
+ _infoX(f(x) for x in dummyVars)
+ except:
+ _info_exc()
+
+ name = dummyVars[0]
+ value = "foo"
+ tag = "os.putenv(%s) to %r" % (name, value)
+ try:
+ results = []
+ _collect(results, "before", name, t)
+ os.putenv(name, value)
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[1]
+ value = "bar"
+ tag = "os.environ[%s] to %r" % (name, value)
+ try:
+ results = []
+ _collect(results, "before", name, t)
+ os.environ[name] = value
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[1]
+ value = "baz"
+ tag = "os.putenv(%s) to %r" % (name, value)
+ try:
+ results = []
+ _collect(results, "before", name, t)
+ os.putenv(name, value)
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[1]
+ value = ""
+ tag = "os.putenv(%s) to %r" % (name, value)
+ try:
+ results = []
+ _collect(results, "before", name, t)
+ os.putenv(name, value)
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[2]
+ value = "foo"
+ tag = "os.unsetenv(%s) from %r" % (name, value)
+ try:
+ results = []
+ os.environ[name] = value
+ _collect(results, "before", name, t)
+ os.unsetenv(name)
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[2]
+ value = "foo"
+ tag = "del os.environ[%s] from %r" % (name, value)
+ try:
+ results = []
+ os.environ[name] = value
+ _collect(results, "before", name, t)
+ del os.environ[name]
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[2]
+ value = "foo"
+ tag = "os.environ.pop(%s) from %r" % (name, value)
+ try:
+ results = []
+ os.environ[name] = value
+ _collect(results, "before", name, t)
+ os.environ.pop(name)
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[2]
+ value1 = "foo"
+ value2 = ""
+ tag = "os.environ[%s] to %r from %r" % (name, value2, value1)
+ try:
+ results = []
+ os.environ[name] = value1
+ _collect(results, "before", name, t)
+ os.environ[name] = value2
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+ name = dummyVars[3]
+ value = '""'
+ tag = "os.environ[%s] to %r" % (name, value)
+ try:
+ results = []
+ _collect(results, "before", name, t)
+ os.environ[name] = value
+ _collect(results, "after", name, t)
+ _infoX(results)
+ except:
+ _info_exc()
+
+
+def _getExternalValues(t, *args):
+ t.run_build_system(["---var-name=%s" % x for x in args])
+ result = dict()
+ for x in args:
+ m = re.search(r"^\*\*\*ENV\*\*\* %s: '(.*)' \*\*\*$" % x, t.stdout(),
+ re.MULTILINE)
+ if m:
+ result[x] = m.group(1)
+ else:
+ result[x] = None
+ return result
+
+
+def _getJamVersionInfo(t):
+ result = []
+
+ # JAM version variables.
+ t.run_build_system(["---version"])
+ for m in re.finditer(r"^\*\*\*VAR\*\*\* ([^:]*): (.*)\*\*\*$", t.stdout(),
+ re.MULTILINE):
+ name = m.group(1)
+ value = m.group(2)
+ if not value:
+ value = []
+ elif value[-1] == ' ':
+ value = value[:-1].split(varSeparator)
+ else:
+ value = "!!!INVALID!!! - '%s'" % value
+ result.append("%s = %s" % (name, value))
+ result.append("")
+
+ # bjam -v output.
+ t.run_build_system(["-v"])
+ result.append("--- output for 'bjam -v' ---")
+ result.append(t.stdout())
+
+ # bjam --version output.
+ t.run_build_system(["--version"], status=1)
+ result.append("--- output for 'bjam --version' ---")
+ result.append(t.stdout())
+
+ return result
+
+
+def _init():
+ toolsetName = "__myDummyToolset__"
+
+ t = BoostBuild.Tester(["toolset=%s" % toolsetName], pass_toolset=False,
+ use_test_config=False)
+
+ # Prepare a dummy toolset so we do not get errors in case the default one
+ # is not found.
+ t.write(toolsetName + ".jam", """\
+import feature ;
+feature.extend toolset : %s ;
+rule init ( ) { }
+""" % toolsetName )
+
+ # Python version of the same dummy toolset.
+ t.write(toolsetName + ".py", """\
+from b2.build import feature
+feature.extend('toolset', ['%s'])
+def init(): pass
+""" % toolsetName )
+
+ t.write("jamroot.jam", """\
+import os ;
+.argv = [ modules.peek : ARGV ] ;
+local names = [ MATCH ^---var-name=(.*) : $(.argv) ] ;
+for x in $(names)
+{
+ value = [ os.environ $(x) ] ;
+ ECHO ***ENV*** $(x): '$(value)' *** ;
+}
+if ---version in $(.argv)
+{
+ for x in JAMVERSION JAM_VERSION JAMUNAME JAM_TIMESTAMP_RESOLUTION OS
+ {
+ v = [ modules.peek : $(x) ] ;
+ ECHO ***VAR*** $(x): "$(v:J=%s)" *** ;
+ }
+}
+""" % varSeparator)
+
+ return t
+
+
+def _info(*values):
+ values = list(values) + [""]
+ BoostBuild.annotation(tag, "\n".join(str(x) for x in values))
+
+
+def _infoX(values):
+ _info(*values)
+
+
+def _info_exc():
+ _info(_str_exc())
+
+
+def _str_exc():
+ exc_type, exc_value = sys.exc_info()[0:2]
+ if exc_type is None:
+ exc_type_name = "None"
+ else:
+ exc_type_name = exc_type.__name__
+ return "*** EXCEPTION *** %s - %s ***" % (exc_type_name, exc_value)
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+collectDebugInfo()
diff --git a/src/boost/tools/build/test/command_line_properties.py b/src/boost/tools/build/test/command_line_properties.py
new file mode 100644
index 000000000..518991b6c
--- /dev/null
+++ b/src/boost/tools/build/test/command_line_properties.py
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+
+import BoostBuild
+
+def test_basic():
+ '''Tests that feature=value works'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ feature f1 : 1 2 ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1=2'])
+ t.expect_content("bin/*/output.txt", "2")
+ t.cleanup()
+
+def test_implicit():
+ '''Tests that implicit features can be named without a feature'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ feature f1 : v1 v2 : implicit ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['v2'])
+ t.expect_content("bin/*/output.txt", "v2")
+ t.cleanup()
+
+def test_optional():
+ '''Tests that feature= works for optional features'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ feature f1 : 1 2 : optional ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1> ;
+ actions run { echo b $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1='])
+ t.expect_content("bin/*/output.txt", "b")
+ t.cleanup()
+
+def test_free():
+ '''Free features named on the command line apply to all targets
+ everywhere. Free features can contain any characters, even those
+ that have a special meaning.'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ feature f1 : : free ;
+ make output1.txt : : @run : <dependency>output2.txt ;
+ make output2.txt : : @run ;
+ explicit output2.txt ;
+ flags run OPTIONS <f1> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1=x,/:-'])
+ t.expect_content("bin*/output1.txt", "x,/:-")
+ t.expect_content("bin*/output2.txt", "x,/:-")
+ t.cleanup()
+
+def test_subfeature():
+ '''Subfeatures should be expressed as feature=value-subvalue'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature subfeature ;
+ import toolset : flags ;
+ feature f1 : 1 2 ;
+ subfeature f1 2 : sub : x y ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1-2:sub> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1=2-y'])
+ t.expect_content("bin/*/output.txt", "y")
+ t.cleanup()
+
+def test_multiple_values():
+ '''Multiple values of a feature can be given in a comma-separated list'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ feature f1 : 1 2 3 ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1=2,3'])
+ t.expect_content("bin*/f1-2*/output.txt", "2")
+ t.expect_content("bin*/f1-3*/output.txt", "3")
+ t.cleanup()
+
+def test_multiple_properties():
+ '''Multiple properties can be grouped with /'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ feature f1 : 1 2 ;
+ feature f2 : 3 4 ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1> ;
+ flags run OPTIONS <f2> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1=2/f2=4'])
+ t.expect_content("bin/*/output.txt", "2 4")
+ t.cleanup()
+
+def test_cross_product():
+ '''If multiple properties are specified on the command line
+ we expand to every possible maximum set of non-conflicting features.
+ This test should be run after testing individual components in
+ isolation.'''
+ t = BoostBuild.Tester()
+ t.write('Jamroot.jam', '''
+ import feature : feature ;
+ import toolset : flags ;
+ # Make features symmetric to make the paths easier to distinguish
+ feature f1 : 11 12 13 14 15 : symmetric ;
+ feature f2 : 21 22 23 : symmetric ;
+ feature f3 : v1 v2 v3 v4 : implicit symmetric ;
+ feature f4 : : free ;
+ make output.txt : : @run ;
+ flags run OPTIONS <f1> ;
+ flags run OPTIONS <f2> ;
+ flags run OPTIONS <f3> ;
+ flags run OPTIONS <f4> ;
+ actions run { echo $(OPTIONS) > $(<) }
+ ''')
+ t.run_build_system(['f1=12,13/f2=22', 'v2', 'v3', 'f1=14', 'f2=23',
+ 'f4=xxx', 'f4=yyy', 'v4/f1=15/f4=zzz'])
+ t.expect_content("bin*/v2*/f1-12/f2-22*/output.txt", "12 22 v2 xxx yyy")
+ t.expect_addition("bin*/v2*/f1-12/f2-22*/output.txt")
+ t.expect_content("bin*/v2*/f1-13/f2-22*/output.txt", "13 22 v2 xxx yyy")
+ t.expect_addition("bin*/v2*/f1-13/f2-22*/output.txt")
+ t.expect_content("bin*/v2*/f1-14/f2-23*/output.txt", "14 23 v2 xxx yyy")
+ t.expect_addition("bin*/v2*/f1-14/f2-23*/output.txt")
+ t.expect_content("bin*/v3*/f1-12/f2-22*/output.txt", "12 22 v3 xxx yyy")
+ t.expect_addition("bin*/v3*/f1-12/f2-22*/output.txt")
+ t.expect_content("bin*/v3*/f1-13/f2-22*/output.txt", "13 22 v3 xxx yyy")
+ t.expect_addition("bin*/v3*/f1-13/f2-22*/output.txt")
+ t.expect_content("bin*/v3*/f1-14/f2-23*/output.txt", "14 23 v3 xxx yyy")
+ t.expect_addition("bin*/v3*/f1-14/f2-23*/output.txt")
+ t.expect_content("bin*/v4*/f1-15/f2-23*/output.txt", "15 23 v4 xxx yyy zzz")
+ t.expect_addition("bin*/v4*/f1-15/f2-23*/output.txt")
+ t.expect_nothing_more()
+ t.cleanup()
+
+test_basic()
+test_implicit()
+test_optional()
+test_free()
+test_subfeature()
+test_multiple_values()
+test_multiple_properties()
+test_cross_product()
diff --git a/src/boost/tools/build/test/composite.py b/src/boost/tools/build/test/composite.py
new file mode 100644
index 000000000..cb93240d6
--- /dev/null
+++ b/src/boost/tools/build/test/composite.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that composite properties are handled correctly.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+exe hello : hello.cpp : <variant>release ;
+""")
+
+t.write("hello.cpp", """
+int main() {}
+""")
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/release*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/conditionals.py b/src/boost/tools/build/test/conditionals.py
new file mode 100644
index 000000000..9df4c3488
--- /dev/null
+++ b/src/boost/tools/build/test/conditionals.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test conditional properties.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Arrange a project which will build only if 'a.cpp' is compiled with "STATIC"
+# define.
+t.write("a.cpp", """\
+#ifdef STATIC
+int main() {}
+#endif
+""")
+
+# Test conditionals in target requirements.
+t.write("jamroot.jam", "exe a : a.cpp : <link>static:<define>STATIC ;")
+t.run_build_system(["link=static"])
+t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
+t.rm("bin")
+
+# Test conditionals in project requirements.
+t.write("jamroot.jam", """
+project : requirements <link>static:<define>STATIC ;
+exe a : a.cpp ;
+""")
+t.run_build_system(["link=static"])
+t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
+t.rm("bin")
+
+# Regression test for a bug found by Ali Azarbayejani. Conditionals inside
+# usage requirement were not being evaluated.
+t.write("jamroot.jam", """
+lib l : l.cpp : : : <link>static:<define>STATIC ;
+exe a : a.cpp l ;
+""")
+t.write("l.cpp", "int i;")
+t.run_build_system(["link=static"])
+t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/conditionals2.py b/src/boost/tools/build/test/conditionals2.py
new file mode 100644
index 000000000..65a225fd7
--- /dev/null
+++ b/src/boost/tools/build/test/conditionals2.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Regression test: it was possible that due to evaluation of conditional
+# requirements, two different values of non-free features were present in a
+# property set.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("a.cpp", "")
+
+t.write("jamroot.jam", """
+import feature ;
+import common ;
+
+feature.feature the_feature : false true : propagated ;
+
+rule maker ( targets * : sources * : properties * )
+{
+ if <the_feature>false in $(properties) &&
+ <the_feature>true in $(properties)
+ {
+ EXIT "Oops, two different values of non-free feature" ;
+ }
+ CMD on $(targets) = [ common.file-creation-command ] ;
+}
+
+actions maker
+{
+ $(CMD) $(<) ;
+}
+
+make a : a.cpp : maker : <variant>debug:<the_feature>true ;
+""")
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/conditionals3.py b/src/boost/tools/build/test/conditionals3.py
new file mode 100644
index 000000000..d2045bec0
--- /dev/null
+++ b/src/boost/tools/build/test/conditionals3.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that conditional properties work, even if property is free, and value
+# includes a colon.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+exe hello : hello.cpp : <variant>debug:<define>"CLASS=Foo::Bar" ;
+""")
+
+t.write("hello.cpp", """
+namespace Foo { class Bar { } ; }
+int main()
+{
+ CLASS c;
+ c; // Disables the unused variable warning.
+}
+""")
+
+t.run_build_system(stdout=None, stderr=None)
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/conditionals4.py b/src/boost/tools/build/test/conditionals4.py
new file mode 100644
index 000000000..28b787207
--- /dev/null
+++ b/src/boost/tools/build/test/conditionals4.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+# Copyright 2021 Dmitry Arkhipov (grisumbras@gmail.com)
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test conditionals returning conditionals
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("a.cpp", "")
+
+t.write("jamroot.jam", """
+import feature ;
+import common ;
+
+feature.feature the_feature : false true : propagated ;
+
+rule add-feature ( properties * )
+{
+ return <the_feature>true ;
+}
+
+rule maker ( targets * : sources * : properties * )
+{
+ if ! <the_feature>true in $(properties)
+ {
+ EXIT "Need <the_feature>true to build" ;
+ }
+ CMD on $(targets) = [ common.file-creation-command ] ;
+}
+
+actions maker
+{
+ $(CMD) $(<) ;
+}
+
+make a : a.cpp : maker : <variant>debug:<conditional>@add-feature ;
+""")
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/conditionals_multiple.py b/src/boost/tools/build/test/conditionals_multiple.py
new file mode 100755
index 000000000..4d204a9d0
--- /dev/null
+++ b/src/boost/tools/build/test/conditionals_multiple.py
@@ -0,0 +1,312 @@
+#!/usr/bin/python
+
+# Copyright 2008 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that properties conditioned on more than one other property work as
+# expected.
+
+import BoostBuild
+
+
+###############################################################################
+#
+# test_multiple_conditions()
+# --------------------------
+#
+###############################################################################
+
+def test_multiple_conditions():
+ """Basic tests for properties conditioned on multiple other properties."""
+
+ t = BoostBuild.Tester(["--user-config=", "--ignore-site-config",
+ "toolset=testToolset"], pass_toolset=False, use_test_config=False)
+
+ t.write("testToolset.jam", """\
+import feature ;
+feature.extend toolset : testToolset ;
+rule init ( ) { }
+""")
+
+ t.write("testToolset.py", """\
+from b2.build import feature
+feature.extend('toolset', ["testToolset"])
+def init ( ): pass
+""")
+
+ t.write("jamroot.jam", """\
+import feature ;
+import notfile ;
+import toolset ;
+
+feature.feature description : : free incidental ;
+feature.feature aaa : 1 0 : incidental ;
+feature.feature bbb : 1 0 : incidental ;
+feature.feature ccc : 1 0 : incidental ;
+
+rule buildRule ( name : targets ? : properties * )
+{
+ for local description in [ feature.get-values description : $(properties) ]
+ {
+ ECHO "description:" /$(description)/ ;
+ }
+}
+
+notfile testTarget1 : @buildRule : :
+ <description>d
+ <aaa>0:<description>a0
+ <aaa>1:<description>a1
+ <aaa>0,<bbb>0:<description>a0-b0
+ <aaa>0,<bbb>1:<description>a0-b1
+ <aaa>1,<bbb>0:<description>a1-b0
+ <aaa>1,<bbb>1:<description>a1-b1
+ <aaa>0,<bbb>0,<ccc>0:<description>a0-b0-c0
+ <aaa>0,<bbb>0,<ccc>1:<description>a0-b0-c1
+ <aaa>0,<bbb>1,<ccc>1:<description>a0-b1-c1
+ <aaa>1,<bbb>0,<ccc>1:<description>a1-b0-c1
+ <aaa>1,<bbb>1,<ccc>0:<description>a1-b1-c0
+ <aaa>1,<bbb>1,<ccc>1:<description>a1-b1-c1 ;
+""")
+
+ t.run_build_system(["aaa=1", "bbb=1", "ccc=1"])
+ t.expect_output_lines("description: /d/" )
+ t.expect_output_lines("description: /a0/" , False)
+ t.expect_output_lines("description: /a1/" )
+ t.expect_output_lines("description: /a0-b0/" , False)
+ t.expect_output_lines("description: /a0-b1/" , False)
+ t.expect_output_lines("description: /a1-b0/" , False)
+ t.expect_output_lines("description: /a1-b1/" )
+ t.expect_output_lines("description: /a0-b0-c0/", False)
+ t.expect_output_lines("description: /a0-b0-c1/", False)
+ t.expect_output_lines("description: /a0-b1-c1/", False)
+ t.expect_output_lines("description: /a1-b0-c1/", False)
+ t.expect_output_lines("description: /a1-b1-c0/", False)
+ t.expect_output_lines("description: /a1-b1-c1/" )
+
+ t.run_build_system(["aaa=0", "bbb=0", "ccc=1"])
+ t.expect_output_lines("description: /d/" )
+ t.expect_output_lines("description: /a0/" )
+ t.expect_output_lines("description: /a1/" , False)
+ t.expect_output_lines("description: /a0-b0/" )
+ t.expect_output_lines("description: /a0-b1/" , False)
+ t.expect_output_lines("description: /a1-b0/" , False)
+ t.expect_output_lines("description: /a1-b1/" , False)
+ t.expect_output_lines("description: /a0-b0-c0/", False)
+ t.expect_output_lines("description: /a0-b0-c1/" )
+ t.expect_output_lines("description: /a0-b1-c1/", False)
+ t.expect_output_lines("description: /a1-b0-c1/", False)
+ t.expect_output_lines("description: /a1-b1-c0/", False)
+ t.expect_output_lines("description: /a1-b1-c1/", False)
+
+ t.run_build_system(["aaa=0", "bbb=0", "ccc=0"])
+ t.expect_output_lines("description: /d/" )
+ t.expect_output_lines("description: /a0/" )
+ t.expect_output_lines("description: /a1/" , False)
+ t.expect_output_lines("description: /a0-b0/" )
+ t.expect_output_lines("description: /a0-b1/" , False)
+ t.expect_output_lines("description: /a1-b0/" , False)
+ t.expect_output_lines("description: /a1-b1/" , False)
+ t.expect_output_lines("description: /a0-b0-c0/" )
+ t.expect_output_lines("description: /a0-b0-c1/", False)
+ t.expect_output_lines("description: /a0-b1-c1/", False)
+ t.expect_output_lines("description: /a1-b0-c1/", False)
+ t.expect_output_lines("description: /a1-b1-c0/", False)
+ t.expect_output_lines("description: /a1-b1-c1/", False)
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_multiple_conditions_with_toolset_version()
+# -----------------------------------------------
+#
+###############################################################################
+
+def test_multiple_conditions_with_toolset_version():
+ """
+ Regression tests for properties conditioned on the toolset version
+ subfeature and some additional properties.
+
+ """
+ toolset = "testToolset" ;
+
+ t = BoostBuild.Tester(["--user-config=", "--ignore-site-config"],
+ pass_toolset=False, use_test_config=False)
+
+ t.write(toolset + ".jam", """\
+import feature ;
+feature.extend toolset : %(toolset)s ;
+feature.subfeature toolset %(toolset)s : version : 0 1 ;
+rule init ( version ? ) { }
+""" % {"toolset": toolset})
+
+ t.write("testToolset.py", """\
+from b2.build import feature
+feature.extend('toolset', ["%(toolset)s"])
+feature.subfeature('toolset', "%(toolset)s", "version", ['0','1'])
+def init (version=''): pass
+""" % {"toolset": toolset})
+
+ t.write("jamroot.jam", """\
+import feature ;
+import notfile ;
+import toolset ;
+
+toolset.using testToolset ;
+
+feature.feature description : : free incidental ;
+feature.feature aaa : 0 1 : incidental ;
+feature.feature bbb : 0 1 : incidental ;
+feature.feature ccc : 0 1 : incidental ;
+
+rule buildRule ( name : targets ? : properties * )
+{
+ local ttt = [ feature.get-values toolset : $(properties) ] ;
+ local vvv = [ feature.get-values "toolset-testToolset:version" : $(properties) ] ;
+ local aaa = [ feature.get-values aaa : $(properties) ] ;
+ local bbb = [ feature.get-values bbb : $(properties) ] ;
+ local ccc = [ feature.get-values ccc : $(properties) ] ;
+ ECHO "toolset:" /$(ttt)/ "version:" /$(vvv)/ "aaa/bbb/ccc:" /$(aaa)/$(bbb)/$(ccc)/ ;
+ for local description in [ feature.get-values description : $(properties) ]
+ {
+ ECHO "description:" /$(description)/ ;
+ }
+}
+
+notfile testTarget1 : @buildRule : :
+ <toolset>testToolset,<aaa>0:<description>t-a0
+ <toolset>testToolset,<aaa>1:<description>t-a1
+
+ <toolset>testToolset-0,<aaa>0:<description>t0-a0
+ <toolset>testToolset-0,<aaa>1:<description>t0-a1
+ <toolset>testToolset-1,<aaa>0:<description>t1-a0
+ <toolset>testToolset-1,<aaa>1:<description>t1-a1
+
+ <toolset>testToolset,<aaa>0,<bbb>0:<description>t-a0-b0
+ <toolset>testToolset,<aaa>0,<bbb>1:<description>t-a0-b1
+ <toolset>testToolset,<aaa>1,<bbb>0:<description>t-a1-b0
+ <toolset>testToolset,<aaa>1,<bbb>1:<description>t-a1-b1
+
+ <aaa>0,<toolset>testToolset,<bbb>0:<description>a0-t-b0
+ <aaa>0,<toolset>testToolset,<bbb>1:<description>a0-t-b1
+ <aaa>1,<toolset>testToolset,<bbb>0:<description>a1-t-b0
+ <aaa>1,<toolset>testToolset,<bbb>1:<description>a1-t-b1
+
+ <aaa>0,<bbb>0,<toolset>testToolset:<description>a0-b0-t
+ <aaa>0,<bbb>1,<toolset>testToolset:<description>a0-b1-t
+ <aaa>1,<bbb>0,<toolset>testToolset:<description>a1-b0-t
+ <aaa>1,<bbb>1,<toolset>testToolset:<description>a1-b1-t
+
+ <toolset>testToolset-0,<aaa>0,<bbb>0:<description>t0-a0-b0
+ <toolset>testToolset-0,<aaa>0,<bbb>1:<description>t0-a0-b1
+ <toolset>testToolset-0,<aaa>1,<bbb>0:<description>t0-a1-b0
+ <toolset>testToolset-0,<aaa>1,<bbb>1:<description>t0-a1-b1
+ <toolset>testToolset-1,<aaa>0,<bbb>0:<description>t1-a0-b0
+ <toolset>testToolset-1,<aaa>0,<bbb>1:<description>t1-a0-b1
+ <toolset>testToolset-1,<aaa>1,<bbb>0:<description>t1-a1-b0
+ <toolset>testToolset-1,<aaa>1,<bbb>1:<description>t1-a1-b1
+
+ <aaa>0,<toolset>testToolset-1,<bbb>0:<description>a0-t1-b0
+ <aaa>0,<toolset>testToolset-1,<bbb>1:<description>a0-t1-b1
+ <aaa>1,<toolset>testToolset-0,<bbb>0:<description>a1-t0-b0
+ <aaa>1,<toolset>testToolset-0,<bbb>1:<description>a1-t0-b1
+
+ <bbb>0,<aaa>1,<toolset>testToolset-0:<description>b0-a1-t0
+ <bbb>0,<aaa>0,<toolset>testToolset-1:<description>b0-a0-t1
+ <bbb>0,<aaa>1,<toolset>testToolset-1:<description>b0-a1-t1
+ <bbb>1,<aaa>0,<toolset>testToolset-1:<description>b1-a0-t1
+ <bbb>1,<aaa>1,<toolset>testToolset-0:<description>b1-a1-t0
+ <bbb>1,<aaa>1,<toolset>testToolset-1:<description>b1-a1-t1 ;
+""")
+
+ t.run_build_system(["aaa=1", "bbb=1", "ccc=1", "toolset=%s-0" % toolset])
+ t.expect_output_lines("description: /t-a0/" , False)
+ t.expect_output_lines("description: /t-a1/" )
+ t.expect_output_lines("description: /t0-a0/" , False)
+ t.expect_output_lines("description: /t0-a1/" )
+ t.expect_output_lines("description: /t1-a0/" , False)
+ t.expect_output_lines("description: /t1-a1/" , False)
+ t.expect_output_lines("description: /t-a0-b0/" , False)
+ t.expect_output_lines("description: /t-a0-b1/" , False)
+ t.expect_output_lines("description: /t-a1-b0/" , False)
+ t.expect_output_lines("description: /t-a1-b1/" )
+ t.expect_output_lines("description: /a0-t-b0/" , False)
+ t.expect_output_lines("description: /a0-t-b1/" , False)
+ t.expect_output_lines("description: /a1-t-b0/" , False)
+ t.expect_output_lines("description: /a1-t-b1/" )
+ t.expect_output_lines("description: /a0-b0-t/" , False)
+ t.expect_output_lines("description: /a0-b1-t/" , False)
+ t.expect_output_lines("description: /a1-b0-t/" , False)
+ t.expect_output_lines("description: /a1-b1-t/" )
+ t.expect_output_lines("description: /t0-a0-b0/", False)
+ t.expect_output_lines("description: /t0-a0-b1/", False)
+ t.expect_output_lines("description: /t0-a1-b0/", False)
+ t.expect_output_lines("description: /t0-a1-b1/" )
+ t.expect_output_lines("description: /t1-a0-b0/", False)
+ t.expect_output_lines("description: /t1-a0-b1/", False)
+ t.expect_output_lines("description: /t1-a1-b0/", False)
+ t.expect_output_lines("description: /t1-a1-b1/", False)
+ t.expect_output_lines("description: /a0-t1-b0/", False)
+ t.expect_output_lines("description: /a0-t1-b1/", False)
+ t.expect_output_lines("description: /a1-t0-b0/", False)
+ t.expect_output_lines("description: /a1-t0-b1/" )
+ t.expect_output_lines("description: /b0-a1-t0/", False)
+ t.expect_output_lines("description: /b0-a0-t1/", False)
+ t.expect_output_lines("description: /b0-a1-t1/", False)
+ t.expect_output_lines("description: /b1-a0-t1/", False)
+ t.expect_output_lines("description: /b1-a1-t0/" )
+ t.expect_output_lines("description: /b1-a1-t1/", False)
+
+ t.run_build_system(["aaa=1", "bbb=1", "ccc=1", "toolset=%s-1" % toolset])
+ t.expect_output_lines("description: /t-a0/" , False)
+ t.expect_output_lines("description: /t-a1/" )
+ t.expect_output_lines("description: /t0-a0/" , False)
+ t.expect_output_lines("description: /t0-a1/" , False)
+ t.expect_output_lines("description: /t1-a0/" , False)
+ t.expect_output_lines("description: /t1-a1/" )
+ t.expect_output_lines("description: /t-a0-b0/" , False)
+ t.expect_output_lines("description: /t-a0-b1/" , False)
+ t.expect_output_lines("description: /t-a1-b0/" , False)
+ t.expect_output_lines("description: /t-a1-b1/" )
+ t.expect_output_lines("description: /a0-t-b0/" , False)
+ t.expect_output_lines("description: /a0-t-b1/" , False)
+ t.expect_output_lines("description: /a1-t-b0/" , False)
+ t.expect_output_lines("description: /a1-t-b1/" )
+ t.expect_output_lines("description: /a0-b0-t/" , False)
+ t.expect_output_lines("description: /a0-b1-t/" , False)
+ t.expect_output_lines("description: /a1-b0-t/" , False)
+ t.expect_output_lines("description: /a1-b1-t/" )
+ t.expect_output_lines("description: /t0-a0-b0/", False)
+ t.expect_output_lines("description: /t0-a0-b1/", False)
+ t.expect_output_lines("description: /t0-a1-b0/", False)
+ t.expect_output_lines("description: /t0-a1-b1/", False)
+ t.expect_output_lines("description: /t1-a0-b0/", False)
+ t.expect_output_lines("description: /t1-a0-b1/", False)
+ t.expect_output_lines("description: /t1-a1-b0/", False)
+ t.expect_output_lines("description: /t1-a1-b1/" )
+ t.expect_output_lines("description: /a0-t1-b0/", False)
+ t.expect_output_lines("description: /a0-t1-b1/", False)
+ t.expect_output_lines("description: /a1-t0-b0/", False)
+ t.expect_output_lines("description: /a1-t0-b1/", False)
+ t.expect_output_lines("description: /b0-a1-t0/", False)
+ t.expect_output_lines("description: /b0-a0-t1/", False)
+ t.expect_output_lines("description: /b0-a1-t1/", False)
+ t.expect_output_lines("description: /b1-a0-t1/", False)
+ t.expect_output_lines("description: /b1-a1-t0/", False)
+ t.expect_output_lines("description: /b1-a1-t1/" )
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+test_multiple_conditions()
+test_multiple_conditions_with_toolset_version()
diff --git a/src/boost/tools/build/test/configuration.py b/src/boost/tools/build/test/configuration.py
new file mode 100755
index 000000000..d34fa0e8d
--- /dev/null
+++ b/src/boost/tools/build/test/configuration.py
@@ -0,0 +1,397 @@
+#!/usr/bin/python
+
+# Copyright 2008, 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test Boost Build configuration file handling.
+
+import BoostBuild
+import TestCmd
+
+import os
+import os.path
+import re
+
+
+###############################################################################
+#
+# test_user_configuration()
+# -------------------------
+#
+###############################################################################
+
+def test_user_configuration():
+ """
+ Test Boost Build user configuration handling. Both relative and absolute
+ path handling is tested.
+
+ """
+
+ implicitConfigLoadMessage = \
+ "notice: Loading user-config configuration file: *"
+ explicitConfigLoadMessage = \
+ "notice: Loading explicitly specified user configuration file:"
+ disabledConfigLoadMessage = \
+ "notice: User configuration file loading explicitly disabled."
+ testMessage = "_!_!_!_!_!_!_!_!_ %s _!_!_!_!_!_!_!_!_"
+ toolsetName = "__myDummyToolset__"
+ subdirName = "ASubDirectory"
+ configFileNames = ["ups_lala_1.jam", "ups_lala_2.jam",
+ os.path.join(subdirName, "ups_lala_3.jam")]
+
+ t = BoostBuild.Tester(["toolset=%s" % toolsetName,
+ "--debug-configuration"], pass_toolset=False, use_test_config=False)
+
+ for configFileName in configFileNames:
+ message = "ECHO \"%s\" ;" % testMessage % configFileName
+ # We need to double any backslashes in the message or Jam will
+ # interpret them as escape characters.
+ t.write(configFileName, message.replace("\\", "\\\\"))
+
+ # Prepare a dummy toolset so we do not get errors in case the default one
+ # is not found.
+ t.write(toolsetName + ".jam", """\
+import feature ;
+feature.extend toolset : %s ;
+rule init ( ) { }
+""" % toolsetName)
+
+ # Python version of the same dummy toolset.
+ t.write(toolsetName + ".py", """\
+from b2.build import feature
+feature.extend('toolset', ['%s'])
+def init(): pass
+""" % toolsetName)
+
+ t.write("jamroot.jam", """\
+local test-index = [ MATCH ---test-id---=(.*) : [ modules.peek : ARGV ] ] ;
+ECHO test-index: $(test-index:E=(unknown)) ;
+""")
+
+ class LocalTester:
+ def __init__(self, tester):
+ self.__tester = tester
+ self.__test_ids = []
+
+ def __assertionFailure(self, message):
+ BoostBuild.annotation("failure", "Internal test assertion failure "
+ "- %s" % message)
+ self.__tester.fail_test(1)
+
+ def __call__(self, test_id, env, extra_args=None, *args, **kwargs):
+ if env == "" and not canSetEmptyEnvironmentVariable:
+ self.__assertionFailure("Can not set empty environment "
+ "variables on this platform.")
+ self.__registerTestId(str(test_id))
+ if extra_args is None:
+ extra_args = []
+ extra_args.append("---test-id---=%s" % test_id)
+ env_name = "BOOST_BUILD_USER_CONFIG"
+ previous_env = os.environ.get(env_name)
+ _env_set(env_name, env)
+ try:
+ self.__tester.run_build_system(extra_args, *args, **kwargs)
+ finally:
+ _env_set(env_name, previous_env)
+
+ def __registerTestId(self, test_id):
+ if test_id in self.__test_ids:
+ self.__assertionFailure("Multiple test cases encountered "
+ "using the same test id '%s'." % test_id)
+ self.__test_ids.append(test_id)
+
+ test = LocalTester(t)
+
+ test(1, None)
+ t.expect_output_lines(explicitConfigLoadMessage, False)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(2, None, ["--user-config="])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage, False)
+ t.expect_output_lines(disabledConfigLoadMessage)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(3, None, ['--user-config=""'])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage, False)
+ t.expect_output_lines(disabledConfigLoadMessage)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(4, None, ['--user-config="%s"' % configFileNames[0]])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0])
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(5, None, ['--user-config="%s"' % configFileNames[2]])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2])
+
+ test(6, None, ['--user-config="%s"' % os.path.abspath(configFileNames[1])])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1])
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(7, None, ['--user-config="%s"' % os.path.abspath(configFileNames[2])])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2])
+
+ if canSetEmptyEnvironmentVariable:
+ test(8, "")
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage, False)
+ t.expect_output_lines(disabledConfigLoadMessage, True)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(9, '""')
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage, False)
+ t.expect_output_lines(disabledConfigLoadMessage)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(10, configFileNames[1])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1])
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(11, configFileNames[1], ['--user-config=""'])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage, False)
+ t.expect_output_lines(disabledConfigLoadMessage)
+ t.expect_output_lines(testMessage % configFileNames[0], False)
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(12, configFileNames[1], ['--user-config="%s"' % configFileNames[0]])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0])
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ if canSetEmptyEnvironmentVariable:
+ test(13, "", ['--user-config="%s"' % configFileNames[0]])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0])
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(14, '""', ['--user-config="%s"' % configFileNames[0]])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0])
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ test(15, "invalid", ['--user-config="%s"' % configFileNames[0]])
+ t.expect_output_lines(implicitConfigLoadMessage, False)
+ t.expect_output_lines(explicitConfigLoadMessage)
+ t.expect_output_lines(disabledConfigLoadMessage, False)
+ t.expect_output_lines(testMessage % configFileNames[0])
+ t.expect_output_lines(testMessage % configFileNames[1], False)
+ t.expect_output_lines(testMessage % configFileNames[2], False)
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# Private interface.
+#
+###############################################################################
+
+def _canSetEmptyEnvironmentVariable():
+ """
+ Unfortunately different OSs (and possibly Python implementations as well)
+ have different interpretations of what it means to set an environment
+ variable to an empty string. Some (e.g. Windows) interpret it as unsetting
+ the variable and some (e.g. AIX or Darwin) actually set it to an empty
+ string.
+
+ """
+ dummyName = "UGNABUNGA_FOO_BAR_BAZ_FEE_FAE_FOU_FAM"
+ original = os.environ.get(dummyName)
+ _env_set(dummyName, "")
+ result = _getExternalEnv(dummyName) == ""
+ _env_set(dummyName, original)
+ return result
+
+
+def _env_del(name):
+ """
+ Unsets the given environment variable if it is currently set.
+
+ Note that we can not use os.environ.pop() or os.environ.clear() here
+ since prior to Python 2.6 these functions did not remove the actual
+ environment variable by calling os.unsetenv().
+
+ """
+ try:
+ del os.environ[name]
+ except KeyError:
+ pass
+
+
+def _env_set(name, value):
+ """
+ Sets the given environment variable value or unsets it, if the value is
+ None.
+
+ """
+ if value is None:
+ _env_del(name)
+ else:
+ os.environ[name] = value
+
+
+def _getExternalEnv(name):
+ toolsetName = "__myDummyToolset__"
+
+ t = BoostBuild.Tester(["toolset=%s" % toolsetName], pass_toolset=False,
+ use_test_config=False)
+ try:
+ # Prepare a dummy toolset so we do not get errors in case the default
+ # one is not found.
+ t.write(toolsetName + ".jam", """\
+import feature ;
+feature.extend toolset : %s ;
+rule init ( ) { }
+""" % toolsetName)
+
+ # Python version of the same dummy toolset.
+ t.write(toolsetName + ".py", """\
+from b2.build import feature
+feature.extend('toolset', ['%s'])
+def init(): pass
+""" % toolsetName)
+
+ t.write("jamroot.jam", """\
+import os ;
+local names = [ MATCH ^---var-name---=(.*) : [ modules.peek : ARGV ] ] ;
+for x in $(names)
+{
+ value = [ os.environ $(x) ] ;
+ ECHO "###" $(x): '$(value)' "###" ;
+}
+""")
+
+ t.run_build_system(["---var-name---=%s" % name])
+ m = re.search("^### %s: '(.*)' ###$" % name, t.stdout(), re.MULTILINE)
+ if m:
+ return m.group(1)
+ finally:
+ t.cleanup()
+
+
+def test_site_config():
+ # Ignore user-config, just in case it depends on the user's site-config.jam
+ t = BoostBuild.Tester(["--user-config="], use_test_config=False,
+ pass_toolset=0)
+ # We can immediately exit after we finish loading the config files
+ t.write("Jamroot", "EXIT Done : 0 ;")
+ t.write("my-site-config.jam", "ECHO Loaded my-site-config ;")
+
+ t.run_build_system(["--site-config=my-site-config.jam"],
+ stdout="Loaded my-site-config\nDone\n")
+
+ t.run_build_system(["--ignore-site-config", "--debug-configuration"])
+ t.expect_output_lines("""\
+notice: Site configuration files will be ignored due to the
+notice: --ignore-site-config command-line option.""")
+
+ t.run_build_system(["--site-config=", "--debug-configuration"])
+ t.expect_output_lines("""\
+notice: Site configuration file loading explicitly disabled.""")
+
+ t.cleanup()
+
+def test_global_config():
+ t = BoostBuild.Tester(use_test_config=False, pass_toolset=0)
+ t.write("my-config.jam", "ECHO Loading my-config ;")
+ t.write("Jamroot", "EXIT Done : 0 ;")
+ t.write("project-config.jam", "ECHO bad ;")
+ t.run_build_system(["--config=my-config.jam", "--debug-configuration"],
+ match=TestCmd.match_re, stdout=
+r"""notice: found boost-build\.jam at .*
+notice: loading B2 from .*
+notice: Searching '.*' for all-config configuration file 'my-config\.jam'\.
+notice: Loading all-config configuration file 'my-config\.jam' from '.*'\.
+Loading my-config
+notice: Regular configuration files will be ignored due
+notice: to the global configuration being loaded\.
+Done
+""")
+ t.run_build_system(["--config=", "--debug-configuration"],
+ match=TestCmd.match_re, stdout=
+r"""notice: found boost-build\.jam at .*
+notice: loading B2 from .*
+notice: Configuration file loading explicitly disabled.
+Done
+""")
+ t.cleanup()
+
+def test_project_config():
+ t = BoostBuild.Tester(["--user-config=", "--site-config="],
+ use_test_config=False, pass_toolset=False)
+ t.write("Jamroot", "EXIT Done : 0 ;")
+ t.write("project-config.jam", "ECHO Loading Root ;")
+ t.write("my-project-config.jam", "ECHO Loading explicit ;")
+ t.write("sub/project-config.jam", "ECHO Loading subdir ;")
+ t.write("sub/Jamfile", "")
+
+ t.run_build_system(stdout="Loading Root\nDone\n")
+ t.run_build_system(subdir="sub", stdout="Loading subdir\nDone\n")
+ t.rm("sub/project-config.jam")
+ t.run_build_system(subdir="sub", stdout="Loading Root\nDone\n")
+ t.run_build_system(["--project-config=my-project-config.jam"],
+ stdout="Loading explicit\nDone\n")
+
+ t.cleanup()
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+canSetEmptyEnvironmentVariable = _canSetEmptyEnvironmentVariable()
+
+test_user_configuration()
+test_site_config()
+test_global_config()
+test_project_config()
diff --git a/src/boost/tools/build/test/configure.py b/src/boost/tools/build/test/configure.py
new file mode 100644
index 000000000..7f287a514
--- /dev/null
+++ b/src/boost/tools/build/test/configure.py
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+
+# Copyright 2017 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests configure.check-target-builds and friends
+
+import BoostBuild
+
+def test_check_target_builds():
+ t = BoostBuild.Tester(use_test_config=0)
+ t.write("Jamroot", """
+import configure ;
+obj pass : pass.cpp ;
+obj fail : fail.cpp ;
+explicit pass fail ;
+obj foo : foo.cpp :
+ [ configure.check-target-builds pass : <define>PASS : <define>FAIL ] ;
+obj bar : foo.cpp :
+ [ configure.check-target-builds fail : <define>FAIL : <define>PASS ] ;
+""")
+ t.write("pass.cpp", "void f() {}\n")
+ t.write("fail.cpp", "#error fail.cpp\n")
+ t.write("foo.cpp", """
+#ifndef PASS
+#error PASS not defined
+#endif
+#ifdef FAIL
+#error FAIL is defined
+#endif
+""")
+ t.run_build_system()
+ t.expect_output_lines([
+ " - pass builds : yes*",
+ " - fail builds : no*"])
+ t.expect_addition("bin/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/$toolset/debug*/foo.obj")
+ t.expect_addition("bin/$toolset/debug*/bar.obj")
+ t.expect_nothing_more()
+
+ # An up-to-date build should use the cache
+ t.run_build_system()
+ t.expect_output_lines([
+ " - pass builds : yes (cached)*",
+ " - fail builds : no (cached)*"])
+ t.expect_nothing_more()
+
+ # -a should re-run everything, including configuration checks
+ t.run_build_system(["-a"])
+ t.expect_output_lines([
+ " - pass builds : yes*",
+ " - fail builds : no*"])
+ t.expect_touch("bin/$toolset/debug*/pass.obj")
+ t.expect_touch("bin/$toolset/debug*/foo.obj")
+ t.expect_touch("bin/$toolset/debug*/bar.obj")
+ t.expect_nothing_more()
+
+ # --reconfigure should re-run configuration checks only
+ t.run_build_system(["--reconfigure"])
+ t.expect_output_lines([
+ " - pass builds : yes*",
+ " - fail builds : no*"])
+ t.expect_touch("bin/$toolset/debug*/pass.obj")
+ t.expect_nothing_more()
+
+ # -a -n should not rebuild configuration checks
+ t.run_build_system(["-a", "-n"])
+ t.expect_output_lines([
+ " - pass builds : yes (cached)*",
+ " - fail builds : no (cached)*"])
+ t.expect_nothing_more()
+
+ # --clean-all should clear all configuration checks
+ t.run_build_system(["--clean-all"])
+ t.expect_output_lines([
+ " - pass builds : yes (cached)*",
+ " - fail builds : no (cached)*"])
+ t.expect_removal("bin/$toolset/debug*/pass.obj")
+ t.expect_removal("bin/$toolset/debug*/foo.obj")
+ t.expect_removal("bin/$toolset/debug*/bar.obj")
+ t.expect_nothing_more()
+
+ # If configuration checks are absent, then --clean-all
+ # should create them and then delete them again. This
+ # currently fails because clean cannot remove targets
+ # that were created in the same build.
+ #t.run_build_system(["--clean-all"])
+ #t.expect_output_lines([
+ # " - pass builds : yes",
+ # " - fail builds : no"])
+ #t.expect_nothing_more()
+
+ # Just verify that we're actually in the initial
+ # state here.
+ t.run_build_system()
+ t.expect_output_lines([
+ " - pass builds : yes*",
+ " - fail builds : no*"])
+ t.expect_addition("bin/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/$toolset/debug*/foo.obj")
+ t.expect_addition("bin/$toolset/debug*/bar.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_choose():
+ t = BoostBuild.Tester(use_test_config=0)
+ t.write("Jamroot", """
+import configure ;
+obj pass : pass.cpp ;
+obj fail : fail.cpp ;
+explicit pass fail ;
+obj foo : foo.cpp :
+ [ configure.choose "which one?" : fail <define>FAIL : pass <define>PASS ] ;
+""")
+ t.write("pass.cpp", "void f() {}\n")
+ t.write("fail.cpp", "#error fail.cpp\n")
+ t.write("foo.cpp", """
+#ifndef PASS
+#error PASS not defined
+#endif
+#ifdef FAIL
+#error FAIL is defined
+#endif
+""")
+ t.run_build_system()
+ t.expect_output_lines([
+ " - which one? : pass*"])
+ t.expect_addition("bin/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/$toolset/debug*/foo.obj")
+ t.expect_nothing_more()
+
+ # An up-to-date build should use the cache
+ t.run_build_system()
+ t.expect_output_lines([
+ " - which one? : pass (cached)*"])
+ t.expect_nothing_more()
+
+ # -a should re-run everything, including configuration checks
+ t.run_build_system(["-a"])
+ t.expect_output_lines([
+ " - which one? : pass*"])
+ t.expect_touch("bin/$toolset/debug*/pass.obj")
+ t.expect_touch("bin/$toolset/debug*/foo.obj")
+ t.expect_nothing_more()
+
+ # --reconfigure should re-run configuration checks only
+ t.run_build_system(["--reconfigure"])
+ t.expect_output_lines([
+ " - which one? : pass*"])
+ t.expect_touch("bin/$toolset/debug*/pass.obj")
+ t.expect_nothing_more()
+
+ # -a -n should not rebuild configuration checks
+ t.run_build_system(["-a", "-n"])
+ t.expect_output_lines([
+ " - which one? : pass (cached)*"])
+ t.expect_nothing_more()
+
+ # --clean-all should clear all configuration checks
+ t.run_build_system(["--clean-all"])
+ t.expect_output_lines([
+ " - which one? : pass (cached)*"])
+ t.expect_removal("bin/$toolset/debug*/pass.obj")
+ t.expect_removal("bin/$toolset/debug*/foo.obj")
+ t.expect_nothing_more()
+
+ # If configuration checks are absent, then --clean-all
+ # should create them and then delete them again. This
+ # currently fails because clean cannot remove targets
+ # that were created in the same build.
+ #t.run_build_system(["--clean-all"])
+ #t.expect_output_lines([
+ # " - which one? : pass"])
+ #t.expect_nothing_more()
+
+ # Just verify that we're actually in the initial
+ # state here.
+ t.run_build_system()
+ t.expect_output_lines([
+ " - which one? : pass*"])
+ t.expect_addition("bin/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/$toolset/debug*/foo.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_translation():
+ """Tests scoping for targets, paths, and rules within check-target-builds"""
+ t = BoostBuild.Tester(use_test_config=0)
+ t.write("Jamroot", "")
+ t.write("subdir/Jamfile", """
+import configure ;
+obj pass : pass.cpp ;
+obj fail : fail.cpp ;
+explicit pass fail ;
+obj foo : :
+ [ configure.check-target-builds pass
+ : [ configure.check-target-builds fail : <define>FAIL
+ : <define>PASS <include>include1 <conditional>@c1 ]
+ : <define>FAIL ] ;
+obj bar : :
+ [ configure.choose "which one?" : pass
+ [ configure.choose "Try again?" : pass
+ <define>PASS <include>include1 <conditional>@c1 ] ] ;
+rule c1 ( properties * )
+{
+ return <include>include2 <source>foo.cpp ;
+}
+""")
+ t.write("subdir/include1/a.h", "")
+ t.write("subdir/include2/b.h", "")
+ t.write("subdir/pass.cpp", "void f() {}\n")
+ t.write("subdir/fail.cpp", "#error fail.cpp\n")
+ t.write("subdir/foo.cpp", """
+#include <a.h>
+#include <b.h>
+#ifndef PASS
+#error PASS not defined
+#endif
+#ifdef FAIL
+#error FAIL is defined
+#endif
+""")
+ t.run_build_system(["subdir"])
+ t.expect_output_lines([
+ " - pass builds : yes*",
+ " - fail builds : no*"])
+ t.expect_addition("subdir/bin/$toolset/debug*/pass.obj")
+ t.expect_addition("subdir/bin/$toolset/debug*/foo.obj")
+ t.expect_addition("subdir/bin/$toolset/debug*/bar.obj")
+ t.expect_nothing_more()
+ t.cleanup()
+
+def test_choose_none():
+ """Tests choose when none of the alternatives match."""
+ t = BoostBuild.Tester(use_test_config=0)
+ t.write("Jamroot", """
+import configure ;
+obj fail : fail.cpp ;
+explicit pass fail ;
+obj foo : foo.cpp :
+ [ configure.choose "which one?" : fail <define>FAIL ] ;
+""")
+ t.write("fail.cpp", "#error fail.cpp\n")
+ t.write("foo.cpp", """
+#ifdef FAIL
+#error FAIL is defined
+#endif
+""")
+ t.run_build_system()
+ t.expect_output_lines([
+ " - which one? : none*"])
+
+ # An up-to-date build should use the cache
+ t.run_build_system()
+ t.expect_output_lines([
+ " - which one? : none (cached)*"])
+ t.expect_nothing_more()
+ t.cleanup()
+
+test_check_target_builds()
+test_choose()
+test_translation()
+test_choose_none()
diff --git a/src/boost/tools/build/test/copy_time.py b/src/boost/tools/build/test/copy_time.py
new file mode 100755
index 000000000..12809f09c
--- /dev/null
+++ b/src/boost/tools/build/test/copy_time.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2008 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that the common.copy rule set the modification date of the new file to
+# the current time.
+
+import BoostBuild
+
+tester = BoostBuild.Tester(use_test_config=False)
+
+tester.write("test1.cpp", """\
+template<bool, int M, class Next>
+struct time_waster {
+ typedef typename time_waster<true, M-1, time_waster>::type type1;
+ typedef typename time_waster<false, M-1, time_waster>::type type2;
+ typedef void type;
+};
+template<bool B, class Next>
+struct time_waster<B, 0, Next> {
+ typedef void type;
+};
+typedef time_waster<true, 10, void>::type type;
+int f() { return 0; }
+""")
+
+tester.write("test2.cpp", """\
+template<bool, int M, class Next>
+struct time_waster {
+ typedef typename time_waster<true, M-1, time_waster>::type type1;
+ typedef typename time_waster<false, M-1, time_waster>::type type2;
+ typedef void type;
+};
+template<bool B, class Next>
+struct time_waster<B, 0, Next> {
+ typedef void type;
+};
+typedef time_waster<true, 10, void>::type type;
+int g() { return 0; }
+""")
+
+tester.write("jamroot.jam", """\
+obj test2 : test2.cpp ;
+obj test1 : test1.cpp : <dependency>test2 ;
+install test2i : test2 : <dependency>test1 ;
+""")
+
+tester.run_build_system()
+tester.expect_addition("bin/$toolset/debug*/test2.obj")
+tester.expect_addition("bin/$toolset/debug*/test1.obj")
+tester.expect_addition("test2i/test2.obj")
+tester.expect_nothing_more()
+
+test2src = tester.read("test2i/test2.obj", binary=True)
+test2dest = tester.read("bin/$toolset/debug*/test2.obj", binary=True)
+if test2src != test2dest:
+ BoostBuild.annotation("failure", "The object file was not copied "
+ "correctly")
+ tester.fail_test(1)
+
+tester.run_build_system(["-d1"])
+tester.expect_output_lines("common.copy*", False)
+tester.expect_nothing_more()
+
+tester.cleanup()
diff --git a/src/boost/tools/build/test/core-language/test.jam b/src/boost/tools/build/test/core-language/test.jam
new file mode 100644
index 000000000..bd237d902
--- /dev/null
+++ b/src/boost/tools/build/test/core-language/test.jam
@@ -0,0 +1,1571 @@
+# Copyright 2011 Steven Watanabe.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tools
+
+passed = 0 ;
+failed = 0 ;
+
+rule show-result ( id : test-result )
+{
+ if ! ( --quiet in $(ARGV) )
+ {
+ ECHO "$(test-result):" $(id) ;
+ }
+ $(test-result) = [ CALC $($(test-result)) + 1 ] ;
+}
+
+rule check-equal ( id : values * : expected * )
+{
+ local test-result ;
+ local location = [ BACKTRACE ] ;
+ location = $(location[5-6]) ;
+ location = "$(location[1]):$(location[2])" ;
+ if x$(values) = x$(expected)
+ {
+ test-result = passed ;
+ }
+ else
+ {
+ ECHO "error:" "[" $(values) "] != [" $(expected) "]" ;
+ test-result = failed ;
+ }
+ show-result "$(location):$(id)" : $(test-result) ;
+}
+
+rule mark-order ( id : result * )
+{
+ order += $(id) ;
+ return $(result) ;
+}
+
+rule check-order ( id : expected * )
+{
+ check-equal $(id) : $(order) : $(expected) ;
+ order = ;
+}
+
+# Check variable expansion
+
+{
+
+local v1 = 1 2 3 ;
+local v2 = 4 5 6 ;
+local v3 = 0 1 2 3 4 5 6 7 8 9 10 ;
+local g = g1 g2 ;
+local v4 = String/With/Mixed/Case ;
+local v5 = path\\with\\backslashes ;
+local v6 = <grist>generic/path.txt(member.txt) ;
+local v7 = <Grist1>Dir1/File1.cpp(M1.c) <Grist2>Dir2/File2.hpp(M2.c) ;
+local v8 = <Grist3>Dir3/File3.c(M3.c) <Grist4>Dir4/File4.h(M4.c) ;
+local select1 = GU BL DBST ;
+local case1 = L U ;
+local vars = 7 8 ;
+local sub = 2 1 ;
+local p0 = name ;
+local p1 = dir/name ;
+local p2 = dir/sub/name ;
+local j1 = , - ;
+
+check-equal var-product : $(v1)$(v2) : 14 15 16 24 25 26 34 35 36 ;
+
+check-equal var-set-grist : $(v1:G=grist) : <grist>1 <grist>2 <grist>3 ;
+check-equal var-set-grist-multi : $(v1:G=$(g)) : <g1>1 <g1>2 <g1>3 <g2>1 <g2>2 <g2>3 ;
+
+check-equal var-lower : $(v4:L) : string/with/mixed/case ;
+check-equal var-upper : $(v4:U) : STRING/WITH/MIXED/CASE ;
+check-equal var-LU : $(v4:LU) : STRING/WITH/MIXED/CASE ;
+check-equal var-slashes : $(v5:T) : path/with/backslashes ;
+check-equal var-grist : $(v6:G) : <grist> ;
+check-equal var-grist-none : $(v1:G) : "" "" "" ;
+check-equal var-base : $(v6:B) : path ;
+check-equal var-suffix : $(v6:S) : .txt ;
+check-equal var-dir : $(v6:D) : generic ;
+check-equal var-member : $(v6:M) : (member.txt) ;
+check-equal var-multi : $(v6:$(select1)) : <GRIST> path generic/path.txt ;
+
+check-equal var-join-0 : $(:J=,) : ;
+check-equal var-join-1 : $(p0:J=,) : name ;
+check-equal var-join-3 : $(v1:J=,) : 1,2,3 ;
+check-equal var-set-grist-join : $(v1:G=grist:J=,) : <grist>1,<grist>2,<grist>3 ;
+# behavior change. In the past, a J= modifier would
+# cause only the last element of the other modifiers
+# to take effect.
+check-equal var-set-grist-multi-join : $(v1:G=$(g):J=,) : <g1>1,<g1>2,<g1>3 <g2>1,<g2>2,<g2>3 ;
+check-equal var-set-grist-multi-join-multi : $(v1:G=$(g):J=$(j1)) : <g1>1,<g1>2,<g1>3 <g1>1-<g1>2-<g1>3 <g2>1,<g2>2,<g2>3 <g2>1-<g2>2-<g2>3 ;
+
+check-equal var-D=-0 : name : $(p0:D=) ;
+check-equal var-D=-1 : name : $(p1:D=) ;
+check-equal var-D=-2 : name : $(p2:D=) ;
+check-equal var-D-0 : "" : $(p0:D) ;
+check-equal var-D-1 : dir : $(p1:D) ;
+check-equal var-D-2 : dir/sub : $(p2:D) ;
+check-equal var-S-1 : "" : $(p0:S) ;
+check-equal var-no-at-file-0 : ($(p0)) : [ MATCH ^@(.*) : "@($(p0))" ] ;
+check-equal var-no-at-file-1 : ($(p0)) : [ MATCH @(.*) : "--@($(p0))" ] ;
+
+if $(OS) = CYGWIN
+{
+ local cyg-root = $(:WE=/) ;
+ local cyg1 = /cygdrive/c/path1.txt ;
+ check-equal cygwin-to-cygdrive : $(cyg1:W) : C:\\path1.txt ;
+ local cyg2 = /bin/bash ;
+ check-equal cygwin-to-windows : $(cyg2:W) : $(cyg-root)\\bin\\bash ;
+ check-equal cygwin-combine-WT : $(cyg2:WT) : $(cyg-root)\\bin\\bash ;
+
+ local cyg3 = /home/boost/devel/trunk/bin.v2/ ; # exactly 31 characters
+ local win3 = $(cyg-root)\\home\\boost\\devel\\trunk\\bin.v2\\ ;
+ # This is is the easiest way to demonstrate a bug
+ # that used to cause undefined behavior. Longer paths
+ # resulted in a use-after-free error, which happened
+ # to work most of the time.
+ check-equal cygwin-long-WU : $(cyg3:WU) : $(win3:U) ;
+
+ local cyg-grist = <grist>$(cyg1) ;
+ check-equal cygwin-grist : $(cyg-grist:W) : <grist>\\cygdrive\\c\\path1.txt ;
+
+ check-equal cygwin-WU : $(cyg2:WU) : $(cyg-root:U)\\BIN\\BASH ;
+ # behavior change: L now consistently applied after W.
+ # used to affect all except the drive letter.
+ check-equal cygwin-WL : $(cyg2:WL) : $(cyg-root:L)\\bin\\bash ;
+}
+
+# behavior change
+check-equal var-test1 : $(v7[2]:G:L) : <grist2> ;
+
+check-equal var-multi-product-smm : $(v$(vars)[$(sub)]:G=$(g):$(case1)) :
+ <g1>dir2/file2.hpp(m2.c) <G1>DIR2/FILE2.HPP(M2.C)
+ <g2>dir2/file2.hpp(m2.c) <G2>DIR2/FILE2.HPP(M2.C)
+ <g1>dir1/file1.cpp(m1.c) <G1>DIR1/FILE1.CPP(M1.C)
+ <g2>dir1/file1.cpp(m1.c) <G2>DIR1/FILE1.CPP(M1.C)
+ <g1>dir4/file4.h(m4.c) <G1>DIR4/FILE4.H(M4.C)
+ <g2>dir4/file4.h(m4.c) <G2>DIR4/FILE4.H(M4.C)
+ <g1>dir3/file3.c(m3.c) <G1>DIR3/FILE3.C(M3.C)
+ <g2>dir3/file3.c(m3.c) <G2>DIR3/FILE3.C(M3.C)
+;
+check-equal var-nopathmods : $(:E=//) : // ;
+
+# showcases all the idiosyncracies of indexing
+# key: h = high, l = low, p = positive, m = minus, e = end.
+
+check-equal var-subscript-one-p : $(v3[3]) : 2 ;
+check-equal var-subscript-one-m : $(v3[-3]) : 8 ;
+check-equal var-subscript-one-0 : $(v3[0]) : 0 ;
+check-equal var-subscript-one-h : $(v3[20]) : ;
+check-equal var-subscript-one-l : $(v3[-20]) : 0 ;
+check-equal var-subscript-range-pp : $(v3[2-4]) : 1 2 3 ;
+check-equal var-subscript-range-pm : $(v3[2--3]) : 1 2 3 4 5 6 7 8 ;
+check-equal var-subscript-range-pe : $(v3[2-]) : 1 2 3 4 5 6 7 8 9 10 ;
+check-equal var-subscript-range-ph : $(v3[2-20]) : 1 2 3 4 5 6 7 8 9 10 ;
+check-equal var-subscript-range-pl : $(v3[2--20]) : ;
+check-equal var-subscript-range-mp : $(v3[-3-10]) : 8 9 ;
+check-equal var-subscript-range-mm : $(v3[-4--2]) : 7 8 9 ;
+check-equal var-subscript-range-me : $(v3[-4-]) : 7 8 9 10 ;
+check-equal var-subscript-range-mh : $(v3[-4-20]) : 7 8 9 10 ;
+check-equal var-subscript-range-mh : $(v3[-4--20]) : ;
+check-equal var-subscript-range-0p : $(v3[0-2]) : 0 1 2 ;
+check-equal var-subscript-range-0m : $(v3[0--4]) : 0 1 2 3 4 5 6 7 8 ;
+check-equal var-subscript-range-0e : $(v3[0-]) : 0 1 2 3 4 5 6 7 8 9 10 ;
+check-equal var-subscript-range-0h : $(v3[0-20]) : 0 1 2 3 4 5 6 7 8 9 10 ;
+check-equal var-subscript-range-0l : $(v3[0--20]) : ;
+check-equal var-subscript-range-hp : $(v3[20-4]) : ;
+check-equal var-subscript-range-hm : $(v3[20--4]) : ;
+check-equal var-subscript-range-he : $(v3[20-]) : ;
+check-equal var-subscript-range-hh : $(v3[20-20]) : ;
+check-equal var-subscript-range-hl : $(v3[20--20]) : ;
+check-equal var-subscript-range-lp : $(v3[-13-4]) : 0 1 2 3 4 5 ;
+check-equal var-subscript-range-lm : $(v3[-13--4]) : 0 1 2 3 4 5 6 7 8 9 ;
+check-equal var-subscript-range-le : $(v3[-13-]) : 0 1 2 3 4 5 6 7 8 9 10 ;
+check-equal var-subscript-range-lh : $(v3[-13-20]) : 0 1 2 3 4 5 6 7 8 9 10 ;
+check-equal var-subscript-range-ll : $(v3[-13--13]) : 0 ;
+check-equal var-subscript-range-empty : $(v3[4-3]) : ;
+
+}
+
+# Check rules
+
+{
+
+rule test-rule
+{
+ return $(<) - $(>) - $(1) - $(2) - $(3) - $(4) - $(5) - $(6) - $(7) - $(8) - $(9) - $(10) - $(11) - $(12) - $(13) - $(14) - $(15) - $(16) - $(17) - $(18) - $(19) ;
+}
+
+check-equal rule-arguments-numbered :
+ [ test-rule a1 : a2 : a3 : a4 : a5 : a6 : a7 : a8 : a9 : a10 : a11 : a12 : a13 : a14 : a15 : a16 : a17 : a18 : a19 ] :
+ a1 - a2 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 ;
+
+rule test-rule
+{
+ return $(<:L) - $(>:L) - $(1:L) - $(2:L) - $(3:L) - $(4:L) - $(5:L) - $(6:L) - $(7:L) - $(8:L) - $(9:L) - $(10:L) - $(11:L) - $(12:L) - $(13:L) - $(14:L) - $(15:L) - $(16:L) - $(17:L) - $(18:L) - $(19:L) ;
+}
+
+# behavior change
+check-equal rule-arguments-numbered-lower :
+ [ test-rule a1 : a2 : a3 : a4 : a5 : a6 : a7 : a8 : a9 : a10 : a11 : a12 : a13 : a14 : a15 : a16 : a17 : a18 : a19 ] :
+ a1 - a2 - a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 ;
+
+
+rule test-rule ( p1 : p2 : p3 : p4 : p5 : p6 : p7 : p8 : p9 :
+ p10 : p11 : p12 : p13 : p14 : p15 : p16 : p17 : p18 : p19 )
+
+
+{
+ return $(p1) - $(p2) - $(p3) - $(p4) - $(p5) - $(p6) - $(p7) - $(p8) - $(p9) - $(p10) - $(p11) - $(p12) - $(p13) - $(p14) - $(p15) - $(p16) - $(p17) - $(p18) - $(p19) ;
+}
+
+check-equal rule-arguments-named :
+ [ test-rule a1 : a2 : a3 : a4 : a5 : a6 : a7 : a8 : a9 : a10 : a11 : a12 : a13 : a14 : a15 : a16 : a17 : a18 : a19 ] :
+ a1 - a2 - a3 - a4 - a5 - a6 - a7 - a8 - a9 - a10 - a11 - a12 - a13 - a14 - a15 - a16 - a17 - a18 - a19 ;
+
+#
+# test rule indirection
+#
+rule select ( n list * )
+{
+ return $(list[$(n)]) ;
+}
+
+rule indirect1 ( rule + : args * )
+{
+ return [ $(rule) $(args) ] ;
+}
+
+check-equal rule-indirect-1 : [ indirect1 select 1 : a b c d e ] : a ;
+check-equal rule-indirect-2 : [ indirect1 select 2 : a b c d e ] : b ;
+
+x = reset ;
+rule reset-x ( new-value )
+{
+ x = $(new-value) ;
+}
+$(x)-x bar ; # invokes reset-x...
+check-equal rule-reset : $(x) : bar ; # which changes x
+
+rule bar-x ( new-value )
+{
+ mark-order r3 ;
+}
+
+# The arguments are evaluated in forward order
+# before the rule name
+$(x)-x [ mark-order r1 : [ reset-x reset ] ] : [ mark-order r2 ] ;
+check-order rule-order : r1 r2 ;
+
+# Cases that look like member calls
+rule looks.like-a-member ( args * )
+{
+ return $(args) ;
+}
+
+rule call-non-member ( rule + )
+{
+ return [ $(rule).like-a-member ] ;
+}
+
+rule call-non-member-with-args ( rule + )
+{
+ return [ $(rule).like-a-member a2 ] ;
+}
+
+check-equal rule-non-member : [ call-non-member looks ] : ;
+#check-equal rule-non-member-a1 : [ call-non-member looks a1 ] : looks.a1 ;
+check-equal rule-non-member-args : [ call-non-member-with-args looks ] : a2 ;
+#check-equal rule-non-member-args-a1 : [ call-non-member-with-args looks a1 ] : looks.a1 a2 ;
+
+}
+
+# Check append
+
+{
+
+local value = [ mark-order r1 : v1 v2 ] [ mark-order r2 : v3 v4 ] ;
+check-equal append : $(value) : v1 v2 v3 v4 ;
+check-order append-order : r1 r2 ;
+
+}
+
+# Check foreach
+
+{
+
+local v1 = 1 2 3 ;
+local x = old ;
+local result ;
+
+for local x in $(v1)
+{
+ result += $(x) + ;
+}
+
+check-equal foreach-local-item : $(result) : 1 + 2 + 3 + ;
+check-equal foreach-local : $(x) : old ;
+
+result = ;
+
+for x in $(v1)
+{
+ result += $(x) + ;
+}
+
+check-equal foreach-nonlocal-item : $(result) : 1 + 2 + 3 + ;
+check-equal foreach-nonlocal : $(x) : 3 ;
+
+rule call-foreach ( values * )
+{
+ for local x in $(values)
+ {
+ return $(x) ;
+ }
+}
+
+check-equal foreach-result : [ call-foreach 1 2 3 ] : 1 ;
+
+result = ;
+local varname = x ;
+x = old ;
+
+for local $(varname) in $(v1)
+{
+ result += $(x) + ;
+}
+
+check-equal foreach-no-expand : $(result) : old + old + old + ;
+
+result = ;
+
+for local v1 in $(v1)
+{
+ result += $(v1) + ;
+}
+
+check-equal foreach-order : $(result) : 1 + 2 + 3 + ;
+
+}
+
+# Check if
+
+{
+
+if true
+{
+ mark-order r1 ;
+}
+
+check-order if-true : r1 ;
+
+if $(false)
+{
+ mark-order r1 ;
+}
+
+check-order if-false : ;
+
+if true
+{
+ mark-order r1 ;
+}
+else
+{
+ mark-order r2 ;
+}
+
+check-order if-else-true : r1 ;
+
+if $(false)
+{
+ mark-order r1 ;
+}
+else
+{
+ mark-order r2 ;
+}
+
+check-order if-else-false : r2 ;
+
+rule test-rule
+{
+ if true
+ {
+ return result ;
+ }
+}
+
+check-equal if-true-result : [ test-rule ] : result ;
+
+rule test-rule
+{
+ local idx = 1 2 ;
+ local values = true ;
+ while $(idx)
+ {
+ local v = $(values[$(idx[1])]) ;
+ idx = $(idx[2-]) ;
+ if $(v)
+ {
+ return result ;
+ }
+ }
+}
+
+check-equal if-false-result : [ test-rule ] : result ;
+
+rule test-rule
+{
+ if true
+ {
+ return r1 ;
+ }
+ else
+ {
+ return r2 ;
+ }
+}
+
+check-equal if-else-true-result : [ test-rule ] : r1 ;
+
+rule test-rule
+{
+ if $(false)
+ {
+ return r1 ;
+ }
+ else
+ {
+ return r2 ;
+ }
+}
+
+check-equal if-else-false-result : [ test-rule ] : r2 ;
+
+}
+
+# Check the evaluation of conditions
+
+{
+
+local test-result ;
+local v1 = "" "" "" ;
+local v2 = ;
+local v3 = a b c ;
+local v4 = a b c d ;
+local v5 = a b d ;
+local v6 = "" "" "" d ;
+
+rule test-comparison ( id : equal less greater )
+{
+ check-equal $(id)-empty-1 : [ eval-$(id) $(v1) : $(v2) ] : $(equal) ;
+ check-equal $(id)-empty-2 : [ eval-$(id) $(v1) : $(v2) ] : $(equal) ;
+ check-equal $(id)-equal : [ eval-$(id) $(v3) : $(v3) ] : $(equal) ;
+ check-equal $(id)-less-1 : [ eval-$(id) $(v3) : $(v4) ] : $(less) ;
+ check-equal $(id)-less-2 : [ eval-$(id) $(v3) : $(v5) ] : $(less) ;
+ check-equal $(id)-less-3 : [ eval-$(id) $(v4) : $(v5) ] : $(less) ;
+ check-equal $(id)-greater-1 : [ eval-$(id) $(v4) : $(v3) ] : $(greater) ;
+ check-equal $(id)-greater-2 : [ eval-$(id) $(v5) : $(v3) ] : $(greater) ;
+ check-equal $(id)-greater-3 : [ eval-$(id) $(v5) : $(v4) ] : $(greater) ;
+}
+
+rule eval-lt ( lhs * : rhs * )
+{
+ if $(lhs) < $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-comparison lt : false true false ;
+
+rule eval-gt ( lhs * : rhs * )
+{
+ if $(lhs) > $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-comparison gt : false false true ;
+
+rule eval-le ( lhs * : rhs * )
+{
+ if $(lhs) <= $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-comparison le : true true false ;
+
+rule eval-ge ( lhs * : rhs * )
+{
+ if $(lhs) >= $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-comparison ge : true false true ;
+
+rule eval-eq ( lhs * : rhs * )
+{
+ if $(lhs) = $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-comparison eq : true false false ;
+
+rule eval-ne ( lhs * : rhs * )
+{
+ if $(lhs) != $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-comparison ne : false true true ;
+
+rule eval-not-lt ( lhs * : rhs * )
+{
+ if ! ( $(lhs) < $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-comparison not-lt : true false true ;
+
+rule eval-not-gt ( lhs * : rhs * )
+{
+ if ! ( $(lhs) > $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-comparison not-gt : true true false ;
+
+rule eval-not-le ( lhs * : rhs * )
+{
+ if ! ( $(lhs) <= $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-comparison not-le : false false true ;
+
+rule eval-not-ge ( lhs * : rhs * )
+{
+ if ! ( $(lhs) >= $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-comparison not-ge : false true false ;
+
+rule eval-not-eq ( lhs * : rhs * )
+{
+ if ! ( $(lhs) = $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-comparison not-eq : false true true ;
+
+rule eval-not-ne ( lhs * : rhs * )
+{
+ if ! ( $(lhs) != $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-comparison not-ne : true false false ;
+
+local v7 = a a a a a a ;
+local v8 = c b ;
+local v9 = c d b ;
+local v10 = c a b c c b a a a ;
+
+rule test-in ( id : subset not-subset )
+{
+ check-equal $(id)-0-0 : [ eval-$(id) $(v2) : $(v2) ] : $(subset) ;
+ check-equal $(id)-0-empty : [ eval-$(id) $(v2) : $(v1) ] : $(subset) ;
+ check-equal $(id)-empty-0 : [ eval-$(id) $(v1) : $(v2) ] : $(not-subset) ;
+ check-equal $(id)-equal : [ eval-$(id) $(v3) : $(v3) ] : $(subset) ;
+ check-equal $(id)-simple : [ eval-$(id) $(v3) : $(v4) ] : $(subset) ;
+ check-equal $(id)-extra : [ eval-$(id) $(v4) : $(v3) ] : $(not-subset) ;
+ check-equal $(id)-multiple : [ eval-$(id) $(v7) : $(v3) ] : $(subset) ;
+ check-equal $(id)-unordered : [ eval-$(id) $(v8) : $(v3) ] : $(subset) ;
+ check-equal $(id)-unordered-extra : [ eval-$(id) $(v9) : $(v3) ] : $(not-subset) ;
+ check-equal $(id)-unordered-multiple : [ eval-$(id) $(v10) : $(v3) ] : $(subset) ;
+}
+
+rule eval-in ( lhs * : rhs * )
+{
+ if $(lhs) in $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-in "in" : true false ;
+
+rule eval-not-in ( lhs * : rhs * )
+{
+ if ! ( $(lhs) in $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-in not-in : false true ;
+
+rule test-truth-table ( id : tt tf ft ff )
+{
+ check-equal $(id)-tt : [ eval-$(id) 1 : 1 ] : $(tt) ;
+ check-equal $(id)-tf : [ eval-$(id) 1 : ] : $(tf) ;
+ check-equal $(id)-ft : [ eval-$(id) : 1 ] : $(ft) ;
+ check-equal $(id)-ff : [ eval-$(id) : ] : $(ff) ;
+}
+
+rule eval-and ( lhs ? : rhs ? )
+{
+ if $(lhs) && $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-truth-table and : true false false false ;
+
+rule eval-or ( lhs ? : rhs ? )
+{
+ if $(lhs) || $(rhs) { return true ; }
+ else { return false ; }
+}
+
+test-truth-table or : true true true false ;
+
+rule eval-not-and ( lhs ? : rhs ? )
+{
+ if ! ( $(lhs) && $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-truth-table not-and : false true true true ;
+
+rule eval-not-or ( lhs ? : rhs ? )
+{
+ if ! ( $(lhs) || $(rhs) ) { return true ; }
+ else { return false ; }
+}
+
+test-truth-table not-or : false false false true ;
+
+if [ mark-order r1 : test1 ] < [ mark-order r2 : test2 ] { }
+check-order lt-order : r1 r2 ;
+if [ mark-order r1 : test1 ] > [ mark-order r2 : test2 ] { }
+check-order gt-order : r1 r2 ;
+if [ mark-order r1 : test1 ] <= [ mark-order r2 : test2 ] { }
+check-order le-order : r1 r2 ;
+if [ mark-order r1 : test1 ] >= [ mark-order r2 : test2 ] { }
+check-order ge-order : r1 r2 ;
+if [ mark-order r1 : test1 ] = [ mark-order r2 : test2 ] { }
+check-order eq-order : r1 r2 ;
+if [ mark-order r1 : test1 ] != [ mark-order r2 : test2 ] { }
+check-order ne-order : r1 r2 ;
+if [ mark-order r1 : test1 ] in [ mark-order r2 : test2 ] { }
+check-order in-order : r1 r2 ;
+
+if [ mark-order r1 : test1 ] && [ mark-order r2 : test2 ] { }
+check-order and-order : r1 r2 ;
+if [ mark-order r1 ] && [ mark-order r2 : test2 ] { }
+check-order and-order-short-circuit : r1 ;
+
+if [ mark-order r1 ] || [ mark-order r2 : test2 ] { }
+check-order or-order : r1 r2 ;
+if [ mark-order r1 : test1 ] || [ mark-order r2 : test2 ] { }
+check-order or-order-short-circuit : r1 ;
+
+}
+
+# Check include
+
+{
+#FIXME:
+# plain include
+# include in module
+# include returns an empty list
+# rule arguments are available inside include
+}
+
+# Check local
+
+{
+
+local v1 = a b c ;
+local v2 = f g h ;
+
+{
+ local v1 ;
+ check-equal local-no-init : $(v1) : ;
+}
+
+check-equal local-restore : $(v1) : a b c ;
+
+{
+ local v1 = d e f ;
+ check-equal local-init : $(v1) : d e f ;
+}
+
+check-equal local-restore-init : $(v1) : a b c ;
+
+{
+ local v1 v2 ;
+ check-equal local-multiple-no-init : $(v1) - $(v2) : - ;
+}
+
+check-equal local-multiple-restore : $(v1) - $(v2) : a b c - f g h ;
+
+{
+ local v1 v2 = d e f ;
+ check-equal local-multiple-init : $(v1) - $(v2) : d e f - d e f ;
+}
+
+{
+ local v1 v1 = d e f ;
+ check-equal local-duplicate : $(v1) - $(v1) : d e f - d e f ;
+}
+
+check-equal local-duplicate-restore : $(v1) : a b c ;
+
+{
+ local [ mark-order r1 : v1 ] = [ mark-order r2 : d e f ] ;
+ check-order local-order : r1 r2 ;
+}
+
+}
+
+# Check module
+
+{
+ local var1 = root-module-var ;
+ module my_module
+ {
+ var1 = module-var ;
+ rule get ( )
+ {
+ return $(var1) ;
+ }
+ local rule not_really ( ) { return nothing ; }
+ }
+
+ check-equal module-var-not-root : $(var1) : root-module-var ;
+
+ check-equal module-rulenames : [ RULENAMES my_module ] : get ;
+
+ IMPORT_MODULE my_module ;
+ check-equal module-rule-import-module : [ my_module.get ] : module-var ;
+
+ IMPORT my_module : get : : module-get ;
+ check-equal module-rule-imort : [ module-get ] : module-var ;
+
+ IMPORT my_module : get : : module-get : LOCALIZE ;
+ check-equal module-rule-imort-localize : [ module-get ] : root-module-var ;
+
+}
+
+# Check class
+{
+#FIXME:
+# ...
+}
+
+# Check on
+
+{
+
+local target1 = test-on-target1 ;
+local target2 = test-on-target2 ;
+local targets = $(target1) $(target2) ;
+local v1 v2 v3 ;
+
+VAR on $(target1) = value1 ;
+V2 on $(target2) = value2 ;
+
+check-equal on-return : [ on $(target1) return $(VAR) ] : value1 ;
+
+rule test-rule
+{
+ return $(VAR) ;
+}
+
+check-equal on-rule : [ on $(target1) test-rule ] : value1 ;
+
+check-equal on-multiple : [ on $(targets) return $(V2) ] : ;
+
+rule test-rule
+{
+ on $(target1)
+ {
+ return $(VAR) ;
+ }
+}
+
+check-equal on-block : [ test-rule ] : value1 ;
+
+# FIXME: crazy implementation artifacts:
+
+v1 on test-on-target3 = x1 ;
+on test-on-target3
+{
+ v1 on test-on-target3 += x1 ;
+ v1 = y1 ;
+ v2 on test-on-target3 += x2 ;
+ v2 = y2 ;
+ v3 = y3 ;
+}
+
+check-equal on-swap-old1 : $(v1) : x1 ;
+check-equal on-swap-old2 : [ on test-on-target3 return $(v1) ] : y1 ;
+check-equal on-swap-new1 : $(v2) : x2 ;
+check-equal on-swap-new2 : [ on test-on-target3 return $(v2) ] : y2 ;
+check-equal on-no-swap : $(v3) : y3 ;
+
+}
+
+# Check rule
+
+{
+#FIXME:
+# argument order
+# expand rule name
+}
+
+# Check rules
+
+{
+#FIXME:
+}
+
+# Check set
+
+{
+local v1 ;
+local v2 ;
+local v3 ;
+local vars = v1 v2 v3 ;
+
+v1 = x1 ;
+check-equal set-set-empty : $(v1) : x1 ;
+v2 += x2 ;
+check-equal set-append-empty : $(v2) : x2 ;
+v3 ?= x3 ;
+check-equal set-default-empty : $(v3) : x3 ;
+
+v1 = y1 ;
+check-equal set-set-non-empty : $(v1) : y1 ;
+v2 += y2 ;
+check-equal set-append-non-empty : $(v2) : x2 y2 ;
+v3 ?= y3 ;
+check-equal set-default-non-empty : $(v3) : x3 ;
+
+v1 = ;
+v2 = ;
+v3 = ;
+$(vars) = z ;
+check-equal set-set-empty-group : $(v1) - $(v2) - $(v3) : z - z - z ;
+
+v1 = ;
+v2 = ;
+v3 = ;
+$(vars) += z ;
+check-equal set-append-empty-group : $(v1) - $(v2) - $(v3) : z - z - z ;
+
+v1 = ;
+v2 = ;
+v3 = ;
+$(vars) ?= z ;
+check-equal set-default-empty-group : $(v1) - $(v2) - $(v3) : z - z - z ;
+
+v1 = x1 ;
+v2 = x2 ;
+v3 = x3 ;
+$(vars) = z ;
+check-equal set-set-non-empty-group : $(v1) - $(v2) - $(v3) : z - z - z ;
+
+v1 = x1 ;
+v2 = x2 ;
+v3 = x3 ;
+$(vars) += z ;
+check-equal set-append-non-empty-group : $(v1) - $(v2) - $(v3) : x1 z - x2 z - x3 z ;
+
+v1 = x1 ;
+v2 = x2 ;
+v3 = x3 ;
+$(vars) ?= z ;
+check-equal set-default-non-empty-group : $(v1) - $(v2) - $(v3) : x1 - x2 - x3 ;
+
+v1 = x1 ;
+v2 = ;
+v3 = x3 ;
+$(vars) = z ;
+check-equal set-set-mixed-group : $(v1) - $(v2) - $(v3) : z - z - z ;
+
+v1 = x1 ;
+v2 = ;
+v3 = x3 ;
+$(vars) += z ;
+check-equal set-append-mixed-group : $(v1) - $(v2) - $(v3) : x1 z - z - x3 z ;
+
+v1 = x1 ;
+v2 = ;
+v3 = x3 ;
+$(vars) ?= z ;
+check-equal set-default-mixed-group : $(v1) - $(v2) - $(v3) : x1 - z - x3 ;
+
+vars = v1 v1 ;
+
+v1 = ;
+$(vars) = z ;
+check-equal set-set-duplicate-empty : $(v1) : z ;
+v1 = ;
+$(vars) += z ;
+check-equal set-append-duplicate-empty : $(v1) : z z ;
+v1 = ;
+$(vars) ?= z ;
+check-equal set-default-duplicate-empty : $(v1) : z ;
+
+v1 = x1 ;
+$(vars) = z ;
+check-equal set-set-duplicate-non-empty : $(v1) : z ;
+v1 = x1 ;
+$(vars) += z ;
+check-equal set-append-duplicate-non-empty : $(v1) : x1 z z ;
+v1 = x1 ;
+$(vars) ?= z ;
+check-equal set-default-duplicate-non-empty : $(v1) : x1 ;
+
+rule test-rule { v1 = x1 ; }
+check-equal set-set-result : [ test-rule ] : x1 ;
+rule test-rule { v1 += x1 ; }
+check-equal set-append-result : [ test-rule ] : x1 ;
+rule test-rule { v1 ?= x1 ; }
+check-equal set-default-result : [ test-rule ] : x1 ;
+
+[ mark-order r1 ] = [ mark-order r2 ] ;
+check-order set-set-order : r1 r2 ;
+[ mark-order r1 ] += [ mark-order r2 ] ;
+check-order set-append-order : r1 r2 ;
+[ mark-order r1 ] ?= [ mark-order r2 ] ;
+check-order set-default-order : r1 r2 ;
+
+}
+
+# Check setcomp
+
+{
+#FIXME
+# Expand arguments
+# Don't expand name
+}
+
+# Check setexec
+
+{
+#FIXME:
+# Don't expand name
+# Evaluate bindlist
+}
+
+# Check settings ;
+
+{
+
+local target1 = test-settings-target1 ;
+local target2 = test-settings-target2 ;
+local target3 = test-settings-target3 ;
+local targets = $(target2) $(target3) ;
+
+local vars = v1 v2 v3 ;
+
+v1 on $(target1) = x1 ;
+check-equal settings-set-empty : [ on $(target1) return $(v1) ] : x1 ;
+v2 on $(target1) += x2 ;
+check-equal settings-append-empty : [ on $(target1) return $(v2) ] : x2 ;
+v3 on $(target1) ?= x3 ;
+check-equal settings-default-empty : [ on $(target1) return $(v3) ] : x3 ;
+
+v1 on $(target1) = y1 ;
+check-equal settings-set-non-empty : [ on $(target1) return $(v1) ] : y1 ;
+v2 on $(target1) += y2 ;
+check-equal settings-append-non-empty : [ on $(target1) return $(v2) ] : x2 y2 ;
+v3 on $(target1) ?= y3 ;
+check-equal settings-default-non-empty : [ on $(target1) return $(v3) ] : x3 ;
+
+$(vars) on setting-target2 = z ;
+check-equal settings-set-empty-group : [ on setting-target2 return $(v1) ] - [ on setting-target2 return $(v2) ] - [ on setting-target2 return $(v3) ] : z - z - z ;
+
+$(vars) on setting-target3 += z ;
+check-equal settings-append-empty-group : [ on setting-target3 return $(v1) ] - [ on setting-target3 return $(v2) ] - [ on setting-target3 return $(v3) ] : z - z - z ;
+
+$(vars) on setting-target4 ?= z ;
+check-equal settings-default-empty-group : [ on setting-target4 return $(v1) ] - [ on setting-target4 return $(v2) ] - [ on setting-target4 return $(v3) ] : z - z - z ;
+
+v1 on $(target1) = x1 ;
+v2 on $(target1) = x2 ;
+v3 on $(target1) = x3 ;
+$(vars) on $(target1) = z ;
+check-equal settings-set-non-empty-group : [ on $(target1) return $(v1) ] - [ on $(target1) return $(v2) ] - [ on $(target1) return $(v3) ] : z - z - z ;
+
+v1 on $(target1) = x1 ;
+v2 on $(target1) = x2 ;
+v3 on $(target1) = x3 ;
+$(vars) on $(target1) += z ;
+check-equal settings-append-non-empty-group : [ on $(target1) return $(v1) ] - [ on $(target1) return $(v2) ] - [ on $(target1) return $(v3) ] : x1 z - x2 z - x3 z ;
+
+v1 on $(target1) = x1 ;
+v2 on $(target1) = x2 ;
+v3 on $(target1) = x3 ;
+$(vars) on $(target1) ?= z ;
+check-equal settings-default-non-empty-group : [ on $(target1) return $(v1) ] - [ on $(target1) return $(v2) ] - [ on $(target1) return $(v3) ] : x1 - x2 - x3 ;
+
+v1 on setting-target5 = x1 ;
+v3 on setting-target5 = x3 ;
+$(vars) on setting-target5 = z ;
+check-equal settings-set-mixed-group : [ on setting-target5 return $(v1) ] - [ on setting-target5 return $(v2) ] - [ on setting-target5 return $(v3) ] : z - z - z ;
+
+v1 on setting-target6 = x1 ;
+v3 on setting-target6 = x3 ;
+$(vars) on setting-target6 += z ;
+check-equal settings-append-mixed-group : [ on setting-target6 return $(v1) ] - [ on setting-target6 return $(v2) ] - [ on setting-target6 return $(v3) ] : x1 z - z - x3 z ;
+
+v1 on setting-target7 = x1 ;
+v3 on setting-target7 = x3 ;
+$(vars) on setting-target7 ?= z ;
+check-equal settings-default-mixed-group : [ on setting-target7 return $(v1) ] - [ on setting-target7 return $(v2) ] - [ on setting-target7 return $(v3) ] : x1 - z - x3 ;
+
+vars = v1 v1 ;
+
+$(vars) on setting-target8 = z ;
+check-equal settings-set-duplicate-empty : [ on setting-target8 return $(v1) ] : z ;
+$(vars) on setting-target9 += z ;
+check-equal settings-append-duplicate-empty : [ on setting-target9 return $(v1) ] : z z ;
+$(vars) on setting-target10 ?= z ;
+check-equal settings-default-duplicate-empty : [ on setting-target10 return $(v1) ] : z ;
+
+v1 on $(target1) = x1 ;
+$(vars) on $(target1) = z ;
+check-equal settings-set-duplicate-non-empty : [ on $(target1) return $(v1) ] : z ;
+v1 on $(target1) = x1 ;
+$(vars) on $(target1) += z ;
+check-equal settings-append-duplicate-non-empty : [ on $(target1) return $(v1) ] : x1 z z ;
+v1 on $(target1) = x1 ;
+$(vars) on $(target1) ?= z ;
+check-equal settings-default-duplicate-non-empty : [ on $(target1) return $(v1) ] : x1 ;
+
+v1 on $(target1) = ;
+v1 on $(target1) ?= z ;
+check-equal settings-default-set-but-empty : [ on $(target1) return $(v1) ] : ;
+
+v1 on $(targets) = multi ;
+check-equal settings-set-multi-empty : [ on $(target2) return $(v1) ] - [ on $(target3) return $(v1) ] : multi - multi ;
+v2 on $(targets) += multi ;
+check-equal settings-append-multi-empty : [ on $(target2) return $(v2) ] - [ on $(target3) return $(v2) ] : multi - multi ;
+v3 on $(targets) ?= multi ;
+check-equal settings-default-multi-empty : [ on $(target2) return $(v3) ] - [ on $(target3) return $(v3) ] : multi - multi ;
+
+v1 on $(targets) = multi2 ;
+check-equal settings-set-multi-empty : [ on $(target2) return $(v1) ] - [ on $(target3) return $(v1) ] : multi2 - multi2 ;
+v2 on $(targets) += multi2 ;
+check-equal settings-append-multi-empty : [ on $(target2) return $(v2) ] - [ on $(target3) return $(v2) ] : multi multi2 - multi multi2 ;
+v3 on $(targets) ?= multi2 ;
+check-equal settings-default-multi-empty : [ on $(target2) return $(v3) ] - [ on $(target3) return $(v3) ] : multi - multi ;
+
+rule test-rule { v1 on $(target1) = x1 ; }
+check-equal settings-set-result : [ test-rule ] : x1 ;
+rule test-rule { v1 on $(target1) += x1 ; }
+check-equal settings-append-result : [ test-rule ] : x1 ;
+rule test-rule { v1 on $(target1) ?= x1 ; }
+check-equal settings-default-result : [ test-rule ] : x1 ;
+
+[ mark-order r1 : var ] on [ mark-order r3 : $(target1) ] = [ mark-order r2 : value ] ;
+check-order settings-set-order : r1 r2 r3 ;
+[ mark-order r1 : var ] on [ mark-order r3 : $(target1) ] += [ mark-order r2 : value ] ;
+check-order settings-append-order : r1 r2 r3 ;
+[ mark-order r1 : var ] on [ mark-order r3 : $(target1) ] ?= [ mark-order r2 : value ] ;
+check-order settings-default-order : r1 r2 r3 ;
+
+}
+
+# Check switch
+
+{
+
+local pattern = * ;
+
+switch value
+{
+ case * : mark-order r1 ;
+}
+
+check-order switch-match-any : r1 ;
+
+switch value
+{
+ case v2 : mark-order r1 ;
+}
+
+check-order switch-no-match : ;
+
+switch value
+{
+ case $(pattern) : mark-order r1 ;
+}
+
+check-order switch-no-expand : ;
+
+switch value
+{
+ case value : mark-order r1 ;
+ case * : mark-order r2 ;
+}
+
+check-order switch-match-several : r1 ;
+
+rule test-rule ( value )
+{
+ switch $(value)
+ {
+ case value : return 1 ;
+ }
+}
+
+check-equal switch-result-match : [ test-rule value ] : 1 ;
+check-equal switch-result-match : [ test-rule v1 ] : ;
+
+switch $()
+{
+ case "" : mark-order r1 ;
+ case * : mark-order r2 ;
+}
+
+check-order switch-empty : r1 ;
+
+local values = v1 v2 v3 ;
+switch $(values)
+{
+ case v1 : mark-order r1 ;
+ case v2 : mark-order r2 ;
+ case v3 : mark-order r3 ;
+}
+
+check-order switch-multiple : r1 ;
+
+# Test glob matching
+
+switch value { case * : mark-order r1 ; }
+check-order switch-glob-star : r1 ;
+
+switch value { case va*e : mark-order r1 ; }
+check-order switch-glob-star-1 : r1 ;
+
+switch value { case *a* : mark-order r1 ; }
+check-order switch-glob-star-2 : r1 ;
+
+switch value { case *a*ue* : mark-order r1 ; }
+check-order switch-glob-star-3 : r1 ;
+
+switch value { case *[eaiou]*ue : mark-order r1 ; }
+check-order switch-glob-group : r1 ;
+
+switch value { case *[eaiou]ue : mark-order r1 ; }
+check-order switch-glob-group-fail : ;
+
+switch value { case ?a?ue : mark-order r1 ; }
+check-order switch-glob-any : r1 ;
+
+switch value { case ?lue : mark-order r1 ; }
+check-order switch-glob-any-fail : ;
+
+}
+
+# Test while
+
+{
+
+local value = 1 2 3 ;
+
+while $(value)
+{
+ mark-order r$(value[1]) ;
+ value = $(value[2-]) ;
+}
+
+check-order while-exec : r1 r2 r3 ;
+
+rule test-rule
+{
+ local value = 1 2 3 ;
+ while $(value)
+ {
+ value = $(value[2-]) ;
+ return x ;
+ }
+}
+
+check-equal while-result : [ test-rule ] : x ;
+
+rule test-rule
+{
+ local value = 1 2 ;
+ while $(value)
+ {
+ value = $(value[2-]) ;
+ local inner = $(value) ;
+ while $(inner)
+ {
+ inner = $(inner[2-]) ;
+ return x ;
+ }
+ }
+}
+
+check-equal while-result-2 : [ test-rule ] : x ;
+
+}
+
+
+#
+# test break
+#
+
+{
+
+local z = original ;
+local done ;
+while ! $(done)
+{
+ local z = inner ;
+ mark-order r1 ;
+ break ;
+ mark-order r2 ;
+ done = true ;
+}
+
+check-order break-while-exec : r1 ;
+check-equal break-while-cleanup : $(z) : original ;
+
+local values = v1 v2 ;
+
+for y in $(values)
+{
+ local z = inner ;
+ mark-order r1-$(y) ;
+ break ;
+ mark-order r2-$(y) ;
+}
+
+check-order break-for-exec : r1-v1 ;
+check-equal break-for-cleanup : $(z) : original ;
+
+for local y in $(values)
+{
+ local z = inner ;
+ mark-order r1-$(y) ;
+ break ;
+ mark-order r2-$(y) ;
+}
+
+check-order break-for-local-exec : r1-v1 ;
+check-equal break-for-local-cleanup : $(z) : original ;
+
+local z1 = z1val ;
+local z2 = z2val ;
+done = ;
+while ! $(done)
+{
+ local z1 = z1new ;
+ mark-order r1 ;
+ for local y in $(values)
+ {
+ local z2 = z2new ;
+ mark-order r2 ;
+ break ;
+ mark-order r3 ;
+ }
+ mark-order r4 ;
+ break ;
+ mark-order r5 ;
+ done = true ;
+}
+
+check-order break-nested-exec : r1 r2 r4 ;
+check-equal break-nested-cleanup1 : $(z1) : z1val ;
+check-equal break-nested-cleanup2 : $(z2) : z2val ;
+
+}
+
+#
+# test continue
+#
+
+{
+
+local z = original ;
+local done ;
+while ! [ mark-order r1 : $(done) ]
+{
+ local z = inner ;
+ done = true ;
+ mark-order r2 ;
+ continue ;
+ mark-order r3 ;
+}
+
+check-order continue-while-exec : r1 r2 r1 ;
+check-equal continue-while-cleanup : $(z) : original ;
+
+local values = v1 v2 ;
+for y in $(values)
+{
+ local z = inner ;
+ mark-order r1-$(y) ;
+ continue ;
+ mark-order r2-$(y) ;
+}
+
+check-order continue-for-exec : r1-v1 r1-v2 ;
+check-equal continue-for-cleanup : $(z) : original ;
+
+for local y in $(values)
+{
+ local z = inner ;
+ mark-order r1-$(y) ;
+ continue ;
+ mark-order r2-$(y) ;
+}
+
+check-order continue-for-local-exec : r1-v1 r1-v2 ;
+check-equal continue-for-local-cleanup : $(z) : original ;
+
+local z1 = z1val ;
+local z2 = z2val ;
+done = ;
+while ! [ mark-order r1 : $(done) ]
+{
+ local z1 = z1new ;
+ done = true ;
+ mark-order r2 ;
+ for local y in $(values)
+ {
+ local z2 = z2new ;
+ mark-order r3-$(y) ;
+ continue ;
+ mark-order r4-$(y) ;
+ }
+ mark-order r5 ;
+ continue ;
+ mark-order r6 ;
+}
+
+check-order continue-nested-exec : r1 r2 r3-v1 r3-v2 r5 r1 ;
+check-equal continue-nested-cleanup1 : $(z1) : z1val ;
+check-equal continue-nested-cleanup2 : $(z2) : z2val ;
+
+}
+
+#
+# test CALLER_MODULE and backtrace
+#
+
+{
+ local base = [ BACKTRACE ] ;
+ base = $(base[2]) ;
+ rule backtrace ( )
+ {
+ local bt = [ BACKTRACE ] ;
+ check-equal backtrace-1-file : $(bt) :
+ test.jam [ CALC $(base) + 4 ] "" backtrace
+ test.jam [ CALC $(base) + 28 ] module2. module2.f
+ test.jam [ CALC $(base) + 19 ] module1. module1.f
+ test.jam [ CALC $(base) + 32 ] "" "module scope"
+ ;
+ }
+ module module1
+ {
+ IMPORT_MODULE module2 : module1 ;
+ rule f ( )
+ {
+ local m = [ CALLER_MODULE ] ;
+ check-equal caller-module-root : $(m) ;
+ module2.f ;
+ }
+ }
+ module module2
+ {
+ rule f ( )
+ {
+ local m = [ CALLER_MODULE ] ;
+ check-equal caller-module : module1 : $(m) ;
+ backtrace ;
+ }
+ }
+ IMPORT_MODULE module1 ;
+ module1.f ;
+}
+
+
+# Test NORMALIZE_PATH
+
+{
+check-equal normalize-path : "." : [ NORMALIZE_PATH ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "" ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "." ] ;
+check-equal normalize-path : ".." : [ NORMALIZE_PATH ".." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "/" ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "\\" ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "//" ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "\\\\" ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "//\\\\//\\\\" ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "/." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "/./" ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "\\\\///.///\\\\\\" ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "./././././." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "/./././././." ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo" ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo/" ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo\\" ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo\\\\/////" ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo\\\\/////././." ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo\\\\/////./././" ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "foo/.." ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "foo////.." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "///foo/\\\\/.." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "\\\\\\foo\\//\\.." ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "foo/./.." ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "foo/././././.." ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo/./././bar/./././.././././baz/./././.." ] ;
+check-equal normalize-path : "/foo" : [ NORMALIZE_PATH "/foo/./././bar/./././.././././baz/./././.." ] ;
+check-equal normalize-path : "foo" : [ NORMALIZE_PATH "foo/./././bar/./././////.././././baz/./././.." ] ;
+check-equal normalize-path : "/foo" : [ NORMALIZE_PATH "/foo/./././bar/./././////.././././baz/./././.." ] ;
+check-equal normalize-path : ".." : [ NORMALIZE_PATH "./.." ] ;
+check-equal normalize-path : ".." : [ NORMALIZE_PATH "././././.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "../.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "./../.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "././././../.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "./.././././.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "././././.././././.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "..//\\\\\\//.." ] ;
+check-equal normalize-path : "../.." : [ NORMALIZE_PATH "../..\\\\/\\\\" ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "foo/../bar/../baz/.." ] ;
+check-equal normalize-path : "." : [ NORMALIZE_PATH "foo////..////bar////.//////.////../baz/.." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "/foo/../bar/../baz/.." ] ;
+check-equal normalize-path : "/" : [ NORMALIZE_PATH "/foo////..////bar////.//////.////../baz/.." ] ;
+
+# Invalid rooted paths with leading dotdots.
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/../" ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "//\\\\//\\\\/.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "\\\\//\\\\//\\.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/../.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/../../.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/foo/bar/../baz/../../.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/../for/././../././bar/././../././.." ] ;
+check-equal normalize-path-invalid : : [ NORMALIZE_PATH "/../foo/bar" ] ;
+
+check-equal normalize-path : "../d" : [ NORMALIZE_PATH "../d" ] ;
+check-equal normalize-path : "../d" : [ NORMALIZE_PATH ".." "d" ] ;
+check-equal normalize-path : "../../d" : [ NORMALIZE_PATH ".." ".." "d" ] ;
+check-equal normalize-path : "../d" : [ NORMALIZE_PATH "" ".." "d" ] ;
+
+}
+
+# Test W32_GETREGNAMES
+
+{
+
+if $(NT)
+{
+ local sound = "Beep" "ExtendedSounds" ;
+ local r1 = [ W32_GETREGNAMES "HKEY_CURRENT_USER\\Control Panel\\Sound" :
+ values ] ;
+ check-equal w32_getregnames : $(sound:L) : [ SORT $(r1:L) ] ;
+ local r2 = [ W32_GETREGNAMES "HKCU\\Control Panel\\Sound" : values ] ;
+ check-equal w32_getregnames : $(sound:L) : [ SORT $(r2:L) ] ;
+
+ # Some Windows platforms may have additional keys under
+ # 'CurrentControlSet' which we then remove here so they would not be
+ # reported as errors by our test.
+ local rule remove-policies ( param * )
+ {
+ local found ;
+ local r ;
+ for local x in $(param:L)
+ {
+ if ! x in $(found) &&
+ $(x) in "addservices" "policies" "deleted device ids" "software"
+ {
+ found += $(x) ;
+ }
+ else
+ {
+ r += $(x) ;
+ }
+ }
+ return $(r) ;
+ }
+ local CurrentControlSet = "Control" "Enum" "Hardware Profiles" "Services" ;
+ local r3 = [ W32_GETREGNAMES "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet"
+ : subkeys ] ;
+ check-equal w32_getregnames : $(CurrentControlSet:L) : [ remove-policies
+ $(r3:L) ] ;
+ local r4 = [ W32_GETREGNAMES "HKLM\\SYSTEM\\CurrentControlSet" : subkeys ] ;
+ check-equal w32_getregnames : $(CurrentControlSet:L) : [ remove-policies
+ $(r4:L) ] ;
+}
+
+}
+
+# Test SHELL
+
+{
+
+local c = "echo value" ;
+if $(OS) = VMS { c = "PIPE WRITE SYS$OUTPUT \"value\"" ; }
+
+check-equal shell : "value\n" : [ SHELL $(c) ] ;
+check-equal shell : "" : [ SHELL $(c) : no-output ] ;
+check-equal shell : "value\n" 0 : [ SHELL $(c) : exit-status ] ;
+check-equal shell : "" 0 : [ SHELL $(c) : no-output : exit-status ] ;
+check-equal shell : "" 0 : [ SHELL $(c) : no-output : exit-status : strip-eol ] ;
+check-equal command : "value\n" : [ COMMAND $(c) ] ;
+check-equal command : "" : [ COMMAND $(c) : no-output ] ;
+check-equal command : "value\n" 0 : [ COMMAND $(c) : exit-status ] ;
+check-equal command : "" 0 : [ COMMAND $(c) : no-output : exit-status ] ;
+
+# buffered output
+
+local expected = "When the shell output buffer splits on whitespace, the whitespace shouldn't be trimmed. end." ;
+local buffered = "echo \"$(expected)\"" ;
+if $(OS) = VMS { buffered = "PIPE WRITE SYS$OUTPUT \"$(expected)\"" ; }
+if $(OS) = NT { buffered = "echo $(expected)" ; }
+
+check-equal shell : "$(expected)\n" : [ SHELL $(buffered) ] ;
+check-equal shell : "" : [ SHELL $(buffered) : no-output ] ;
+check-equal shell : "$(expected)\n" 0 : [ SHELL $(buffered) : exit-status ] ;
+check-equal shell : "$(expected)" 0 : [ SHELL $(buffered) : strip-eol : exit-status ] ;
+check-equal shell : "" 0 : [ SHELL $(buffered) : no-output : exit-status ] ;
+check-equal shell : "" 0 : [ SHELL $(buffered) : no-output : exit-status : strip-eol ] ;
+check-equal shell : "$(expected)" 0 : [ SHELL $(buffered) : strip-eol : exit-status ] ;
+
+check-equal command : "$(expected)\n" : [ COMMAND $(buffered) ] ;
+check-equal command : "" : [ COMMAND $(buffered) : no-output ] ;
+check-equal command : "$(expected)\n" 0 : [ COMMAND $(buffered) : exit-status ] ;
+check-equal command : "$(expected)" 0 : [ COMMAND $(buffered) : strip-eol : exit-status ] ;
+check-equal command : "" 0 : [ COMMAND $(buffered) : no-output : exit-status ] ;
+
+}
+
+# Test SUBST
+
+{
+
+# Check that unmatched subst returns an empty list
+check-equal subst-nomatch : [ SUBST "abc" "d+" x ] : ;
+
+# Check that a matched subst works
+check-equal subst-match : [ SUBST "ddd" "d+" x ] : x ;
+
+# Check that we can get multiple substitutions from a single invocation
+check-equal subst-multiple : [ SUBST "x/y/z" "([^/]*)/([^/]*).*" "\\1" $2 "\\1-\\2" ] : x y x-y ;
+
+}
+
+# Test summary
+
+if $(failed) = 0
+{
+ status = 0 ;
+}
+else
+{
+ status = 1 ;
+}
+
+EXIT $(passed) passed $(failed) failed : $(status) ;
diff --git a/src/boost/tools/build/test/core_action_output.py b/src/boost/tools/build/test/core_action_output.py
new file mode 100755
index 000000000..52d5e43e7
--- /dev/null
+++ b/src/boost/tools/build/test/core_action_output.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+# Copyright 2012. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test correct "-p" option handling.
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-d1"], pass_toolset=False)
+
+t.write("file.jam", """\
+prefix = "echo \\"" ;
+suffix = "\\"" ;
+if $(NT)
+{
+ prefix = "(echo " ;
+ suffix = ")" ;
+}
+actions go
+{
+ $(prefix)stdout$(suffix)
+ >&2 $(prefix)stderr$(suffix)
+}
+ECHO "{{{" $(XXX) "}}}" ;
+ALWAYS all ;
+go all ;
+""")
+
+t.run_build_system(["-ffile.jam", "-sXXX=1"], stderr="")
+t.expect_output_lines("{{{ 1 }}}")
+t.expect_output_lines("stdout")
+t.expect_output_lines("stderr")
+t.expect_nothing_more()
+
+t.run_build_system(["-ffile.jam", "-sXXX=2", "-p0"], stderr="")
+t.expect_output_lines("{{{ 2 }}}")
+t.expect_output_lines("stdout")
+t.expect_output_lines("stderr")
+t.expect_nothing_more()
+
+t.run_build_system(["-ffile.jam", "-sXXX=3", "-p1"], stderr="")
+t.expect_output_lines("{{{ 3 }}}")
+t.expect_output_lines("stdout")
+t.expect_output_lines("stderr*", False)
+t.expect_nothing_more()
+
+t.run_build_system(["-ffile.jam", "-sXXX=4", "-p2"], stderr="stderr\n")
+t.expect_output_lines("{{{ 4 }}}")
+t.expect_output_lines("stdout*", False)
+t.expect_output_lines("stderr*", False)
+t.expect_nothing_more()
+
+t.run_build_system(["-ffile.jam", "-sXXX=5", "-p3"], stderr="stderr\n")
+t.expect_output_lines("{{{ 5 }}}")
+t.expect_output_lines("stdout")
+t.expect_output_lines("stderr*", False)
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_action_status.py b/src/boost/tools/build/test/core_action_status.py
new file mode 100755
index 000000000..e9d5966ab
--- /dev/null
+++ b/src/boost/tools/build/test/core_action_status.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+# Copyright 2007 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """\
+actions quietly .a. { $(ACTION) }
+
+rule .a.
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+NOTFILE subtest ;
+.a. subtest_a : subtest ;
+DEPENDS all : subtest_a ;
+""")
+
+t.run_build_system(["-ffile.jam", "-sACTION=invalid"], status=1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_actions_quietly.py b/src/boost/tools/build/test/core_actions_quietly.py
new file mode 100755
index 000000000..a8779d389
--- /dev/null
+++ b/src/boost/tools/build/test/core_actions_quietly.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+# Copyright 2007 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """\
+actions quietly .a.
+{
+echo [$(<:B)] 0
+echo [$(<:B)] 1
+echo [$(<:B)] 2
+}
+
+rule .a.
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+NOTFILE subtest ;
+.a. subtest_a : subtest ;
+.a. subtest_b : subtest ;
+DEPENDS all : subtest_a subtest_b ;
+""")
+
+t.run_build_system(["-ffile.jam", "-d2"], stdout="""\
+...found 4 targets...
+...updating 2 targets...
+.a. subtest_a
+
+echo [subtest_a] 0
+echo [subtest_a] 1
+echo [subtest_a] 2
+
+[subtest_a] 0
+[subtest_a] 1
+[subtest_a] 2
+.a. subtest_b
+
+echo [subtest_b] 0
+echo [subtest_b] 1
+echo [subtest_b] 2
+
+[subtest_b] 0
+[subtest_b] 1
+[subtest_b] 2
+...updated 2 targets...
+""")
+
+t.run_build_system(["-ffile.jam", "-d1"], stdout="""\
+...found 4 targets...
+...updating 2 targets...
+...updated 2 targets...
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_arguments.py b/src/boost/tools/build/test/core_arguments.py
new file mode 100755
index 000000000..a743cfae0
--- /dev/null
+++ b/src/boost/tools/build/test/core_arguments.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+
+# Copyright 2001 Dave Abrahams
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+
+def simple_args(start, finish):
+ return " : ".join("%d" % x for x in range(start, finish + 1))
+
+
+def test(t, type, input, output, status=0):
+ code = ["include echo_args.jam ; echo_%s" % type]
+ if input: code.append(input)
+ code.append(";")
+ t.write("file.jam", " ".join(code))
+ t.run_build_system(["-ffile.jam"], status=status)
+ t.expect_output_lines(output)
+
+
+def test_args(t, *args, **kwargs):
+ test(t, "args", *args, **kwargs)
+
+
+def test_varargs(t, *args, **kwargs):
+ test(t, "varargs", *args, **kwargs)
+
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("echo_args.jam", """\
+NOCARE all ;
+
+rule echo_args ( a b ? c ? : d + : e * )
+{
+ ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e) ;
+}
+
+rule echo_varargs ( a b ? c ? : d + : e * : * )
+{
+ ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e)
+ ": rest= "$(4[1]) $(4[2-])
+ ": "$(5[1]) $(5[2-]) ": "$(6[1]) $(6[2-]) ": "$(7[1]) $(7[2-])
+ ": "$(8[1]) $(8[2-]) ": "$(9[1]) $(9[2-]) ": "$(10[1]) $(10[2-])
+ ": "$(11[1]) $(11[2-]) ": "$(12[1]) $(12[2-]) ": "$(13[1]) $(13[2-])
+ ": "$(14[1]) $(14[2-]) ": "$(15[1]) $(15[2-]) ": "$(16[1]) $(16[2-])
+ ": "$(17[1]) $(17[2-]) ": "$(18[1]) $(18[2-]) ": "$(19[1]) $(19[2-])
+ ": "$(20[1]) $(20[2-]) ": "$(21[1]) $(21[2-]) ": "$(22[1]) $(22[2-])
+ ": "$(23[1]) $(23[2-]) ": "$(24[1]) $(24[2-]) ": "$(25[1]) $(25[2-]) ;
+}
+""")
+
+test_args(t, "", "* missing argument a", status=1)
+test_args(t, "1 2 : 3 : 4 : 5", "* extra argument 5", status=1)
+test_args(t, "a b c1 c2 : d", "* extra argument c2", status=1)
+
+# Check modifier '?'
+test_args(t, "1 2 3 : 4", "a= 1 b= 2 c= 3 : d= 4 : e=")
+test_args(t, "1 2 : 3", "a= 1 b= 2 c= : d= 3 : e=")
+test_args(t, "1 2 : 3", "a= 1 b= 2 c= : d= 3 : e=")
+test_args(t, "1 : 2", "a= 1 b= c= : d= 2 : e=")
+
+# Check modifier '+'
+test_args(t, "1", "* missing argument d", status=1)
+test_args(t, "1 : 2 3", "a= 1 b= c= : d= 2 3 : e=")
+test_args(t, "1 : 2 3 4", "a= 1 b= c= : d= 2 3 4 : e=")
+
+# Check modifier '*'
+test_args(t, "1 : 2 : 3", "a= 1 b= c= : d= 2 : e= 3")
+test_args(t, "1 : 2 : 3 4", "a= 1 b= c= : d= 2 : e= 3 4")
+test_args(t, "1 : 2 : 3 4 5", "a= 1 b= c= : d= 2 : e= 3 4 5")
+
+# Check varargs
+test_varargs(t, "1 : 2 : 3 4 5", "a= 1 b= c= : d= 2 : e= 3 4 5")
+test_varargs(t, "1 : 2 : 3 4 5 : 6", "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6")
+test_varargs(t, "1 : 2 : 3 4 5 : 6 7",
+ "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7")
+test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8",
+ "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8")
+test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8 : 9",
+ "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8 : 9")
+test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : "
+ "16 : 17 : 18 : 19a 19b", "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8 : "
+ "9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19a 19b")
+test_varargs(t, "1 : 2 : 3 4 5 : 6 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : "
+ "16 : 17 : 18 : 19a 19b 19c : 20", "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= "
+ "6 7 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19a 19b 19c : "
+ "20")
+
+# Check varargs upper limit
+expected = "a= 1 b= c= : d= 2 : e= 3 : rest= " + simple_args(4, 19)
+test_varargs(t, simple_args(1, 19), expected)
+test_varargs(t, simple_args(1, 19) + " 19b 19c 19d", expected + " 19b 19c 19d")
+'''FIXME: 19 (=LOL_MAX) args is the limit
+test_varargs(t, simple_args(1, 19) + " 19b 19c 19d : 20", expected + " 19b "
+ "19c 19d")
+test_varargs(t, simple_args(1, 20), expected)
+test_varargs(t, simple_args(1, 50), expected)
+'''
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_at_file.py b/src/boost/tools/build/test/core_at_file.py
new file mode 100755
index 000000000..b8caf2bab
--- /dev/null
+++ b/src/boost/tools/build/test/core_at_file.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+# Copyright 2011 Steven Watanabe
+# Copyright 2020 Rene Ferdinand Rivera Morell
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
+
+t.write("file.jam", """\
+name = n1 n2 ;
+contents = M1 M2 ;
+EXIT "file:" "@(o$(name:J=) .txt:E= test -D$(contents))" : 0 ;
+""")
+
+t.run_build_system()
+t.expect_output_lines("file: on1n2 .txt")
+t.expect_addition("on1n2 .txt")
+t.expect_content("on1n2 .txt", " test -DM1 -DM2", True)
+
+t.rm(".")
+
+t.write("file.jam", """\
+name = n1 n2 ;
+contents = M1 M2 ;
+actions run { echo file: "@(o$(name:J=) .txt:E= test -D$(contents))" }
+run all ;
+""")
+
+t.run_build_system(["-d2"])
+t.expect_output_lines(' echo file: "on1n2 .txt" ')
+t.expect_addition("on1n2 .txt")
+t.expect_content("on1n2 .txt", " test -DM1 -DM2", True)
+
+t.rm(".")
+
+t.write("file.jam", """\
+name = n1 n2 ;
+contents = M1 M2 ;
+file = "@($(STDOUT):E= test -D$(contents)\n)" ;
+actions run { $(file) }
+run all ;
+""")
+
+t.run_build_system(["-d1"])
+t.expect_output_lines(" test -DM1 -DM2")
+
+t.rm(".")
+
+t.write("file.jam", """\
+name = n1 n2 ;
+contents = M1 M2 ;
+actions run { @($(STDOUT):E= test -D$(contents)\n) }
+run all ;
+""")
+
+t.run_build_system(["-d1"])
+t.expect_output_lines(" test -DM1 -DM2")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_bindrule.py b/src/boost/tools/build/test/core_bindrule.py
new file mode 100755
index 000000000..bd1682bf5
--- /dev/null
+++ b/src/boost/tools/build/test/core_bindrule.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+# Copyright 2001 Dave Abrahams
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+
+t = BoostBuild.Tester(["-d1"], pass_toolset=0)
+
+t.write("subdir1/file-to-bind", "# This file intentionally left blank")
+
+t.write("file.jam", """\
+rule do-nothing ( target : source )
+{
+ DEPENDS $(target) : $(source) ;
+}
+actions quietly do-nothing { }
+
+# Make a non-file target which depends on a file that exists
+NOTFILE fake-target ;
+SEARCH on file-to-bind = subdir1 ;
+
+do-nothing fake-target : file-to-bind ;
+
+# Set jam up to call our bind-rule
+BINDRULE = bind-rule ;
+
+rule bind-rule ( target : path )
+{
+ ECHO "found:" $(target) at $(path) ;
+}
+
+DEPENDS all : fake-target ;
+""")
+
+t.run_build_system(["-ffile.jam"], stdout="""\
+found: all at all
+found: file-to-bind at subdir1%sfile-to-bind
+...found 3 targets...
+""" % os.sep)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_d12.py b/src/boost/tools/build/test/core_d12.py
new file mode 100644
index 000000000..c330b77c7
--- /dev/null
+++ b/src/boost/tools/build/test/core_d12.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests correct handling of "-d1" and "-d2" options.
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
+
+t.write("file.jam", """\
+actions a { }
+actions quietly b { }
+ALWAYS all ;
+a all ;
+b all ;
+""")
+
+t.run_build_system(["-d0"], stdout="")
+
+t.run_build_system(["-d1"])
+t.expect_output_lines("a all")
+t.expect_output_lines("b all", False)
+
+t.run_build_system(["-d2"])
+t.expect_output_lines("a all")
+t.expect_output_lines("b all")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_delete_module.py b/src/boost/tools/build/test/core_delete_module.py
new file mode 100644
index 000000000..6b3bda566
--- /dev/null
+++ b/src/boost/tools/build/test/core_delete_module.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the facilities for deleting modules.
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """
+module foo
+{
+ rule bar { }
+ var = x y ;
+}
+DELETE_MODULE foo ;
+if [ RULENAMES foo ]
+{
+ EXIT DELETE_MODULE failed to kill foo's rules: [ RULENAMES foo ] ;
+}
+
+module foo
+{
+ if $(var)
+ {
+ EXIT DELETE_MODULE failed to kill foo's variables ;
+ }
+
+ rule bar { }
+ var = x y ;
+
+ DELETE_MODULE foo ;
+
+ if $(var)
+ {
+ EXIT internal DELETE_MODULE failed to kill foo's variables ;
+ }
+ if [ RULENAMES foo ]
+ {
+ EXIT internal DELETE_MODULE failed to kill foo's rules: [ RULENAMES foo ] ;
+ }
+}
+DEPENDS all : xx ;
+NOTFILE xx ;
+""")
+
+t.run_build_system(["-ffile.jam"], status=0)
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_dependencies.py b/src/boost/tools/build/test/core_dependencies.py
new file mode 100644
index 000000000..c8aca1592
--- /dev/null
+++ b/src/boost/tools/build/test/core_dependencies.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests correct handling of dependencies, specifically, on generated
+# sources, and from generated sources.
+
+import BoostBuild
+
+import string
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("core-dependency-helpers", """
+rule hdrrule
+{
+ INCLUDES $(1) : $(2) ;
+}
+actions copy
+{
+ cp $(>) $(<) || copy $(>) $(<)
+}
+""")
+
+code = """include core-dependency-helpers ;
+DEPENDS all : a ;
+DEPENDS a : b ;
+
+actions create-b
+{
+ echo "#include <foo.h>" > $(<)
+}
+copy a : b ;
+create-b b ;
+HDRRULE on b foo.h bar.h = hdrrule ;
+HDRSCAN on b foo.h bar.h = \"#include <(.*)>\" ;
+"""
+
+# This creates 'a' which depends on 'b', which is generated. The generated 'b'
+# contains '#include <foo.h>' and no rules for foo.h are given. The system
+# should error out on the first invocation.
+t.run_build_system(["-f-"], status=1, stdin=code)
+t.fail_test(t.stdout().find("...skipped a for lack of foo.h...") == -1)
+
+t.rm('b')
+
+# Now test that if target 'c' also depends on 'b', then it will not be built, as
+# well.
+t.run_build_system(["-f-"], status=1, stdin=code + " copy c : b ; DEPENDS c : b ; DEPENDS all : c ; ")
+t.fail_test(t.stdout().find("...skipped c for lack of foo.h...") == -1)
+
+t.rm('b')
+
+# Now add a rule for creating foo.h.
+code += """
+actions create-foo
+{
+ echo // > $(<)
+}
+create-foo foo.h ;
+"""
+t.run_build_system(["-f-"], stdin=code)
+
+# Run two times, adding explicit dependency from all to foo.h at the beginning
+# and at the end, to make sure that foo.h is generated before 'a' in all cases.
+
+def mk_correct_order_func(s1, s2):
+ def correct_order(s):
+ n1 = s.find(s1)
+ n2 = s.find(s2)
+ return ( n1 != -1 ) and ( n2 != -1 ) and ( n1 < n2 )
+ return correct_order
+
+correct_order = mk_correct_order_func("create-foo", "copy a")
+
+t.rm(["a", "b", "foo.h"])
+t.run_build_system(["-d+2", "-f-"], stdin=code + " DEPENDS all : foo.h ;")
+t.fail_test(not correct_order(t.stdout()))
+
+t.rm(["a", "b", "foo.h"])
+t.run_build_system(["-d+2", "-f-"], stdin=" DEPENDS all : foo.h ; " + code)
+t.fail_test(not correct_order(t.stdout()))
+
+# Now foo.h exists. Test include from b -> foo.h -> bar.h -> biz.h. b and foo.h
+# already have updating actions.
+t.rm(["a", "b"])
+t.write("foo.h", "#include <bar.h>")
+t.write("bar.h", "#include <biz.h>")
+t.run_build_system(["-d+2", "-f-"], status=1, stdin=code)
+t.fail_test(t.stdout().find("...skipped a for lack of biz.h...") == -1)
+
+# Add an action for biz.h.
+code += """
+actions create-biz
+{
+ echo // > $(<)
+}
+create-biz biz.h ;
+"""
+
+t.rm(["b"])
+correct_order = mk_correct_order_func("create-biz", "copy a")
+t.run_build_system(["-d+2", "-f-"], stdin=code + " DEPENDS all : biz.h ;")
+t.fail_test(not correct_order(t.stdout()))
+
+t.rm(["a", "biz.h"])
+t.run_build_system(["-d+2", "-f-"], stdin=" DEPENDS all : biz.h ; " + code)
+t.fail_test(not correct_order(t.stdout()))
+
+t.write("a", "")
+
+code="""
+DEPENDS all : main d ;
+
+actions copy
+{
+ cp $(>) $(<) || copy $(>) $(<)
+}
+
+DEPENDS main : a ;
+copy main : a ;
+
+INCLUDES a : <1>c ;
+
+NOCARE <1>c ;
+SEARCH on <1>c = . ;
+
+actions create-c
+{
+ echo d > $(<)
+}
+
+actions create-d
+{
+ echo // > $(<)
+}
+
+create-c <2>c ;
+LOCATE on <2>c = . ;
+create-d d ;
+
+HDRSCAN on <1>c = (.*) ;
+HDRRULE on <1>c = hdrrule ;
+
+rule hdrrule
+{
+ INCLUDES $(1) : d ;
+}
+"""
+
+correct_order = mk_correct_order_func("create-d", "copy main")
+t.run_build_system(["-d2", "-f-"], stdin=code)
+t.fail_test(not correct_order(t.stdout()))
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_fail_expected.py b/src/boost/tools/build/test/core_fail_expected.py
new file mode 100644
index 000000000..1d4be557b
--- /dev/null
+++ b/src/boost/tools/build/test/core_fail_expected.py
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+
+# Copyright 2017 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+def test_basic():
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+ actions fail
+ {
+ invalid-dd0eeb5899734622
+ }
+
+ FAIL_EXPECTED t1 ;
+ fail t1 ;
+
+ UPDATE t1 ;
+ """)
+
+ t.run_build_system(["-ffile.jam"])
+ t.expect_output_lines("...failed*", False)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_error():
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+ actions pass
+ {
+ echo okay >$(<)
+ }
+
+ FAIL_EXPECTED t1 ;
+ pass t1 ;
+
+ UPDATE t1 ;
+ """)
+
+ t.run_build_system(["-ffile.jam"], status=1)
+ t.expect_output_lines("...failed pass t1...")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_multiple_actions():
+ """FAIL_EXPECTED targets are considered to pass if the first
+ updating action fails. Further actions will be skipped."""
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+ actions fail
+ {
+ invalid-dd0eeb5899734622
+ }
+
+ actions pass
+ {
+ echo okay >$(<)
+ }
+
+ FAIL_EXPECTED t1 ;
+ fail t1 ;
+ pass t1 ;
+
+ UPDATE t1 ;
+ """)
+
+ t.run_build_system(["-ffile.jam", "-d1"])
+ t.expect_output_lines("...failed*", False)
+ t.expect_output_lines("pass t1", False)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_quitquick():
+ """Tests that FAIL_EXPECTED targets do not cause early exit
+ on failure."""
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+ actions fail
+ {
+ invalid-dd0eeb5899734622
+ }
+
+ actions pass
+ {
+ echo okay >$(<)
+ }
+
+ FAIL_EXPECTED t1 ;
+ fail t1 ;
+
+ pass t2 ;
+
+ UPDATE t1 t2 ;
+ """)
+
+ t.run_build_system(["-ffile.jam", "-q", "-d1"])
+ t.expect_output_lines("pass t2")
+ t.expect_addition("t2")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_quitquick_error():
+ """FAIL_EXPECTED targets should cause early exit if they unexpectedly pass."""
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+ actions pass
+ {
+ echo okay >$(<)
+ }
+
+ FAIL_EXPECTED t1 ;
+ pass t1 ;
+ pass t2 ;
+
+ UPDATE t1 t2 ;
+ """)
+
+ t.run_build_system(["-ffile.jam", "-q", "-d1"], status=1)
+ t.expect_output_lines("pass t2", False)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+test_basic()
+test_error()
+test_multiple_actions()
+test_quitquick()
+test_quitquick_error()
diff --git a/src/boost/tools/build/test/core_import_module.py b/src/boost/tools/build/test/core_import_module.py
new file mode 100644
index 000000000..1fd0c650b
--- /dev/null
+++ b/src/boost/tools/build/test/core_import_module.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("code", """\
+module a
+{
+ rule r1 ( )
+ {
+ ECHO R1 ;
+ }
+
+ local rule l1 ( )
+ {
+ ECHO A.L1 ;
+ }
+}
+module a2
+{
+ rule r2 ( )
+ {
+ ECHO R2 ;
+ }
+}
+IMPORT a2 : r2 : : a2.r2 ;
+
+rule a.l1 ( )
+{
+ ECHO L1 ;
+}
+
+module b
+{
+ IMPORT_MODULE a : b ;
+ rule test
+ {
+ # Call rule visible via IMPORT_MODULE
+ a.r1 ;
+ # Call rule in global scope
+ a2.r2 ;
+ # Call rule in global scope. Doesn't find local rule
+ a.l1 ;
+ # Make l1 visible
+ EXPORT a : l1 ;
+ a.l1 ;
+ }
+}
+
+IMPORT b : test : : test ;
+test ;
+
+module c
+{
+ rule test
+ {
+ ECHO CTEST ;
+ }
+}
+
+IMPORT_MODULE c : ;
+c.test ;
+
+EXIT : 0 ;
+""")
+
+t.run_build_system(["-fcode"], stdout="""\
+R1
+R2
+L1
+A.L1
+CTEST
+
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_jamshell.py b/src/boost/tools/build/test/core_jamshell.py
new file mode 100644
index 000000000..b1886d0f9
--- /dev/null
+++ b/src/boost/tools/build/test/core_jamshell.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+
+# Copyright 2014 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import sys
+
+t = BoostBuild.Tester(pass_toolset=False)
+
+t.write("file.jam", """
+actions run {
+ $(ACTION)
+}
+
+# Raw commands only work on Windows
+if $(OS) = NT
+{
+ JAMSHELL on test-raw = % ;
+ JAMSHELL on test-raw-fail = % ;
+}
+ACTION on test-raw = "\"$(PYTHON)\" -V" ;
+run test-raw ;
+
+ACTION on test-raw-fail = missing-executable ;
+run test-raw-fail ;
+
+# On Windows, the command is stored in a temporary
+# file. On other systems it is passed directly.
+if $(OS) = NT
+{
+ JAMSHELL on test-py = $(PYTHON) ;
+}
+else
+{
+ JAMSHELL on test-py = $(PYTHON) -c ;
+}
+ACTION on test-py = "
+from __future__ import print_function
+print(\\\",\\\".join([str(x) for x in range(3)]))
+" ;
+run test-py ;
+
+DEPENDS all : test-raw test-raw-fail test-py ;
+""")
+
+t.run_build_system(["-ffile.jam", "-d1", "-sPYTHON=" + sys.executable], status=1)
+t.expect_output_lines([
+ "...failed run test-raw-fail...",
+ "0,1,2",
+ "...failed updating 1 target...",
+ "...updated 2 targets..."])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_language.py b/src/boost/tools/build/test/core_language.py
new file mode 100755
index 000000000..66875c27c
--- /dev/null
+++ b/src/boost/tools/build/test/core_language.py
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+t.set_tree("core-language")
+t.run_build_system(["-ftest.jam"])
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_modifiers.py b/src/boost/tools/build/test/core_modifiers.py
new file mode 100644
index 000000000..37cb933a3
--- /dev/null
+++ b/src/boost/tools/build/test/core_modifiers.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the "existing" and "updated" modifiers on actions.
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+code = """
+DEPENDS all : a ;
+ALWAYS a ;
+NOTFILE a ;
+
+actions existing make-a
+{
+ echo $(>) > list
+}
+make-a a : a-1 a-2 a-3 ;
+DEPENDS a : a-1 a-2 a-3 ;
+NOCARE a-1 a-2 ;
+
+actions make-a3
+{
+ echo foo > $(<)
+}
+make-a3 a-3 ;
+"""
+
+t.write("file.jam", code)
+t.write("a-1", "")
+
+t.run_build_system(["-ffile.jam"])
+t.fail_test(t.read("list").strip() != "a-1")
+t.rm(["a-3", "list"])
+
+code = code.replace("existing", "updated")
+t.write("file.jam", code)
+t.run_build_system(["-ffile.jam"])
+t.fail_test(t.read("list").strip() != "a-3")
+
+code = code.replace("updated", "existing updated")
+t.write("file.jam", code)
+t.run_build_system(["-ffile.jam"])
+t.fail_test(t.read("list").strip() != "a-1 a-3")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_multifile_actions.py b/src/boost/tools/build/test/core_multifile_actions.py
new file mode 100755
index 000000000..d44d53cb0
--- /dev/null
+++ b/src/boost/tools/build/test/core_multifile_actions.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+
+# Copyright 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that actions that produce multiple targets are handled
+# correctly. The rules are as follows:
+#
+# - If any action that updates a target is run, then the target
+# is considered to be out-of-date and all of its updating actions
+# are run in order.
+# - A target is considered updated when all of its updating actions
+# have completed successfully.
+# - If any updating action for a target fails, then the remaining
+# actions are skipped and the target is marked as failed.
+#
+# Note that this is a more thorough test case for the same
+# problem that core_parallel_multifile_actions_N.py checks for.
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-d1"], pass_toolset=0)
+
+t.write("file.jam", """
+actions update
+{
+ echo updating $(<)
+}
+
+update x1 x2 ;
+update x2 x3 ;
+""")
+
+# Updating x1 should force x2 to update as well.
+t.run_build_system(["-ffile.jam", "x1"], stdout="""\
+...found 3 targets...
+...updating 3 targets...
+update x1
+updating x1 x2
+update x2
+updating x2 x3
+...updated 3 targets...
+""")
+
+# If x1 is up-to-date, we don't need to update x2,
+# even though x2 is missing.
+t.write("x1", "")
+t.run_build_system(["-ffile.jam", "x1"], stdout="""\
+...found 1 target...
+""")
+
+# Building x3 should update x1 and x2, even though
+# x1 would be considered up-to-date, taken alone.
+t.run_build_system(["-ffile.jam", "x3"], stdout="""\
+...found 3 targets...
+...updating 2 targets...
+update x1
+updating x1 x2
+update x2
+updating x2 x3
+...updated 3 targets...
+""")
+
+# Updating x2 should succeed, but x3 should be skipped
+t.rm("x1")
+t.write("file.jam", """\
+actions update
+{
+ echo updating $(<)
+}
+actions fail
+{
+ echo failed $(<)
+ exit 1
+}
+
+update x1 x2 ;
+fail x1 ;
+update x1 x3 ;
+update x2 ;
+update x3 ;
+""")
+
+t.run_build_system(["-ffile.jam", "x3"], status=1, stdout="""\
+...found 3 targets...
+...updating 3 targets...
+update x1
+updating x1 x2
+fail x1
+failed x1
+
+ echo failed x1
+ exit 1
+
+...failed fail x1...
+update x2
+updating x2
+...failed updating 2 targets...
+...updated 1 target...
+""")
+
+# Make sure that dependencies of targets that are
+# updated as a result of a multifile action are
+# processed correctly.
+t.rm("x1")
+t.write("file.jam", """\
+actions update
+{
+ echo updating $(<)
+}
+
+update x1 ;
+update x2 ;
+DEPENDS x2 : x1 ;
+update x2 x3 ;
+""")
+t.run_build_system(["-ffile.jam", "x3"], stdout="""\
+...found 3 targets...
+...updating 3 targets...
+update x1
+updating x1
+update x2
+updating x2
+update x2
+updating x2 x3
+...updated 3 targets...
+""")
+
+# JAM_SEMAPHORE rules:
+#
+# - if two updating actions have targets that share a semaphore,
+# these actions cannot be run in parallel.
+#
+t.write("file.jam", """\
+actions update
+{
+ echo updating $(<)
+}
+
+targets = x1 x2 ;
+JAM_SEMAPHORE on $(targets) = <s>update_sem ;
+update x1 x2 ;
+""")
+t.run_build_system(["-ffile.jam", "x1"], stdout="""\
+...found 2 targets...
+...updating 2 targets...
+update x1
+updating x1 x2
+...updated 2 targets...
+""")
+
+# A target can appear multiple times in an action
+t.write("file.jam", """\
+actions update
+{
+ echo updating $(<)
+}
+
+update x1 x1 ;
+""")
+t.run_build_system(["-ffile.jam", "x1"], stdout="""\
+...found 1 target...
+...updating 1 target...
+update x1
+updating x1 x1
+...updated 1 target...
+""")
+
+# Together actions should check that all the targets are the same
+# before combining.
+t.write("file.jam", """\
+actions together update
+{
+ echo updating $(<) : $(>)
+}
+
+update x1 x2 : s1 ;
+update x1 x2 : s2 ;
+
+update x3 : s3 ;
+update x3 x4 : s4 ;
+update x4 x3 : s5 ;
+DEPENDS all : x1 x2 x3 x4 ;
+""")
+t.run_build_system(["-ffile.jam"], stdout="""\
+...found 5 targets...
+...updating 4 targets...
+update x1
+updating x1 x2 : s1 s2
+update x3
+updating x3 : s3
+update x3
+updating x3 x4 : s4
+update x4
+updating x4 x3 : s5
+...updated 4 targets...
+""")
+
+
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_nt_cmd_line.py b/src/boost/tools/build/test/core_nt_cmd_line.py
new file mode 100755
index 000000000..5796922cf
--- /dev/null
+++ b/src/boost/tools/build/test/core_nt_cmd_line.py
@@ -0,0 +1,266 @@
+#!/usr/bin/python
+
+# Copyright 2001 Dave Abrahams
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests Windows command line construction.
+#
+# Note that the regular 'echo' is an internal shell command on Windows and
+# therefore can not be called directly as a standalone Windows process.
+
+import BoostBuild
+import os
+import re
+import sys
+
+
+executable = sys.executable.replace("\\", "/")
+if " " in executable:
+ executable = '"%s"' % executable
+
+
+def string_of_length(n):
+ if n <= 0:
+ return ""
+ n -= 1
+ y = ['', '$(1x10-1)', '$(10x10-1)', '$(100x10-1)', '$(1000x10-1)']
+ result = []
+ for i in reversed(range(5)):
+ x, n = divmod(n, 10 ** i)
+ result += [y[i]] * x
+ result.append('x')
+ return " ".join(result)
+
+
+# Boost Jam currently does not allow preparing actions with completely empty
+# content and always requires at least a single whitespace after the opening
+# brace in order to satisfy its Boost Jam language grammar rules.
+def test_raw_empty():
+ whitespace_in = " \n\n\r\r\v\v\t\t \t \r\r \n\n"
+
+ # We tell the testing system to read its child process output as raw
+ # binary data but the bjam process we run will read its input file and
+ # write out its output as text, i.e. convert all of our "\r\n" sequences to
+ # "\n" on input and all of its "\n" characters back to "\r\n" on output.
+ # This means that any lone "\n" input characters not preceded by "\r" will
+ # get an extra "\r" added in front of it on output.
+ whitespace_out = whitespace_in.replace("\r\n", "\n").replace("\n", "\r\n")
+
+ t = BoostBuild.Tester(["-d2", "-d+4"], pass_toolset=0,
+ use_test_config=False)
+ t.write("file.jam", """\
+actions do_empty {%s}
+JAMSHELL = %% ;
+do_empty all ;
+""" % (whitespace_in))
+ t.run_build_system(["-ffile.jam"], universal_newlines=False)
+ t.expect_output_lines("do_empty all")
+ t.expect_output_lines("Executing raw command directly", False)
+ if "\r\n%s\r\n" % whitespace_out not in t.stdout():
+ BoostBuild.annotation("failure", "Whitespace action content not found "
+ "on stdout.")
+ t.fail_test(1, dump_difference=False)
+ t.cleanup()
+
+
+def test_raw_nt(n=None, error=False):
+ t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0,
+ use_test_config=False)
+
+ cmd_prefix = "%s -c \"print('XXX: " % executable
+ cmd_suffix = "')\""
+ cmd_extra_length = len(cmd_prefix) + len(cmd_suffix)
+
+ if n == None:
+ n = cmd_extra_length
+
+ data_length = n - cmd_extra_length
+ if data_length < 0:
+ BoostBuild.annotation("failure", """\
+Can not construct Windows command of desired length. Requested command length
+too short for the current test configuration.
+ Requested command length: %d
+ Minimal supported command length: %d
+""" % (n, cmd_extra_length))
+ t.fail_test(1, dump_difference=False)
+
+ # Each $(Xx10-1) variable contains X words of 9 characters each, which,
+ # including spaces between words, brings the total number of characters in
+ # its string representation to X * 10 - 1 (X * 9 characters + (X - 1)
+ # spaces).
+ t.write("file.jam", """\
+ten = 0 1 2 3 4 5 6 7 8 9 ;
+
+1x10-1 = 123456789 ;
+10x10-1 = $(ten)12345678 ;
+100x10-1 = $(ten)$(ten)1234567 ;
+1000x10-1 = $(ten)$(ten)$(ten)123456 ;
+
+actions do_echo
+{
+ %s%s%s
+}
+JAMSHELL = %% ;
+do_echo all ;
+""" % (cmd_prefix, string_of_length(data_length), cmd_suffix))
+ if error:
+ expected_status = 1
+ else:
+ expected_status = 0
+ t.run_build_system(["-ffile.jam"], status=expected_status)
+ if error:
+ t.expect_output_lines("Executing raw command directly", False)
+ t.expect_output_lines("do_echo action is too long (%d, max 32766):" % n
+ )
+ t.expect_output_lines("XXX: *", False)
+ else:
+ t.expect_output_lines("Executing raw command directly")
+ t.expect_output_lines("do_echo action is too long*", False)
+
+ m = re.search("^XXX: (.*)$", t.stdout(), re.MULTILINE)
+ if not m:
+ BoostBuild.annotation("failure", "Expected output line starting "
+ "with 'XXX: ' not found.")
+ t.fail_test(1, dump_difference=False)
+ if len(m.group(1)) != data_length:
+ BoostBuild.annotation("failure", """Unexpected output data length.
+ Expected: %d
+ Received: %d""" % (n, len(m.group(1))))
+ t.fail_test(1, dump_difference=False)
+
+ t.cleanup()
+
+
+def test_raw_to_shell_fallback_nt():
+ t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0,
+ use_test_config=False)
+
+ cmd_prefix = '%s -c print(' % executable
+ cmd_suffix = ')'
+
+ t.write("file_multiline.jam", """\
+actions do_multiline
+{
+ echo one
+
+
+ echo two
+}
+JAMSHELL = % ;
+do_multiline all ;
+""")
+ t.run_build_system(["-ffile_multiline.jam"])
+ t.expect_output_lines("do_multiline all")
+ t.expect_output_lines("one")
+ t.expect_output_lines("two")
+ t.expect_output_lines("Executing raw command directly", False)
+ t.expect_output_lines("Executing using a command file and the shell: "
+ "cmd.exe /Q/C")
+
+ t.write("file_redirect.jam", """\
+actions do_redirect { echo one > two.txt }
+JAMSHELL = % ;
+do_redirect all ;
+""")
+ t.run_build_system(["-ffile_redirect.jam"])
+ t.expect_output_lines("do_redirect all")
+ t.expect_output_lines("one", False)
+ t.expect_output_lines("Executing raw command directly", False)
+ t.expect_output_lines("Executing using a command file and the shell: "
+ "cmd.exe /Q/C")
+ t.expect_addition("two.txt")
+
+ t.write("file_pipe.jam", """\
+actions do_pipe
+{
+ echo one | echo two
+}
+JAMSHELL = % ;
+do_pipe all ;
+""")
+ t.run_build_system(["-ffile_pipe.jam"])
+ t.expect_output_lines("do_pipe all")
+ t.expect_output_lines("one*", False)
+ t.expect_output_lines("two")
+ t.expect_output_lines("Executing raw command directly", False)
+ t.expect_output_lines("Executing using a command file and the shell: "
+ "cmd.exe /Q/C")
+
+ t.write("file_single_quoted.jam", """\
+actions do_single_quoted { %s'5>10'%s }
+JAMSHELL = %% ;
+do_single_quoted all ;
+""" % (cmd_prefix, cmd_suffix))
+ t.run_build_system(["-ffile_single_quoted.jam"])
+ t.expect_output_lines("do_single_quoted all")
+ t.expect_output_lines("5>10")
+ t.expect_output_lines("Executing raw command directly")
+ t.expect_output_lines("Executing using a command file and the shell: "
+ "cmd.exe /Q/C", False)
+ t.expect_nothing_more()
+
+ t.write("file_double_quoted.jam", """\
+actions do_double_quoted { %s"5>10"%s }
+JAMSHELL = %% ;
+do_double_quoted all ;
+""" % (cmd_prefix, cmd_suffix))
+ t.run_build_system(["-ffile_double_quoted.jam"])
+ t.expect_output_lines("do_double_quoted all")
+ # The difference between this example and the similar previous one using
+ # single instead of double quotes stems from how the used Python executable
+ # parses the command-line string received from Windows.
+ t.expect_output_lines("False")
+ t.expect_output_lines("Executing raw command directly")
+ t.expect_output_lines("Executing using a command file and the shell: "
+ "cmd.exe /Q/C", False)
+ t.expect_nothing_more()
+
+ t.write("file_escaped_quote.jam", """\
+actions do_escaped_quote { %s\\"5>10\\"%s }
+JAMSHELL = %% ;
+do_escaped_quote all ;
+""" % (cmd_prefix, cmd_suffix))
+ t.run_build_system(["-ffile_escaped_quote.jam"])
+ t.expect_output_lines("do_escaped_quote all")
+ t.expect_output_lines("5>10")
+ t.expect_output_lines("Executing raw command directly", False)
+ t.expect_output_lines("Executing using a command file and the shell: "
+ "cmd.exe /Q/C")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+if os.name == 'nt':
+ test_raw_empty()
+
+ # Can not test much shorter lengths as the shortest possible command line
+ # line length constructed in this depends on the runtime environment, e.g.
+ # path to the Panther executable running this test.
+ test_raw_nt()
+ test_raw_nt(255)
+ test_raw_nt(1000)
+ test_raw_nt(8000)
+ test_raw_nt(8191)
+ test_raw_nt(8192)
+ test_raw_nt(10000)
+ test_raw_nt(30000)
+ test_raw_nt(32766)
+ # CreateProcessA() Windows API places a limit of 32768 on the allowed
+ # command-line length, including a trailing Unicode (2-byte) nul-terminator
+ # character.
+ test_raw_nt(32767, error=True)
+ test_raw_nt(40000, error=True)
+ test_raw_nt(100001, error=True)
+
+ test_raw_to_shell_fallback_nt() \ No newline at end of file
diff --git a/src/boost/tools/build/test/core_option_d2.py b/src/boost/tools/build/test/core_option_d2.py
new file mode 100755
index 000000000..fbd57362a
--- /dev/null
+++ b/src/boost/tools/build/test/core_option_d2.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+
+# Copyright 2007 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """\
+actions .a.
+{
+echo [$(<:B)] 0
+echo [$(<:B)] 1
+echo [$(<:B)] 2
+}
+
+rule .a.
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+NOTFILE subtest ;
+.a. subtest_a : subtest ;
+.a. subtest_b : subtest ;
+DEPENDS all : subtest_a subtest_b ;
+""")
+
+t.run_build_system(["-ffile.jam", "-d2"], stdout="""\
+...found 4 targets...
+...updating 2 targets...
+.a. subtest_a
+
+echo [subtest_a] 0
+echo [subtest_a] 1
+echo [subtest_a] 2
+
+[subtest_a] 0
+[subtest_a] 1
+[subtest_a] 2
+.a. subtest_b
+
+echo [subtest_b] 0
+echo [subtest_b] 1
+echo [subtest_b] 2
+
+[subtest_b] 0
+[subtest_b] 1
+[subtest_b] 2
+...updated 2 targets...
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_option_l.py b/src/boost/tools/build/test/core_option_l.py
new file mode 100755
index 000000000..e35afe819
--- /dev/null
+++ b/src/boost/tools/build/test/core_option_l.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+# Copyright 2007 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("sleep.bat", """\
+::@timeout /T %1 /NOBREAK >nul
+@ping 127.0.0.1 -n 2 -w 1000 >nul
+@ping 127.0.0.1 -n %1 -w 1000 >nul
+@exit /B 0
+""")
+
+t.write("file.jam", """\
+if $(NT)
+{
+ SLEEP = @call sleep.bat ;
+}
+else
+{
+ SLEEP = sleep ;
+}
+
+actions .a. {
+echo 001
+$(SLEEP) 4
+echo 002
+}
+
+.a. sleeper ;
+
+DEPENDS all : sleeper ;
+""")
+
+t.run_build_system(["-ffile.jam", "-d1", "-l2"], status=1)
+t.expect_output_lines("2 second time limit exceeded")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_option_n.py b/src/boost/tools/build/test/core_option_n.py
new file mode 100755
index 000000000..bfdccefdd
--- /dev/null
+++ b/src/boost/tools/build/test/core_option_n.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Copyright 2007 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """\
+actions .a.
+{
+echo [$(<:B)] 0
+echo [$(<:B)] 1
+echo [$(<:B)] 2
+}
+
+rule .a.
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+NOTFILE subtest ;
+.a. subtest_a : subtest ;
+.a. subtest_b : subtest ;
+FAIL_EXPECTED subtest_b ;
+DEPENDS all : subtest_a subtest_b ;
+""")
+
+t.run_build_system(["-ffile.jam", "-n"], stdout="""\
+...found 4 targets...
+...updating 2 targets...
+.a. subtest_a
+
+echo [subtest_a] 0
+echo [subtest_a] 1
+echo [subtest_a] 2
+
+.a. subtest_b
+
+echo [subtest_b] 0
+echo [subtest_b] 1
+echo [subtest_b] 2
+
+...updated 2 targets...
+""")
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_parallel_actions.py b/src/boost/tools/build/test/core_parallel_actions.py
new file mode 100755
index 000000000..98be1b0e1
--- /dev/null
+++ b/src/boost/tools/build/test/core_parallel_actions.py
@@ -0,0 +1,103 @@
+#!/usr/bin/python
+
+# Copyright 2006 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-d1"], pass_toolset=0)
+
+t.write("sleep.bat", """\
+::@timeout /T %1 /NOBREAK >nul
+@ping 127.0.0.1 -n 2 -w 1000 >nul
+@ping 127.0.0.1 -n %1 -w 1000 >nul
+@exit /B 0
+""")
+
+t.write("file.jam", """\
+if $(NT)
+{
+ actions sleeper
+ {
+ echo [$(<:S)] 0
+ call sleep.bat 1
+ echo [$(<:S)] 1
+ call sleep.bat 1
+ echo [$(<:S)] 2
+ call sleep.bat $(<:B)
+ }
+}
+else
+{
+ actions sleeper
+ {
+ echo "[$(<:S)] 0"
+ sleep 1
+ echo "[$(<:S)] 1"
+ sleep 1
+ echo "[$(<:S)] 2"
+ sleep $(<:B)
+ }
+}
+
+rule sleeper
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+NOTFILE front ;
+sleeper 1.a : front ;
+sleeper 2.a : front ;
+sleeper 3.a : front ;
+sleeper 4.a : front ;
+NOTFILE choke ;
+DEPENDS choke : 1.a 2.a 3.a 4.a ;
+sleeper 1.b : choke ;
+sleeper 2.b : choke ;
+sleeper 3.b : choke ;
+sleeper 4.b : choke ;
+DEPENDS bottom : 1.b 2.b 3.b 4.b ;
+DEPENDS all : bottom ;
+""")
+
+t.run_build_system(["-ffile.jam", "-j4"], stdout="""\
+...found 12 targets...
+...updating 8 targets...
+sleeper 1.a
+[.a] 0
+[.a] 1
+[.a] 2
+sleeper 2.a
+[.a] 0
+[.a] 1
+[.a] 2
+sleeper 3.a
+[.a] 0
+[.a] 1
+[.a] 2
+sleeper 4.a
+[.a] 0
+[.a] 1
+[.a] 2
+sleeper 1.b
+[.b] 0
+[.b] 1
+[.b] 2
+sleeper 2.b
+[.b] 0
+[.b] 1
+[.b] 2
+sleeper 3.b
+[.b] 0
+[.b] 1
+[.b] 2
+sleeper 4.b
+[.b] 0
+[.b] 1
+[.b] 2
+...updated 8 targets...
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_parallel_multifile_actions_1.py b/src/boost/tools/build/test/core_parallel_multifile_actions_1.py
new file mode 100755
index 000000000..214d00504
--- /dev/null
+++ b/src/boost/tools/build/test/core_parallel_multifile_actions_1.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+
+# Copyright 2007 Rene Rivera.
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Added to guard against a bug causing targets to be used before they
+# themselves have finished building. This used to happen for targets built by a
+# multi-file action that got triggered by another target.
+#
+# Example:
+# When target A and target B were declared as created by a single action and
+# target A triggered running that action then, while the action was still
+# running, target B was already reporting as being built causing other targets
+# depending on target A to be built prematurely.
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-d1"], pass_toolset=0)
+
+t.write("sleep.bat", """\
+::@timeout /T %1 /NOBREAK >nul
+@ping 127.0.0.1 -n 2 -w 1000 >nul
+@ping 127.0.0.1 -n %1 -w 1000 >nul
+@exit /B 0
+""")
+
+t.write("file.jam", """\
+if $(NT)
+{
+ SLEEP = @call sleep.bat ;
+}
+else
+{
+ SLEEP = sleep ;
+}
+
+actions .gen.
+{
+ echo 001
+ $(SLEEP) 4
+ echo 002
+}
+rule .use.1 { DEPENDS $(<) : $(>) ; }
+actions .use.1
+{
+ echo 003
+}
+
+rule .use.2 { DEPENDS $(<) : $(>) ; }
+actions .use.2
+{
+ $(SLEEP) 1
+ echo 004
+}
+
+.gen. g1.generated g2.generated ;
+.use.1 u1.user : g1.generated ;
+.use.2 u2.user : g2.generated ;
+
+DEPENDS all : u1.user u2.user ;
+""")
+
+t.run_build_system(["-ffile.jam", "-j2"], stdout="""\
+...found 5 targets...
+...updating 4 targets...
+.gen. g1.generated
+001
+002
+.use.1 u1.user
+003
+.use.2 u2.user
+004
+...updated 4 targets...
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_parallel_multifile_actions_2.py b/src/boost/tools/build/test/core_parallel_multifile_actions_2.py
new file mode 100755
index 000000000..a6911f6ff
--- /dev/null
+++ b/src/boost/tools/build/test/core_parallel_multifile_actions_2.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+
+# Copyright 2008 Jurko Gospodnetic, Vladimir Prus
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Added to guard against a bug causing targets to be used before they
+# themselves have finished building. This used to happen for targets built by a
+# multi-file action that got triggered by another target, except when the
+# target triggering the action was the first one in the list of targets
+# produced by that action.
+#
+# Example:
+# When target A and target B were declared as created by a single action with
+# A being the first one listed, and target B triggered running that action
+# then, while the action was still running, target A was already reporting as
+# being built causing other targets depending on target A to be built
+# prematurely.
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("sleep.bat", """\
+::@timeout /T %1 /NOBREAK >nul
+@ping 127.0.0.1 -n 2 -w 1000 >nul
+@ping 127.0.0.1 -n %1 -w 1000 >nul
+@exit /B 0
+""")
+
+t.write("file.jam", """\
+if $(NT)
+{
+ SLEEP = @call sleep.bat ;
+}
+else
+{
+ SLEEP = sleep ;
+}
+
+actions link
+{
+ $(SLEEP) 1
+ echo 001 - linked
+}
+
+link dll lib ;
+
+actions install
+{
+ echo 002 - installed
+}
+
+install installed_dll : dll ;
+DEPENDS installed_dll : dll ;
+
+DEPENDS all : lib installed_dll ;
+""")
+
+t.run_build_system(["-ffile.jam", "-j2"], stdout="""\
+...found 4 targets...
+...updating 3 targets...
+link dll
+001 - linked
+install installed_dll
+002 - installed
+...updated 3 targets...
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_scanner.py b/src/boost/tools/build/test/core_scanner.py
new file mode 100644
index 000000000..ee2c6f059
--- /dev/null
+++ b/src/boost/tools/build/test/core_scanner.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the parsing of tokens
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """\
+rule test1 ( args * )
+{
+ EXIT $(args) : 0 ;
+}
+
+test1
+a # a comment
+# another comment
+b
+c #| a multiline comment |# d
+#| another
+multiline
+comment
+|#
+e "#f" ;
+""")
+
+t.run_build_system(["-ffile.jam"], stdout="""\
+a b c d e #f
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_source_line_tracking.py b/src/boost/tools/build/test/core_source_line_tracking.py
new file mode 100755
index 000000000..2bd9d562b
--- /dev/null
+++ b/src/boost/tools/build/test/core_source_line_tracking.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+# Copyright 2012. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test Boost Jam parser's source line tracking & reporting.
+
+import BoostBuild
+
+
+def test_eof_in_string():
+ t = BoostBuild.Tester(pass_toolset=False)
+ t.write("file.jam", '\n\n\naaa = "\n\n\n\n\n\n')
+ t.run_build_system(["-ffile.jam"], status=1)
+ t.expect_output_lines('file.jam:4: unmatched " in string at keyword =')
+ t.expect_output_lines("file.jam:4: syntax error at EOF")
+ t.cleanup()
+
+
+def test_error_missing_argument(eof):
+ """
+ This use case used to cause a missing argument error to be reported in
+ module '(builtin)' in line -1 when the input file did not contain a
+ trailing newline.
+
+ """
+ t = BoostBuild.Tester(pass_toolset=False)
+ t.write("file.jam", """\
+rule f ( param ) { }
+f ;%s""" % __trailing_newline(eof))
+ t.run_build_system(["-ffile.jam"], status=1)
+ t.expect_output_lines("file.jam:2: in module scope")
+ t.expect_output_lines("file.jam:1:see definition of rule 'f' being called")
+ t.cleanup()
+
+
+def test_error_syntax(eof):
+ t = BoostBuild.Tester(pass_toolset=False)
+ t.write("file.jam", "ECHO%s" % __trailing_newline(eof))
+ t.run_build_system(["-ffile.jam"], status=1)
+ t.expect_output_lines("file.jam:1: syntax error at EOF")
+ t.cleanup()
+
+
+def test_traceback():
+ t = BoostBuild.Tester(pass_toolset=False)
+ t.write("file.jam", """\
+NOTFILE all ;
+ECHO [ BACKTRACE ] ;""")
+ t.run_build_system(["-ffile.jam"])
+ t.expect_output_lines("file.jam 2 module scope")
+ t.cleanup()
+
+
+def __trailing_newline(eof):
+ """
+ Helper function returning an empty string or a newling character to
+ append to the current output line depending on whether we want that line to
+ be the last line in the file (eof == True) or not (eof == False).
+
+ """
+ if eof:
+ return ""
+ return "\n"
+
+
+test_error_missing_argument(eof=False)
+test_error_missing_argument(eof=True)
+test_error_syntax(eof=False)
+test_error_syntax(eof=True)
+test_traceback()
+test_eof_in_string()
diff --git a/src/boost/tools/build/test/core_syntax_error_exit_status.py b/src/boost/tools/build/test/core_syntax_error_exit_status.py
new file mode 100644
index 000000000..bffa676b7
--- /dev/null
+++ b/src/boost/tools/build/test/core_syntax_error_exit_status.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+# Copyright (C) Mateusz Loskot 2020.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that Jam syntax error results in non-zero exit status
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester()
+
+# Create the needed files.
+t.write("jamroot.jam", """
+exe hello : hello.cpp
+
+""")
+
+t.run_build_system(status=1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_typecheck.py b/src/boost/tools/build/test/core_typecheck.py
new file mode 100644
index 000000000..5d0e991f9
--- /dev/null
+++ b/src/boost/tools/build/test/core_typecheck.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the typechecking facilities.
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0)
+
+t.write("file.jam", """
+module .typecheck
+{
+ rule "[path]" ( x )
+ {
+ if ! [ MATCH "^(::)" : $(x) ]
+ {
+ ECHO "Error: $(x) is not a path" ;
+ return true ;
+ }
+ }
+}
+
+rule do ( [path] a )
+{
+}
+
+do $(ARGUMENT) ;
+
+actions dummy { }
+dummy all ;
+""")
+
+t.run_build_system(["-sARGUMENT=::a/b/c"])
+t.run_build_system(["-sARGUMENT=a/b/c"], status=1, stdout="""\
+Error: a/b/c is not a path
+file.jam:18: in module scope
+*** argument error
+* rule do ( [path] a )
+* called with: ( a/b/c )
+* true a
+file.jam:16:see definition of rule 'do' being called
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_update_now.py b/src/boost/tools/build/test/core_update_now.py
new file mode 100755
index 000000000..9c02807b9
--- /dev/null
+++ b/src/boost/tools/build/test/core_update_now.py
@@ -0,0 +1,377 @@
+#!/usr/bin/python
+
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+
+
+def basic():
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+actions do-print
+{
+ echo updating $(<)
+}
+
+NOTFILE target1 ;
+ALWAYS target1 ;
+do-print target1 ;
+
+UPDATE_NOW target1 ;
+
+DEPENDS all : target1 ;
+""")
+
+ t.run_build_system(["-ffile.jam"], stdout="""\
+...found 1 target...
+...updating 1 target...
+do-print target1
+updating target1
+...updated 1 target...
+...found 1 target...
+""")
+
+ t.cleanup()
+
+
+def ignore_minus_n():
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+actions do-print
+{
+ echo updating $(<)
+}
+
+NOTFILE target1 ;
+ALWAYS target1 ;
+do-print target1 ;
+
+UPDATE_NOW target1 : : ignore-minus-n ;
+
+DEPENDS all : target1 ;
+""")
+
+ t.run_build_system(["-ffile.jam", "-n"], stdout="""\
+...found 1 target...
+...updating 1 target...
+do-print target1
+
+ echo updating target1
+
+updating target1
+...updated 1 target...
+...found 1 target...
+""")
+
+ t.cleanup()
+
+
+def failed_target():
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+actions fail
+{
+ exit 1
+}
+
+NOTFILE target1 ;
+ALWAYS target1 ;
+fail target1 ;
+
+actions do-print
+{
+ echo updating $(<)
+}
+
+NOTFILE target2 ;
+do-print target2 ;
+DEPENDS target2 : target1 ;
+
+UPDATE_NOW target1 : : ignore-minus-n ;
+
+DEPENDS all : target1 target2 ;
+""")
+
+ t.run_build_system(["-ffile.jam", "-n"], stdout="""\
+...found 1 target...
+...updating 1 target...
+fail target1
+
+ exit 1
+
+...failed fail target1...
+...failed updating 1 target...
+...found 2 targets...
+...updating 1 target...
+do-print target2
+
+ echo updating target2
+
+...updated 1 target...
+""")
+
+ t.cleanup()
+
+
+def missing_target():
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+actions do-print
+{
+ echo updating $(<)
+}
+
+NOTFILE target2 ;
+do-print target2 ;
+DEPENDS target2 : target1 ;
+
+UPDATE_NOW target1 : : ignore-minus-n ;
+
+DEPENDS all : target1 target2 ;
+""")
+
+ t.run_build_system(["-ffile.jam", "-n"], status=1, stdout="""\
+don't know how to make target1
+...found 1 target...
+...can't find 1 target...
+...found 2 targets...
+...can't make 1 target...
+""")
+
+ t.cleanup()
+
+
+def build_once():
+ """
+ Make sure that if we call UPDATE_NOW with ignore-minus-n, the target gets
+ updated exactly once regardless of previous calls to UPDATE_NOW with -n in
+ effect.
+
+ """
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+actions do-print
+{
+ echo updating $(<)
+}
+
+NOTFILE target1 ;
+ALWAYS target1 ;
+do-print target1 ;
+
+UPDATE_NOW target1 ;
+UPDATE_NOW target1 : : ignore-minus-n ;
+UPDATE_NOW target1 : : ignore-minus-n ;
+
+DEPENDS all : target1 ;
+""")
+
+ t.run_build_system(["-ffile.jam", "-n"], stdout="""\
+...found 1 target...
+...updating 1 target...
+do-print target1
+
+ echo updating target1
+
+...updated 1 target...
+do-print target1
+
+ echo updating target1
+
+updating target1
+...updated 1 target...
+...found 1 target...
+""")
+
+ t.cleanup()
+
+
+def return_status():
+ """
+ Make sure that UPDATE_NOW returns a failure status if
+ the target failed in a previous call to UPDATE_NOW
+ """
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+actions fail
+{
+ exit 1
+}
+
+NOTFILE target1 ;
+ALWAYS target1 ;
+fail target1 ;
+
+ECHO "update1:" [ UPDATE_NOW target1 ] ;
+ECHO "update2:" [ UPDATE_NOW target1 ] ;
+
+DEPENDS all : target1 ;
+""")
+
+ t.run_build_system(["-ffile.jam"], status=1, stdout="""\
+...found 1 target...
+...updating 1 target...
+fail target1
+
+ exit 1
+
+...failed fail target1...
+...failed updating 1 target...
+update1:
+update2:
+...found 1 target...
+""")
+
+ t.cleanup()
+
+
+def save_restore():
+ """Tests that ignore-minus-n and ignore-minus-q are
+ local to the call to UPDATE_NOW"""
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("actions.jam", """\
+rule fail
+{
+ NOTFILE $(<) ;
+ ALWAYS $(<) ;
+}
+actions fail
+{
+ exit 1
+}
+
+rule pass
+{
+ NOTFILE $(<) ;
+ ALWAYS $(<) ;
+}
+actions pass
+{
+ echo updating $(<)
+}
+""")
+ t.write("file.jam", """
+include actions.jam ;
+fail target1 ;
+fail target2 ;
+UPDATE_NOW target1 target2 : : $(IGNORE_MINUS_N) : $(IGNORE_MINUS_Q) ;
+fail target3 ;
+fail target4 ;
+UPDATE_NOW target3 target4 ;
+UPDATE ;
+""")
+ t.run_build_system(['-n', '-sIGNORE_MINUS_N=1', '-ffile.jam'],
+ stdout='''...found 2 targets...
+...updating 2 targets...
+fail target1
+
+ exit 1
+
+...failed fail target1...
+fail target2
+
+ exit 1
+
+...failed fail target2...
+...failed updating 2 targets...
+...found 2 targets...
+...updating 2 targets...
+fail target3
+
+ exit 1
+
+fail target4
+
+ exit 1
+
+...updated 2 targets...
+''')
+
+ t.run_build_system(['-q', '-sIGNORE_MINUS_N=1', '-ffile.jam'],
+ status=1, stdout='''...found 2 targets...
+...updating 2 targets...
+fail target1
+
+ exit 1
+
+...failed fail target1...
+...failed updating 1 target...
+...found 2 targets...
+...updating 2 targets...
+fail target3
+
+ exit 1
+
+...failed fail target3...
+...failed updating 1 target...
+''')
+
+ t.run_build_system(['-n', '-sIGNORE_MINUS_Q=1', '-ffile.jam'],
+ stdout='''...found 2 targets...
+...updating 2 targets...
+fail target1
+
+ exit 1
+
+fail target2
+
+ exit 1
+
+...updated 2 targets...
+...found 2 targets...
+...updating 2 targets...
+fail target3
+
+ exit 1
+
+fail target4
+
+ exit 1
+
+...updated 2 targets...
+''')
+
+ t.run_build_system(['-q', '-sIGNORE_MINUS_Q=1', '-ffile.jam'],
+ status=1, stdout='''...found 2 targets...
+...updating 2 targets...
+fail target1
+
+ exit 1
+
+...failed fail target1...
+fail target2
+
+ exit 1
+
+...failed fail target2...
+...failed updating 2 targets...
+...found 2 targets...
+...updating 2 targets...
+fail target3
+
+ exit 1
+
+...failed fail target3...
+...failed updating 1 target...
+''')
+
+ t.cleanup()
+
+
+basic()
+ignore_minus_n()
+failed_target()
+missing_target()
+build_once()
+return_status()
+save_restore()
diff --git a/src/boost/tools/build/test/core_variables_in_actions.py b/src/boost/tools/build/test/core_variables_in_actions.py
new file mode 100755
index 000000000..f72f50c81
--- /dev/null
+++ b/src/boost/tools/build/test/core_variables_in_actions.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+# Copyright 2012. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that variables in actions get expanded but double quote characters
+# get treated as regular characters and not string literal delimiters when
+# determining string tokens concatenated to the variable being expanded.
+#
+# We also take care to make this test work correctly when run using both
+# Windows and Unix echo command variant. That is why we add the extra single
+# quotes around the text being echoed - they will make the double quotes be
+# displayed as regular characters in both cases but will be displayed
+# themselves only when using the Windows cmd shell's echo command.
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+t.write("file.jam", """\
+rule dummy ( i )
+{
+ local a = 1 2 3 ;
+ ECHO From "rule:" $(a)" seconds" ;
+ a on $(i) = $(a) ;
+}
+
+actions dummy
+{
+ echo 'From action: $(a)" seconds"'
+}
+
+dummy all ;
+""")
+t.run_build_system(["-ffile.jam", "-d1"])
+t.expect_output_lines("From rule: 1 seconds 2 seconds 3 seconds")
+t.expect_output_lines('*From action: 1" 2" 3" seconds"*')
+t.cleanup()
diff --git a/src/boost/tools/build/test/core_varnames.py b/src/boost/tools/build/test/core_varnames.py
new file mode 100644
index 000000000..9d66a65ba
--- /dev/null
+++ b/src/boost/tools/build/test/core_varnames.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This tests the core rule for enumerating the variable names in a module.
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("file.jam", """\
+module foo
+{
+ rule bar { }
+ var1 = x y ;
+ var2 = fubar ;
+}
+
+expected = var1 var2 ;
+names = [ VARNAMES foo ] ;
+if $(names) in $(expected) && $(expected) in $(names)
+{
+ # everything OK
+}
+else
+{
+ EXIT expected to find variables $(expected:J=", ") in module foo,
+ but found $(names:J=", ") instead. ;
+}
+DEPENDS all : xx ;
+NOTFILE xx ;
+""")
+
+t.run_build_system(["-ffile.jam"], status=0)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/custom_generator.py b/src/boost/tools/build/test/custom_generator.py
new file mode 100644
index 000000000..2609c74a7
--- /dev/null
+++ b/src/boost/tools/build/test/custom_generator.py
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2004, 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Attempt to declare a generator for creating OBJ from RC files. That generator
+# should be considered together with standard CPP->OBJ generators and
+# successfully create the target. Since we do not have a RC compiler everywhere,
+# we fake the action. The resulting OBJ will be unusable, but it must be
+# created.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """
+import rcc ;
+""")
+
+t.write("rcc.jam", """
+import type ;
+import generators ;
+import print ;
+
+# Use 'RCC' to avoid conflicts with definitions in the standard rc.jam and
+# msvc.jam
+type.register RCC : rcc ;
+
+rule resource-compile ( targets * : sources * : properties * )
+{
+ print.output $(targets[1]) ;
+ print.text "rc-object" ;
+}
+
+generators.register-standard rcc.resource-compile : RCC : OBJ ;
+""")
+
+t.write("rcc.py", """
+import b2.build.type as type
+import b2.build.generators as generators
+
+from b2.manager import get_manager
+
+# Use 'RCC' to avoid conflicts with definitions in the standard rc.jam and
+# msvc.jam
+type.register('RCC', ['rcc'])
+
+generators.register_standard("rcc.resource-compile", ["RCC"], ["OBJ"])
+
+get_manager().engine().register_action(
+ "rcc.resource-compile",
+ '@($(STDOUT):E=rc-object) > "$(<)"')
+""")
+
+t.write("jamfile.jam", """
+obj r : r.rcc ;
+""")
+
+t.write("r.rcc", """
+""")
+
+t.run_build_system()
+t.expect_content("bin/r.obj", "rc-object")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/debugger-mi.py b/src/boost/tools/build/test/debugger-mi.py
new file mode 100644
index 000000000..25c52c7e4
--- /dev/null
+++ b/src/boost/tools/build/test/debugger-mi.py
@@ -0,0 +1,326 @@
+#!/usr/bin/python
+
+# Copyright 2016 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the mi interface for the debugger
+
+import BoostBuild
+import TestCmd
+import re
+
+def split_stdin_stdout(text):
+ """stdin is all text after the prompt up to and including
+ the next newline. Everything else is stdout. stdout
+ may contain regular expressions enclosed in {{}}."""
+ prompt = re.escape('(gdb) \n')
+ pattern = re.compile('(?<=%s)((?:\d*-.*)\n)' % prompt)
+ stdin = ''.join(re.findall(pattern, text))
+ stdout = re.sub(pattern, '', text)
+ outside_pattern = re.compile(r'(?:\A|(?<=\}\}))(?:[^\{]|(?:\{(?!\{)))*(?:(?=\{\{)|\Z)')
+
+ def escape_line(line):
+ line = re.sub(outside_pattern, lambda m: re.escape(m.group(0)), line)
+ return re.sub(r'\{\{|\}\}', '', line)
+
+ stdout = '\n'.join([escape_line(line) for line in stdout.split('\n')])
+ return (stdin,stdout)
+
+def run(tester, io):
+ (input,output) = split_stdin_stdout(io)
+ tester.run_build_system(stdin=input, stdout=output, match=TestCmd.match_re)
+
+def make_tester():
+ return BoostBuild.Tester(["-dmi"], pass_toolset=False, pass_d0=False,
+ use_test_config=False, ignore_toolset_requirements=False, match=TestCmd.match_re)
+
+def test_exec_run():
+ t = make_tester()
+ t.write("test.jam", """\
+ UPDATE ;
+ """)
+
+ run(t, """\
+=thread-group-added,id="i1"
+(gdb)
+72-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+72^running
+(gdb)
+*stopped,reason="exited-normally"
+(gdb)
+73-gdb-exit
+73^exit
+""")
+
+ t.cleanup()
+
+def test_exit_status():
+ t = make_tester()
+ t.write("test.jam", """\
+ EXIT : 1 ;
+ """)
+ run(t, """\
+=thread-group-added,id="i1"
+(gdb)
+72-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+72^running
+(gdb)
+
+*stopped,reason="exited",exit-code="1"
+(gdb)
+73-gdb-exit
+73^exit
+""")
+ t.cleanup()
+
+def test_exec_step():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule g ( )
+ {
+ a = 1 ;
+ b = 2 ;
+ }
+ rule f ( )
+ {
+ g ;
+ c = 3 ;
+ }
+ f ;
+ """)
+ run(t, """\
+=thread-group-added,id="i1"
+(gdb)
+-break-insert f
+^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
+(gdb)
+72-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+72^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="8"},thread-id="1",stopped-threads="all"
+(gdb)
+1-exec-step
+1^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1"
+(gdb)
+2-exec-step
+2^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="4"},thread-id="1"
+(gdb)
+3-exec-step
+3^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="9"},thread-id="1"
+(gdb)
+73-gdb-exit
+73^exit
+""")
+ t.cleanup()
+
+def test_exec_next():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule g ( )
+ {
+ a = 1 ;
+ }
+ rule f ( )
+ {
+ g ;
+ b = 2 ;
+ c = 3 ;
+ }
+ rule h ( )
+ {
+ f ;
+ g ;
+ }
+ h ;
+ d = 4 ;
+ """)
+ run(t, """\
+=thread-group-added,id="i1"
+(gdb)
+-break-insert f
+^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
+(gdb)
+72-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+72^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="7"},thread-id="1",stopped-threads="all"
+(gdb)
+1-exec-next
+1^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="8"},thread-id="1"
+(gdb)
+2-exec-next
+2^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="9"},thread-id="1"
+(gdb)
+3-exec-next
+3^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="h",args=[],file="test.jam",fullname="{{.*}}test.jam",line="14"},thread-id="1"
+(gdb)
+4-exec-next
+4^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="module scope",args=[],file="test.jam",fullname="{{.*}}test.jam",line="17"},thread-id="1"
+(gdb)
+73-gdb-exit
+73^exit
+""")
+ t.cleanup()
+
+def test_exec_finish():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( )
+ {
+ a = 1 ;
+ }
+ rule g ( )
+ {
+ f ;
+ b = 2 ;
+ i ;
+ }
+ rule h ( )
+ {
+ g ;
+ i ;
+ }
+ rule i ( )
+ {
+ c = 3 ;
+ }
+ h ;
+ d = 4 ;
+ """)
+ run(t, """\
+=thread-group-added,id="i1"
+(gdb)
+-break-insert f
+^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
+(gdb)
+72-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+72^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1",stopped-threads="all"
+(gdb)
+1-exec-finish
+1^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="8"},thread-id="1"
+(gdb)
+2-exec-finish
+2^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="h",args=[],file="test.jam",fullname="{{.*}}test.jam",line="14"},thread-id="1"
+(gdb)
+3-exec-finish
+3^running
+(gdb)
+*stopped,reason="end-stepping-range",frame={func="module scope",args=[],file="test.jam",fullname="{{.*}}test.jam",line="21"},thread-id="1"
+(gdb)
+73-gdb-exit
+73^exit
+""")
+ t.cleanup()
+
+
+def test_breakpoints():
+ """Tests the interaction between the following commands:
+ break, clear, delete, disable, enable"""
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( )
+ {
+ a = 1 ;
+ }
+ rule g ( )
+ {
+ b = 2 ;
+ }
+ rule h ( )
+ {
+ c = 3 ;
+ d = 4 ;
+ }
+ f ;
+ g ;
+ h ;
+ UPDATE ;
+ """)
+ run(t, """\
+=thread-group-added,id="i1"
+(gdb)
+-break-insert f
+^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",func="f"}
+(gdb)
+72-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+72^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1",stopped-threads="all"
+(gdb)
+-interpreter-exec console kill
+^done
+(gdb)
+-break-insert g
+^done,bkpt={number="2",type="breakpoint",disp="keep",enabled="y",func="g"}
+(gdb)
+-break-disable 1
+^done
+(gdb)
+73-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+73^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="2",disp="keep",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="7"},thread-id="1",stopped-threads="all"
+(gdb)
+-interpreter-exec console kill
+^done
+(gdb)
+-break-enable 1
+^done
+(gdb)
+74-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+74^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="1",disp="keep",frame={func="f",args=[],file="test.jam",fullname="{{.*}}test.jam",line="3"},thread-id="1",stopped-threads="all"
+(gdb)
+-interpreter-exec console kill
+^done
+(gdb)
+-break-delete 1
+^done
+(gdb)
+75-exec-run -ftest.jam
+=thread-created,id="1",group-id="i1"
+75^running
+(gdb)
+*stopped,reason="breakpoint-hit",bkptno="2",disp="keep",frame={func="g",args=[],file="test.jam",fullname="{{.*}}test.jam",line="7"},thread-id="1",stopped-threads="all"
+(gdb)
+76-gdb-exit
+76^exit
+""")
+ t.cleanup()
+
+test_exec_run()
+test_exit_status()
+test_exec_step()
+test_exec_next()
+test_exec_finish()
+test_breakpoints()
diff --git a/src/boost/tools/build/test/debugger.py b/src/boost/tools/build/test/debugger.py
new file mode 100644
index 000000000..46c8aca6d
--- /dev/null
+++ b/src/boost/tools/build/test/debugger.py
@@ -0,0 +1,674 @@
+#!/usr/bin/python
+
+# Copyright 2016 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test for the debugger
+
+import BoostBuild
+import TestCmd
+import re
+
+def split_stdin_stdout(text):
+ """stdin is all text after the prompt up to and including
+ the next newline. Everything else is stdout. stdout
+ may contain regular expressions enclosed in {{}}."""
+ prompt = re.escape('(b2db) ')
+ pattern = re.compile('(?<=%s)(.*\n)' % prompt)
+ text = text.replace("{{bjam}}", "{{.*}}b2{{(?:\\.exe)?}}")
+ stdin = ''.join(re.findall(pattern, text))
+ stdout = re.sub(pattern, '', text)
+ outside_pattern = re.compile(r'(?:\A|(?<=\}\}))(?:[^\{]|(?:\{(?!\{)))*(?:(?=\{\{)|\Z)')
+
+ def escape_line(line):
+ line = re.sub(outside_pattern, lambda m: re.escape(m.group(0)), line)
+ return re.sub(r'\{\{|\}\}', '', line)
+
+ stdout = '\n'.join([escape_line(line) for line in stdout.split('\n')])
+ return (stdin,stdout)
+
+def run(tester, io):
+ (input,output) = split_stdin_stdout(io)
+ tester.run_build_system(stdin=input, stdout=output, match=TestCmd.match_re)
+
+def make_tester():
+ return BoostBuild.Tester(["-dconsole"], pass_toolset=False, pass_d0=False,
+ use_test_config=False, ignore_toolset_requirements=False, match=TestCmd.match_re)
+
+def test_run():
+ t = make_tester()
+ t.write("test.jam", """\
+ UPDATE ;
+ """)
+
+ run(t, """\
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Child {{\d+}} exited with status 0
+(b2db) quit
+""")
+
+ t.cleanup()
+
+def test_exit_status():
+ t = make_tester()
+ t.write("test.jam", """\
+ EXIT : 1 ;
+ """)
+ run(t, """\
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+
+Child {{\d+}} exited with status 1
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_step():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule g ( )
+ {
+ a = 1 ;
+ b = 2 ;
+ }
+ rule f ( )
+ {
+ g ;
+ c = 3 ;
+ }
+ f ;
+ """)
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( ) at test.jam:8
+8 g ;
+(b2db) step
+3 a = 1 ;
+(b2db) step
+4 b = 2 ;
+(b2db) step
+9 c = 3 ;
+(b2db) quit
+""")
+ t.cleanup()
+
+# Note: step doesn't need to worry about breakpoints,
+# as it always stops at the next line executed.
+
+def test_next():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule g ( )
+ {
+ a = 1 ;
+ }
+ rule f ( )
+ {
+ g ;
+ b = 2 ;
+ c = 3 ;
+ }
+ rule h ( )
+ {
+ f ;
+ g ;
+ }
+ h ;
+ d = 4 ;
+ """)
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( ) at test.jam:7
+7 g ;
+(b2db) next
+8 b = 2 ;
+(b2db) next
+9 c = 3 ;
+(b2db) next
+14 g ;
+(b2db) next
+17 d = 4 ;
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_next_breakpoint():
+ """next should stop if it encounters a breakpoint.
+ If the normal end point happens to be a breakpoint,
+ then it should be reported as normal stepping."""
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( recurse ? )
+ {
+ if $(recurse) { f ; }
+ a = 1 ;
+ }
+ rule g ( )
+ {
+ b = 2 ;
+ }
+ f true ;
+ g ;
+ """)
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) break g
+Breakpoint 2 set at g
+(b2db) break test.jam:4
+Breakpoint 3 set at test.jam:4
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( true ) at test.jam:3
+3 if $(recurse) { f ; }
+(b2db) next
+Breakpoint 1, f ( ) at test.jam:3
+3 if $(recurse) { f ; }
+(b2db) next
+4 a = 1 ;
+(b2db) next
+4 a = 1 ;
+(b2db) next
+11 g ;
+(b2db) next
+Breakpoint 2, g ( ) at test.jam:8
+8 b = 2 ;
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_finish():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( )
+ {
+ a = 1 ;
+ }
+ rule g ( )
+ {
+ f ;
+ b = 2 ;
+ i ;
+ }
+ rule h ( )
+ {
+ g ;
+ i ;
+ }
+ rule i ( )
+ {
+ c = 3 ;
+ }
+ h ;
+ d = 4 ;
+ """)
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( ) at test.jam:3
+3 a = 1 ;
+(b2db) finish
+8 b = 2 ;
+(b2db) finish
+14 i ;
+(b2db) finish
+21 d = 4 ;
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_finish_breakpoints():
+ """finish should stop when it reaches a breakpoint."""
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( recurse * )
+ {
+ if $(recurse)
+ {
+ a = [ f $(recurse[2-]) ] ;
+ }
+ }
+ rule g ( list * )
+ {
+ for local v in $(list)
+ {
+ x = $(v) ;
+ }
+ }
+ f 1 2 ;
+ g 1 2 ;
+ """)
+ run(t, """\
+(b2db) break test.jam:5
+Breakpoint 1 set at test.jam:5
+(b2db) break test.jam:12
+Breakpoint 2 set at test.jam:12
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( 1 2 ) at test.jam:5
+5 a = [ f $(recurse[2-]) ] ;
+(b2db) finish
+Breakpoint 1, f ( 2 ) at test.jam:5
+5 a = [ f $(recurse[2-]) ] ;
+(b2db) finish
+5 a = [ f $(recurse[2-]) ] ;
+(b2db) finish
+16 g 1 2 ;
+(b2db) finish
+Breakpoint 2, g ( 1 2 ) at test.jam:12
+12 x = $(v) ;
+(b2db) finish
+Breakpoint 2, g ( 1 2 ) at test.jam:12
+12 x = $(v) ;
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_continue_breakpoints():
+ """continue should stop when it reaches a breakpoint"""
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( recurse * )
+ {
+ if $(recurse)
+ {
+ a = [ f $(recurse[2-]) ] ;
+ }
+ }
+ rule g ( list * )
+ {
+ for local v in $(list)
+ {
+ x = $(v) ;
+ }
+ }
+ f 1 2 ;
+ g 1 2 ;
+ """)
+ run(t, """\
+(b2db) break test.jam:5
+Breakpoint 1 set at test.jam:5
+(b2db) break test.jam:12
+Breakpoint 2 set at test.jam:12
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( 1 2 ) at test.jam:5
+5 a = [ f $(recurse[2-]) ] ;
+(b2db) continue
+Breakpoint 1, f ( 2 ) at test.jam:5
+5 a = [ f $(recurse[2-]) ] ;
+(b2db) continue
+Breakpoint 1, f ( 1 2 ) at test.jam:5
+5 a = [ f $(recurse[2-]) ] ;
+(b2db) continue
+Breakpoint 2, g ( 1 2 ) at test.jam:12
+12 x = $(v) ;
+(b2db) continue
+Breakpoint 2, g ( 1 2 ) at test.jam:12
+12 x = $(v) ;
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_breakpoints():
+ """Tests the interaction between the following commands:
+ break, clear, delete, disable, enable"""
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( )
+ {
+ a = 1 ;
+ }
+ rule g ( )
+ {
+ b = 2 ;
+ }
+ rule h ( )
+ {
+ c = 3 ;
+ d = 4 ;
+ }
+ f ;
+ g ;
+ h ;
+ UPDATE ;
+ """)
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( ) at test.jam:3
+3 a = 1 ;
+(b2db) kill
+(b2db) break g
+Breakpoint 2 set at g
+(b2db) disable 1
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 2, g ( ) at test.jam:7
+7 b = 2 ;
+(b2db) kill
+(b2db) enable 1
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( ) at test.jam:3
+3 a = 1 ;
+(b2db) kill
+(b2db) delete 1
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 2, g ( ) at test.jam:7
+7 b = 2 ;
+(b2db) kill
+(b2db) break test.jam:12
+Breakpoint 3 set at test.jam:12
+(b2db) clear g
+Deleted breakpoint 2
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 3, h ( ) at test.jam:12
+12 d = 4 ;
+(b2db) kill
+(b2db) clear test.jam:12
+Deleted breakpoint 3
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Child {{\d+}} exited with status 0
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_breakpoints_running():
+ """Tests that breakpoints can be added and modified
+ while the program is running."""
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( )
+ {
+ a = 1 ;
+ }
+ rule g ( )
+ {
+ b = 2 ;
+ }
+ rule h ( )
+ {
+ c = 3 ;
+ d = 4 ;
+ }
+ f ;
+ g ;
+ h ;
+ UPDATE ;
+ """)
+ run(t, """\
+(b2db) break test.jam:14
+Breakpoint 1 set at test.jam:14
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:14
+14 f ;
+(b2db) break f
+Breakpoint 2 set at f
+(b2db) continue
+Breakpoint 2, f ( ) at test.jam:3
+3 a = 1 ;
+(b2db) kill
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:14
+14 f ;
+(b2db) break g
+Breakpoint 3 set at g
+(b2db) disable 2
+(b2db) continue
+Breakpoint 3, g ( ) at test.jam:7
+7 b = 2 ;
+(b2db) kill
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:14
+14 f ;
+(b2db) enable 2
+(b2db) continue
+Breakpoint 2, f ( ) at test.jam:3
+3 a = 1 ;
+(b2db) kill
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:14
+14 f ;
+(b2db) delete 2
+(b2db) continue
+Breakpoint 3, g ( ) at test.jam:7
+7 b = 2 ;
+(b2db) kill
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:14
+14 f ;
+(b2db) break test.jam:12
+Breakpoint 4 set at test.jam:12
+(b2db) clear g
+Deleted breakpoint 3
+(b2db) continue
+Breakpoint 4, h ( ) at test.jam:12
+12 d = 4 ;
+(b2db) kill
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:14
+14 f ;
+(b2db) clear test.jam:12
+Deleted breakpoint 4
+(b2db) continue
+Child {{\d+}} exited with status 0
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_backtrace():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( x * : y * : z * )
+ {
+ return $(x) ;
+ }
+ rule g ( x * : y * : z * )
+ {
+ return [ f $(x) : $(y) : $(z) ] ;
+ }
+ g 1 : 2 : 3 ;
+ """)
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( 1 : 2 : 3 ) at test.jam:3
+3 return $(x) ;
+(b2db) backtrace
+#0 in f ( 1 : 2 : 3 ) at test.jam:3
+#1 in g ( 1 : 2 : 3 ) at test.jam:7
+#2 in module scope at test.jam:9
+(b2db) quit
+""")
+ t.cleanup()
+
+def test_print():
+ t = make_tester()
+ t.write("test.jam", """\
+ rule f ( args * )
+ {
+ return $(args) ;
+ }
+ f x ;
+ f x y ;
+ """)
+
+ run(t, """\
+(b2db) break f
+Breakpoint 1 set at f
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, f ( x ) at test.jam:3
+3 return $(args) ;
+(b2db) print $(args)
+x
+(b2db) continue
+Breakpoint 1, f ( x y ) at test.jam:3
+3 return $(args) ;
+(b2db) print $(args)
+x y
+(b2db) disable 1
+(b2db) print [ f z ]
+z
+(b2db) quit
+""")
+
+ t.cleanup()
+
+def test_run_running():
+ t = make_tester()
+ t.write("test.jam", """\
+ UPDATE ;
+ """)
+
+ run(t, """\
+(b2db) break test.jam:1
+Breakpoint 1 set at test.jam:1
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:1
+1 UPDATE ;
+(b2db) run -ftest.jam
+Child {{\d+}} exited with status 0
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:1
+1 UPDATE ;
+(b2db) quit
+""")
+
+ t.cleanup()
+
+def test_error_not_running():
+ t = make_tester()
+ run(t, """\
+(b2db) continue
+The program is not being run.
+(b2db) step
+The program is not being run.
+(b2db) next
+The program is not being run.
+(b2db) finish
+The program is not being run.
+(b2db) kill
+The program is not being run.
+(b2db) backtrace
+The program is not being run.
+(b2db) print 1
+The program is not being run.
+(b2db) quit
+""")
+
+ t.cleanup()
+
+def test_bad_arguments():
+ t = make_tester()
+ t.write("test.jam", """\
+ UPDATE ;
+ """)
+
+ run(t, """\
+(b2db) break test.jam:1
+Breakpoint 1 set at test.jam:1
+(b2db) run -ftest.jam
+Starting program: {{bjam}} -ftest.jam
+Breakpoint 1, module scope at test.jam:1
+1 UPDATE ;
+(b2db) continue 1
+Too many arguments to continue.
+(b2db) step 1
+Too many arguments to step.
+(b2db) next 1
+Too many arguments to next.
+(b2db) finish 1
+Too many arguments to finish.
+(b2db) break
+Missing argument to break.
+(b2db) break x y
+Too many arguments to break.
+(b2db) disable
+Missing argument to disable.
+(b2db) disable 1 2
+Too many arguments to disable.
+(b2db) disable x
+Invalid breakpoint number x.
+(b2db) disable 2
+Unknown breakpoint 2.
+(b2db) enable
+Missing argument to enable.
+(b2db) enable 1 2
+Too many arguments to enable.
+(b2db) enable x
+Invalid breakpoint number x.
+(b2db) enable 2
+Unknown breakpoint 2.
+(b2db) delete
+Missing argument to delete.
+(b2db) delete 1 2
+Too many arguments to delete.
+(b2db) delete x
+Invalid breakpoint number x.
+(b2db) delete 2
+Unknown breakpoint 2.
+(b2db) clear
+Missing argument to clear.
+(b2db) clear test.jam:1 test.jam:1
+Too many arguments to clear.
+(b2db) clear test.jam:2
+No breakpoint at test.jam:2.
+(b2db) quit
+""")
+
+ t.cleanup()
+
+def test_unknown_command():
+ t = make_tester()
+ run(t, """\
+(b2db) xyzzy
+Unknown command: xyzzy
+(b2db) gnusto rezrov
+Unknown command: gnusto
+(b2db) quit
+""")
+
+ t.cleanup()
+
+test_run()
+test_exit_status()
+test_step()
+test_next()
+test_next_breakpoint()
+test_finish()
+test_finish_breakpoints()
+test_continue_breakpoints()
+test_breakpoints()
+test_breakpoints_running()
+test_backtrace()
+test_print()
+test_run_running()
+test_error_not_running()
+test_bad_arguments()
+test_unknown_command()
diff --git a/src/boost/tools/build/test/default_build.py b/src/boost/tools/build/test/default_build.py
new file mode 100644
index 000000000..207a03753
--- /dev/null
+++ b/src/boost/tools/build/test/default_build.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that default build clause actually has any effect.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "")
+t.write("jamfile.jam", "exe a : a.cpp : : debug release ;")
+t.write("a.cpp", "int main() {}\n")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+t.expect_addition("bin/$toolset/release*/a.exe")
+
+# Check that explictly-specified build variant suppresses default-build.
+t.rm("bin")
+t.run_build_system(["release"])
+t.expect_addition(BoostBuild.List("bin/$toolset/release*/") * "a.exe a.obj")
+t.ignore_addition('bin/*/a.rsp')
+t.expect_nothing_more()
+
+# Now check that we can specify explicit build request and default-build will be
+# combined with it.
+t.run_build_system(["optimization=space"])
+t.expect_addition("bin/$toolset/debug/optimization-space*/a.exe")
+t.expect_addition("bin/$toolset/release/optimization-space*/a.exe")
+
+# Test that default-build must be identical in all alternatives. Error case.
+t.write("jamfile.jam", """\
+exe a : a.cpp : : debug ;
+exe a : b.cpp : : ;
+""")
+t.run_build_system(["-n", "--no-error-backtrace"], status=1)
+t.fail_test(t.stdout().find("default build must be identical in all alternatives") == -1)
+
+# Test that default-build must be identical in all alternatives. No Error case,
+# empty default build.
+t.write("jamfile.jam", """\
+exe a : a.cpp : <variant>debug ;
+exe a : b.cpp : <variant>release ;
+""")
+t.run_build_system(["-n", "--no-error-backtrace"], status=0)
+
+# Now try a harder example: default build which contains <define> should cause
+# <define> to be present when "b" is compiled. This happens only if
+# "build-project b" is placed first.
+t.write("jamfile.jam", """\
+project : default-build <define>FOO ;
+build-project a ;
+build-project b ;
+""")
+
+t.write("a/jamfile.jam", "exe a : a.cpp ../b//b ;")
+t.write("a/a.cpp", """\
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+void foo();
+int main() { foo(); }
+""")
+
+t.write("b/jamfile.jam", "lib b : b.cpp ;")
+t.write("b/b.cpp", """\
+#ifdef FOO
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void foo() {}
+#endif
+""")
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/default_features.py b/src/boost/tools/build/test/default_features.py
new file mode 100644
index 000000000..7d3d4d22f
--- /dev/null
+++ b/src/boost/tools/build/test/default_features.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that features with default values are always present in build properties
+# of any target.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Declare *non-propagated* feature foo.
+t.write("jamroot.jam", """
+import feature : feature ;
+feature foo : on off ;
+""")
+
+# Note that '<foo>on' will not be propagated to 'd/l'.
+t.write("jamfile.jam", """
+exe hello : hello.cpp d//l ;
+""")
+
+t.write("hello.cpp", """
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+void foo();
+int main() { foo(); }
+""")
+
+t.write("d/jamfile.jam", """
+lib l : l.cpp : <foo>on:<define>FOO ;
+""")
+
+t.write("d/l.cpp", """
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+#ifdef FOO
+void foo() {}
+#endif
+""")
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/default_toolset.py b/src/boost/tools/build/test/default_toolset.py
new file mode 100755
index 000000000..a403a4dc9
--- /dev/null
+++ b/src/boost/tools/build/test/default_toolset.py
@@ -0,0 +1,215 @@
+#!/usr/bin/python
+
+# Copyright 2008 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that the expected default toolset is used when no toolset is explicitly
+# specified on the command line or used from code via the using rule. Test that
+# the default toolset is correctly used just like any other explicitly used
+# toolset (e.g. toolset prerequisites, properties conditioned on toolset
+# related features, etc.).
+#
+# Note that we need to ignore regular site/user/test configuration files to
+# avoid them marking any toolsets not under our control as used.
+
+import BoostBuild
+
+
+# Line displayed by Boost Build when using the default toolset.
+configuring_default_toolset_message = \
+ 'warning: Configuring default toolset "%s".'
+
+
+###############################################################################
+#
+# test_conditions_on_default_toolset()
+# ------------------------------------
+#
+###############################################################################
+
+def test_conditions_on_default_toolset():
+ """Test that toolset and toolset subfeature conditioned properties get
+ applied correctly when the toolset is selected by default. Implicitly tests
+ that we can use the set-default-toolset rule to set the default toolset to
+ be used by Boost Build.
+ """
+
+ t = BoostBuild.Tester("--user-config= --ignore-site-config",
+ pass_toolset=False, use_test_config=False)
+
+ toolset_name = "myCustomTestToolset"
+ toolset_version = "v"
+ toolset_version_unused = "v_unused"
+ message_loaded = "Toolset '%s' loaded." % toolset_name
+ message_initialized = "Toolset '%s' initialized." % toolset_name ;
+
+ # Custom toolset.
+ t.write(toolset_name + ".jam", """
+import feature ;
+ECHO "%(message_loaded)s" ;
+feature.extend toolset : %(toolset_name)s ;
+feature.subfeature toolset %(toolset_name)s : version : %(toolset_version)s %(toolset_version_unused)s ;
+rule init ( version ) { ECHO "%(message_initialized)s" ; }
+""" % {'message_loaded' : message_loaded ,
+ 'message_initialized' : message_initialized,
+ 'toolset_name' : toolset_name ,
+ 'toolset_version' : toolset_version ,
+ 'toolset_version_unused': toolset_version_unused})
+
+ # Main Boost Build project script.
+ t.write("jamroot.jam", """
+import build-system ;
+import errors ;
+import feature ;
+import notfile ;
+
+build-system.set-default-toolset %(toolset_name)s : %(toolset_version)s ;
+
+feature.feature description : : free incidental ;
+
+# We use a rule instead of an action to avoid problems with action output not
+# getting piped to stdout by the testing system.
+rule buildRule ( names : targets ? : properties * )
+{
+ local descriptions = [ feature.get-values description : $(properties) ] ;
+ ECHO "descriptions:" /$(descriptions)/ ;
+ local toolset = [ feature.get-values toolset : $(properties) ] ;
+ ECHO "toolset:" /$(toolset)/ ;
+ local toolset-version = [ feature.get-values "toolset-$(toolset):version" : $(properties) ] ;
+ ECHO "toolset-version:" /$(toolset-version)/ ;
+}
+
+notfile testTarget
+ : @buildRule
+ :
+ :
+ <description>stand-alone
+ <toolset>%(toolset_name)s:<description>toolset
+ <toolset>%(toolset_name)s-%(toolset_version)s:<description>toolset-version
+ <toolset>%(toolset_name)s-%(toolset_version_unused)s:<description>toolset-version-unused ;
+""" % {'toolset_name' : toolset_name ,
+ 'toolset_version' : toolset_version,
+ 'toolset_version_unused': toolset_version_unused})
+
+ t.run_build_system()
+ t.expect_output_lines(configuring_default_toolset_message % toolset_name)
+ t.expect_output_lines(message_loaded)
+ t.expect_output_lines(message_initialized)
+ t.expect_output_lines("descriptions: /stand-alone/ /toolset/ "
+ "/toolset-version/")
+ t.expect_output_lines("toolset: /%s/" % toolset_name)
+ t.expect_output_lines("toolset-version: /%s/" % toolset_version)
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_default_toolset_on_os()
+# ----------------------------
+#
+###############################################################################
+
+def test_default_toolset_on_os( os, expected_toolset ):
+ """Test that the given toolset is used as the default toolset on the given
+ os. Uses hardcoded knowledge of how Boost Build decides on which host OS it
+ is currently running. Note that we must not do much after tricking Boost
+ Build into believing it has a specific host OS as this might mess up other
+ important internal Boost Build state.
+ """
+
+ t = BoostBuild.Tester("--user-config= --ignore-site-config",
+ pass_toolset=False, use_test_config=False)
+
+ t.write("jamroot.jam", "modules.poke os : .name : %s ;" % os)
+
+ # We need to tell the test system to ignore stderr output as attempting to
+ # load missing toolsets might cause random failures with which we are not
+ # concerned in this test.
+ t.run_build_system(stderr=None)
+ t.expect_output_lines(configuring_default_toolset_message %
+ expected_toolset)
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_default_toolset_requirements()
+# -----------------------------------
+#
+###############################################################################
+
+def test_default_toolset_requirements():
+ """Test that default toolset's requirements get applied correctly.
+ """
+
+ t = BoostBuild.Tester("--user-config= --ignore-site-config",
+ pass_toolset=False, use_test_config=False,
+ ignore_toolset_requirements=False)
+
+ toolset_name = "customTestToolsetWithRequirements"
+
+ # Custom toolset.
+ t.write(toolset_name + ".jam", """
+import feature ;
+import toolset ;
+feature.extend toolset : %(toolset_name)s ;
+toolset.add-requirements <description>toolset-requirement ;
+rule init ( ) { }
+""" % {'toolset_name': toolset_name})
+
+ # Main Boost Build project script.
+ t.write("jamroot.jam", """
+import build-system ;
+import errors ;
+import feature ;
+import notfile ;
+
+build-system.set-default-toolset %(toolset_name)s ;
+
+feature.feature description : : free incidental ;
+
+# We use a rule instead of an action to avoid problems with action output not
+# getting piped to stdout by the testing system.
+rule buildRule ( names : targets ? : properties * )
+{
+ local descriptions = [ feature.get-values description : $(properties) ] ;
+ ECHO "descriptions:" /$(descriptions)/ ;
+ local toolset = [ feature.get-values toolset : $(properties) ] ;
+ ECHO "toolset:" /$(toolset)/ ;
+}
+
+notfile testTarget
+ : @buildRule
+ :
+ :
+ <description>target-requirement
+ <description>toolset-requirement:<description>conditioned-requirement
+ <description>unrelated-condition:<description>unrelated-description ;
+""" % {'toolset_name': toolset_name})
+
+ t.run_build_system()
+ t.expect_output_lines(configuring_default_toolset_message % toolset_name)
+ t.expect_output_lines("descriptions: /conditioned-requirement/ "
+ "/target-requirement/ /toolset-requirement/")
+ t.expect_output_lines("toolset: /%s/" % toolset_name)
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+test_default_toolset_on_os("NT" , "msvc")
+test_default_toolset_on_os("LINUX" , "gcc" )
+test_default_toolset_on_os("CYGWIN" , "gcc" )
+test_default_toolset_on_os("SomeOtherOS", "gcc" )
+test_default_toolset_requirements()
+test_conditions_on_default_toolset()
diff --git a/src/boost/tools/build/test/dependency_property.py b/src/boost/tools/build/test/dependency_property.py
new file mode 100644
index 000000000..7f0a56df1
--- /dev/null
+++ b/src/boost/tools/build/test/dependency_property.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Regression test: virtual targets with different dependency properties were
+# considered different by 'virtual-target.register', but the code which
+# determined the actual target paths ignored dependency properties so both
+# targets ended up being in the same location.
+#
+# This test has flip-flopped several times between passing and failing.
+# Currently, the library is only considered relevant for linking and thus
+# does not cause a conflict. SJW 20180115
+
+import BoostBuild
+
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """\
+lib foo : foo.cpp ;
+exe hello : hello.cpp ;
+exe hello2 : hello.cpp : <library>foo ;
+""")
+
+t.write("hello.cpp", "int main() {}\n")
+
+t.write("foo.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void foo() {}
+""")
+
+t.run_build_system(["--no-error-backtrace"], status=0)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/dependency_test.py b/src/boost/tools/build/test/dependency_test.py
new file mode 100644
index 000000000..a74df12ae
--- /dev/null
+++ b/src/boost/tools/build/test/dependency_test.py
@@ -0,0 +1,243 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2005, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+
+def test_basic():
+ t = BoostBuild.Tester(["-d3", "-d+12"], use_test_config=False)
+
+ t.write("a.cpp", """
+#include <a.h>
+# include "a.h"
+#include <x.h>
+int main() {}
+""")
+ t.write("a.h", "\n")
+ t.write("a_c.c", """\
+#include <a.h>
+# include "a.h"
+#include <x.h>
+""")
+ t.write("b.cpp", """\
+#include "a.h"
+int main() {}
+""")
+ t.write("b.h", "\n")
+ t.write("c.cpp", """\
+#include "x.h"
+int main() {}
+""")
+ t.write("e.cpp", """\
+#include "x.h"
+int main() {}
+""")
+ t.write("x.foo", "")
+ t.write("y.foo", "")
+
+ t.write("src1/a.h", '#include "b.h"\n')
+ t.write("src1/b.h", '#include "c.h"\n')
+ t.write("src1/c.h", "\n")
+ t.write("src1/z.h", """\
+extern int dummy_variable_suppressing_empty_file_warning_on_hp_cxx_compiler;
+""")
+
+ t.write("src2/b.h", "\n")
+
+ t.write("jamroot.jam", """\
+import foo ;
+import types/cpp ;
+import types/exe ;
+
+project test : requirements <include>src1 ;
+
+exe a : x.foo a.cpp a_c.c ;
+exe b : b.cpp ;
+
+# Because of <define>FOO, c.cpp will be compiled to a different directory than
+# everything for main target "a". Therefore, without <implicit-dependency>, C
+# preprocessor processing that module will not find "x.h", which is part of
+# "a"'s dependency graph.
+#
+# --------------------------
+# More detailed explanation:
+# --------------------------
+# c.cpp includes x.h which does not exist on the current include path so Boost
+# Jam will try to match it to existing Jam targets to cover cases as this one
+# where the file is generated by the same build.
+#
+# However, as x.h is not part of "c" metatarget's dependency graph, Boost
+# Build will not actualize its target by default, i.e. create its Jam target.
+#
+# To get the Jam target created in time, we use the <implicit-dependency>
+# feature. This tells Boost Build that it needs to actualize the dependency
+# graph for metatarget "a", even though that metatarget has not been directly
+# mentioned and is not a dependency for any of the metatargets mentioned in the
+# current build request.
+#
+# Note that Boost Build does not automatically add a dependency between the
+# Jam targets in question so, if Boost Jam does not add a dependency on a target
+# from that other dependency graph (x.h in our case), i.e. if c.cpp does not
+# actually include x.h, us actualizing it will have no effect in the end as
+# Boost Jam will not have a reason to actually build those targets in spite of
+# knowing about them.
+exe c : c.cpp : <define>FOO <implicit-dependency>a ;
+""")
+
+ t.write("foo.jam", """\
+import generators ;
+import modules ;
+import os ;
+import print ;
+import type ;
+import types/cpp ;
+
+type.register FOO : foo ;
+
+generators.register-standard foo.foo : FOO : CPP H ;
+
+nl = "
+" ;
+
+rule foo ( targets * : sources * : properties * )
+{
+ # On NT, you need an exported symbol in order to have an import library
+ # generated. We will not really use the symbol defined here, just force the
+ # import library creation.
+ if ( [ os.name ] = NT || [ modules.peek : OS ] in CYGWIN ) &&
+ <main-target-type>LIB in $(properties)
+ {
+ .decl = "void __declspec(dllexport) foo() {}" ;
+ }
+ print.output $(<[1]) ;
+ print.text $(.decl:E="//")$(nl) ;
+ print.output $(<[2]) ;
+ print.text "#include <z.h>"$(nl) ;
+}
+""")
+
+ t.write("foo.py",
+r"""import bjam
+import b2.build.type as type
+import b2.build.generators as generators
+
+from b2.manager import get_manager
+
+type.register("FOO", ["foo"])
+generators.register_standard("foo.foo", ["FOO"], ["CPP", "H"])
+
+def prepare_foo(targets, sources, properties):
+ if properties.get('os') in ['windows', 'cygwin']:
+ bjam.call('set-target-variable', targets, "DECL",
+ "void __declspec(dllexport) foo() {}")
+
+get_manager().engine().register_action("foo.foo",
+ "echo -e $(DECL:E=//)\\n > $(<[1])\n"
+ "echo -e "#include <z.h>\\n" > $(<[2])\n", function=prepare_foo)
+""")
+
+ # Check that main target 'c' was able to find 'x.h' from 'a's dependency
+ # graph.
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/c.exe")
+
+ # Check handling of first level includes.
+
+ # Both 'a' and 'b' include "a.h" and should be updated.
+ t.touch("a.h")
+ t.run_build_system()
+
+ t.expect_touch("bin/$toolset/debug*/a.exe")
+ t.expect_touch("bin/$toolset/debug*/a.obj")
+ t.expect_touch("bin/$toolset/debug*/a_c.obj")
+ t.expect_touch("bin/$toolset/debug*/b.exe")
+ t.expect_touch("bin/$toolset/debug*/b.obj")
+ t.ignore_touch("bin/*/a.rsp")
+ t.ignore_touch("bin/*/b.rsp")
+ t.expect_nothing_more()
+
+ # Only source files using include <a.h> should be compiled.
+ t.touch("src1/a.h")
+ t.run_build_system()
+
+ t.expect_touch("bin/$toolset/debug*/a.exe")
+ t.expect_touch("bin/$toolset/debug*/a.obj")
+ t.expect_touch("bin/$toolset/debug*/a_c.obj")
+ t.ignore_touch("bin/*/a.rsp")
+ t.expect_nothing_more()
+
+ # "src/a.h" includes "b.h" (in the same dir).
+ t.touch("src1/b.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/a.exe")
+ t.expect_touch("bin/$toolset/debug*/a.obj")
+ t.expect_touch("bin/$toolset/debug*/a_c.obj")
+ t.ignore_touch("bin/*/a.rsp")
+ t.expect_nothing_more()
+
+ # Included by "src/b.h". We had a bug: file included using double quotes
+ # (e.g. "b.h") was not scanned at all in this case.
+ t.touch("src1/c.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/a.exe")
+
+ t.touch("b.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ # Test dependency on a generated header.
+ #
+ # TODO: we have also to check that generated header is found correctly if
+ # it is different for different subvariants. Lacking any toolset support,
+ # this check will be implemented later.
+ t.touch("x.foo")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/a.obj")
+ t.expect_touch("bin/$toolset/debug*/a_c.obj")
+
+ # Check that generated headers are scanned for dependencies as well.
+ t.touch("src1/z.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/a.obj")
+ t.expect_touch("bin/$toolset/debug*/a_c.obj")
+
+ t.cleanup()
+
+
+def test_scanned_includes_with_absolute_paths():
+ """
+ Regression test: on Windows, <includes> with absolute paths were not
+ considered when scanning dependencies.
+
+ """
+ t = BoostBuild.Tester(["-d3", "-d+12"])
+
+ t.write("jamroot.jam", """\
+path-constant TOP : . ;
+exe app : main.cpp : <include>$(TOP)/include ;
+""");
+
+ t.write("main.cpp", """\
+#include <dir/header.h>
+int main() {}
+""")
+
+ t.write("include/dir/header.h", "\n")
+
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/main.obj")
+
+ t.touch("include/dir/header.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/main.obj")
+
+ t.cleanup()
+
+
+test_basic()
+test_scanned_includes_with_absolute_paths()
diff --git a/src/boost/tools/build/test/disambiguation.py b/src/boost/tools/build/test/disambiguation.py
new file mode 100644
index 000000000..18cc13c19
--- /dev/null
+++ b/src/boost/tools/build/test/disambiguation.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that it is possible to add a suffix to a main target name to disambiguate
+# that main target from another, and that this does not affect the names of the
+# generated targets.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+exe hello.exe : hello.obj ;
+obj hello.obj : hello.cpp : <variant>debug ;
+obj hello.obj2 : hello.cpp : <variant>release ;
+""")
+
+t.write("hello.cpp", """
+int main() {}
+""")
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+t.expect_addition("bin/$toolset/debug*/hello.obj")
+t.expect_addition("bin/$toolset/release*/hello.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/dll_path.py b/src/boost/tools/build/test/dll_path.py
new file mode 100644
index 000000000..456911659
--- /dev/null
+++ b/src/boost/tools/build/test/dll_path.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+
+# Copyright (C) 2003. Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that the <dll-path> property is correctly set when using
+# <hardcode-dll-paths>true.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# The point of this test is to have exe "main" which uses library "b", which
+# uses library "a". When "main" is built with <hardcode-dll-paths>true, paths
+# to both libraries should be present as values of <dll-path> feature. We
+# create a special target type which reports <dll-path> values on its sources
+# and compare the list of found values with out expectations.
+
+t.write("jamroot.jam", "using dll_paths ;")
+t.write("jamfile.jam", """\
+exe main : main.cpp b//b ;
+explicit main ;
+path-list mp : main ;
+""")
+
+t.write("main.cpp", "int main() {}\n")
+t.write("dll_paths.jam", """\
+import "class" : new ;
+import feature ;
+import generators ;
+import print ;
+import sequence ;
+import type ;
+
+rule init ( )
+{
+ type.register PATH_LIST : pathlist ;
+
+ class dll-paths-list-generator : generator
+ {
+ rule __init__ ( )
+ {
+ generator.__init__ dll_paths.list : EXE : PATH_LIST ;
+ }
+
+ rule generated-targets ( sources + : property-set : project name ? )
+ {
+ local dll-paths ;
+ for local s in $(sources)
+ {
+ local a = [ $(s).action ] ;
+ if $(a)
+ {
+ local p = [ $(a).properties ] ;
+ dll-paths += [ $(p).get <dll-path> ] ;
+ }
+ }
+ return [ generator.generated-targets $(sources) :
+ [ $(property-set).add-raw $(dll-paths:G=<dll-path>) ] :
+ $(project) $(name) ] ;
+ }
+ }
+ generators.register [ new dll-paths-list-generator ] ;
+}
+
+rule list ( target : sources * : properties * )
+{
+ local paths = [ feature.get-values <dll-path> : $(properties) ] ;
+ paths = [ sequence.insertion-sort $(paths) ] ;
+ print.output $(target) ;
+ print.text $(paths) ;
+}
+""")
+
+t.write("dll_paths.py", """\
+import bjam
+
+import b2.build.type as type
+import b2.build.generators as generators
+
+from b2.manager import get_manager
+
+def init():
+ type.register("PATH_LIST", ["pathlist"])
+
+ class DllPathsListGenerator(generators.Generator):
+
+ def __init__(self):
+ generators.Generator.__init__(self, "dll_paths.list", False,
+ ["EXE"], ["PATH_LIST"])
+
+ def generated_targets(self, sources, ps, project, name):
+ dll_paths = []
+ for s in sources:
+ a = s.action()
+ if a:
+ p = a.properties()
+ dll_paths += p.get('dll-path')
+ dll_paths.sort()
+ return generators.Generator.generated_targets(self, sources,
+ ps.add_raw(["<dll-path>" + p for p in dll_paths]), project,
+ name)
+
+ generators.register(DllPathsListGenerator())
+
+command = \"\"\"
+echo $(PATHS) > $(<[1])
+\"\"\"
+def function(target, sources, ps):
+ bjam.call('set-target-variable', target, "PATHS", ps.get('dll-path'))
+
+get_manager().engine().register_action("dll_paths.list", command,
+ function=function)
+""")
+
+t.write("a/jamfile.jam", "lib a : a.cpp ;")
+t.write("a/a.cpp", """\
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.write("b/jamfile.jam", "lib b : b.cpp ../a//a ;")
+t.write("b/b.cpp", """\
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+bar() {}
+""")
+
+t.run_build_system(["hardcode-dll-paths=true"])
+
+t.expect_addition("bin/$toolset/debug*/mp.pathlist")
+
+es1 = t.adjust_name("a/bin/$toolset/debug*")
+es2 = t.adjust_name("b/bin/$toolset/debug*")
+
+t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es1)
+t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es2)
+
+t.rm("bin/$toolset/debug*/mp.pathlist")
+
+# Now run the same checks with pre-built libraries
+adll = t.glob_file("a/bin/$toolset/debug*/a.dll")
+bdll = t.glob_file("b/bin/$toolset/debug*/b.dll")
+t.write("b/jamfile.jam", """
+local bdll = %s ;
+# Make sure that it is found even with multiple source-locations
+project : source-location c $(bdll:D) ;
+lib b : ../a//a : <file>$(bdll:D=) ;
+""" % bdll.replace("\\", "\\\\"))
+t.run_build_system(["hardcode-dll-paths=true"])
+t.expect_addition("bin/$toolset/debug*/mp.pathlist")
+
+t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es1)
+t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es2)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/double_loading.py b/src/boost/tools/build/test/double_loading.py
new file mode 100644
index 000000000..9b99964cb
--- /dev/null
+++ b/src/boost/tools/build/test/double_loading.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+# Regression test for double loading of the same Jamfile.
+t.write("jamroot.jam", "")
+t.write("jamfile.jam", "build-project subdir ;")
+t.write("subdir/jamfile.jam", 'ECHO "Loaded subdir" ;')
+
+t.run_build_system(subdir="subdir")
+t.expect_output_lines("Loaded subdir")
+
+
+# Regression test for a more contrived case. The top-level Jamfile refers to
+# subdir via use-project, while subdir's Jamfile is being loaded. The
+# motivation why use-project referring to subprojects is useful can be found
+# at: http://article.gmane.org/gmane.comp.lib.boost.build/3906
+t.write("jamroot.jam", "")
+t.write("jamfile.jam", "use-project /subdir : subdir ;")
+t.write("subdir/jamfile.jam", "project subdir ;")
+
+t.run_build_system(subdir="subdir");
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/duplicate.py b/src/boost/tools/build/test/duplicate.py
new file mode 100644
index 000000000..6e65f9117
--- /dev/null
+++ b/src/boost/tools/build/test/duplicate.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This test tries to stage the same file to the same location by *two* different
+# stage rules, in two different projects. This is not exactly good thing to do,
+# but still, V2 should handle this. We had two bugs:
+# - since the file is referred from two projects, we created to different
+# virtual targets
+# - we also failed to figure out that the two target corresponding to the copied
+# files (created in two projects) are actually equivalent.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("a.cpp", """
+""")
+
+t.write("jamroot.jam", """
+build-project a ;
+build-project b ;
+""")
+
+t.write("a/jamfile.jam", """
+stage bin : ../a.cpp : <location>../dist ;
+""")
+
+t.write("b/jamfile.jam", """
+stage bin : ../a.cpp : <location>../dist ;
+""")
+
+t.run_build_system()
+t.expect_addition("dist/a.cpp")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/example_customization.py b/src/boost/tools/build/test/example_customization.py
new file mode 100644
index 000000000..16c100ec7
--- /dev/null
+++ b/src/boost/tools/build/test/example_customization.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'customization' example.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.set_tree("../example/customization")
+
+t.run_build_system()
+
+t.expect_addition(["bin/$toolset/debug*/codegen.exe",
+ "bin/$toolset/debug*/usage.cpp"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/example_gettext.py b/src/boost/tools/build/test/example_gettext.py
new file mode 100644
index 000000000..346070203
--- /dev/null
+++ b/src/boost/tools/build/test/example_gettext.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'gettext' example.
+
+import BoostBuild
+import os
+import string
+
+t = BoostBuild.Tester()
+
+t.set_tree("../example/gettext")
+
+t.run_build_system(stderr=None)
+
+t.expect_addition(["bin/$toolset/debug*/main.exe",
+ "bin/$toolset/debug*/russian.mo"])
+
+file = t.adjust_names(["bin/$toolset/debug*/main.exe"])[0]
+
+input_fd = os.popen(file)
+input = input_fd.read();
+
+t.fail_test(input.find("international hello") != 0)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/example_libraries.py b/src/boost/tools/build/test/example_libraries.py
new file mode 100644
index 000000000..12f2d0998
--- /dev/null
+++ b/src/boost/tools/build/test/example_libraries.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'libraries' example.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.set_tree("../example/libraries")
+
+t.run_build_system()
+
+t.expect_addition(["app/bin/$toolset/debug*/app.exe",
+ "util/foo/bin/$toolset/debug*/bar.dll"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/example_make.py b/src/boost/tools/build/test/example_make.py
new file mode 100644
index 000000000..7036a9772
--- /dev/null
+++ b/src/boost/tools/build/test/example_make.py
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'make' example.
+
+import BoostBuild
+import sys
+
+t = BoostBuild.Tester(['example.python.interpreter=%s' % sys.executable])
+t.set_tree("../example/make")
+t.run_build_system()
+t.expect_addition(["bin/main.cpp"])
+t.cleanup()
diff --git a/src/boost/tools/build/test/example_qt4.py b/src/boost/tools/build/test/example_qt4.py
new file mode 100644
index 000000000..ec6bb32de
--- /dev/null
+++ b/src/boost/tools/build/test/example_qt4.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'qt4' examples.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.set_tree("../example/qt/qt4/hello")
+t.run_build_system()
+t.expect_addition(["bin/$toolset/debug*/threading-multi/arrow"])
+
+t.set_tree("../example/qt/qt4/moccable-cpp")
+t.run_build_system()
+t.expect_addition(["bin/$toolset/debug*/threading-multi/main"])
+
+t.set_tree("../example/qt/qt4/uic")
+t.run_build_system()
+t.expect_addition(["bin/$toolset/debug*/threading-multi/hello"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/exit_status.py b/src/boost/tools/build/test/exit_status.py
new file mode 100755
index 000000000..1e14dea58
--- /dev/null
+++ b/src/boost/tools/build/test/exit_status.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2010.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that build failure results in non-zero exit status
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester()
+
+# Create the needed files.
+t.write("jamroot.jam", """
+exe hello : hello.cpp ;
+""")
+
+t.write("hello.cpp", """
+int main() {
+""")
+
+t.run_build_system(status=1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/expansion.py b/src/boost/tools/build/test/expansion.py
new file mode 100644
index 000000000..7ab350d8b
--- /dev/null
+++ b/src/boost/tools/build/test/expansion.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+import BoostBuild
+
+t = BoostBuild.Tester(arguments=["--config="], pass_toolset=0)
+
+t.write("source.input", "")
+
+t.write("test-properties.jam", """
+import feature : feature ;
+import generators ;
+import toolset ;
+import type ;
+
+# We're not using the toolset at all, and we want to
+# suppress toolset initialization to avoid surprises.
+feature.extend toolset : null ;
+
+type.register CHECK : check ;
+type.register INPUT : input ;
+feature expected-define : : free ;
+feature unexpected-define : : free ;
+toolset.flags test-properties DEFINES : <define> ;
+toolset.flags test-properties EXPECTED : <expected-define> ;
+toolset.flags test-properties UNEXPECTED : <unexpected-define> ;
+generators.register-standard test-properties.check : INPUT : CHECK ;
+rule check ( target : source : properties * )
+{
+ local defines = [ on $(target) return $(DEFINES) ] ;
+ for local macro in [ on $(target) return $(EXPECTED) ]
+ {
+ if ! ( $(macro) in $(defines) )
+ {
+ EXIT expected $(macro) for $(target) in $(properties) : 1 ;
+ }
+ }
+ for local macro in [ on $(target) return $(UNEXPECTED) ]
+ {
+ if $(macro) in $(defines)
+ {
+ EXIT unexpected $(macro) for $(target) in $(properties) : 1 ;
+ }
+ }
+}
+actions check
+{
+ echo okay > $(<)
+}
+""")
+
+t.write("jamfile.jam", """
+import test-properties ;
+# See if default value of composite feature 'cf' will be expanded to
+# <define>CF_IS_OFF.
+check a : source.input : <expected-define>CF_IS_OFF ;
+
+# See if subfeature in requirements in expanded.
+check b : source.input : <cf>on-1
+ <expected-define>CF_1 <unexpected-define>CF_IS_OFF ;
+
+# See if conditional requirements are recursively expanded.
+check c : source.input : <toolset>null:<variant>release
+ <variant>release:<define>FOO <expected-define>FOO
+ ;
+
+# Composites specified in the default build should not
+# be expanded if they are overridden in the the requirements.
+check d : source.input : <cf>on <unexpected-define>CF_IS_OFF : <cf>off ;
+
+# Overriding a feature should clear subfeatures and
+# apply default values of subfeatures.
+check e : source.input : <cf>always
+ <unexpected-define>CF_IS_OFF <expected-define>CF_2 <unexpected-define>CF_1
+ : <cf>on-1 ;
+
+# Subfeatures should not be changed if the parent feature doesn't change
+check f : source.input : <cf>on <expected-define>CF_1 : <cf>on-1 ;
+
+# If a subfeature is not specific to the value of the parent feature,
+# then changing the parent value should not clear the subfeature.
+check g : source.input : <fopt>off <expected-define>FOPT_2 : <fopt>on-2 ;
+
+# If the default value of a composite feature adds an optional
+# feature which has a subfeature with a default, then that
+# default should be added.
+check h : source.input : <expected-define>CX_2 ;
+
+# If the default value of a feature is used, then the
+# default value of its subfeatures should also be used.
+check i : source.input : <expected-define>SF_1 ;
+
+# Subfeatures should be expanded when listed in a
+# target reference.
+check j-impl : source.input : <expected-define>CF_1 ;
+explicit j-impl ;
+alias j : j-impl/<cf>on-1 ;
+""")
+
+t.write("jamroot.jam", """
+import feature ;
+feature.feature cf : off on always : composite incidental ;
+feature.compose <cf>off : <define>CF_IS_OFF ;
+feature.subfeature cf on : version : 1 2 : composite optional incidental ;
+feature.compose <cf-on:version>1 : <define>CF_1 ;
+feature.subfeature cf always : version : 1 2 : composite incidental ;
+feature.compose <cf-always:version>1 : <define>CF_2 ;
+feature.feature fopt : on off : optional incidental ;
+feature.subfeature fopt : version : 1 2 : composite incidental ;
+feature.compose <fopt-version>2 : <define>FOPT_2 ;
+
+feature.feature cx1 : on : composite incidental ;
+feature.feature cx2 : on : optional incidental ;
+feature.subfeature cx2 on : sub : 1 : composite incidental ;
+feature.compose <cx1>on : <cx2>on ;
+feature.compose <cx2-on:sub>1 : <define>CX_2 ;
+
+feature.feature sf : a : incidental ;
+feature.subfeature sf a : sub : 1 : composite incidental ;
+feature.compose <sf-a:sub>1 : <define>SF_1 ;
+""")
+
+t.expand_toolset("jamfile.jam")
+
+t.run_build_system()
+t.expect_addition(["bin/debug/a.check",
+ "bin/debug/b.check",
+ "bin/null/release/c.check",
+ "bin/debug/d.check",
+ "bin/debug/e.check",
+ "bin/debug/f.check",
+ "bin/debug/g.check",
+ "bin/debug/h.check",
+ "bin/debug/i.check"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/explicit.py b/src/boost/tools/build/test/explicit.py
new file mode 100644
index 000000000..918994cb1
--- /dev/null
+++ b/src/boost/tools/build/test/explicit.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """\
+exe hello : hello.cpp ;
+exe hello2 : hello.cpp ;
+explicit hello2 ;
+""")
+
+t.write("hello.cpp", "int main() {}\n")
+
+t.run_build_system()
+t.ignore("*.tds")
+t.expect_addition(BoostBuild.List("bin/$toolset/debug*/hello") * \
+ [".exe", ".obj"])
+t.ignore_addition("bin/*/hello.rsp")
+t.expect_nothing_more()
+
+t.run_build_system(["hello2"])
+t.expect_addition("bin/$toolset/debug*/hello2.exe")
+
+t.rm(".")
+
+
+# Test that 'explicit' used in a helper rule applies to the current project, and
+# not to the Jamfile where the helper rule is defined.
+t.write("jamroot.jam", """\
+rule myinstall ( name : target )
+{
+ install $(name)-bin : $(target) ;
+ explicit $(name)-bin ;
+ alias $(name) : $(name)-bin ;
+}
+""")
+
+t.write("sub/a.cpp", "\n")
+t.write("sub/jamfile.jam", "myinstall dist : a.cpp ;")
+
+t.run_build_system(subdir="sub")
+t.expect_addition("sub/dist-bin/a.cpp")
+
+t.rm("sub/dist-bin")
+
+t.write("sub/jamfile.jam", """\
+myinstall dist : a.cpp ;
+explicit dist ;
+""")
+
+t.run_build_system(subdir="sub")
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/feature_cxxflags.py b/src/boost/tools/build/test/feature_cxxflags.py
new file mode 100755
index 000000000..0e5aeba55
--- /dev/null
+++ b/src/boost/tools/build/test/feature_cxxflags.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+# Copyright 2014 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the cxxflags feature
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# cxxflags should be applied to C++ compilation,
+# but not to C.
+t.write("Jamroot.jam", """
+obj test-cpp : test.cpp : <cxxflags>-DOKAY ;
+obj test-c : test.c : <cxxflags>-DBAD ;
+""")
+
+t.write("test.cpp", """
+#ifndef OKAY
+#error Cannot compile without OKAY
+#endif
+""")
+
+t.write("test.c", """
+#ifdef BAD
+#error Cannot compile with BAD
+#endif
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/test-cpp.obj")
+t.expect_addition("bin/$toolset/debug*/test-c.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/feature_force_include.py b/src/boost/tools/build/test/feature_force_include.py
new file mode 100644
index 000000000..03aea4891
--- /dev/null
+++ b/src/boost/tools/build/test/feature_force_include.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+# Copyright 2020 Nikita Kniazev
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the force-include feature
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("Jamroot.jam", """
+obj test-cpp : test.cpp : <force-include>one.h <force-include>two.h ;
+obj test-c : test.c : <force-include>one.h <force-include>two.h ;
+""")
+
+for name in ("test.cpp", "test.c"):
+ t.write(name, """
+#ifndef ONE
+#error Cannot compile without ONE
+#endif
+#ifndef TWO
+#error Cannot compile without TWO
+#endif
+""")
+
+t.write("one.h", """
+#define ONE
+""")
+
+t.write("two.h", """
+#define TWO
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/test-cpp.obj")
+t.expect_addition("bin/$toolset/debug*/test-c.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/feature_implicit_dependency.py b/src/boost/tools/build/test/feature_implicit_dependency.py
new file mode 100644
index 000000000..2d22f3408
--- /dev/null
+++ b/src/boost/tools/build/test/feature_implicit_dependency.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+
+# Copyright (c) Steven Watanabe 2018.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that a single main target can be used for
+# implicit dependencies of multiple different types.
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=False)
+
+t.write("input.sss", "")
+
+t.write("Jamroot.jam", """
+import type ;
+import common ;
+import generators ;
+import "class" : new ;
+import feature : feature ;
+import toolset : flags ;
+
+type.register AAA : aaa ;
+type.register BBB : bbb ;
+type.register CCC : ccc ;
+type.register DDD : ddd ;
+type.register SSS : sss ;
+
+feature aaa-path : : free path ;
+feature bbb-path : : free path ;
+
+class aaa-action : action
+{
+ rule adjust-properties ( property-set )
+ {
+ local s = [ $(self.targets[1]).creating-subvariant ] ;
+ return [ $(property-set).add-raw
+ [ $(s).implicit-includes aaa-path : AAA ] ] ;
+ }
+}
+
+class aaa-generator : generator
+{
+ rule action-class ( )
+ {
+ return aaa-action ;
+ }
+}
+
+class bbb-action : action
+{
+ rule adjust-properties ( property-set )
+ {
+ local s = [ $(self.targets[1]).creating-subvariant ] ;
+ return [ $(property-set).add-raw
+ [ $(s).implicit-includes bbb-path : BBB ] ] ;
+ }
+}
+
+class bbb-generator : generator
+{
+ rule action-class ( )
+ {
+ return bbb-action ;
+ }
+}
+
+generators.register-standard common.copy : SSS : AAA ;
+generators.register-standard common.copy : SSS : BBB ;
+
+# Produce two targets from a single source
+rule make-aaa-bbb ( project name ? : property-set : sources * )
+{
+ local result ;
+ local aaa = [ generators.construct $(project) $(name) : AAA :
+ [ $(property-set).add-raw <location-prefix>a-loc ] : $(sources) ] ;
+ local bbb = [ generators.construct $(project) $(name) : BBB :
+ [ $(property-set).add-raw <location-prefix>b-loc ] : $(sources) ] ;
+ return [ $(aaa[1]).add $(bbb[1]) ] $(aaa[2-]) $(bbb[2-]) ;
+}
+
+generate input : input.sss : <generating-rule>@make-aaa-bbb ;
+explicit input ;
+
+flags make-ccc AAAPATH : <aaa-path> ;
+rule make-ccc ( target : sources * : properties * )
+{
+ ECHO aaa path\: [ on $(target) return $(AAAPATH) ] ;
+ common.copy $(target) : $(sources) ;
+}
+
+flags make-ddd BBBPATH : <bbb-path> ;
+rule make-ddd ( target : sources * : properties * )
+{
+ ECHO bbb path\: [ on $(target) return $(BBBPATH) ] ;
+ common.copy $(target) : $(sources) ;
+}
+
+generators.register [ new aaa-generator $(__name__).make-ccc : SSS : CCC ] ;
+generators.register [ new bbb-generator $(__name__).make-ddd : SSS : DDD ] ;
+
+# This should have <aaapath>bin/a-loc
+ccc output-c : input.sss : <implicit-dependency>input ;
+# This should have <bbbpath>bin/b-loc
+ddd output-d : input.sss : <implicit-dependency>input ;
+""")
+
+t.run_build_system()
+t.expect_output_lines(["aaa path: bin/a-loc", "bbb path: bin/b-loc"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/feature_relevant.py b/src/boost/tools/build/test/feature_relevant.py
new file mode 100644
index 000000000..a6e20aafc
--- /dev/null
+++ b/src/boost/tools/build/test/feature_relevant.py
@@ -0,0 +1,142 @@
+#!/usr/bin/python
+
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the <relevant> feature
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("xxx.jam", """
+import type ;
+import feature : feature ;
+import toolset : flags ;
+import generators ;
+type.register XXX : xxx ;
+type.register YYY : yyy ;
+feature xxxflags : : free ;
+generators.register-standard xxx.run : YYY : XXX ;
+# xxxflags is relevant because it is used by flags
+flags xxx.run OPTIONS : <xxxflags> ;
+actions run
+{
+ echo okay > $(<)
+}
+""")
+
+t.write("zzz.jam", """
+import xxx ;
+import type ;
+import feature : feature ;
+import generators ;
+type.register ZZZ : zzz ;
+feature zzz.enabled : off on : propagated ;
+# zzz.enabled is relevant because it is used in the generator's
+# requirements
+generators.register-standard zzz.run : XXX : ZZZ : <zzz.enabled>on ;
+actions run
+{
+ echo okay > $(<)
+}
+""")
+
+t.write("aaa.jam", """
+import zzz ;
+import type ;
+import feature : feature ;
+import generators ;
+import toolset : flags ;
+type.register AAA : aaa ;
+feature aaaflags : : free ;
+generators.register-standard aaa.run : ZZZ : AAA ;
+flags aaa.run OPTIONS : <aaaflags> ;
+actions run
+{
+ echo okay > $(<)
+}
+""")
+
+t.write("Jamroot.jam", """
+import xxx ;
+import zzz ;
+import aaa ;
+import feature : feature ;
+
+# f1 is relevant, because it is composite and <xxxflags> is relevant
+feature f1 : n y : composite propagated ;
+feature.compose <f1>y : <xxxflags>-no1 ;
+# f2 is relevant, because it is used in a conditional
+feature f2 : n y : propagated ;
+# f3 is relevant, because it is used to choose the target alternative
+feature f3 : n y : propagated ;
+# f4 is relevant, because it is marked as such explicitly
+feature f4 : n y : propagated ;
+# f5 is relevant because of the conditional usage-requirements
+feature f5 : n y : propagated ;
+# f6 is relevant because the indirect conditional indicates so
+feature f6 : n y : propagated ;
+# f7 is relevant because the icond7 says so
+feature f7 : n y : propagated ;
+
+# The same as f[n], except not propagated
+feature g1 : n y : composite ;
+feature.compose <g1>y : <xxxflags>-no1 ;
+feature g2 : n y ;
+feature g3 : n y ;
+feature g4 : n y ;
+feature g5 : n y ;
+feature g6 : n y ;
+feature g7 : n y ;
+
+project : default-build
+ <f1>y <f2>y <f3>y <f4>y <f5>y <f6>y <f7>y
+ <g1>y <g2>y <g3>y <g4>y <g5>y <g6>y <g7>y <zzz.enabled>on ;
+
+rule icond6 ( properties * )
+{
+ local result ;
+ if <f6>y in $(properties) || <g6>y in $(properties)
+ {
+ result += <xxxflags>-yes6 ;
+ }
+ return $(result)
+ <relevant>xxxflags:<relevant>f6
+ <relevant>xxxflags:<relevant>g6 ;
+}
+
+rule icond7 ( properties * )
+{
+ local result ;
+ if <f7>y in $(properties) || <g7>y in $(properties)
+ {
+ result += <aaaflags>-yes7 ;
+ }
+ return $(result)
+ <relevant>aaaflags:<relevant>f7
+ <relevant>aaaflags:<relevant>g7 ;
+}
+
+zzz out : in.yyy
+ : <f2>y:<xxxflags>-no2 <g2>y:<xxxflags>-no2 <relevant>f4 <relevant>g4
+ <conditional>@icond6
+ :
+ : <f5>y:<aaaflags>-yes5 <g5>y:<aaaflags>-yes5 <conditional>@icond7
+ ;
+alias out : : <f3>n ;
+alias out : : <g3>n ;
+# Features that are relevant for out are also relevant for check-propagate
+aaa check-propagate : out ;
+""")
+
+t.write("in.yyy", "")
+
+t.run_build_system()
+t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f6-y/g1-y/g2-y/g3-y/g4-y/g6-y/out.xxx")
+t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f6-y/g1-y/g2-y/g3-y/g4-y/g6-y/zzz.enabled-on/out.zzz")
+t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f5-y/f6-y/f7-y/zzz.enabled-on/check-propagate.aaa")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/feature_suppress_import_lib.py b/src/boost/tools/build/test/feature_suppress_import_lib.py
new file mode 100644
index 000000000..84de4c222
--- /dev/null
+++ b/src/boost/tools/build/test/feature_suppress_import_lib.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the suppress-import-lib feature
+
+# This used to cause the pdb and the import lib to get mixed up
+# if there are any exports.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("Jamroot.jam", """
+lib l : l.cpp : <suppress-import-lib>true ;
+""")
+
+t.write("l.cpp", """
+void
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+f() {}
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/l.obj")
+t.expect_addition("bin/$toolset/debug*/l.dll")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/file_types.py b/src/boost/tools/build/test/file_types.py
new file mode 100644
index 000000000..9924e336a
--- /dev/null
+++ b/src/boost/tools/build/test/file_types.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+#
+# Copyright 2018 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the mapping of various suffixes
+# In particular, .so[.version] needs to
+# be mapped as a SHARED_LIB.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("Jamroot.jam", """\
+import type : type ;
+ECHO [ type source.c ] ;
+ECHO [ type source.cc ] ;
+ECHO [ type source.cxx ] ;
+ECHO [ type source.cpp ] ;
+ECHO [ type source.o ] ;
+ECHO [ type source.obj ] ;
+ECHO [ type boost_system.lib ] ;
+ECHO [ type boost_system.so ] ;
+ECHO [ type boost_system.dll ] ;
+EXIT [ type boost_system.so.1.66.0 ] : 0 ;
+""")
+
+t.run_build_system(stdout="""\
+C
+CPP
+CPP
+CPP
+OBJ
+OBJ
+STATIC_LIB
+SHARED_LIB
+SHARED_LIB
+SHARED_LIB
+""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/flags.py b/src/boost/tools/build/test/flags.py
new file mode 100644
index 000000000..3a12c94d9
--- /dev/null
+++ b/src/boost/tools/build/test/flags.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+# Copyright (C) Steven Watanabe 2018
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the check-has-flag rule
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# We need an object file before we can run the actual test.
+t.write('input.cpp', 'void f() {}\n')
+t.write('Jamroot.jam', 'obj input : input.cpp ;')
+t.run_build_system()
+
+linker_input = t.glob_file('bin/$toolset/debug*/input.obj')
+
+# Check every possible result of pass or fail.
+t.write('Jamroot.jam', '''
+import flags ;
+import modules ;
+OBJECT_FILE = [ modules.peek : OBJECT_FILE ] ;
+obj fail_cpp : test.cpp : [ check-has-flag <cxxflags>--illegal-flag-cpp
+ : <define>ERROR : <define>OK ] ;
+obj pass_cpp : test.cpp : [ check-has-flag <cxxflags>-DMACRO_CPP
+ : <define>OK : <define>ERROR ] ;
+obj fail_c : test.cpp : [ check-has-flag <cflags>--illegal-flag-c
+ : <define>ERROR : <define>OK ] ;
+obj pass_c : test.cpp : [ check-has-flag <cflags>-DMACRO_C
+ : <define>OK : <define>ERROR ] ;
+obj fail_link : test.cpp : [ check-has-flag <linkflags>--illegal-flag-link
+ : <define>ERROR : <define>OK ] ;
+# The only thing that we can be certain the linker
+# will accept is the name of an object file.
+obj pass_link : test.cpp : [ check-has-flag <linkflags>$(OBJECT_FILE)
+ : <define>OK : <define>ERROR ] ;
+''')
+
+t.write('test.cpp', '''
+#ifdef ERROR
+#error ERROR defined
+#endif
+#ifndef OK
+#error ERROR not defined
+#endif
+''')
+
+# Don't check the status immediately, so that we have a chance
+# to print config.log. Also, we need a minimum of d2 to make
+# sure that we always see the commands and output.
+t.run_build_system(['-sOBJECT_FILE=' + linker_input, '-d2'], status=None)
+
+if t.status != 0:
+ log_file = t.read('bin/config.log')
+ BoostBuild.annotation("config.log", log_file)
+ t.fail_test(True)
+
+t.expect_output_lines([' - has --illegal-flag-cpp : no*',
+ ' - has -DMACRO_CPP : yes*',
+ ' - has --illegal-flag-c : no*',
+ ' - has -DMACRO_C : yes*',
+ ' - has --illegal-flag-link : no*',
+ ' - has *bin*/input.* : yes*'])
+t.expect_addition('bin/$toolset/debug*/fail_cpp.obj')
+t.expect_addition('bin/$toolset/debug*/pass_cpp.obj')
+t.expect_addition('bin/$toolset/debug*/fail_c.obj')
+t.expect_addition('bin/$toolset/debug*/pass_c.obj')
+t.expect_addition('bin/$toolset/debug*/fail_link.obj')
+t.expect_addition('bin/$toolset/debug*/pass_link.obj')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/gcc_runtime.py b/src/boost/tools/build/test/gcc_runtime.py
new file mode 100644
index 000000000..a7963c344
--- /dev/null
+++ b/src/boost/tools/build/test/gcc_runtime.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that on gcc, we correctly report a problem when static runtime is
+# requested for building a shared library.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+t.write("jamroot.jam", "lib hello : hello.cpp ;")
+t.write("hello.cpp", "int main() {}\n")
+
+t.run_build_system(["runtime-link=static"])
+t.expect_output_lines("warning: On gcc, DLLs can not be built with "
+ "'<runtime-link>static'.")
+t.expect_nothing_more()
+
+t.run_build_system(["link=static", "runtime-link=static"])
+t.expect_addition("bin/$toolset/debug*/link-static*/hello.obj")
+t.expect_addition("bin/$toolset/debug*/link-static*/hello.lib")
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/generator_selection.py b/src/boost/tools/build/test/generator_selection.py
new file mode 100755
index 000000000..84fc43158
--- /dev/null
+++ b/src/boost/tools/build/test/generator_selection.py
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+
+# Copyright 2008, 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that generators get selected correctly.
+#
+# We do not use the internal C++-compiler CPP --> OBJ generator to avoid
+# problems with specific compilers or their configurations, e.g. IBM's AIX test
+# runner 'AIX Version 5.3 TL7 SP5 (5300-07-05-0831)' using the 'IBM XL C/C++
+# for AIX, V12.1 (Version: 12.01.0000.0000)' reporting errors when run with a
+# source file whose suffix is not '.cpp'.
+
+import BoostBuild
+
+
+###############################################################################
+#
+# test_generator_added_after_already_building_a_target_of_its_target_type()
+# -------------------------------------------------------------------------
+#
+###############################################################################
+
+def test_generator_added_after_already_building_a_target_of_its_target_type():
+ """
+ Regression test for a Boost Build bug causing it to not use a generator
+ if it got added after already building a target of its target type.
+
+ """
+ t = BoostBuild.Tester()
+
+ t.write("dummy.cpp", "void f() {}\n")
+
+ t.write("jamroot.jam", """\
+import common ;
+import generators ;
+import type ;
+type.register MY_OBJ : my_obj ;
+generators.register-standard common.copy : CPP : MY_OBJ ;
+
+# Building this dummy target must not cause a later defined CPP target type
+# generator not to be recognized as viable.
+my-obj dummy : dummy.cpp ;
+alias the-other-obj : Other//other-obj ;
+""")
+
+ t.write("Other/source.extension", "A dummy source file.")
+
+ t.write("Other/mygen.jam", """\
+import common ;
+import generators ;
+import type ;
+type.register MY_TYPE : extension ;
+generators.register-standard $(__name__).generate-a-cpp-file : MY_TYPE : CPP ;
+rule generate-a-cpp-file { ECHO Generating a CPP file... ; }
+CREATE-FILE = [ common.file-creation-command ] ;
+actions generate-a-cpp-file { $(CREATE-FILE) "$(<)" }
+""")
+
+ t.write("Other/mygen.py", """\
+from __future__ import print_function
+import b2.build.generators as generators
+import b2.build.type as type
+
+from b2.manager import get_manager
+
+import os
+
+type.register('MY_TYPE', ['extension'])
+generators.register_standard('mygen.generate-a-cpp-file', ['MY_TYPE'], ['CPP'])
+if os.name == 'nt':
+ action = 'echo void g() {} > "$(<)"'
+else:
+ action = 'echo "void g() {}" > "$(<)"'
+def f(*args):
+ print("Generating a CPP file...")
+
+get_manager().engine().register_action("mygen.generate-a-cpp-file", action,
+ function=f)
+""")
+
+ t.write("Other/jamfile.jam", """\
+import mygen ;
+my-obj other-obj : source.extension ;
+""")
+
+ t.run_build_system()
+ t.expect_output_lines("Generating a CPP file...")
+ t.expect_addition("bin/dummy.my_obj")
+ t.expect_addition("Other/bin/other-obj.cpp")
+ t.expect_addition("Other/bin/other-obj.my_obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# test_using_a_derived_source_type_created_after_generator_already_used()
+# -----------------------------------------------------------------------
+#
+###############################################################################
+
+def test_using_a_derived_source_type_created_after_generator_already_used():
+ """
+ Regression test for a Boost Build bug causing it to not use a generator
+ with a source type derived from one of the generator's sources but created
+ only after already using the generateor.
+
+ """
+ t = BoostBuild.Tester()
+
+ t.write("dummy.xxx", "Hello. My name is Peter Pan.\n")
+
+ t.write("jamroot.jam", """\
+import common ;
+import generators ;
+import type ;
+type.register XXX : xxx ;
+type.register YYY : yyy ;
+generators.register-standard common.copy : XXX : YYY ;
+
+# Building this dummy target must not cause a later defined XXX2 target type not
+# to be recognized as a viable source type for building YYY targets.
+yyy dummy : dummy.xxx ;
+alias the-test-output : Other//other ;
+""")
+
+ t.write("Other/source.xxx2", "Hello. My name is Tinkerbell.\n")
+
+ t.write("Other/jamfile.jam", """\
+import type ;
+type.register XXX2 : xxx2 : XXX ;
+# We are careful not to do anything between defining our new XXX2 target type
+# and using the XXX --> YYY generator that could potentially cover the Boost
+# Build bug by clearing its internal viable source target type state.
+yyy other : source.xxx2 ;
+""")
+
+ t.run_build_system()
+ t.expect_addition("bin/dummy.yyy")
+ t.expect_addition("Other/bin/other.yyy")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+test_generator_added_after_already_building_a_target_of_its_target_type()
+test_using_a_derived_source_type_created_after_generator_already_used()
diff --git a/src/boost/tools/build/test/generators_test.py b/src/boost/tools/build/test/generators_test.py
new file mode 100644
index 000000000..148044fa5
--- /dev/null
+++ b/src/boost/tools/build/test/generators_test.py
@@ -0,0 +1,433 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004, 2005 Vladimir Prus
+# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import re
+
+
+def test_basic():
+ t = BoostBuild.Tester()
+ __write_appender(t, "appender.jam")
+ t.write("a.cpp", "")
+ t.write("b.cxx", "")
+ t.write("c.tui", "")
+ t.write("d.wd", "")
+ t.write("e.cpp", "")
+ t.write("x.l", "")
+ t.write("y.x_pro", "")
+ t.write("z.cpp", "")
+ t.write("lib/c.cpp", "int bar() { return 0; }\n")
+ t.write("lib/jamfile.jam", "my-lib auxilliary : c.cpp ;")
+ t.write("jamroot.jam",
+r"""import appender ;
+
+import "class" : new ;
+import generators ;
+import type ;
+
+
+################################################################################
+#
+# We use our own custom EXE, LIB & OBJ target generators as using the regular
+# ones would force us to have to deal with different compiler/linker specific
+# 'features' that really have nothing to do with this test. For example, IBM XL
+# C/C++ for AIX, V12.1 (Version: 12.01.0000.0000) compiler exits with a non-zero
+# exit code and thus fails our build when run with a source file using an
+# unknown suffix like '.marked_cpp'.
+#
+################################################################################
+
+type.register MY_EXE : my_exe ;
+type.register MY_LIB : my_lib ;
+type.register MY_OBJ : my_obj ;
+
+appender.register compile-c : C : MY_OBJ ;
+appender.register compile-cpp : CPP : MY_OBJ ;
+appender.register link-lib composing : MY_OBJ : MY_LIB ;
+appender.register link-exe composing : MY_OBJ MY_LIB : MY_EXE ;
+
+
+################################################################################
+#
+# LEX --> C
+#
+################################################################################
+
+type.register LEX : l ;
+
+appender.register lex-to-c : LEX : C ;
+
+
+################################################################################
+#
+# /--> tUI_H --\
+# tUI --< >--> CPP
+# \------------/
+#
+################################################################################
+
+type.register tUI : tui ;
+type.register tUI_H : tui_h ;
+
+appender.register ui-to-cpp : tUI tUI_H : CPP ;
+appender.register ui-to-h : tUI : tUI_H ;
+
+
+################################################################################
+#
+# /--> X1 --\
+# X_PRO --< >--> CPP
+# \--> X2 --/
+#
+################################################################################
+
+type.register X1 : x1 ;
+type.register X2 : x2 ;
+type.register X_PRO : x_pro ;
+
+appender.register x1-x2-to-cpp : X1 X2 : CPP ;
+appender.register x-pro-to-x1-x2 : X_PRO : X1 X2 ;
+
+
+################################################################################
+#
+# When the main target type is NM_EXE, build OBJ from CPP-MARKED and not from
+# anything else, e.g. directly from CPP.
+#
+################################################################################
+
+type.register CPP_MARKED : marked_cpp : CPP ;
+type.register POSITIONS : positions ;
+type.register NM.TARGET.CPP : target_cpp : CPP ;
+type.register NM_EXE : : MY_EXE ;
+
+appender.register marked-to-target-cpp : CPP_MARKED : NM.TARGET.CPP ;
+appender.register cpp-to-marked-positions : CPP : CPP_MARKED POSITIONS ;
+
+class "nm::target::cpp-obj-generator" : generator
+{
+ rule __init__ ( id )
+ {
+ generator.__init__ $(id) : NM.TARGET.CPP : MY_OBJ ;
+ generator.set-rule-name appender.appender ;
+ }
+
+ rule requirements ( )
+ {
+ return <main-target-type>NM_EXE ;
+ }
+
+ rule run ( project name ? : properties * : source : multiple ? )
+ {
+ if [ $(source).type ] = CPP
+ {
+ local converted = [ generators.construct $(project) : NM.TARGET.CPP
+ : $(properties) : $(source) ] ;
+ if $(converted)
+ {
+ return [ generators.construct $(project) : MY_OBJ :
+ $(properties) : $(converted[2]) ] ;
+ }
+ }
+ }
+}
+generators.register [ new "nm::target::cpp-obj-generator" target-obj ] ;
+generators.override target-obj : all ;
+
+
+################################################################################
+#
+# A more complex test case scenario with the following generators:
+# 1. WHL --> CPP, WHL_LR0, H, H(%_symbols)
+# 2. DLP --> CPP
+# 3. WD --> WHL(%_parser) DLP(%_lexer)
+# 4. A custom generator of higher priority than generators 1. & 2. that helps
+# disambiguate between them when generating CPP files from WHL and DLP
+# sources.
+#
+################################################################################
+
+type.register WHL : whl ;
+type.register DLP : dlp ;
+type.register WHL_LR0 : lr0 ;
+type.register WD : wd ;
+
+local whale-generator-id = [ appender.register whale : WHL : CPP WHL_LR0 H
+ H(%_symbols) ] ;
+local dolphin-generator-id = [ appender.register dolphin : DLP : CPP ] ;
+appender.register wd : WD : WHL(%_parser) DLP(%_lexer) ;
+
+class wd-to-cpp : generator
+{
+ rule __init__ ( id : sources * : targets * )
+ {
+ generator.__init__ $(id) : $(sources) : $(targets) ;
+ }
+
+ rule run ( project name ? : property-set : source )
+ {
+ local new-sources = $(source) ;
+ if ! [ $(source).type ] in WHL DLP
+ {
+ local r1 = [ generators.construct $(project) $(name) : WHL :
+ $(property-set) : $(source) ] ;
+ local r2 = [ generators.construct $(project) $(name) : DLP :
+ $(property-set) : $(source) ] ;
+ new-sources = [ sequence.unique $(r1[2-]) $(r2[2-]) ] ;
+ }
+
+ local result ;
+ for local i in $(new-sources)
+ {
+ local t = [ generators.construct $(project) $(name) : CPP :
+ $(property-set) : $(i) ] ;
+ result += $(t[2-]) ;
+ }
+ return $(result) ;
+ }
+}
+generators.override $(__name__).wd-to-cpp : $(whale-generator-id) ;
+generators.override $(__name__).wd-to-cpp : $(dolphin-generator-id) ;
+generators.register [ new wd-to-cpp $(__name__).wd-to-cpp : : CPP ] ;
+
+
+################################################################################
+#
+# Declare build targets.
+#
+################################################################################
+
+# This should not cause two CPP --> MY_OBJ constructions for a.cpp or b.cpp.
+my-exe a : a.cpp b.cxx obj_1 obj_2 c.tui d.wd x.l y.x_pro lib//auxilliary ;
+my-exe f : a.cpp b.cxx obj_1 obj_2 lib//auxilliary ;
+
+# This should cause two CPP --> MY_OBJ constructions for z.cpp.
+my-obj obj_1 : z.cpp ;
+my-obj obj_2 : z.cpp ;
+
+nm-exe e : e.cpp ;
+""")
+
+ t.run_build_system()
+ t.expect_addition("bin/" * BoostBuild.List("a.my_exe "
+ "a.my_obj b.my_obj c.tui_h c.cpp c.my_obj d_parser.whl d_lexer.dlp "
+ "d_parser.cpp d_lexer.cpp d_lexer.my_obj d_parser.lr0 d_parser.h "
+ "d_parser.my_obj d_parser_symbols.h x.c x.my_obj y.x1 y.x2 y.cpp "
+ "y.my_obj e.marked_cpp e.positions e.target_cpp e.my_obj e.my_exe "
+ "f.my_exe obj_1.my_obj obj_2.my_obj"))
+ t.expect_addition("lib/bin/" * BoostBuild.List("c.my_obj "
+ "auxilliary.my_lib"))
+ t.expect_nothing_more()
+
+ folder = "bin"
+ t.expect_content_lines("%s/obj_1.my_obj" % folder, " Sources: 'z.cpp'")
+ t.expect_content_lines("%s/obj_2.my_obj" % folder, " Sources: 'z.cpp'")
+ t.expect_content_lines("%s/a.my_obj" % folder, " Sources: 'a.cpp'")
+
+ lines = t.stdout().splitlines()
+ source_lines = [x for x in lines if re.match("^ Sources: '", x)]
+ if not __match_count_is(source_lines, "'z.cpp'", 2):
+ BoostBuild.annotation("failure", "z.cpp must be compiled exactly "
+ "twice.")
+ t.fail_test(1)
+ if not __match_count_is(source_lines, "'a.cpp'", 1):
+ BoostBuild.annotation("failure", "a.cpp must be compiled exactly "
+ "once.")
+ t.fail_test(1)
+ t.cleanup()
+
+
+def test_generated_target_names():
+ """
+ Test generator generated target names. Unless given explicitly, target
+ names should be determined based on their specified source names. All
+ sources for generating a target need to have matching names in order for
+ Boost Build to be able to implicitly determine the target's name.
+
+ We use the following target generation structure with differently named
+ BBX targets:
+ /---> BB1 ---\
+ AAA --<----> BB2 ---->--> CCC --(composing)--> DDD
+ \---> BB3 ---/
+
+ The extra generator at the end is needed because generating a top-level
+ CCC target directly would requires us to explicitly specify a name for it.
+ The extra generator needs to be composing in order not to explicitly
+ request a specific name for its CCC source target based on its own target
+ name.
+
+ We also check for a regression where only the first two sources were
+ checked to see if their names match. Note that we need to try out all file
+ renaming combinations as we do not know what ordering Boost Build is going
+ to use when passing in those files as generator sources.
+
+ """
+ jamfile_template = """\
+import type ;
+type.register AAA : _a ;
+type.register BB1 : _b1 ;
+type.register BB2 : _b2 ;
+type.register BB3 : _b3 ;
+type.register CCC : _c ;
+type.register DDD : _d ;
+
+import appender ;
+appender.register aaa-to-bbX : AAA : BB1%s BB2%s BB3%s ;
+appender.register bbX-to-ccc : BB1 BB2 BB3 : CCC ;
+appender.register ccc-to-ddd composing : CCC : DDD ;
+
+ddd _xxx : _xxx._a ;
+"""
+
+ t = BoostBuild.Tester()
+ __write_appender(t, "appender.jam")
+ t.write("_xxx._a", "")
+
+ def test_one(t, rename1, rename2, rename3, status):
+ def f(rename):
+ if rename: return "(%_x)"
+ return ""
+
+ jamfile = jamfile_template % (f(rename1), f(rename2), f(rename3))
+ t.write("jamroot.jam", jamfile, wait=False)
+
+ # Remove any preexisting targets left over from a previous test run
+ # so we do not have to be careful about tracking which files have been
+ # newly added and which preexisting ones have only been modified.
+ t.rm("bin")
+
+ t.run_build_system(status=status)
+
+ if status:
+ t.expect_output_lines("*.bbX-to-ccc: source targets have "
+ "different names: cannot determine target name")
+ else:
+ def suffix(rename):
+ if rename: return "_x"
+ return ""
+ name = "bin/_xxx"
+ e = t.expect_addition
+ e("%s%s._b1" % (name, suffix(rename1)))
+ e("%s%s._b2" % (name, suffix(rename2)))
+ e("%s%s._b3" % (name, suffix(rename3)))
+ e("%s%s._c" % (name, suffix(rename1 and rename2 and rename3)))
+ e("%s._d" % name)
+ t.expect_nothing_more()
+
+ test_one(t, False, False, False, status=0)
+ test_one(t, True , False, False, status=1)
+ test_one(t, False, True , False, status=1)
+ test_one(t, False, False, True , status=1)
+ test_one(t, True , True , False, status=1)
+ test_one(t, True , False, True , status=1)
+ test_one(t, False, True , True , status=1)
+ test_one(t, True , True , True , status=0)
+ t.cleanup()
+
+
+def __match_count_is(lines, pattern, expected):
+ count = 0
+ for x in lines:
+ if re.search(pattern, x):
+ count += 1
+ if count > expected:
+ return False
+ return count == expected
+
+
+def __write_appender(t, name):
+ t.write(name,
+r"""# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Support for registering test generators that construct their targets by
+# simply appending their given input data, e.g. list of sources & targets.
+
+import "class" : new ;
+import generators ;
+import modules ;
+import sequence ;
+
+rule register ( id composing ? : source-types + : target-types + )
+{
+ local caller-module = [ CALLER_MODULE ] ;
+ id = $(caller-module).$(id) ;
+ local g = [ new generator $(id) $(composing) : $(source-types) :
+ $(target-types) ] ;
+ $(g).set-rule-name $(__name__).appender ;
+ generators.register $(g) ;
+ return $(id) ;
+}
+
+if [ modules.peek : NT ]
+{
+ X = ")" ;
+ ECHO_CMD = (echo. ;
+}
+else
+{
+ X = \" ;
+ ECHO_CMD = "echo $(X)" ;
+}
+
+local appender-runs ;
+
+# We set up separate actions for building each target in order to avoid having
+# to iterate over them in action (i.e. shell) code. We have to be extra careful
+# though to achieve the exact same effect as if doing all the work in just one
+# action. Otherwise Boost Jam might, under some circumstances, run only some of
+# our actions. To achieve this we register a series of actions for all the
+# targets (since they all have the same target list - either all or none of them
+# get run independent of which target actually needs to get built), each
+# building only a single target. Since all our actions use the same targets, we
+# can not use 'on-target' parameters to pass data to a specific action so we
+# pass them using the second 'sources' parameter which our actions then know how
+# to interpret correctly. This works well since Boost Jam does not automatically
+# add dependency relations between specified action targets & sources and so the
+# second argument, even though most often used to pass in a list of sources, can
+# actually be used for passing in any type of information.
+rule appender ( targets + : sources + : properties * )
+{
+ appender-runs = [ CALC $(appender-runs:E=0) + 1 ] ;
+ local target-index = 0 ;
+ local target-count = [ sequence.length $(targets) ] ;
+ local original-targets ;
+ for t in $(targets)
+ {
+ target-index = [ CALC $(target-index) + 1 ] ;
+ local appender-run = $(appender-runs) ;
+ if $(targets[2])-defined
+ {
+ appender-run += "[$(target-index)/$(target-count)]" ;
+ }
+ append $(targets) : $(appender-run:J=" ") $(t) $(sources) ;
+ }
+}
+
+actions append
+{
+ $(ECHO_CMD)-------------------------------------------------$(X)
+ $(ECHO_CMD)Appender run: $(>[1])$(X)
+ $(ECHO_CMD)Appender run: $(>[1])$(X)>> "$(>[2])"
+ $(ECHO_CMD)Target group: $(<:J=' ')$(X)
+ $(ECHO_CMD)Target group: $(<:J=' ')$(X)>> "$(>[2])"
+ $(ECHO_CMD) Target: '$(>[2])'$(X)
+ $(ECHO_CMD) Target: '$(>[2])'$(X)>> "$(>[2])"
+ $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)
+ $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)>> "$(>[2])"
+ $(ECHO_CMD)=================================================$(X)
+ $(ECHO_CMD)-------------------------------------------------$(X)>> "$(>[2])"
+}
+""")
+
+
+test_basic()
+test_generated_target_names()
diff --git a/src/boost/tools/build/test/implicit_dependency.py b/src/boost/tools/build/test/implicit_dependency.py
new file mode 100644
index 000000000..49f82296c
--- /dev/null
+++ b/src/boost/tools/build/test/implicit_dependency.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the <implicit-dependency> is respected even if the target referred to is
+# not built itself, but only referred to by <implicit-dependency>.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+make a.h : : gen-header ;
+explicit a.h ;
+
+exe hello : hello.cpp : <implicit-dependency>a.h ;
+
+import os ;
+if [ os.name ] = NT
+{
+ actions gen-header
+ {
+ echo int i; > $(<)
+ }
+}
+else
+{
+ actions gen-header
+ {
+ echo "int i;" > $(<)
+ }
+}
+""")
+
+t.write("hello.cpp", """
+#include "a.h"
+int main() { return i; }
+""")
+
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+
+t.rm("bin")
+
+t.write("jamroot.jam", """
+make dir/a.h : : gen-header ;
+explicit dir/a.h ;
+
+exe hello : hello.cpp : <implicit-dependency>dir/a.h ;
+
+import os ;
+if [ os.name ] = NT
+{
+ actions gen-header
+ {
+ echo int i; > $(<)
+ }
+}
+else
+{
+ actions gen-header
+ {
+ echo "int i;" > $(<)
+ }
+}
+""")
+
+t.write("hello.cpp", """
+#include "dir/a.h"
+int main() { return i; }
+""")
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/indirect_conditional.py b/src/boost/tools/build/test/indirect_conditional.py
new file mode 100644
index 000000000..4d8373cb1
--- /dev/null
+++ b/src/boost/tools/build/test/indirect_conditional.py
@@ -0,0 +1,150 @@
+#!/usr/bin/python
+
+# Copyright (C) 2006. Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+def test_basic():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+exe a1 : a1.cpp : <conditional>@a1-rule ;
+rule a1-rule ( properties * )
+{
+ if <variant>debug in $(properties)
+ {
+ return <define>OK ;
+ }
+}
+
+exe a2 : a2.cpp : <conditional>@$(__name__).a2-rule
+ <variant>debug:<optimization>speed ;
+rule a2-rule ( properties * )
+{
+ if <optimization>speed in $(properties)
+ {
+ return <define>OK ;
+ }
+}
+
+exe a3 : a3.cpp :
+ <conditional>@$(__name__).a3-rule-1
+ <conditional>@$(__name__).a3-rule-2 ;
+rule a3-rule-1 ( properties * )
+{
+ if <optimization>speed in $(properties)
+ {
+ return <define>OK ;
+ }
+}
+rule a3-rule-2 ( properties * )
+{
+ if <variant>debug in $(properties)
+ {
+ return <optimization>speed ;
+ }
+}
+""")
+
+ t.write("a1.cpp", "#ifdef OK\nint main() {}\n#endif\n")
+ t.write("a2.cpp", "#ifdef OK\nint main() {}\n#endif\n")
+ t.write("a3.cpp", "#ifdef OK\nint main() {}\n#endif\n")
+
+ t.run_build_system()
+
+ t.expect_addition("bin/$toolset/debug*/a1.exe")
+ t.expect_addition("bin/$toolset/debug/optimization-speed*/a2.exe")
+ t.expect_addition("bin/$toolset/debug/optimization-speed*/a3.exe")
+
+ t.cleanup()
+
+def test_inherit():
+ """Tests that paths etc. are handled correctly when an indirect
+ conditional is inherited by a subproject."""
+ t = BoostBuild.Tester(use_test_config=False)
+ t.write("Jamroot.jam", """
+import feature ;
+import indirect ;
+exe d1 : d1.cpp ;
+explicit d1 ;
+project : requirements <conditional>@c1 ;
+build-project subdir ;
+feature.feature myrule : : free ;
+rule c1 ( properties * )
+{
+ return <dependency>d1 <include>include <myrule>@parent-generate ;
+}
+rule parent-generate ( project name : property-set : sources * )
+{
+ return $(sources) ;
+}
+rule my-generate ( project name : property-set : sources * )
+{
+ local r = [ $(property-set).get <myrule> ] ;
+ r = [ MATCH @(.*) : $(r) ] ;
+ return [ indirect.call
+ $(r) $(project) $(name) : $(property-set) : $(sources) ] ;
+}
+""")
+ t.write("d1.cpp", "int main(){}\n")
+ t.write("subdir/Jamfile", """
+generate srcs : main.cpp : <generating-rule>@my-generate ;
+exe main : srcs ;
+""")
+ t.write("include/a.h", "")
+ t.write("subdir/main.cpp", "#include <a.h>\nint main() {}\n")
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/d1.obj")
+ t.expect_addition("bin/$toolset/debug*/d1.exe")
+ t.ignore_addition("bin/*/d1.rsp")
+ t.expect_addition("subdir/bin/$toolset/debug*/main.obj")
+ t.expect_addition("subdir/bin/$toolset/debug*/main.exe")
+ t.ignore_addition("subdir/bin/*/main.rsp")
+ t.expect_nothing_more()
+ t.cleanup()
+
+def test_glob_in_indirect_conditional():
+ """
+ Regression test: project-rules.glob rule run from inside an indirect
+ conditional should report an error as it depends on the 'currently loaded
+ project' concept and indirect conditional rules get called only after all
+ the project modules have already finished loading.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", """\
+use-project /library-example/foo : util/foo ;
+build-project app ;
+""")
+ t.write("app/app.cpp", "int main() {}\n");
+ t.write("app/jamfile.jam", "exe app : app.cpp /library-example/foo//bar ;")
+ t.write("util/foo/bar.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void foo() {}
+""")
+ t.write("util/foo/jamfile.jam", """\
+rule print-my-sources ( properties * )
+{
+ ECHO My sources: ;
+ ECHO [ glob *.cpp ] ;
+}
+lib bar : bar.cpp : <conditional>@print-my-sources ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_output_lines(["My sources:", "bar.cpp"], False)
+ t.expect_output_lines("error: Reference to the project currently being "
+ "loaded requested when there was no project module being loaded.")
+
+ t.cleanup()
+
+
+test_basic()
+test_inherit()
+test_glob_in_indirect_conditional()
diff --git a/src/boost/tools/build/test/inherit_toolset.py b/src/boost/tools/build/test/inherit_toolset.py
new file mode 100644
index 000000000..defdeadec
--- /dev/null
+++ b/src/boost/tools/build/test/inherit_toolset.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import string
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("a.cpp", "\n")
+
+t.write("yfc1.jam", """\
+import feature ;
+import generators ;
+
+feature.extend toolset : yfc1 ;
+rule init ( ) { }
+
+generators.register-standard yfc1.compile : CPP : OBJ : <toolset>yfc1 ;
+generators.register-standard yfc1.link : OBJ : EXE : <toolset>yfc1 ;
+
+actions compile { yfc1-compile }
+actions link { yfc1-link }
+""")
+
+t.write(
+ 'yfc1.py',
+"""
+from b2.build import feature, generators
+from b2.manager import get_manager
+
+MANAGER = get_manager()
+ENGINE = MANAGER.engine()
+
+feature.extend('toolset', ['yfc1'])
+
+generators.register_standard('yfc1.compile', ['CPP'], ['OBJ'], ['<toolset>yfc1'])
+generators.register_standard('yfc1.link', ['OBJ'], ['EXE'], ['<toolset>yfc1'])
+
+ENGINE.register_action(
+ 'yfc1.compile',
+ 'yfc1-compile'
+)
+
+ENGINE.register_action(
+ 'yfc1.link',
+ 'yfc1-link'
+)
+
+def init(*args):
+ pass
+
+"""
+)
+
+t.write("yfc2.jam", """\
+import feature ;
+import toolset ;
+
+feature.extend toolset : yfc2 ;
+toolset.inherit yfc2 : yfc1 ;
+rule init ( ) { }
+
+actions link { yfc2-link }
+""")
+
+t.write(
+ 'yfc2.py',
+"""
+from b2.build import feature, toolset
+from b2.manager import get_manager
+
+MANAGER = get_manager()
+ENGINE = MANAGER.engine()
+
+feature.extend('toolset', ['yfc2'])
+toolset.inherit('yfc2', 'yfc1')
+
+ENGINE.register_action('yfc2.link', 'yfc2-link')
+
+def init(*args):
+ pass
+"""
+)
+
+t.write("jamfile.jam", "exe a : a.cpp ;")
+t.write("jamroot.jam", "using yfc1 ;")
+
+t.run_build_system(["-n", "-d2", "yfc1"])
+t.fail_test(t.stdout().find("yfc1-link") == -1)
+
+# Make sure we do not have to explicitly 'use' yfc1.
+t.write("jamroot.jam", "using yfc2 ;")
+
+t.run_build_system(["-n", "-d2", "yfc2"])
+t.fail_test(t.stdout().find("yfc2-link") == -1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/inherited_dependency.py b/src/boost/tools/build/test/inherited_dependency.py
new file mode 100755
index 000000000..412fa239b
--- /dev/null
+++ b/src/boost/tools/build/test/inherited_dependency.py
@@ -0,0 +1,237 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2008 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt) or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+tester = BoostBuild.Tester(use_test_config=False)
+
+
+################################################################################
+#
+# Test without giving the project an explicit id.
+#
+################################################################################
+
+tester.write("jamroot.jam", """
+lib test : test.cpp ;
+project : requirements <library>test ;
+build-project a ;
+""")
+
+tester.write("test.cpp", """
+#ifdef _WIN32
+ __declspec(dllexport)
+#endif
+void foo() {}
+""")
+
+tester.write("a/test1.cpp", """
+int main() {}
+""")
+
+tester.write("a/jamfile.jam", """
+exe test1 : test1.cpp ;
+""")
+
+tester.run_build_system()
+
+tester.expect_addition("bin/$toolset/debug*/test.obj")
+tester.expect_addition("a/bin/$toolset/debug*/test1.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# Run the same test from the "a" directory.
+#
+################################################################################
+
+tester.run_build_system(subdir="a")
+
+tester.expect_addition("bin/$toolset/debug*/test.obj")
+tester.expect_addition("a/bin/$toolset/debug*/test1.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# This time, do give the project an id.
+#
+################################################################################
+
+tester.write("jamroot.jam", """
+lib test : test.cpp ;
+project test_project : requirements <library>test ;
+build-project a ;
+""")
+
+tester.run_build_system()
+
+tester.expect_addition("bin/$toolset/debug*/test.obj")
+tester.expect_addition("a/bin/$toolset/debug*/test1.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# Now, give the project an id in its attributes.
+#
+################################################################################
+
+tester.write("jamroot.jam", """
+lib test : test.cpp ;
+project : id test_project : requirements <library>test ;
+build-project a ;
+""")
+
+tester.run_build_system()
+
+tester.expect_addition("bin/$toolset/debug*/test.obj")
+tester.expect_addition("a/bin/$toolset/debug*/test1.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# Give the project an id in both ways at once.
+#
+################################################################################
+
+tester.write("jamroot.jam", """
+lib test : test.cpp ;
+project test_project1 : id test_project : requirements <library>test ;
+build-project a ;
+""")
+
+tester.run_build_system()
+
+tester.expect_addition("bin/$toolset/debug*/test.obj")
+tester.expect_addition("a/bin/$toolset/debug*/test1.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# Test an absolute path in native format.
+#
+################################################################################
+
+tester.write("jamroot.jam", """
+import path ;
+path-constant here : . ;
+current-location = [ path.native [ path.root [ path.make $(here) ] [ path.pwd ]
+ ] ] ;
+project test : requirements <source>$(current-location)/a/test1.cpp ;
+exe test : test.cpp ;
+""")
+
+tester.run_build_system()
+tester.expect_addition("bin/$toolset/debug*/test.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# Test an absolute path in canonical format.
+#
+################################################################################
+
+tester.write("jamroot.jam", """
+import path ;
+path-constant here : . ;
+current-location = [ path.root [ path.make $(here) ] [ path.pwd ] ] ;
+project test : requirements <source>$(current-location)/a/test1.cpp ;
+exe test : test.cpp ;
+""")
+
+tester.run_build_system()
+tester.expect_addition("bin/$toolset/debug*/test.exe")
+
+tester.rm("bin")
+tester.rm("a/bin")
+
+
+################################################################################
+#
+# Test dependency properties (e.g. <source>) whose targets are specified using a
+# relative path.
+#
+################################################################################
+
+# Use jamroot.jam rather than jamfile.jam to avoid inheriting the <source> from
+# the parent as that would would make test3 a source of itself.
+tester.write("b/jamroot.jam", """
+obj test3 : test3.cpp ;
+""")
+
+tester.write("b/test3.cpp", """
+void bar() {}
+""")
+
+tester.write("jamroot.jam", """
+project test : requirements <source>b//test3 ;
+build-project a ;
+""")
+
+tester.write("a/jamfile.jam", """
+exe test : test1.cpp ;
+""")
+
+tester.write("a/test1.cpp", """
+void bar();
+int main() { bar(); }
+""")
+
+tester.run_build_system()
+tester.expect_addition("b/bin/$toolset/debug*/test3.obj")
+tester.expect_addition("a/bin/$toolset/debug*/test.exe")
+
+tester.rm("bin")
+tester.rm("a")
+tester.rm("jamroot.jam")
+tester.rm("test.cpp")
+
+
+################################################################################
+#
+# Test that source-location is respected.
+#
+################################################################################
+
+tester.write("build/jamroot.jam", """
+project : requirements <source>test.cpp : source-location ../src ;
+""")
+
+tester.write("src/test.cpp", """
+int main() {}
+""")
+
+tester.write("build/a/jamfile.jam", """
+project : source-location ../../a_src ;
+exe test : test1.cpp ;
+""")
+
+tester.write("a_src/test1.cpp", """
+""")
+
+tester.run_build_system(subdir="build/a")
+tester.expect_addition("build/a/bin/$toolset/debug*/test.exe")
+
+tester.cleanup()
diff --git a/src/boost/tools/build/test/inline.py b/src/boost/tools/build/test/inline.py
new file mode 100644
index 000000000..5e62e6f32
--- /dev/null
+++ b/src/boost/tools/build/test/inline.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """\
+project : requirements <link>static ;
+exe a : a.cpp [ lib helper : helper.cpp ] ;
+""")
+
+t.write("a.cpp", """\
+extern void helper();
+int main() {}
+""")
+
+t.write("helper.cpp", "void helper() {}\n")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a__helper.lib")
+t.rm("bin/$toolset/debug*/a__helper.lib")
+
+t.run_build_system(["a__helper"])
+t.expect_addition("bin/$toolset/debug*/a__helper.lib")
+
+t.rm("bin")
+
+
+# Now check that inline targets with the same name but present in different
+# places are not confused between each other, and with top-level targets.
+t.write("jamroot.jam", """\
+project : requirements <link>static ;
+exe a : a.cpp [ lib helper : helper.cpp ] ;
+exe a2 : a.cpp [ lib helper : helper.cpp ] ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug/link-static*/a.exe")
+t.expect_addition("bin/$toolset/debug*/a__helper.lib")
+t.expect_addition("bin/$toolset/debug*/a2__helper.lib")
+
+
+# Check that the 'alias' target does not change the name of inline targets, and
+# that inline targets are explicit.
+t.write("jamroot.jam", """\
+project : requirements <link>static ;
+alias a : [ lib helper : helper.cpp ] ;
+explicit a ;
+""")
+t.rm("bin")
+
+t.run_build_system()
+t.expect_nothing_more()
+
+t.run_build_system(["a"])
+t.expect_addition("bin/$toolset/debug*/helper.lib")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/install_build_no.py b/src/boost/tools/build/test/install_build_no.py
new file mode 100755
index 000000000..0ccf3c5cc
--- /dev/null
+++ b/src/boost/tools/build/test/install_build_no.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+# Copyright 2021 Dmitry Arkhipov (grisumbras@gmail.com)
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Check that <build>no in usage-requirements of dependencies does not affect
+# install rule, i.e. a skipped installed target does not affect insallation of
+# other targets.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("a.cpp", "int main() {}\n")
+
+t.write("jamroot.jam", """
+make x : : maker : <build>no ;
+exe a : a.cpp ;
+install install : x a ;
+""")
+
+t.run_build_system()
+t.expect_addition("install/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/lang_objc.py b/src/boost/tools/build/test/lang_objc.py
new file mode 100644
index 000000000..a3a60d99d
--- /dev/null
+++ b/src/boost/tools/build/test/lang_objc.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+# Copyright Nikita Kniazev 2021.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """
+obj a : hello.m ;
+obj b : hello.mm ;
+""")
+
+t.write("hello.m", '''\
+@interface Foo
+@end
+''')
+t.write("hello.mm", '''\
+@interface Foo
+@end
+
+class Bar {};
+''')
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.obj")
+t.expect_addition("bin/$toolset/debug*/b.obj")
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/lib_source_property.py b/src/boost/tools/build/test/lib_source_property.py
new file mode 100644
index 000000000..5cb36d3bd
--- /dev/null
+++ b/src/boost/tools/build/test/lib_source_property.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Regression test: if a library had no explicit sources, but only <source>
+# properties, it was built as if it were a searched library, and the specified
+# sources were not compiled.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+lib a : : <source>a.cpp ;
+""")
+
+t.write("a.cpp", """
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void foo() {}
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.obj")
+
+t.rm("bin")
+
+
+# Now try with <conditional>.
+t.write("jamroot.jam", """
+rule test ( properties * )
+{
+ return <source>a.cpp ;
+}
+lib a : : <conditional>@test ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/lib_zlib.py b/src/boost/tools/build/test/lib_zlib.py
new file mode 100755
index 000000000..de410b3a2
--- /dev/null
+++ b/src/boost/tools/build/test/lib_zlib.py
@@ -0,0 +1,184 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Build from source
+t.write("zlib/zlib.h", 'zlib')
+t.write("zlib/deflate.c", 'deflate')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zlib : : <source>$(here)/zlib ;
+alias zlib : /zlib//zlib : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, '''
+source_file('deflate.c', 'deflate')
+action('-c -x c -I./zlib -o $deflate.o $deflate.c')
+action('-c -x c -I./zlib -DZLIB_DLL -o $deflate-shared.o $deflate.c')
+action('--dll $deflate-shared.o -o $deflate.so')
+action('--archive $deflate.o -o $deflate.a')
+''')
+
+t.run_build_system()
+t.expect_addition('bin/standalone/zlib/mock/debug/z.dll')
+t.expect_addition('bin/standalone/zlib/mock/debug/link-static/z.lib')
+
+# Build from source specified in the environment
+t.rm('bin')
+t.rm('zlib')
+
+t.write("zlib root/zlib.h", 'zlib')
+t.write("zlib root/deflate.c", 'deflate')
+
+t.write("Jamroot.jam", """
+using zlib ;
+alias zlib : /zlib//zlib : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, '''
+source_file('deflate.c', 'deflate')
+action(['-c', '-x', 'c', '-I./zlib root', '-o', '$deflate.o', '$deflate.c'])
+action(['-c', '-x', 'c', '-I./zlib root', '-DZLIB_DLL', '-o', '$deflate-shared.o', '$deflate.c'])
+action('--dll $deflate-shared.o -o $deflate.so')
+action('--archive $deflate.o -o $deflate.a')
+''')
+t.run_build_system(['-sZLIB_SOURCE=zlib root'])
+t.expect_addition('bin/standalone/zlib/mock/debug/z.dll')
+t.expect_addition('bin/standalone/zlib/mock/debug/link-static/z.lib')
+
+
+t.rm('zlib root')
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('zlib.h.cpp', '#include <zlib.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+# Default initialization - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zlib ;
+exe test : test.cpp /zlib//zlib : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=z -o $config.exe')
+action('-c -x c++ $zlib.h.cpp -o $zlib.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=z -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zlib ;
+exe test : test.cpp /zlib//zlib : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=z -o $config.exe')
+action('-c -x c++ $zlib.h.cpp -o $zlib.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=z -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zlib : : <name>myzlib <include>$(here)/zlib <search>$(here)/zlib ;
+exe test : test.cpp /zlib//zlib : : <link>static <link>shared ;
+""")
+
+t.write('zlib/zlib.h', 'zlib')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./zlib --static-lib=myzlib -o $config.exe')
+action('-c -x c++ $test.cpp -I./zlib -o $test.o')
+action('$test.o -L./zlib --static-lib=myzlib -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zlib : : <name>myzlib <include>$(here)/zlib <search>$(here)/zlib ;
+exe test : test.cpp /zlib//zlib : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./zlib --shared-lib=myzlib -o $config.exe')
+action('-c -x c++ $test.cpp -I./zlib -o $test.o')
+action('$test.o -L./zlib --shared-lib=myzlib -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - both static and shared libraries
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zlib : : <name>myzlib <include>$(here)/zlib <search>$(here)/zlib ;
+exe test : test.cpp /zlib//zlib
+ : <link>shared:<define>SHARED : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./zlib --static-lib=myzlib -o $config.exe')
+action('$main.o -L./zlib --shared-lib=myzlib -o $config.exe')
+action('-c -x c++ $test.cpp -I./zlib -o $test-static.o')
+action('-c -x c++ $test.cpp -I./zlib -DSHARED -o $test-shared.o')
+action('$test-static.o -L./zlib --static-lib=myzlib -o $test')
+action('$test-shared.o -L./zlib --shared-lib=myzlib -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization from the environment
+t.rm('bin')
+t.write('Jamroot.jam', """
+using zlib ;
+exe test : test.cpp /zlib//zlib
+ : : <link>static <link>shared ;
+""")
+t.write('zlib root/zlib.h', 'zlib')
+MockToolset.set_expected(t, common_stuff + '''
+action(['$main.o', '-L./zlib root', '--shared-lib=myzlib', '-o', '$config.exe'])
+action(['-c', '-x', 'c++', '$test.cpp', '-I./zlib root', '-o', '$test.o'])
+action(['$test.o', '-L./zlib root', '--shared-lib=myzlib', '-o', '$test'])
+''')
+t.run_build_system(['-sZLIB_INCLUDE=zlib root',
+ '-sZLIB_LIBRARY_PATH=zlib root',
+ '-sZLIB_NAME=myzlib'])
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/libjpeg.py b/src/boost/tools/build/test/libjpeg.py
new file mode 100755
index 000000000..4201ccfff
--- /dev/null
+++ b/src/boost/tools/build/test/libjpeg.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Build from source
+t.write("libjpeg/jpeglib.h", 'libjpeg')
+t.write("libjpeg/jerror.c", 'jpeg')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libjpeg : : <source>$(here)/libjpeg ;
+alias libjpeg : /libjpeg//libjpeg : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, '''
+source_file('jerror.c', 'jpeg')
+action('-c -x c -I./libjpeg -o $jerror.o $jerror.c')
+action('--dll $jerror.o -o $jpeg.so')
+action('--archive $jerror.o -o $jpeg.a')
+''')
+
+t.run_build_system()
+t.expect_addition('bin/standalone/libjpeg/mock/debug/jpeg.dll')
+t.expect_addition('bin/standalone/libjpeg/mock/debug/link-static/jpeg.lib')
+
+t.rm('libjpeg')
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('jpeg.h.cpp', '#include <stdio.h>\\n#include <jpeglib.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libjpeg ;
+exe test : test.cpp /libjpeg//libjpeg ;
+""")
+
+# Default initialization - static library
+t.rm('bin')
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=jpeg -o $config.exe')
+action('-c -x c++ $jpeg.h.cpp -o $jpeg.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=jpeg -o $test')
+''')
+t.run_build_system(["link=static"])
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libjpeg ;
+exe test : test.cpp /libjpeg//libjpeg ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=jpeg -o $config.exe')
+action('-c -x c++ $jpeg.h.cpp -o $jpeg.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=jpeg -o $test')
+''')
+t.run_build_system(["link=shared"])
+t.expect_addition('bin/mock/debug/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libjpeg : : <name>mylibjpeg <include>$(here)/libjpeg <search>$(here)/libjpeg ;
+exe test : test.cpp /libjpeg//libjpeg : : <link>static <link>shared ;
+""")
+
+t.write('libjpeg/jpeglib.h', 'libjpeg')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./libjpeg --static-lib=mylibjpeg -o $config.exe')
+action('-c -x c++ $test.cpp -I./libjpeg -o $test.o')
+action('$test.o -L./libjpeg --static-lib=mylibjpeg -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libjpeg : : <name>mylibjpeg <include>$(here)/libjpeg <search>$(here)/libjpeg ;
+exe test : test.cpp /libjpeg//libjpeg : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./libjpeg --shared-lib=mylibjpeg -o $config.exe')
+action('-c -x c++ $test.cpp -I./libjpeg -o $test.o')
+action('$test.o -L./libjpeg --shared-lib=mylibjpeg -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/liblzma.py b/src/boost/tools/build/test/liblzma.py
new file mode 100755
index 000000000..ffa16b6f9
--- /dev/null
+++ b/src/boost/tools/build/test/liblzma.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+
+# Copy-paste-modify from zlib.py
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('lzma.h.cpp', '#include <lzma.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+# Default initialization - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using lzma ;
+exe test : test.cpp /lzma//lzma : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=lzma -o $config.exe')
+action('-c -x c++ $lzma.h.cpp -o $lzma.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=lzma -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using lzma ;
+exe test : test.cpp /lzma//lzma : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=lzma -o $config.exe')
+action('-c -x c++ $lzma.h.cpp -o $lzma.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=lzma -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using lzma : : <name>mylzma <include>$(here)/lzma <search>$(here)/lzma ;
+exe test : test.cpp /lzma//lzma : : <link>static <link>shared ;
+""")
+
+t.write('lzma/lzma.h', 'lzma')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./lzma --static-lib=mylzma -o $config.exe')
+action('-c -x c++ $test.cpp -I./lzma -o $test.o')
+action('$test.o -L./lzma --static-lib=mylzma -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using lzma : : <name>mylzma <include>$(here)/lzma <search>$(here)/lzma ;
+exe test : test.cpp /lzma//lzma : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./lzma --shared-lib=mylzma -o $config.exe')
+action('-c -x c++ $test.cpp -I./lzma -o $test.o')
+action('$test.o -L./lzma --shared-lib=mylzma -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - both static and shared libraries
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using lzma : : <name>mylzma <include>$(here)/lzma <search>$(here)/lzma ;
+exe test : test.cpp /lzma//lzma
+ : <link>shared:<define>SHARED : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./lzma --static-lib=mylzma -o $config.exe')
+action('$main.o -L./lzma --shared-lib=mylzma -o $config.exe')
+action('-c -x c++ $test.cpp -I./lzma -o $test-static.o')
+action('-c -x c++ $test.cpp -I./lzma -DSHARED -o $test-shared.o')
+action('$test-static.o -L./lzma --static-lib=mylzma -o $test')
+action('$test-shared.o -L./lzma --shared-lib=mylzma -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/libpng.py b/src/boost/tools/build/test/libpng.py
new file mode 100755
index 000000000..ad11ce6ed
--- /dev/null
+++ b/src/boost/tools/build/test/libpng.py
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Build from source
+t.write("libpng/png.h", 'libpng')
+t.write("libpng/png.c", 'png')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libpng : : <source>$(here)/libpng ;
+alias libpng : /libpng//libpng ;
+""")
+
+MockToolset.set_expected(t, '''
+source_file('png.c', 'png')
+action('-c -x c -I./libpng -o $png.o $png.c')
+action('--archive $png.o -o $png.a')
+''')
+
+t.run_build_system(["link=static"])
+t.expect_addition('bin/standalone/libpng/mock/debug/link-static/png.lib')
+
+MockToolset.set_expected(t, '''
+source_file('png.c', 'png')
+action('-c -x c -I./libpng -DLIBPNG_DLL -o $png.o $png.c')
+action('--dll $png.o -o $png.so')
+''')
+
+t.rm('bin')
+t.run_build_system(["link=shared"])
+t.expect_addition('bin/standalone/libpng/mock/debug/png.dll')
+
+t.rm('libpng')
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('png.h.cpp', '#include <png.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libpng ;
+exe test : test.cpp /libpng//libpng ;
+""")
+
+# Default initialization - static library
+t.rm('bin')
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=libpng -o $config.exe')
+action('-c -x c++ $png.h.cpp -o $png.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=libpng -o $test')
+''')
+t.run_build_system(["link=static"])
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=png -o $config.exe')
+action('-c -x c++ $png.h.cpp -o $png.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=png -o $test')
+''')
+t.run_build_system(["link=shared"])
+t.expect_addition('bin/mock/debug/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libpng : : <name>mylibpng <include>$(here)/libpng <search>$(here)/libpng ;
+exe test : test.cpp /libpng//libpng : : <link>static <link>shared ;
+""")
+
+t.write('libpng/png.h', 'libpng')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./libpng --static-lib=mylibpng -o $config.exe')
+action('-c -x c++ $test.cpp -I./libpng -o $test.o')
+action('$test.o -L./libpng --static-lib=mylibpng -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libpng : : <name>mylibpng <include>$(here)/libpng <search>$(here)/libpng ;
+exe test : test.cpp /libpng//libpng : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./libpng --shared-lib=mylibpng -o $config.exe')
+action('-c -x c++ $test.cpp -I./libpng -o $test.o')
+action('$test.o -L./libpng --shared-lib=mylibpng -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/library_chain.py b/src/boost/tools/build/test/library_chain.py
new file mode 100644
index 000000000..57751ad42
--- /dev/null
+++ b/src/boost/tools/build/test/library_chain.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2004, 2005, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that a chain of libraries works ok, no matter if we use static or shared
+# linking.
+
+import BoostBuild
+import os
+import string
+import sys
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Stage the binary, so that it will be relinked without hardcode-dll-paths.
+# That will check that we pass correct -rpath-link, even if not passing -rpath.
+t.write("jamfile.jam", """\
+stage dist : main ;
+exe main : main.cpp b ;
+""")
+
+t.write("main.cpp", """\
+void foo();
+int main() { foo(); }
+""")
+
+t.write("jamroot.jam", "")
+
+t.write("a/a.cpp", """\
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+gee() {}
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+geek() {}
+""")
+
+t.write("a/jamfile.jam", "lib a : a.cpp ;")
+
+t.write("b/b.cpp", """\
+void geek();
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+foo() { geek(); }
+""")
+
+t.write("b/jamfile.jam", "lib b : b.cpp ../a//a ;")
+
+t.run_build_system(["-d2"], stderr=None)
+t.expect_addition("bin/$toolset/debug*/main.exe")
+t.rm(["bin", "a/bin", "b/bin"])
+
+t.run_build_system(["link=static"])
+t.expect_addition("bin/$toolset/debug/link-static*/main.exe")
+t.rm(["bin", "a/bin", "b/bin"])
+
+
+# Check that <library> works for static linking.
+t.write("b/jamfile.jam", "lib b : b.cpp : <library>../a//a ;")
+
+t.run_build_system(["link=static"])
+t.expect_addition("bin/$toolset/debug/link-static*/main.exe")
+
+t.rm(["bin", "a/bin", "b/bin"])
+
+t.write("b/jamfile.jam", "lib b : b.cpp ../a//a/<link>shared : <link>static ;")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/main.exe")
+
+t.rm(["bin", "a/bin", "b/bin"])
+
+
+# Test that putting a library in sources of a searched library works.
+t.write("jamfile.jam", """\
+exe main : main.cpp png ;
+lib png : z : <name>png ;
+lib z : : <name>zzz ;
+""")
+
+t.run_build_system(["-a", "-d+2"], status=None, stderr=None)
+# Try to find the "zzz" string either in response file (for Windows compilers),
+# or in the standard output.
+rsp = t.adjust_names("bin/$toolset/debug*/main.exe.rsp")[0]
+if os.path.exists(rsp) and ( open(rsp).read().find("zzz") != -1 ):
+ pass
+elif t.stdout().find("zzz") != -1:
+ pass
+else:
+ t.fail_test(1)
+
+# Test main -> libb -> liba chain in the case where liba is a file and not a
+# B2 target.
+t.rm(".")
+
+t.write("jamroot.jam", "")
+t.write("a/jamfile.jam", """\
+lib a : a.cpp ;
+install dist : a ;
+""")
+
+t.write("a/a.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+void a() {}
+""")
+
+t.run_build_system(subdir="a")
+t.expect_addition("a/dist/a.dll")
+
+if sys.platform == 'win32':
+ # This is a Windows import library.
+ file = t.adjust_name("a.implib")
+else:
+ file = t.adjust_name("a.dll")
+
+t.write("b/jamfile.jam", "lib b : b.cpp ../a/dist/%s ;" % file)
+
+t.write("b/b.cpp", """\
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+void a();
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+void b() { a(); }
+""")
+
+t.write("jamroot.jam", "exe main : main.cpp b//b ;")
+
+t.write("main.cpp", """\
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+void b();
+int main() { b(); }
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/main.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/library_order.py b/src/boost/tools/build/test/library_order.py
new file mode 100644
index 000000000..071dd059d
--- /dev/null
+++ b/src/boost/tools/build/test/library_order.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that on compilers sensitive to library order on linker's command line,
+# we generate the correct order.
+
+import BoostBuild
+
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("main.cpp", """\
+void a();
+int main() { a(); }
+""")
+
+t.write("a.cpp", """\
+void b();
+void a() { b(); }
+""")
+
+t.write("b.cpp", """\
+void c();
+void b() { c(); }
+""")
+
+t.write("c.cpp", """\
+void d();
+void c() { d(); }
+""")
+
+t.write("d.cpp", """\
+void d() {}
+""")
+
+# The order of libraries in 'main' is crafted so that we get an error unless we
+# do something about the order ourselves.
+t.write("jamroot.jam", """\
+exe main : main.cpp libd libc libb liba ;
+lib libd : d.cpp ;
+lib libc : c.cpp : <link>static <use>libd ;
+lib libb : b.cpp : <use>libc ;
+lib liba : a.cpp : <use>libb ;
+""")
+
+t.run_build_system(["-d2"])
+t.expect_addition("bin/$toolset/debug*/main.exe")
+
+
+# Test the order between searched libraries.
+t.write("jamroot.jam", """\
+exe main : main.cpp png z ;
+lib png : z : <name>png ;
+lib z : : <name>zzz ;
+""")
+
+t.run_build_system(["-a", "-n", "-d+2"])
+t.fail_test(t.stdout().find("png") > t.stdout().find("zzz"))
+
+t.write("jamroot.jam", """\
+exe main : main.cpp png z ;
+lib png : : <name>png ;
+lib z : png : <name>zzz ;
+""")
+
+t.run_build_system(["-a", "-n", "-d+2"])
+t.fail_test(t.stdout().find("png") < t.stdout().find("zzz"))
+
+
+# Test the order between prebuilt libraries.
+t.write("first.a", "")
+t.write("second.a", "")
+t.write("jamroot.jam", """\
+exe main : main.cpp first second ;
+lib first : second : <file>first.a ;
+lib second : : <file>second.a ;
+""")
+
+t.run_build_system(["-a", "-n", "-d+2"])
+t.fail_test(t.stdout().find("first") > t.stdout().find("second"))
+
+t.write("jamroot.jam", """
+exe main : main.cpp first second ;
+lib first : : <file>first.a ;
+lib second : first : <file>second.a ;
+""")
+
+t.run_build_system(["-a", "-n", "-d+2"])
+t.fail_test(t.stdout().find("first") < t.stdout().find("second"))
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/library_property.py b/src/boost/tools/build/test/library_property.py
new file mode 100644
index 000000000..f85e3dd99
--- /dev/null
+++ b/src/boost/tools/build/test/library_property.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that the <library> property has no effect on "obj" targets. Previously,
+# it affected all targets, so
+#
+# project : requirements <library>foo ;
+# exe a : a.cpp helper ;
+# obj helper : helper.cpp : <optimization>off ;
+#
+# caused 'foo' to be built with and without optimization.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+project : requirements <library>lib//x ;
+exe a : a.cpp foo ;
+obj foo : foo.cpp : <variant>release ;
+""")
+
+t.write("a.cpp", """
+void aux();
+int main() { aux(); }
+""")
+
+t.write("foo.cpp", """
+void gee();
+void aux() { gee(); }
+""")
+
+t.write("lib/x.cpp", """
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+gee() {}
+""")
+
+t.write("lib/jamfile.jam", """
+lib x : x.cpp ;
+""")
+
+t.write("lib/jamroot.jam", """
+""")
+
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+t.expect_nothing("lib/bin/$toolset/release/x.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/libtiff.py b/src/boost/tools/build/test/libtiff.py
new file mode 100755
index 000000000..b089f43c4
--- /dev/null
+++ b/src/boost/tools/build/test/libtiff.py
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Build from source
+t.write("libtiff/tiff.h", 'libtiff')
+t.write("libtiff/tif_aux.c", 'tif_aux')
+
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libtiff : : <source>$(here)/libtiff ;
+alias libtiff : /libtiff//libtiff : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, '''
+source_file('tif_aux.c', 'tif_aux')
+action('-c -x c -I./libtiff -o $tif_aux.o $tif_aux.c')
+action('--dll $tif_aux.o -o $tiff.so')
+action('--archive $tif_aux.o -o $tiff.a')
+''')
+
+t.run_build_system()
+t.expect_addition('bin/standalone/libtiff/mock/debug/tiff.dll')
+t.expect_addition('bin/standalone/libtiff/mock/debug/link-static/tiff.lib')
+
+t.rm('libtiff')
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('tiff.h.cpp', '#include <tiff.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+# Default initialization - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libtiff ;
+exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=tiff -o $config.exe')
+action('-c -x c++ $tiff.h.cpp -o $tiff.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=tiff -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libtiff ;
+exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=tiff -o $config.exe')
+action('-c -x c++ $tiff.h.cpp -o $tiff.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=tiff -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libtiff : : <name>mylibtiff <include>$(here)/libtiff <search>$(here)/libtiff ;
+exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
+""")
+
+t.write('libtiff/tiff.h', 'libtiff')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./libtiff --static-lib=mylibtiff -o $config.exe')
+action('-c -x c++ $test.cpp -I./libtiff -o $test.o')
+action('$test.o -L./libtiff --static-lib=mylibtiff -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using libtiff : : <name>mylibtiff <include>$(here)/libtiff <search>$(here)/libtiff ;
+exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./libtiff --shared-lib=mylibtiff -o $config.exe')
+action('-c -x c++ $test.cpp -I./libtiff -o $test.o')
+action('$test.o -L./libtiff --shared-lib=mylibtiff -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/libzstd.py b/src/boost/tools/build/test/libzstd.py
new file mode 100755
index 000000000..1dbe47ce3
--- /dev/null
+++ b/src/boost/tools/build/test/libzstd.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+
+# Copy-paste-modify from zlib.py
+# Copyright (C) 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import MockToolset
+
+t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
+
+MockToolset.create(t)
+
+# Generic definitions that aren't configuration specific
+common_stuff = '''
+source_file('test.cpp', 'test.cpp')
+source_file('main.cpp', 'int main() {}')
+source_file('zstd.h.cpp', '#include <zstd.h>\\n')
+action('-c -x c++ $main.cpp -o $main.o')
+'''
+t.write('test.cpp', 'test.cpp')
+
+# Default initialization - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zstd ;
+exe test : test.cpp /zstd//zstd : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --static-lib=zstd -o $config.exe')
+action('-c -x c++ $zstd.h.cpp -o $zstd.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --static-lib=zstd -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Default initialization - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zstd ;
+exe test : test.cpp /zstd//zstd : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o --shared-lib=zstd -o $config.exe')
+action('-c -x c++ $zstd.h.cpp -o $zstd.h.o')
+action('-c -x c++ $test.cpp -o $test.o')
+action('$test.o --shared-lib=zstd -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - static library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zstd : : <name>myzstd <include>$(here)/zstd <search>$(here)/zstd ;
+exe test : test.cpp /zstd//zstd : : <link>static <link>shared ;
+""")
+
+t.write('zstd/zstd.h', 'zstd')
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./zstd --static-lib=myzstd -o $config.exe')
+action('-c -x c++ $test.cpp -I./zstd -o $test.o')
+action('$test.o -L./zstd --static-lib=myzstd -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - shared library
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zstd : : <name>myzstd <include>$(here)/zstd <search>$(here)/zstd ;
+exe test : test.cpp /zstd//zstd : : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./zstd --shared-lib=myzstd -o $config.exe')
+action('-c -x c++ $test.cpp -I./zstd -o $test.o')
+action('$test.o -L./zstd --shared-lib=myzstd -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+# Initialization in explicit location - both static and shared libraries
+t.rm('bin')
+t.write("Jamroot.jam", """
+path-constant here : . ;
+using zstd : : <name>myzstd <include>$(here)/zstd <search>$(here)/zstd ;
+exe test : test.cpp /zstd//zstd
+ : <link>shared:<define>SHARED : <link>static <link>shared ;
+""")
+
+MockToolset.set_expected(t, common_stuff + '''
+action('$main.o -L./zstd --static-lib=myzstd -o $config.exe')
+action('$main.o -L./zstd --shared-lib=myzstd -o $config.exe')
+action('-c -x c++ $test.cpp -I./zstd -o $test-static.o')
+action('-c -x c++ $test.cpp -I./zstd -DSHARED -o $test-shared.o')
+action('$test-static.o -L./zstd --static-lib=myzstd -o $test')
+action('$test-shared.o -L./zstd --shared-lib=myzstd -o $test')
+''')
+t.run_build_system()
+t.expect_addition('bin/mock/debug/test.exe')
+t.expect_addition('bin/mock/debug/link-static/test.exe')
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/link.py b/src/boost/tools/build/test/link.py
new file mode 100755
index 000000000..480e71e5a
--- /dev/null
+++ b/src/boost/tools/build/test/link.py
@@ -0,0 +1,350 @@
+#!/usr/bin/python
+
+# Copyright 2014-2015 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the link-directory rule used to create the
+# common boost/ directory in the new git layout.
+
+import BoostBuild
+
+def ignore_config(t):
+ """These files are created by the configuration logic in link.jam
+ They may or may not exist, depending on the system."""
+ t.ignore("bin/symlink/test-hardlink")
+ t.ignore("bin/test-hardlink-source")
+ t.ignore("bin/test-symlink")
+ t.ignore("bin/test-symlink-source")
+
+def test_basic():
+ """Test creation of a single link"""
+ t = BoostBuild.Tester()
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "file1")
+
+ t.run_build_system()
+
+ t.expect_addition("include/file1.h")
+ t.expect_content("include/file1.h", "file1")
+ ignore_config(t)
+ t.expect_nothing_more()
+ t.cleanup()
+
+def test_merge_two():
+ """Test merging two directories"""
+ t = BoostBuild.Tester()
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "file1")
+ t.write("src/dir2/include/file2.h", "file2")
+
+ t.run_build_system()
+
+ t.expect_addition("include/file1.h")
+ t.expect_content("include/file1.h", "file1")
+ t.expect_addition("include/file2.h")
+ t.expect_content("include/file2.h", "file2")
+ ignore_config(t)
+ t.expect_nothing_more()
+ t.cleanup()
+
+def test_merge_existing(group1, group2):
+ """Test adding a link when a different symlink already exists"""
+ t = BoostBuild.Tester()
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "file1")
+ t.write("src/dir2/include/file2.h", "file2")
+
+ t.run_build_system(group1)
+
+ if "dir1-link" in group1:
+ t.expect_addition("include/file1.h")
+ t.expect_content("include/file1.h", "file1")
+ if "dir2-link" in group1:
+ t.expect_addition("include/file2.h")
+ t.expect_content("include/file2.h", "file2")
+ ignore_config(t)
+ t.expect_nothing_more()
+
+ t.run_build_system(group2)
+
+ if "dir1-link" in group2:
+ if "dir1-link" not in group1:
+ t.expect_addition("include/file1.h")
+ else:
+ # When a directory is split the link needs to be recreated.
+ # On Windows, the test system checks the mod time of the
+ # link rather than the link target, so this may be seen as
+ # an update.
+ t.ignore_touch("include/file1.h")
+ t.expect_content("include/file1.h", "file1")
+ else:
+ t.ignore_removal("include/file1.h")
+
+ if "dir2-link" in group2:
+ if "dir2-link" not in group1:
+ t.expect_addition("include/file2.h")
+ else:
+ t.ignore_touch("include/file2.h")
+ t.expect_content("include/file2.h", "file2")
+ else:
+ t.ignore_removal("include/file2.h")
+ ignore_config(t)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_merge_existing_all():
+ test_merge_existing(["dir1-link"], ["dir2-link"])
+ test_merge_existing(["dir2-link"], ["dir1-link"])
+ test_merge_existing(["dir1-link"], ["dir1-link", "dir2-link"])
+ test_merge_existing(["dir2-link"], ["dir1-link", "dir2-link"])
+
+def test_merge_recursive():
+ "Test merging several directories including common prefixes"
+ t = BoostBuild.Tester()
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ link-directory dir3-link : src/dir3/include : <location>. ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "file1")
+ t.write("src/dir2/include/file2.h", "file2")
+ t.write("src/dir2/include/nested/file3.h", "file3")
+ t.write("src/dir3/include/nested/file4.h", "file4")
+
+ t.run_build_system()
+
+ t.expect_addition("include/file1.h")
+ t.expect_content("include/file1.h", "file1")
+ t.expect_addition("include/file2.h")
+ t.expect_content("include/file2.h", "file2")
+ t.expect_addition("include/nested/file3.h")
+ t.expect_content("include/nested/file3.h", "file3")
+ t.expect_addition("include/nested/file4.h")
+ t.expect_content("include/nested/file4.h", "file4")
+ ignore_config(t)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_merge_recursive_existing(group1, group2):
+ "Test merging several directories including common prefixes."
+ t = BoostBuild.Tester()
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ link-directory dir3-link : src/dir3/include : <location>. ;
+ link-directory dir4-link : src/dir4/include : <location>. ;
+ link-directory dir5-link : src/dir5/include : <location>. ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "file1")
+ t.write("src/dir2/include/nested/file2.h", "file2")
+ t.write("src/dir3/include/nested/file3.h", "file3")
+ t.write("src/dir4/include/nested/xxx/yyy/file4.h", "file4")
+ t.write("src/dir5/include/nested/xxx/yyy/file5.h", "file5")
+
+ t.run_build_system(group1)
+ t.run_build_system(group2 + ["-d+12"])
+
+ def check_file(target, file):
+ if target in group2:
+ if target in group1:
+ t.ignore_touch(file)
+ else:
+ t.expect_addition(file)
+
+ check_file("dir1-link", "include/file1.h")
+ check_file("dir2-link", "include/nested/file2.h")
+ check_file("dir3-link", "include/nested/file3.h")
+ check_file("dir4-link", "include/nested/xxx/yyy/file4.h")
+ check_file("dir5-link", "include/nested/xxx/yyy/file5.h")
+ ignore_config(t)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_merge_recursive_existing_all():
+ # These should create a link
+ test_merge_recursive_existing(["dir2-link"], ["dir2-link", "dir1-link"])
+ test_merge_recursive_existing(["dir2-link"], ["dir1-link", "dir2-link"])
+ # These should create a directory
+ test_merge_recursive_existing(["dir2-link"], ["dir2-link", "dir3-link"])
+ test_merge_recursive_existing(["dir2-link"], ["dir3-link", "dir2-link"])
+ # It should work even if we have to create many intermediate subdirectories
+ test_merge_recursive_existing(["dir4-link"], ["dir4-link", "dir5-link"])
+ test_merge_recursive_existing(["dir4-link"], ["dir5-link", "dir4-link"])
+
+def test_include_scan():
+ """Make sure that the #include scanner finds the headers"""
+ t = BoostBuild.Tester()
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ obj test : test.cpp :
+ <include>include
+ <implicit-dependency>dir1-link
+ <implicit-dependency>dir2-link ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "#include <file2.h>\n")
+ t.write("src/dir2/include/file2.h", "int f();\n")
+ t.write("test.cpp", """\
+ #include <file1.h>
+ int main() { f(); }
+ """);
+
+ t.run_build_system(["test"])
+
+ t.expect_addition("bin/$toolset/debug*/test.obj")
+
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_include_scan_merge_existing():
+ """Make sure that files are replaced if needed when merging in
+ a new directory"""
+ t = BoostBuild.Tester()
+
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ obj test : test.cpp :
+ <include>include
+ <implicit-dependency>dir1-link
+ <implicit-dependency>dir2-link ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "int f();")
+ t.write("src/dir2/include/file2.h", "#include <file1.h>")
+ t.write("test.cpp", """\
+ #include <file2.h>
+ int main() { f(); }
+ """)
+
+ t.run_build_system(["dir2-link"])
+
+ t.run_build_system(["test"])
+ t.expect_addition("include/file1.h")
+ t.expect_addition("bin/$toolset/debug*/test.obj")
+ t.ignore_touch("include/file2.h")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_update_file_link(params1, params2):
+ """Tests the behavior of updates when changing the link mode.
+ The link needs to be updated iff the original was a copy."""
+ t = BoostBuild.Tester()
+
+ t.write("jamroot.jam", """\
+ import link ;
+ import project ;
+ import property-set ;
+ import modules ;
+
+ if --no-symlinks in [ modules.peek : ARGV ]
+ {
+ modules.poke link : .can-symlink : false ;
+ }
+
+ if --no-hardlinks in [ modules.peek : ARGV ]
+ {
+ modules.poke link : .can-hardlink : false ;
+ }
+
+ .project = [ project.current ] ;
+ .has-files = [ glob include/file1.h ] ;
+
+ rule can-link ( properties * ) {
+ if ( ! [ link.can-symlink $(.project) ] ) &&
+ ( ! [ link.can-hardlink $(.project) ] )
+ {
+ ECHO links unsupported ;
+ }
+ }
+
+ # Use two directories so that we link to individual files.
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ alias check-linking : : <conditional>@can-link ;
+ """)
+ t.write("src/dir1/include/file1.h", "file1")
+ t.write("src/dir2/include/file2.h", "file2")
+
+ t.run_build_system(params1)
+ ignore_config(t)
+ t.expect_addition("include/file1.h")
+ t.expect_addition("include/file2.h")
+ t.expect_nothing_more()
+
+ using_links = "links unsupported" not in t.stdout()
+
+ t.touch("src/dir1/include/file1.h")
+
+ t.run_build_system(params2)
+ if not using_links: t.expect_touch("include/file1.h")
+ ignore_config(t)
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_update_file_link_all():
+ """Test all nine possible combinations of two runs."""
+ possible_args = [[], ["--no-symlinks"], ["--no-symlinks", "--no-hardlinks"]]
+ for arg1 in possible_args:
+ for arg2 in possible_args:
+ test_update_file_link(arg1, arg2)
+
+def test_error_duplicate():
+ """Test that linking a single file from
+ multiple sources causes a hard error."""
+ t = BoostBuild.Tester()
+
+ t.write("jamroot.jam", """\
+ import link ;
+ link-directory dir1-link : src/dir1/include : <location>. ;
+ link-directory dir2-link : src/dir2/include : <location>. ;
+ """)
+
+ t.write("src/dir1/include/file1.h", "file1")
+ t.write("src/dir2/include/file1.h", "file2")
+
+ t.run_build_system(status=1)
+ t.expect_output_lines(
+ ["error: Cannot create link include/file1.h to src/dir2/include/file1.h.",
+ "error: Link previously defined to another file, src/dir1/include/file1.h."])
+
+ t.cleanup()
+
+test_basic()
+test_merge_two()
+test_merge_existing_all()
+test_merge_recursive()
+test_merge_recursive_existing_all()
+test_include_scan()
+test_include_scan_merge_existing()
+test_update_file_link_all()
+test_error_duplicate()
diff --git a/src/boost/tools/build/test/load_dir.py b/src/boost/tools/build/test/load_dir.py
new file mode 100644
index 000000000..62d542104
--- /dev/null
+++ b/src/boost/tools/build/test/load_dir.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+"""
+Traverses a directory and output the code that would create the same directory
+structure during testing. Assumes that the instance of Tester is called 't'.
+"""
+
+from __future__ import print_function
+
+import sys
+import os
+import stat
+import string
+
+def usage():
+ print("usage: load_dir.py directory")
+
+
+def remove_first_component(path):
+ result = [path]
+ while 1:
+ s = os.path.split(result[0])
+ if not s[0]:
+ break
+ result[:1] = list(s)
+ return os.path.join(*result[1:])
+
+
+def create_file(arg, dirname, fnames):
+ for n in fnames:
+ path = os.path.join(dirname, n)
+ if not os.path.isdir(path):
+ print("t.write(\"%s\", \"\"\"" % (remove_first_component(path),),)
+ f = open(path, "r")
+ for l in f:
+ print(l)
+ print('\n""")\n')
+
+
+header = """#!/usr/bin/python
+
+# Copyright (C) FILL SOMETHING HERE 2005.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+"""
+
+footer = """
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/FILL_SOME_HERE.exe")
+
+t.cleanup()
+"""
+
+
+def main():
+ if len(sys.argv) != 2:
+ usage()
+ else:
+ path = sys.argv[1]
+
+ if not os.access(path, os.F_OK):
+ print("Path '%s' does not exist" % (path,))
+ sys.exit(1)
+
+ if not os.path.isdir(path):
+ print("Path '%s' is not a directory" % (path,))
+
+ print(header)
+
+ for root, _, files in os.walk(path):
+ create_file(None, root, files)
+
+ print(footer)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/boost/tools/build/test/load_order.py b/src/boost/tools/build/test/load_order.py
new file mode 100644
index 000000000..7fdce85b5
--- /dev/null
+++ b/src/boost/tools/build/test/load_order.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that we load parent projects before loading children.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """\
+use-project /child : child ;
+ECHO "Setting parent requirements" ;
+project : requirements <define>PASS_THE_TEST ;
+alias x : child//main ;
+""")
+
+t.write("child/jamfile.jam", """\
+ECHO "Setting child requirements" ;
+project /child ;
+exe main : main.cpp ;
+""")
+
+t.write("child/main.cpp", """\
+#if defined(PASS_THE_TEST)
+int main() {}
+#endif
+""")
+
+t.run_build_system()
+
+t.expect_addition("child/bin/$toolset/debug*/main.exe")
+t.fail_test(t.stdout().find("Setting child requirements") < t.stdout().find(
+ "Setting parent requirements"))
+
+
+# Regression test: parent requirements were ignored in some cases.
+t.rm(".")
+t.write("jamroot.jam", "build-project src ;")
+t.write("src/jamfile.jam", "project : requirements <define>EVERYTHING_OK ;")
+t.write("src/app/jamfile.jam", "exe test : test.cpp ;")
+t.write("src/app/test.cpp", """\
+#ifdef EVERYTHING_OK
+int main() {}
+#endif
+""")
+
+t.run_build_system(subdir="src/app")
+t.expect_addition("src/app/bin/$toolset/debug*/test.exe")
+
+
+# child/child2 used to be loaded before child
+t.rm(".")
+t.write("jamroot.jam", """\
+use-project /child/child2 : child/child2 ;
+rule parent-rule ( )
+{
+ ECHO "Running parent-rule" ;
+}
+""")
+t.write("child/jamfile.jam", "")
+t.write("child/child1/jamfile.jam", "")
+t.write("child/child2/jamfile.jam", "parent-rule ;")
+
+t.run_build_system(subdir="child/child1")
+t.expect_output_lines("Running parent-rule")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/loop.py b/src/boost/tools/build/test/loop.py
new file mode 100644
index 000000000..bbc6cee77
--- /dev/null
+++ b/src/boost/tools/build/test/loop.py
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import string
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """\
+lib main : main.cpp l ;
+lib l : l.cpp main ;
+""")
+
+t.write("main.cpp", "")
+t.write("l.cpp", "")
+
+t.run_build_system(["--no-error-backtrace"], status=1)
+t.fail_test(t.stdout().find(
+ "error: Recursion in main target references") == -1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/make_rule.py b/src/boost/tools/build/test/make_rule.py
new file mode 100644
index 000000000..81d8405f0
--- /dev/null
+++ b/src/boost/tools/build/test/make_rule.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2003, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'make' rule.
+
+import BoostBuild
+import string
+
+t = BoostBuild.Tester(pass_toolset=1)
+
+t.write("jamroot.jam", """\
+import feature ;
+feature.feature test_feature : : free ;
+
+import toolset ;
+toolset.flags creator STRING : <test_feature> ;
+
+actions creator
+{
+ echo $(STRING) > $(<)
+}
+
+make foo.bar : : creator : <test_feature>12345678 ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/foo.bar")
+t.fail_test(t.read("bin/foo.bar").find("12345678") == -1)
+
+
+# Regression test. Make sure that if a main target is requested two times, and
+# build requests differ only in incidental properties, the main target is
+# created only once. The bug was discovered by Kirill Lapshin.
+t.write("jamroot.jam", """\
+exe a : dir//hello1.cpp ;
+exe b : dir//hello1.cpp/<hardcode-dll-paths>true ;
+""")
+
+t.write("dir/jamfile.jam", """\
+import common ;
+make hello1.cpp : hello.cpp : common.copy ;
+""")
+
+t.write("dir/hello.cpp", "int main() {}\n")
+
+# Show only action names.
+t.run_build_system(["-d1", "-n"])
+t.fail_test(t.stdout().count("copy") != 1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/message.py b/src/boost/tools/build/test/message.py
new file mode 100755
index 000000000..4e0217a88
--- /dev/null
+++ b/src/boost/tools/build/test/message.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2003.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test for the regression testing framework.
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester(["-d0"], use_test_config=False)
+
+t.write("Jamroot.jam", """
+project
+ :
+ : usage-requirements <define>TEST=1
+ : default-build <link>static
+;
+message hello : "Hello World!" ;
+alias hello : : <link>shared ;
+obj test : test.cpp hello : <link>static ;
+""")
+
+t.write("test.cpp", """
+#ifndef TEST
+#error TEST not defined
+#endif
+""")
+
+t.run_build_system(["test"], stdout="""Hello World!
+""")
+
+t.expect_addition("bin/$toolset/link-static*/test.obj")
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/module_actions.py b/src/boost/tools/build/test/module_actions.py
new file mode 100644
index 000000000..a7c08194b
--- /dev/null
+++ b/src/boost/tools/build/test/module_actions.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2006 Rene Rivera
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Demonstration that module variables have the correct effect in actions.
+
+import BoostBuild
+import os
+import re
+
+t = BoostBuild.Tester(["-d+1"], pass_toolset=0)
+
+t.write("boost-build.jam", "boost-build . ;")
+t.write("bootstrap.jam", """\
+# Top-level rule causing a target to be built by invoking the specified action.
+rule make ( target : sources * : act )
+{
+ DEPENDS all : $(target) ;
+ DEPENDS $(target) : $(sources) ;
+ $(act) $(target) : $(sources) ;
+}
+
+X1 = X1-global ;
+X2 = X2-global ;
+X3 = X3-global ;
+
+module A
+{
+ X1 = X1-A ;
+
+ rule act ( target )
+ {
+ NOTFILE $(target) ;
+ ALWAYS $(target) ;
+ }
+
+ actions act { echo A.act $(<): $(X1) $(X2) $(X3) }
+
+ make t1 : : A.act ;
+ make t2 : : A.act ;
+ make t3 : : A.act ;
+}
+
+module B
+{
+ X2 = X2-B ;
+
+ actions act { echo B.act $(<): $(X1) $(X2) $(X3) }
+
+ make t1 : : B.act ;
+ make t2 : : B.act ;
+ make t3 : : B.act ;
+}
+
+actions act { echo act $(<): $(X1) $(X2) $(X3) }
+
+make t1 : : act ;
+make t2 : : act ;
+make t3 : : act ;
+
+X1 on t1 = X1-t1 ;
+X2 on t2 = X2-t2 ;
+X3 on t3 = X3-t3 ;
+
+DEPENDS all : t1 t2 t3 ;
+""")
+
+expected_lines = [
+ "...found 4 targets...",
+ "...updating 3 targets...",
+ "A.act t1",
+ "A.act t1: X1-t1 ",
+ "B.act t1",
+ "B.act t1: X1-t1 X2-B ",
+ "act t1",
+ "act t1: X1-t1 X2-global X3-global ",
+ "A.act t2",
+ "A.act t2: X1-A X2-t2 ",
+ "B.act t2",
+ "B.act t2: X2-t2 ",
+ "act t2",
+ "act t2: X1-global X2-t2 X3-global ",
+ "A.act t3",
+ "A.act t3: X1-A X3-t3 ",
+ "B.act t3",
+ "B.act t3: X2-B X3-t3 ",
+ "act t3",
+ "act t3: X1-global X2-global X3-t3 ",
+ "...updated 3 targets...",
+ ""]
+
+# Accommodate for the fact that on Unixes, a call to 'echo 1 2 3 '
+# produces '1 2 3' (note the spacing).
+if os.name != 'nt':
+ expected_lines = [re.sub(" +", " ", x.rstrip()) for x in expected_lines]
+
+t.run_build_system()
+t.expect_output_lines(expected_lines)
+t.expect_nothing_more()
+t.cleanup()
diff --git a/src/boost/tools/build/test/ndebug.py b/src/boost/tools/build/test/ndebug.py
new file mode 100644
index 000000000..82f3db81e
--- /dev/null
+++ b/src/boost/tools/build/test/ndebug.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that building with optimization brings NDEBUG define, and, more
+# importantly, that dependency targets are built with NDEBUG as well, even if
+# they are not directly requested.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "exe hello : hello.cpp lib//lib1 ;")
+t.write("hello.cpp", """\
+#ifdef NDEBUG
+void foo();
+int main() { foo(); }
+#endif
+""")
+t.write("lib/jamfile.jam", "lib lib1 : lib1.cpp ;")
+t.write("lib/lib1.cpp", """\
+#ifdef NDEBUG
+void foo() {}
+#endif
+""")
+
+# 'release' builds should get the NDEBUG define. We use static linking to avoid
+# messing with imports/exports on Windows.
+t.run_build_system(["link=static", "release"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/no_type.py b/src/boost/tools/build/test/no_type.py
new file mode 100644
index 000000000..9a83886f9
--- /dev/null
+++ b/src/boost/tools/build/test/no_type.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that we cannot specify targets of unknown type as sources. This is based
+# on the fact that Unix 'ar' will happily consume just about anything.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", "static-lib a : a.foo ;")
+t.write("a.foo", "")
+
+t.run_build_system(status=1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/notfile.py b/src/boost/tools/build/test/notfile.py
new file mode 100644
index 000000000..a3e5c58ee
--- /dev/null
+++ b/src/boost/tools/build/test/notfile.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2005.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Basic tests for the 'notfile' rule.
+
+import BoostBuild
+import os
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """\
+import notfile ;
+notfile say : "echo hi" ;
+exe hello : hello.cpp ;
+notfile hello_valgrind : @valgrind : hello ;
+actions valgrind { valgrind $(>[1]) }
+""")
+
+t.write("hello.cpp", """\
+#include <iostream>
+int main() { std::cout << "Hello!\\n"; }
+""")
+
+t.run_build_system(["-n", "-d+2"])
+
+t.fail_test(t.stdout().find("echo hi") == -1)
+
+name = t.adjust_names("bin/$toolset/debug*/hello.exe")[0]
+name = os.path.join(*name.split("/"))
+t.expect_output_lines(" valgrind *%s " % name)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/ordered_include.py b/src/boost/tools/build/test/ordered_include.py
new file mode 100644
index 000000000..12f32c8d0
--- /dev/null
+++ b/src/boost/tools/build/test/ordered_include.py
@@ -0,0 +1,251 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2008 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt) or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+def test_default_order():
+ tester = BoostBuild.Tester(use_test_config=False)
+ tester.write("jamroot.jam", """
+
+ import order ;
+ import "class" : new ;
+
+ obj test : test.cpp : <include>b <include>a ;
+ """)
+
+ tester.write("test.cpp", """
+ #include <test.hpp>
+ int main() { f(); }
+ """)
+
+ tester.write("a/test.hpp", """
+ void f();
+ """)
+
+ tester.write("b/test.hpp", """
+ """)
+
+ tester.run_build_system()
+
+ tester.expect_addition("bin/$toolset/debug*/test.obj")
+
+ # Check that the dependencies are correct
+ tester.touch("a/test.hpp")
+ tester.run_build_system()
+ tester.expect_touch("bin/$toolset/debug*/test.obj")
+ tester.expect_nothing_more()
+
+ tester.touch("b/test.hpp")
+ tester.run_build_system()
+ tester.expect_nothing_more()
+
+ tester.cleanup()
+
+def test_default_order_mixed():
+ tester = BoostBuild.Tester(use_test_config=False)
+ tester.write("jamroot.jam", """
+
+ import order ;
+ import "class" : new ;
+
+ obj test : test.cpp : <include>b <include>a <include>c&&d ;
+ """)
+
+ tester.write("test.cpp", """
+ #include <test.hpp>
+ int main() { f(); }
+ """)
+
+ tester.write("a/test.hpp", """
+ void f();
+ """)
+
+ tester.write("b/test.hpp", """
+ """)
+
+ tester.run_build_system()
+
+ tester.expect_addition("bin/$toolset/debug*/test.obj")
+
+ # Check that the dependencies are correct
+ tester.touch("a/test.hpp")
+ tester.run_build_system()
+ tester.expect_touch("bin/$toolset/debug*/test.obj")
+ tester.expect_nothing_more()
+
+ tester.touch("b/test.hpp")
+ tester.run_build_system()
+ tester.expect_nothing_more()
+
+ tester.cleanup()
+
+def test_basic():
+ tester = BoostBuild.Tester(use_test_config=False)
+ tester.write("jamroot.jam", """
+ obj test : test.cpp : <include>a&&b ;
+ """)
+
+ tester.write("test.cpp", """
+ #include <test1.hpp>
+ #include <test2.hpp>
+ int main() {}
+ """)
+
+ tester.write("a/test1.hpp", """
+ """)
+
+ tester.write("b/test2.hpp", """
+ """)
+
+ tester.run_build_system()
+
+ tester.expect_addition("bin/$toolset/debug*/test.obj")
+
+ # Check that the dependencies are correct
+ tester.touch("a/test1.hpp")
+ tester.run_build_system()
+ tester.expect_touch("bin/$toolset/debug*/test.obj")
+
+ tester.touch("b/test2.hpp")
+ tester.run_build_system()
+ tester.expect_touch("bin/$toolset/debug*/test.obj")
+
+ tester.cleanup()
+
+def test_order1():
+ t = BoostBuild.Tester(use_test_config=False)
+ t.write("jamroot.jam", """
+ obj test : test.cpp : <include>a&&b ;
+ """)
+ t.write("test.cpp", """
+ #include <test.h>
+ int main() {}
+ """)
+ t.write("a/test.h", """
+ """)
+ t.write("b/test.h", """
+ #error should find a/test.h
+ """)
+ t.run_build_system()
+
+ t.touch("a/test.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/test.obj")
+ t.expect_nothing_more()
+
+ t.touch("b/test.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_order2():
+ t = BoostBuild.Tester(use_test_config=False)
+ t.write("jamroot.jam", """
+ obj test : test.cpp : <include>b&&a ;
+ """)
+ t.write("test.cpp", """
+ #include <test.h>
+ int main() {}
+ """)
+ t.write("a/test.h", """
+ #error should find b/test.h
+ """)
+ t.write("b/test.h", """
+ """)
+ t.run_build_system()
+
+ t.touch("a/test.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.touch("b/test.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/test.obj")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_order_graph():
+ t = BoostBuild.Tester(use_test_config=False)
+ t.write("jamroot.jam", """
+ obj test : test.cpp :
+ <include>b&&a
+ <include>c&&b
+ <include>a
+ <include>c
+ <include>b
+ <include>e&&b&&d
+ ;
+ """)
+ t.write("test.cpp", """
+ #include <test1.h>
+ #include <test2.h>
+ #include <test3.h>
+ #include <test4.h>
+ int main() {}
+ """)
+ t.write("b/test1.h", "")
+ t.write("a/test1.h", "#error should find b/test1.h\n")
+
+ t.write("c/test2.h", "")
+ t.write("b/test2.h", "#error should find c/test2.h\n")
+
+ t.write("e/test3.h", "")
+ t.write("b/test3.h", "#error should find e/test3.h\n")
+
+ t.write("b/test4.h", "")
+ t.write("d/test4.h", "#error should find b/test4.h\n")
+
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/test.obj")
+
+ t.touch("b/test1.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/test.obj")
+ t.expect_nothing_more()
+
+ t.touch("a/test1.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.touch("c/test2.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/test.obj")
+ t.expect_nothing_more()
+
+ t.touch("b/test2.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.touch("e/test3.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/test.obj")
+ t.expect_nothing_more()
+
+ t.touch("b/test3.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.touch("b/test4.h")
+ t.run_build_system()
+ t.expect_touch("bin/$toolset/debug*/test.obj")
+ t.expect_nothing_more()
+
+ t.touch("d/test4.h")
+ t.run_build_system()
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+test_default_order()
+test_default_order_mixed()
+test_basic()
+test_order1()
+test_order2()
+test_order_graph()
diff --git a/src/boost/tools/build/test/ordered_properties.py b/src/boost/tools/build/test/ordered_properties.py
new file mode 100644
index 000000000..49eb0a3c1
--- /dev/null
+++ b/src/boost/tools/build/test/ordered_properties.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This checks that B2 does not reorder <include> properties
+# lexicographically.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("a.cpp", """
+#include <a.h>
+int main() { foo(); }
+""")
+
+t.write("jamroot.jam", """
+exe a : a.cpp : <include>d2 <include>d1 ;
+""")
+
+t.write("d1/a.h", """
+""")
+
+t.write("d2/a.h", """
+inline void foo() {}
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/out_of_tree.py b/src/boost/tools/build/test/out_of_tree.py
new file mode 100644
index 000000000..dad21e18f
--- /dev/null
+++ b/src/boost/tools/build/test/out_of_tree.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2005.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that we can build a project when the current directory is outside of
+# that project tree, that is 'bjam some_dir' works.
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester(use_test_config=False)
+
+# Create the needed files.
+t.write("p1/jamroot.jam", "exe hello : hello.cpp ;")
+t.write("p1/hello.cpp", "int main() {}\n")
+t.write("p2/jamroot.jam", """\
+exe hello2 : hello.cpp ;
+exe hello3 : hello.cpp ;
+""")
+t.write("p2/hello.cpp", "int main() {}\n")
+
+t.run_build_system(["p1", "p2//hello3"])
+t.expect_addition("p1/bin/$toolset/debug*/hello.exe")
+t.expect_addition("p2/bin/$toolset/debug*/hello3.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/package.py b/src/boost/tools/build/test/package.py
new file mode 100644
index 000000000..f5fc44dff
--- /dev/null
+++ b/src/boost/tools/build/test/package.py
@@ -0,0 +1,231 @@
+#!/usr/bin/python
+
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the package module.
+
+import BoostBuild
+import os
+
+def setup():
+ t = BoostBuild.Tester(["p//install", "p//data"],
+ use_test_config=False)
+
+ t.write("p/jamroot.jam", "")
+ t.write("p/jamfile.jam", """\
+ import package ;
+ exe a : a.cpp ;
+ lib b : b.cpp ;
+ package.install install Test :
+ : a
+ : b/<link>static b/<link>shared
+ : a.h ;
+ package.install-data data : Test : a.txt ;
+ """)
+ t.write("p/a.cpp", "int main() {}")
+ t.write("p/b.cpp", """
+ int
+ #ifdef _WIN32
+ __declspec(dllexport)
+ #endif
+ must_export_something;
+ """)
+ t.write("p/a.h", "")
+ t.write("p/a.txt", "")
+ return t
+
+def test_defaults():
+ t = setup()
+
+ # Since the default install location is outside out test area,
+ # we don't want to actually execute the build.
+ t.run_build_system(["-n", "-d1"])
+
+ installdir = "C:/Test" if os.name == 'nt' else "/usr/local"
+ t.expect_output_lines([
+ x.replace('/', os.sep) for x in
+ ["common.copy %s/bin/%s" % (installdir, t.adjust_name("a.exe")),
+ "common.copy %s/lib/%s" % (installdir, t.adjust_name("b.dll")),
+ "common.copy %s/lib/%s" % (installdir, t.adjust_name("b.lib")),
+ "common.copy %s/include/a.h" % installdir,
+ "common.copy %s/share/Test/a.txt" % installdir]])
+
+ t.cleanup()
+
+def test_prefix():
+ t = setup()
+ # An explicit --prefix on the command should override all of these:
+ t.write("project-config.jam", """
+ option.set prefix : bad ;
+ option.set bindir : bad/bin ;
+ option.set libdir : bad/lib ;
+ option.set includedir : bad/include ;
+ option.set datarootdir : bad/share ;
+ """)
+
+ t.run_build_system(["--prefix=installdir"])
+ t.expect_addition("installdir/bin/a.exe")
+ t.expect_addition("installdir/lib/b.dll")
+ t.expect_addition("installdir/lib/b.lib")
+ t.expect_addition("installdir/include/a.h")
+ t.expect_addition("installdir/share/Test/a.txt")
+
+ t.cleanup()
+
+def test_subdirs():
+ t = setup()
+ # Command line options override config files
+ t.write("project-config.jam", """
+ option.set prefix : bad ;
+ option.set bindir : bad/bin ;
+ option.set libdir : bad/lib ;
+ option.set includedir : bad/include ;
+ option.set datarootdir : bad/share ;
+ """)
+
+ t.run_build_system(["--libdir=installdir/lib64",
+ "--bindir=installdir/binx",
+ "--includedir=installdir/includex",
+ "--datarootdir=installdir/sharex"])
+ t.expect_addition("installdir/binx/a.exe")
+ t.expect_addition("installdir/lib64/b.dll")
+ t.expect_addition("installdir/lib64/b.lib")
+ t.expect_addition("installdir/includex/a.h")
+ t.expect_addition("installdir/sharex/Test/a.txt")
+
+ t.cleanup()
+
+def test_subdirs_with_prefix():
+ t = setup()
+ # Command line options override config files
+ t.write("project-config.jam", """
+ option.set prefix : bad ;
+ option.set bindir : bad/bin ;
+ option.set libdir : bad/lib ;
+ option.set includedir : bad/include ;
+ option.set datarootdir : bad/share ;
+ """)
+
+ t.run_build_system(["--prefix=bad",
+ "--libdir=installdir/lib64",
+ "--bindir=installdir/binx",
+ "--includedir=installdir/includex",
+ "--datarootdir=installdir/sharex"])
+ t.expect_addition("installdir/binx/a.exe")
+ t.expect_addition("installdir/lib64/b.dll")
+ t.expect_addition("installdir/lib64/b.lib")
+ t.expect_addition("installdir/includex/a.h")
+ t.expect_addition("installdir/sharex/Test/a.txt")
+
+ t.cleanup()
+
+def test_prefix_config_file():
+ t = setup()
+ # An explicit --prefix on the command should override all of these:
+ t.write("project-config.jam", """
+ option.set prefix : installdir ;
+ """)
+
+ t.run_build_system()
+ t.expect_addition("installdir/bin/a.exe")
+ t.expect_addition("installdir/lib/b.dll")
+ t.expect_addition("installdir/lib/b.lib")
+ t.expect_addition("installdir/include/a.h")
+ t.expect_addition("installdir/share/Test/a.txt")
+
+ t.cleanup()
+
+def test_subdirs_config_file():
+ t = setup()
+ # An explicit --prefix on the command should override all of these:
+ t.write("project-config.jam", """
+ option.set prefix : installdir ;
+ option.set libdir : installdir/lib64 ;
+ option.set bindir : installdir/binx ;
+ option.set includedir : installdir/includex ;
+ option.set datarootdir : installdir/sharex ;
+ """)
+
+ t.run_build_system()
+ t.expect_addition("installdir/binx/a.exe")
+ t.expect_addition("installdir/lib64/b.dll")
+ t.expect_addition("installdir/lib64/b.lib")
+ t.expect_addition("installdir/includex/a.h")
+ t.expect_addition("installdir/sharex/Test/a.txt")
+
+ t.cleanup()
+
+def test_multiple():
+ '''If no prefix is specified, we might use several default
+ install prefixes.'''
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("p/jamroot.jam", "")
+ t.write("p/jamfile.jam", """\
+ import package ;
+ exe a : a.cpp ;
+ lib b : b.cpp ;
+ package.install installx TestX : <install-default-prefix>xxx
+ : a
+ : b/<link>static b/<link>shared
+ : a.h ;
+ package.install instally TestY : <install-default-prefix>yyy
+ : a
+ : b/<link>static b/<link>shared
+ : a.h ;
+ """)
+ t.write("p/a.cpp", "int main() {}")
+ t.write("p/b.cpp", """
+ int
+ #ifdef _WIN32
+ __declspec(dllexport)
+ #endif
+ must_export_something;
+ """)
+ t.write("p/a.h", "")
+ t.run_build_system(["p//installx", "p//instally"])
+ t.expect_addition("p/xxx/bin/a.exe")
+ t.expect_addition("p/xxx/lib/b.dll")
+ t.expect_addition("p/xxx/lib/b.lib")
+ t.expect_addition("p/xxx/include/a.h")
+ t.expect_addition("p/yyy/bin/a.exe")
+ t.expect_addition("p/yyy/lib/b.dll")
+ t.expect_addition("p/yyy/lib/b.lib")
+ t.expect_addition("p/yyy/include/a.h")
+
+def test_paths():
+ t = BoostBuild.Tester(pass_toolset=False)
+ t.write("Jamroot.jam", """\
+ import package ;
+ import assert ;
+ import os ;
+ if [ os.name ] = NT
+ {
+ default-prefix = "/C:/Test" ;
+ }
+ else
+ {
+ default-prefix = /usr/local ;
+ }
+ paths = [ package.paths Test ] ;
+ assert.result $(default-prefix) : $(paths).prefix ;
+ assert.result $(default-prefix)/lib : $(paths).libdir ;
+ assert.result $(default-prefix)/bin : $(paths).bindir ;
+ assert.result $(default-prefix)/include : $(paths).includedir ;
+ assert.result $(default-prefix)/share : $(paths).datarootdir ;
+ package.add-path-option bardir : bar : libdir ;
+ assert.result $(default-prefix)/lib/bar : $(paths).get bardir ;
+ """)
+ t.run_build_system()
+ t.cleanup()
+
+test_defaults()
+test_prefix()
+test_subdirs()
+test_subdirs_with_prefix()
+test_prefix_config_file()
+test_subdirs_config_file()
+test_multiple()
+test_paths()
diff --git a/src/boost/tools/build/test/param.py b/src/boost/tools/build/test/param.py
new file mode 100644
index 000000000..feba794e1
--- /dev/null
+++ b/src/boost/tools/build/test/param.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("Jamroot.jam", """\
+import param ;
+import assert ;
+import errors : try catch ;
+rule test1 ( )
+{
+ param.handle-named-params ;
+}
+test1 ;
+rule test2 ( sources * )
+{
+ param.handle-named-params sources ;
+ return $(sources) ;
+}
+assert.result : test2 ;
+assert.result test1.cpp test2.cpp : test2 test1.cpp test2.cpp ;
+assert.result test1.cpp test2.cpp : test2 sources test1.cpp test2.cpp ;
+rule test3 ( sources * : requirements * )
+{
+ param.handle-named-params sources requirements ;
+ return $(sources) -- $(requirements) ;
+}
+assert.result -- : test3 ;
+assert.result -- <link>shared : test3 : <link>shared ;
+assert.result test1.cpp -- <link>shared : test3 test1.cpp : <link>shared ;
+assert.result test1.cpp -- <link>shared
+ : test3 test1.cpp : requirements <link>shared ;
+assert.result test1.cpp -- <link>shared
+ : test3 sources test1.cpp : requirements <link>shared ;
+assert.result test1.cpp -- <link>shared
+ : test3 requirements <link>shared : sources test1.cpp ;
+assert.result -- : test3 sources ;
+assert.result -- : test3 requirements ;
+assert.result -- <link>shared : test3 requirements <link>shared ;
+try ;
+{
+ test3 sources test1.cpp : sources test2.cpp ;
+}
+catch Parameter 'sources' passed more than once. ;
+try ;
+{
+ test3 sources test1.cpp : <link>shared ;
+}
+catch "Positional arguments must appear first." ;
+EXIT : 0 ;
+""")
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/path_features.py b/src/boost/tools/build/test/path_features.py
new file mode 100644
index 000000000..59af7fd8a
--- /dev/null
+++ b/src/boost/tools/build/test/path_features.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+def test_basic():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "lib a : a.cpp : <include>. ;")
+ t.write("a.cpp", """\
+#include <a.h>
+void
+# ifdef _WIN32
+__declspec(dllexport)
+# endif
+foo() {}
+""")
+ t.write("a.h", "//empty file\n")
+ t.write("d/jamfile.jam", "exe b : b.cpp ..//a ;")
+ t.write("d/b.cpp", """\
+void foo();
+int main() { foo(); }
+""")
+ t.run_build_system(subdir="d")
+
+ # Path features with condition.
+ t.write("jamroot.jam", "lib a : a.cpp : <variant>debug:<include>. ;")
+ t.rm("bin")
+ t.run_build_system(subdir="d")
+
+
+ # Path features with condition in usage requirements.
+ t.write("jamroot.jam", """\
+lib a : a.cpp : <include>. : : <variant>debug:<include>. ;
+""")
+ t.write("d/b.cpp", """\
+#include <a.h>
+void foo();
+int main() { foo(); }
+""")
+ t.rm("d/bin")
+ t.run_build_system(subdir="d")
+
+ t.cleanup()
+
+
+def test_absolute_paths():
+ """
+ Test that absolute paths inside requirements are ok. The problems
+ appeared only when building targets in subprojects.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "build-project x ;")
+ t.write("x/jamfile.jam", """\
+local pwd = [ PWD ] ;
+project : requirements <include>$(pwd)/x/include ;
+exe m : m.cpp : <include>$(pwd)/x/include2 ;
+""")
+ t.write("x/m.cpp", """\
+#include <h1.hpp>
+#include <h2.hpp>
+int main() {}
+""")
+ t.write("x/include/h1.hpp", "\n")
+ t.write("x/include2/h2.hpp", "\n")
+
+ t.run_build_system()
+ t.expect_addition("x/bin/$toolset/debug*/m.exe")
+
+ t.cleanup()
+
+
+def test_ordered_paths():
+ """Test that "&&" in path features is handled correctly."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "build-project sub ;")
+ t.write("sub/jamfile.jam", "exe a : a.cpp : <include>../h1&&../h2 ;")
+ t.write("sub/a.cpp", """\
+#include <header.h>
+int main() { return OK; }
+""")
+ t.write("h2/header.h", "int const OK = 0;\n")
+ t.run_build_system()
+ t.expect_addition("sub/bin/$toolset/debug*/a.exe")
+
+ t.cleanup()
+
+
+def test_paths_set_by_indirect_conditionals():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ header = "child_dir/folder_to_include/some_header.h"
+
+ t.write("jamroot.jam", """
+build-project child_dir ;
+rule attach-include-parent ( properties * )
+{
+ return <include>another_folder ;
+}
+# requirements inherited from a parent project will bind paths
+# relative to the project that actually names the rule.
+project : requirements <conditional>@attach-include-parent ;
+""")
+ t.write("child_dir/jamfile.jam", """\
+import remote/remote ;
+
+# If we set the <include>folder_to_include property directly, it will work
+obj x1 : x.cpp : <conditional>@attach-include-local ;
+obj x2 : x.cpp : <conditional>@remote.attach-include-remote ;
+
+rule attach-include-local ( properties * )
+{
+ return <include>folder_to_include ;
+}
+""")
+ t.write("child_dir/remote/remote.jam", """\
+rule attach-include-remote ( properties * )
+{
+ return <include>folder_to_include ;
+}
+""")
+ t.write("child_dir/x.cpp", """\
+#include <some_header.h>
+#include <header2.h>
+int main() {}
+""")
+ t.write(header, "int some_func();\n")
+ t.write("another_folder/header2.h", "int f2();\n")
+ t.write("child_dir/folder_to_include/jamfile.jam", "")
+
+ expected_x1 = "child_dir/bin/$toolset/debug*/x1.obj"
+ expected_x2 = "child_dir/bin/$toolset/debug*/x2.obj"
+
+ t.run_build_system()
+ t.expect_addition(expected_x1)
+ t.expect_addition(expected_x2)
+
+ t.touch(header)
+ t.run_build_system(subdir="child_dir")
+ t.expect_touch(expected_x1)
+ t.expect_touch(expected_x2)
+
+ t.touch(header)
+ t.run_build_system([".."], subdir="child_dir/folder_to_include")
+ t.expect_touch(expected_x1)
+ t.expect_touch(expected_x2)
+
+ t.cleanup()
+
+
+test_basic()
+test_absolute_paths()
+test_ordered_paths()
+test_paths_set_by_indirect_conditionals()
diff --git a/src/boost/tools/build/test/pch.py b/src/boost/tools/build/test/pch.py
new file mode 100644
index 000000000..fe6c792f3
--- /dev/null
+++ b/src/boost/tools/build/test/pch.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+
+# Copyright 2006 Vladimir Prus.
+# Copyright Nikita Kniazev 2020.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+from time import sleep
+
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """
+import pch ;
+project : requirements <warnings-as-errors>on ;
+cpp-pch pch : pch.hpp ;
+cpp-pch pch-afx : pch.hpp : <define>HELLO ;
+cpp-pch pch-msvc-source : pch.hpp : <toolset>msvc:<source>pch.cpp ;
+exe hello : hello.cpp pch ;
+exe hello-afx : hello-afx.cpp pch-afx : <define>HELLO ;
+exe hello-msvc-source : hello-msvc-source.cpp pch-msvc-source ;
+""")
+
+pch_content = """\
+#undef HELLO
+class TestClass
+{
+public:
+ TestClass( int, int ) {}
+};
+"""
+t.write("pch.hpp", pch_content)
+
+t.write("pch.cpp", """#include <pch.hpp>
+""")
+
+toolset = BoostBuild.get_toolset()
+for name in ("hello.cpp", "hello-afx.cpp", "hello-msvc-source.cpp"):
+ t.write(name, """int main() { TestClass c(1, 2); }
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+t.expect_addition("bin/$toolset/debug*/hello-afx.exe")
+t.expect_addition("bin/$toolset/debug*/hello-msvc-source.exe")
+
+
+# Now make the header unusable, replace its content with some garbage, but
+# preserve the size and timestamp to fool the compiler. If everything is OK,
+# B2 will not recreate PCH, and compiler will happily use pre-compiled
+# header, not noticing that the real header is bad.
+
+t.rename("pch.hpp", "pch.hpp.orig")
+s = "THIS WILL NOT COMPILE. "
+t.write("pch.hpp", s + (len(pch_content) - len(s)) * 'x')
+t.copy_timestamp("pch.hpp.orig", "pch.hpp")
+
+t.rm("bin/$toolset/debug*/hello.obj")
+t.rm("bin/$toolset/debug*/hello-afx.obj")
+t.rm("bin/$toolset/debug*/hello-msvc-source.obj")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.obj")
+t.expect_addition("bin/$toolset/debug*/hello-afx.obj")
+t.expect_addition("bin/$toolset/debug*/hello-msvc-source.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/prebuilt.py b/src/boost/tools/build/test/prebuilt.py
new file mode 100644
index 000000000..194cc3967
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that we can use already built sources
+
+import BoostBuild
+
+t = BoostBuild.Tester(["debug", "release"], use_test_config=False)
+
+t.set_tree('prebuilt')
+
+t.expand_toolset("ext/jamroot.jam")
+t.expand_toolset("jamroot.jam")
+
+# First, build the external project.
+t.run_build_system(subdir="ext")
+
+# Then pretend that we do not have the sources for the external project, and
+# can only use compiled binaries.
+t.copy("ext/jamfile2.jam", "ext/jamfile.jam")
+t.expand_toolset("ext/jamfile.jam")
+
+# Now check that we can build the main project, and that correct prebuilt file
+# is picked, depending of variant. This also checks that correct includes for
+# prebuilt libraries are used.
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+t.expect_addition("bin/$toolset/release*/hello.exe")
+
+t.rm("bin")
+
+
+# Now test that prebuilt file specified by absolute name works too.
+t.copy("ext/jamfile3.jam", "ext/jamfile.jam")
+t.expand_toolset("ext/jamfile.jam")
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.exe")
+t.expect_addition("bin/$toolset/release*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/prebuilt/ext/a.cpp b/src/boost/tools/build/test/prebuilt/ext/a.cpp
new file mode 100644
index 000000000..cb6e14e03
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/a.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+#ifdef RELEASE
+void release() {}
+#else
+void debug() {}
+#endif
diff --git a/src/boost/tools/build/test/prebuilt/ext/debug/a.h b/src/boost/tools/build/test/prebuilt/ext/debug/a.h
new file mode 100644
index 000000000..2691a8b42
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/debug/a.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+void debug();
diff --git a/src/boost/tools/build/test/prebuilt/ext/jamfile.jam b/src/boost/tools/build/test/prebuilt/ext/jamfile.jam
new file mode 100644
index 000000000..62cf00313
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/jamfile.jam
@@ -0,0 +1,13 @@
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+project ext
+ : requirements <variant>release:<define>RELEASE
+ ;
+
+lib a : a.cpp ;
+
+install dist : a : <variant>release:<location>release
+ <variant>debug:<location>debug ;
diff --git a/src/boost/tools/build/test/prebuilt/ext/jamfile2.jam b/src/boost/tools/build/test/prebuilt/ext/jamfile2.jam
new file mode 100644
index 000000000..6481808c6
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/jamfile2.jam
@@ -0,0 +1,41 @@
+
+import os ;
+
+local dll-suffix = so ;
+local prefix = "lib" ;
+if [ os.name ] in NT
+{
+ if [ MATCH ^(gcc) : $toolset ]
+ {
+ dll-suffix = dll.a ;
+ prefix = lib ;
+ }
+ else
+ {
+ dll-suffix = lib ;
+ prefix = "" ;
+ }
+}
+else if [ os.name ] in CYGWIN
+{
+ dll-suffix = dll ;
+}
+else if [ os.name ] in MACOSX
+{
+ dll-suffix = dylib ;
+}
+
+project ext ;
+
+lib a :
+ : <file>debug/$(prefix)a.$(dll-suffix) <variant>debug
+ :
+ : <include>debug
+ ;
+
+lib a :
+ : <file>release/$(prefix)a.$(dll-suffix) <variant>release
+ :
+ : <include>release
+ ;
+
diff --git a/src/boost/tools/build/test/prebuilt/ext/jamfile3.jam b/src/boost/tools/build/test/prebuilt/ext/jamfile3.jam
new file mode 100644
index 000000000..be2257fa2
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/jamfile3.jam
@@ -0,0 +1,48 @@
+
+# This Jamfile is the same as Jamfile2, except that
+# it tries to access prebuilt targets using absolute
+# paths. It used to be broken on Windows.
+
+import os ;
+
+local dll-suffix = so ;
+local prefix = "lib" ;
+if [ os.name ] in NT
+{
+ if [ MATCH ^(gcc) : $toolset ]
+ {
+ dll-suffix = dll.a ;
+ prefix = lib ;
+ }
+ else
+ {
+ dll-suffix = lib ;
+ prefix = "" ;
+ }
+}
+else if [ os.name ] in CYGWIN
+{
+ dll-suffix = dll ;
+}
+else if [ os.name ] in MACOSX
+{
+ dll-suffix = dylib ;
+}
+
+project ext ;
+
+# Assumed bjam was invoked from the project root
+local pwd = [ PWD ] ;
+
+lib a :
+ : <file>$(pwd)/ext/debug/$(prefix)a.$(dll-suffix) <variant>debug
+ :
+ : <include>debug
+ ;
+
+lib a :
+ : <file>$(pwd)/ext/release/$(prefix)a.$(dll-suffix) <variant>release
+ :
+ : <include>release
+ ;
+
diff --git a/src/boost/tools/build/test/prebuilt/ext/jamroot.jam b/src/boost/tools/build/test/prebuilt/ext/jamroot.jam
new file mode 100644
index 000000000..67160ada5
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/jamroot.jam
@@ -0,0 +1,5 @@
+# Copyright 2002, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
diff --git a/src/boost/tools/build/test/prebuilt/ext/release/a.h b/src/boost/tools/build/test/prebuilt/ext/release/a.h
new file mode 100644
index 000000000..2e35f4bcb
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/ext/release/a.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+void release();
diff --git a/src/boost/tools/build/test/prebuilt/hello.cpp b/src/boost/tools/build/test/prebuilt/hello.cpp
new file mode 100644
index 000000000..7d027171b
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/hello.cpp
@@ -0,0 +1,17 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#include <a.h>
+
+int main()
+{
+ #ifdef RELEASE
+ release();
+ #else
+ debug();
+ #endif
+ return 0;
+}
diff --git a/src/boost/tools/build/test/prebuilt/jamfile.jam b/src/boost/tools/build/test/prebuilt/jamfile.jam
new file mode 100644
index 000000000..992a68931
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/jamfile.jam
@@ -0,0 +1,13 @@
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+project test
+ : requirements <variant>release:<define>RELEASE
+ ;
+
+use-project /ext : ext ;
+
+exe hello : hello.cpp /ext//a ;
+
diff --git a/src/boost/tools/build/test/prebuilt/jamroot.jam b/src/boost/tools/build/test/prebuilt/jamroot.jam
new file mode 100644
index 000000000..3320a8abb
--- /dev/null
+++ b/src/boost/tools/build/test/prebuilt/jamroot.jam
@@ -0,0 +1,4 @@
+# Copyright 2002, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
diff --git a/src/boost/tools/build/test/preprocessor.py b/src/boost/tools/build/test/preprocessor.py
new file mode 100755
index 000000000..b30790c3d
--- /dev/null
+++ b/src/boost/tools/build/test/preprocessor.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Copyright 2011 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the C/C++ preprocessor.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", """
+project ;
+preprocessed hello : hello.cpp ;
+preprocessed a : a.c ;
+exe test : hello a : <define>FAIL ;
+""")
+
+t.write("hello.cpp", """
+#ifndef __cplusplus
+#error "This file must be compiled as C++"
+#endif
+#ifdef FAIL
+#error "Not preprocessed?"
+#endif
+extern "C" int foo();
+int main() { return foo(); }
+""")
+
+t.write("a.c", """
+/* This will not compile unless in C mode. */
+#ifdef __cplusplus
+#error "This file must be compiled as C"
+#endif
+#ifdef FAIL
+#error "Not preprocessed?"
+#endif
+int foo(void)
+{
+ int new = 0;
+ new = (new+1)*7;
+ return new;
+}
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/hello.ii")
+t.expect_addition("bin/$toolset/debug*/a.i")
+t.expect_addition("bin/$toolset/debug*/test.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/print.py b/src/boost/tools/build/test/print.py
new file mode 100644
index 000000000..10f999c35
--- /dev/null
+++ b/src/boost/tools/build/test/print.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+# Copyright 2003 Douglas Gregor
+# Copyright 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("jamroot.jam", "import gcc ;")
+
+t.write("jamfile.jam", """
+import print ;
+print.output foo ;
+print.text \\\"Something\\\" ;
+DEPENDS all : foo ;
+ALWAYS foo ;
+""")
+
+t.run_build_system()
+t.expect_content("foo", """\"Something\"""")
+
+t.write("jamfile.jam", """
+import print ;
+print.output foo ;
+print.text \\\n\\\"Somethingelse\\\" ;
+DEPENDS all : foo ;
+ALWAYS foo ;
+""")
+
+t.run_build_system()
+t.expect_content("foo", """\"Something\"
+\"Somethingelse\"""")
+
+t.write("jamfile.jam", """
+import print ;
+print.output foo ;
+print.text \\\"Different\\\" : true ;
+DEPENDS all : foo ;
+ALWAYS foo ;
+""")
+
+t.run_build_system()
+t.expect_content("foo", """\"Different\"""")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/project-test3/a.cpp b/src/boost/tools/build/test/project-test3/a.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/a.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test3/jamfile.jam b/src/boost/tools/build/test/project-test3/jamfile.jam
new file mode 100644
index 000000000..1151d5943
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/jamfile.jam
@@ -0,0 +1,13 @@
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+use-project /lib2 : lib2 ;
+use-project /lib3 : lib3 ;
+
+make a.exe : a.obj lib//b.obj /lib2//c.obj lib2//d.obj lib2/helper//e.obj /lib3//f.obj : yfc-link ;
+make a.obj : a.cpp : yfc-compile ;
+
+build-project lib2 ;
+build-project lib ;
diff --git a/src/boost/tools/build/test/project-test3/jamroot.jam b/src/boost/tools/build/test/project-test3/jamroot.jam
new file mode 100644
index 000000000..e27103dce
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/jamroot.jam
@@ -0,0 +1,67 @@
+# Copyright 2002-2005 Vladimir Prus.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import os ;
+import gcc ;
+import property ;
+import toolset ;
+
+rule properties-as-path ( properties * )
+{
+ local r ;
+ for local p in $(properties)
+ {
+ if $(p:G) != <action>
+ {
+ r += $(p) ;
+ }
+ }
+ return [ property.as-path
+ [ property.remove incidental : $(r) ] ] ;
+}
+
+toolset.flags yfc-compile KNOWN-PROPERTIES : <toolset> <optimization> ;
+toolset.flags yfc-link KNOWN-PROPERTIES : <toolset> <optimization> ;
+
+rule yfc-compile ( target : sources * : property-set * )
+{
+ PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ;
+}
+
+actions yfc-compile
+{
+ echo $(PROPERTIES) > $(<)
+ echo $(>) >> $(<)
+}
+
+rule yfc-link ( target : sources * : property-set * )
+{
+ PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ;
+}
+
+actions yfc-link
+{
+ echo $(PROPERTIES) > $(<)
+ echo $(>) >> $(<)
+}
+
+if [ os.name ] = VMS
+{
+ actions yfc-compile
+ {
+ PIPE WRITE SYS$OUTPUT "$(PROPERTIES)" | TYPE SYS$INPUT /OUT=$(<:W)
+ PIPE WRITE SYS$OUTPUT "$(>:J= ",")" | APPEND /NEW SYS$INPUT $(<:W)
+ }
+
+ actions yfc-link
+ {
+ PIPE WRITE SYS$OUTPUT "$(PROPERTIES)" | TYPE SYS$INPUT /OUT=$(<:W)
+ OPEN /APPEND FOUT $(<:W)
+ WRITE FOUT "$(>:J= ",")"
+ CLOSE FOUT
+ }
+}
+
+IMPORT $(__name__) : yfc-compile yfc-link : : yfc-compile yfc-link ;
diff --git a/src/boost/tools/build/test/project-test3/lib/b.cpp b/src/boost/tools/build/test/project-test3/lib/b.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib/b.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test3/lib/jamfile.jam b/src/boost/tools/build/test/project-test3/lib/jamfile.jam
new file mode 100644
index 000000000..bae24c16b
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib/jamfile.jam
@@ -0,0 +1,9 @@
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+use-project /lib2 : ../lib2 ;
+
+make b.obj : b.cpp : yfc-compile ;
+make m.exe : b.obj /lib2//c.obj : yfc-link ;
diff --git a/src/boost/tools/build/test/project-test3/lib2/c.cpp b/src/boost/tools/build/test/project-test3/lib2/c.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib2/c.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test3/lib2/d.cpp b/src/boost/tools/build/test/project-test3/lib2/d.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib2/d.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test3/lib2/helper/e.cpp b/src/boost/tools/build/test/project-test3/lib2/helper/e.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib2/helper/e.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test3/lib2/helper/jamfile.jam b/src/boost/tools/build/test/project-test3/lib2/helper/jamfile.jam
new file mode 100644
index 000000000..7d8966a98
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib2/helper/jamfile.jam
@@ -0,0 +1,9 @@
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+project lib2/helper ;
+
+make e.obj : e.cpp : yfc-compile ;
+
diff --git a/src/boost/tools/build/test/project-test3/lib2/jamfile.jam b/src/boost/tools/build/test/project-test3/lib2/jamfile.jam
new file mode 100644
index 000000000..300aa3228
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib2/jamfile.jam
@@ -0,0 +1,11 @@
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+project lib2 ;
+use-project /lib2/helper : helper ;
+
+make c.obj : c.cpp : yfc-compile ;
+make d.obj : d.cpp : yfc-compile ;
+make l.exe : c.obj ..//a.obj : yfc-link ;
diff --git a/src/boost/tools/build/test/project-test3/lib3/f.cpp b/src/boost/tools/build/test/project-test3/lib3/f.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib3/f.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test3/lib3/jamfile.jam b/src/boost/tools/build/test/project-test3/lib3/jamfile.jam
new file mode 100644
index 000000000..7c4a70398
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib3/jamfile.jam
@@ -0,0 +1,47 @@
+# Copyright 2003, 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This project-root.jam also serves the role of Jamfile
+project lib3 ;
+
+use-project /lib2/helper : ../lib2/helper ;
+
+import os ;
+import property ;
+
+rule properties-as-path ( properties * )
+{
+ local r ;
+ for local p in $(properties)
+ {
+ if $(p:G) != <action>
+ {
+ r += $(p) ;
+ }
+ }
+ return [ property.as-path
+ [ property.remove incidental : $(r) ] ] ;
+}
+
+rule mfc-compile ( target : sources * : property-set * )
+{
+ PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ;
+}
+
+actions mfc-compile
+{
+ echo $(PROPERTIES) > $(<)
+ echo $(>) >> $(<)
+}
+
+if [ os.name ] = VMS
+{
+ actions mfc-compile
+ {
+ PIPE WRITE SYS$OUTPUT "$(PROPERTIES)" | TYPE SYS$INPUT /OUT=$(<:W)
+ PIPE WRITE SYS$OUTPUT "$(>:J= ",")" | APPEND /NEW SYS$INPUT $(<:W)
+ }
+}
+
+make f.obj : f.cpp /lib2/helper//e.obj : mfc-compile ;
diff --git a/src/boost/tools/build/test/project-test3/lib3/jamroot.jam b/src/boost/tools/build/test/project-test3/lib3/jamroot.jam
new file mode 100644
index 000000000..2344236fe
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/lib3/jamroot.jam
@@ -0,0 +1,5 @@
+# Copyright 2002 Rene Rivera
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
diff --git a/src/boost/tools/build/test/project-test3/readme.txt b/src/boost/tools/build/test/project-test3/readme.txt
new file mode 100644
index 000000000..da27e54b2
--- /dev/null
+++ b/src/boost/tools/build/test/project-test3/readme.txt
@@ -0,0 +1,7 @@
+Copyright 2002 Vladimir Prus
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+
+This test checks that we have minimally working 'make' rule and that we can use target from
+different project with different project roots.
diff --git a/src/boost/tools/build/test/project-test4/a.cpp b/src/boost/tools/build/test/project-test4/a.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/a.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test4/a_gcc.cpp b/src/boost/tools/build/test/project-test4/a_gcc.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/a_gcc.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test4/jamfile.jam b/src/boost/tools/build/test/project-test4/jamfile.jam
new file mode 100644
index 000000000..514435963
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/jamfile.jam
@@ -0,0 +1,11 @@
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+project test : requirements <include>everything <threading>single ;
+
+make a.exe : a.obj lib//b.obj/<optimization>speed : yfc-link ;
+make b.exe : a.obj : yfc-link : <define>MACROS ;
+make a.obj : a.cpp : yfc-compile ;
diff --git a/src/boost/tools/build/test/project-test4/jamfile3.jam b/src/boost/tools/build/test/project-test4/jamfile3.jam
new file mode 100644
index 000000000..9770362d7
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/jamfile3.jam
@@ -0,0 +1,5 @@
+
+make a.exe : a.obj lib//b.obj/<optimization>on a_gcc.obj : yfc-link : <toolset>gcc ;
+make a.exe : a.obj lib//b.obj/<optimization>on : yfc-link : <threading>multi ;
+make a.obj : a.cpp : yfc-compile ;
+make a_gcc.obj : a_gcc.cpp : yfc-compile ;
diff --git a/src/boost/tools/build/test/project-test4/jamfile4.jam b/src/boost/tools/build/test/project-test4/jamfile4.jam
new file mode 100644
index 000000000..e3257801a
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/jamfile4.jam
@@ -0,0 +1,4 @@
+
+project test : requirements <include>everything <threading>single ;
+
+build-project lib2 ;
diff --git a/src/boost/tools/build/test/project-test4/jamfile5.jam b/src/boost/tools/build/test/project-test4/jamfile5.jam
new file mode 100644
index 000000000..1010be5e4
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/jamfile5.jam
@@ -0,0 +1,6 @@
+
+project test : requirements <include>everything <threading>single ;
+
+make a.exe : a.obj lib//b.obj/<variant>release : yfc-link ;
+make b.exe : a.obj : yfc-link : <define>MACROS ;
+make a.obj : a.cpp : yfc-compile ;
diff --git a/src/boost/tools/build/test/project-test4/jamroot.jam b/src/boost/tools/build/test/project-test4/jamroot.jam
new file mode 100644
index 000000000..afb858654
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/jamroot.jam
@@ -0,0 +1,68 @@
+# Copyright 2002, 2003, 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import os ;
+import gcc ;
+import property ;
+import toolset ;
+
+rule properties-as-path ( properties * )
+{
+ local r ;
+ for local p in $(properties)
+ {
+ if $(p:G) != <action>
+ {
+ r += $(p) ;
+ }
+ }
+ return [ property.as-path
+ [ property.remove incidental : $(r) ] ] ;
+}
+
+
+toolset.flags yfc-compile KNOWN-PROPERTIES : <toolset> <optimization> ;
+toolset.flags yfc-link KNOWN-PROPERTIES : <toolset> <optimization> ;
+
+
+rule yfc-compile ( target : sources * : property-set * )
+{
+ PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ;
+}
+
+actions yfc-compile
+{
+ echo $(PROPERTIES) > $(<)
+ echo $(>) >> $(<)
+}
+
+rule yfc-link ( target : sources * : property-set * )
+{
+ PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ;
+}
+
+actions yfc-link
+{
+ echo $(PROPERTIES) > $(<)
+ echo $(>) >> $(<)
+}
+
+if [ os.name ] = VMS
+{
+ actions yfc-compile
+ {
+ PIPE WRITE SYS$OUTPUT "$(PROPERTIES)" | TYPE SYS$INPUT /OUT=$(<:W)
+ PIPE WRITE SYS$OUTPUT "$(>:J= ",")" | APPEND /NEW SYS$INPUT $(<:W)
+ }
+
+ actions yfc-link
+ {
+ PIPE WRITE SYS$OUTPUT "$(PROPERTIES)" | TYPE SYS$INPUT /OUT=$(<:W)
+ OPEN /APPEND FOUT $(<:W)
+ WRITE FOUT "$(>:J= ",")"
+ CLOSE FOUT
+ }
+}
+
+#IMPORT $(__name__) : yfc-compile yfc-link : : yfc-compile yfc-link ;
diff --git a/src/boost/tools/build/test/project-test4/lib/b.cpp b/src/boost/tools/build/test/project-test4/lib/b.cpp
new file mode 100644
index 000000000..6547c9232
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib/b.cpp
@@ -0,0 +1,5 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/project-test4/lib/jamfile.jam b/src/boost/tools/build/test/project-test4/lib/jamfile.jam
new file mode 100644
index 000000000..92a3e83e4
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib/jamfile.jam
@@ -0,0 +1,6 @@
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+make b.obj : b.cpp : yfc-compile ;
diff --git a/src/boost/tools/build/test/project-test4/lib/jamfile1.jam b/src/boost/tools/build/test/project-test4/lib/jamfile1.jam
new file mode 100644
index 000000000..be2c3649a
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib/jamfile1.jam
@@ -0,0 +1,2 @@
+
+make b.obj : b.cpp : yfc-compile ;
diff --git a/src/boost/tools/build/test/project-test4/lib/jamfile2.jam b/src/boost/tools/build/test/project-test4/lib/jamfile2.jam
new file mode 100644
index 000000000..d47274bdf
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib/jamfile2.jam
@@ -0,0 +1,4 @@
+
+project lib : requirements <threading>multi ;
+
+make b.obj : b.cpp : yfc-compile ;
diff --git a/src/boost/tools/build/test/project-test4/lib/jamfile3.jam b/src/boost/tools/build/test/project-test4/lib/jamfile3.jam
new file mode 100644
index 000000000..73a78324b
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib/jamfile3.jam
@@ -0,0 +1,2 @@
+
+make b.obj : b.cpp : yfc-compile : <rtti>off ;
diff --git a/src/boost/tools/build/test/project-test4/lib2/jamfile.jam b/src/boost/tools/build/test/project-test4/lib2/jamfile.jam
new file mode 100644
index 000000000..1937059e5
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib2/jamfile.jam
@@ -0,0 +1,8 @@
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+project
+ : requirements <rtti>off
+ ;
diff --git a/src/boost/tools/build/test/project-test4/lib2/jamfile2.jam b/src/boost/tools/build/test/project-test4/lib2/jamfile2.jam
new file mode 100644
index 000000000..94b144d06
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/lib2/jamfile2.jam
@@ -0,0 +1,4 @@
+
+project mylib
+ : requirements <rtti>off
+ ;
diff --git a/src/boost/tools/build/test/project-test4/readme.txt b/src/boost/tools/build/test/project-test4/readme.txt
new file mode 100644
index 000000000..0c0ba2ca4
--- /dev/null
+++ b/src/boost/tools/build/test/project-test4/readme.txt
@@ -0,0 +1,6 @@
+Copyright 2002 Vladimir Prus
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+
+This test checks for correct properties of generated and used targets.
diff --git a/src/boost/tools/build/test/project_dependencies.py b/src/boost/tools/build/test/project_dependencies.py
new file mode 100644
index 000000000..7148dc1ad
--- /dev/null
+++ b/src/boost/tools/build/test/project_dependencies.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that we can specify a dependency property in project requirements, and
+# that it will not cause every main target in the project to be generated in its
+# own subdirectory.
+
+# The whole test is somewhat moot now.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "build-project src ;")
+
+t.write("lib/jamfile.jam", "lib lib1 : lib1.cpp ;")
+
+t.write("lib/lib1.cpp", """
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void foo() {}\n
+""")
+
+t.write("src/jamfile.jam", """
+project : requirements <library>../lib//lib1 ;
+exe a : a.cpp ;
+exe b : b.cpp ;
+""")
+
+t.write("src/a.cpp", """
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+void foo();
+int main() { foo(); }
+""")
+
+t.copy("src/a.cpp", "src/b.cpp")
+
+t.run_build_system()
+
+# Test that there is no "main-target-a" part.
+# t.expect_addition("src/bin/$toolset/debug*/a.exe")
+# t.expect_addition("src/bin/$toolset/debug*/b.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/project_glob.py b/src/boost/tools/build/test/project_glob.py
new file mode 100644
index 000000000..8e80a20c4
--- /dev/null
+++ b/src/boost/tools/build/test/project_glob.py
@@ -0,0 +1,212 @@
+#!/usr/bin/python
+
+# Copyright (C) 2003. Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'glob' rule in Jamfile context.
+
+import BoostBuild
+
+
+def test_basic():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "")
+ t.write("d1/a.cpp", "int main() {}\n")
+ t.write("d1/jamfile.jam", "exe a : [ glob *.cpp ] ../d2/d//l ;")
+ t.write("d2/d/l.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+void force_import_lib_creation() {}
+#endif
+""")
+ t.write("d2/d/jamfile.jam", "lib l : [ glob *.cpp ] ;")
+ t.write("d3/d/jamfile.jam", "exe a : [ glob ../*.cpp ] ;")
+ t.write("d3/a.cpp", "int main() {}\n")
+
+ t.run_build_system(subdir="d1")
+ t.expect_addition("d1/bin/$toolset/debug*/a.exe")
+
+ t.run_build_system(subdir="d3/d")
+ t.expect_addition("d3/d/bin/$toolset/debug*/a.exe")
+
+ t.rm("d2/d/bin")
+ t.run_build_system(subdir="d2/d")
+ t.expect_addition("d2/d/bin/$toolset/debug*/l.dll")
+
+ t.cleanup()
+
+
+def test_source_location():
+ """
+ Test that when 'source-location' is explicitly-specified glob works
+ relative to the source location.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "")
+ t.write("d1/a.cpp", "very bad non-compilable file\n")
+ t.write("d1/src/a.cpp", "int main() {}\n")
+ t.write("d1/jamfile.jam", """\
+project : source-location src ;
+exe a : [ glob *.cpp ] ../d2/d//l ;
+""")
+ t.write("d2/d/l.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+void force_import_lib_creation() {}
+#endif
+""")
+ t.write("d2/d/jamfile.jam", "lib l : [ glob *.cpp ] ;")
+
+ t.run_build_system(subdir="d1")
+ t.expect_addition("d1/bin/$toolset/debug*/a.exe")
+
+ t.cleanup()
+
+
+def test_wildcards_and_exclusion_patterns():
+ """
+ Test that wildcards can include directories. Also test exclusion
+ patterns.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "")
+ t.write("d1/src/foo/a.cpp", "void bar(); int main() { bar(); }\n")
+ t.write("d1/src/bar/b.cpp", "void bar() {}\n")
+ t.write("d1/src/bar/bad.cpp", "very bad non-compilable file\n")
+ t.write("d1/jamfile.jam", """\
+project : source-location src ;
+exe a : [ glob foo/*.cpp bar/*.cpp : bar/bad* ] ../d2/d//l ;
+""")
+ t.write("d2/d/l.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+void force_import_lib_creation() {}
+#endif
+""")
+ t.write("d2/d/jamfile.jam", "lib l : [ glob *.cpp ] ;")
+
+ t.run_build_system(subdir="d1")
+ t.expect_addition("d1/bin/$toolset/debug*/a.exe")
+
+ t.cleanup()
+
+
+def test_glob_tree():
+ """Test that 'glob-tree' works."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "")
+ t.write("d1/src/foo/a.cpp", "void bar(); int main() { bar(); }\n")
+ t.write("d1/src/bar/b.cpp", "void bar() {}\n")
+ t.write("d1/src/bar/bad.cpp", "very bad non-compilable file\n")
+ t.write("d1/jamfile.jam", """\
+project : source-location src ;
+exe a : [ glob-tree *.cpp : bad* ] ../d2/d//l ;
+""")
+ t.write("d2/d/l.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+void force_import_lib_creation() {}
+#endif
+""")
+ t.write("d2/d/jamfile.jam", "lib l : [ glob *.cpp ] ;")
+
+ t.run_build_system(subdir="d1")
+ t.expect_addition("d1/bin/$toolset/debug*/a.exe")
+
+ t.cleanup()
+
+
+def test_directory_names_in_glob_tree():
+ """Test that directory names in patterns for 'glob-tree' are rejected."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "")
+ t.write("d1/src/a.cpp", "very bad non-compilable file\n")
+ t.write("d1/src/foo/a.cpp", "void bar(); int main() { bar(); }\n")
+ t.write("d1/src/bar/b.cpp", "void bar() {}\n")
+ t.write("d1/src/bar/bad.cpp", "very bad non-compilable file\n")
+ t.write("d1/jamfile.jam", """\
+project : source-location src ;
+exe a : [ glob-tree foo/*.cpp bar/*.cpp : bad* ] ../d2/d//l ;
+""")
+ t.write("d2/d/l.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+void force_import_lib_creation() {}
+#endif
+""")
+ t.write("d2/d/jamfile.jam", "lib l : [ glob *.cpp ] ;")
+
+ t.run_build_system(subdir="d1", status=1)
+ t.expect_output_lines("error: The patterns * may not include directory")
+
+ t.cleanup()
+
+
+def test_glob_with_absolute_names():
+ """Test that 'glob' works with absolute names."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "")
+ t.write("d1/src/a.cpp", "very bad non-compilable file\n")
+ t.write("d1/src/foo/a.cpp", "void bar(); int main() { bar(); }\n")
+ t.write("d1/src/bar/b.cpp", "void bar() {}\n")
+ # Note that to get the current dir, we use bjam's PWD, not Python's
+ # os.getcwd(), because the former will always return a long path while the
+ # latter might return a short path, which would confuse path.glob.
+ t.write("d1/jamfile.jam", """\
+project : source-location src ;
+local pwd = [ PWD ] ; # Always absolute.
+exe a : [ glob $(pwd)/src/foo/*.cpp $(pwd)/src/bar/*.cpp ] ../d2/d//l ;
+""")
+ t.write("d2/d/l.cpp", """\
+#if defined(_WIN32)
+__declspec(dllexport)
+void force_import_lib_creation() {}
+#endif
+""")
+ t.write("d2/d/jamfile.jam", "lib l : [ glob *.cpp ] ;")
+
+ t.run_build_system(subdir="d1")
+ t.expect_addition("d1/bin/$toolset/debug*/a.exe")
+
+ t.cleanup()
+
+
+def test_glob_excludes_in_subdirectory():
+ """
+ Regression test: glob excludes used to be broken when building from a
+ subdirectory.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("jamroot.jam", "build-project p ;")
+ t.write("p/p.c", "int main() {}\n")
+ t.write("p/p_x.c", "very bad non-compilable file\n")
+ t.write("p/jamfile.jam", "exe p : [ glob *.c : p_x.c ] ;")
+
+ t.run_build_system(subdir="p")
+ t.expect_addition("p/bin/$toolset/debug*/p.exe")
+
+ t.cleanup()
+
+
+test_basic()
+test_source_location()
+test_wildcards_and_exclusion_patterns()
+test_glob_tree()
+test_directory_names_in_glob_tree()
+test_glob_with_absolute_names()
+test_glob_excludes_in_subdirectory()
diff --git a/src/boost/tools/build/test/project_id.py b/src/boost/tools/build/test/project_id.py
new file mode 100755
index 000000000..d985d3047
--- /dev/null
+++ b/src/boost/tools/build/test/project_id.py
@@ -0,0 +1,414 @@
+#!/usr/bin/python
+
+# Copyright (C) 2012. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests Boost Build's project-id handling.
+
+import BoostBuild
+import sys
+
+
+def test_assigning_project_ids():
+ t = BoostBuild.Tester(pass_toolset=False)
+ t.write("jamroot.jam", """\
+import assert ;
+import modules ;
+import notfile ;
+import project ;
+
+rule assert-project-id ( id ? : module-name ? )
+{
+ module-name ?= [ CALLER_MODULE ] ;
+ assert.result $(id) : project.attribute $(module-name) id ;
+}
+
+# Project rule modifies the main project id.
+assert-project-id ; # Initial project id is empty
+project foo ; assert-project-id /foo ;
+project ; assert-project-id /foo ;
+project foo ; assert-project-id /foo ;
+project bar ; assert-project-id /bar ;
+project /foo ; assert-project-id /foo ;
+project "" ; assert-project-id /foo ;
+
+# Calling the use-project rule does not modify the project's main id.
+use-project id1 : a ;
+# We need to load the 'a' Jamfile module manually as the use-project rule will
+# only schedule the load to be done after the current module load finishes.
+a-module = [ project.load a ] ;
+assert-project-id : $(a-module) ;
+use-project id2 : a ;
+assert-project-id : $(a-module) ;
+modules.call-in $(a-module) : project baz ;
+assert-project-id /baz : $(a-module) ;
+use-project id3 : a ;
+assert-project-id /baz : $(a-module) ;
+
+# Make sure the project id still holds after all the scheduled use-project loads
+# complete. We do this by scheduling the assert for the Jam action scheduling
+# phase.
+notfile x : @assert-a-rule ;
+rule assert-a-rule ( target : : properties * )
+{
+ assert-project-id /baz : $(a-module) ;
+}
+""")
+ t.write("a/jamfile.jam", """\
+# Initial project id for this module is empty.
+assert-project-id ;
+""")
+ t.run_build_system()
+ t.cleanup()
+
+
+def test_using_project_ids_in_target_references():
+ t = BoostBuild.Tester()
+ __write_appender(t, "appender.jam")
+ t.write("jamroot.jam", """\
+import type ;
+type.register AAA : _a ;
+type.register BBB : _b ;
+
+import appender ;
+appender.register aaa-to-bbb : AAA : BBB ;
+
+use-project id1 : a ;
+use-project /id2 : a ;
+
+bbb b1 : /id1//target ;
+bbb b2 : /id2//target ;
+bbb b3 : /id3//target ;
+bbb b4 : a//target ;
+bbb b5 : /project-a1//target ;
+bbb b6 : /project-a2//target ;
+bbb b7 : /project-a3//target ;
+
+use-project id3 : a ;
+""")
+ t.write("a/source._a", "")
+ t.write("a/jamfile.jam", """\
+project project-a1 ;
+project /project-a2 ;
+import alias ;
+alias target : source._a ;
+project /project-a3 ;
+""")
+
+ t.run_build_system()
+ t.expect_addition("bin/b%d._b" % x for x in range(1, 8))
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+def test_repeated_ids_for_different_projects():
+ t = BoostBuild.Tester()
+
+ t.write("a/jamfile.jam", "")
+ t.write("jamroot.jam", "project foo ; use-project foo : a ;")
+ t.run_build_system(status=1)
+ t.expect_output_lines("""\
+error: Attempt to redeclare already registered project id '/foo'.
+error: Original project:
+error: Name: Jamfile<*>
+error: Module: Jamfile<*>
+error: Main id: /foo
+error: File: jamroot.jam
+error: Location: .
+error: New project:
+error: Module: Jamfile<*>
+error: File: a*jamfile.jam
+error: Location: a""")
+
+ t.write("jamroot.jam", "use-project foo : a ; project foo ;")
+ t.run_build_system(status=1)
+ t.expect_output_lines("""\
+error: Attempt to redeclare already registered project id '/foo'.
+error: Original project:
+error: Name: Jamfile<*>
+error: Module: Jamfile<*>
+error: Main id: /foo
+error: File: jamroot.jam
+error: Location: .
+error: New project:
+error: Module: Jamfile<*>
+error: File: a*jamfile.jam
+error: Location: a""")
+
+ t.write("jamroot.jam", """\
+import modules ;
+import project ;
+modules.call-in [ project.load a ] : project foo ;
+project foo ;
+""")
+ t.run_build_system(status=1)
+ t.expect_output_lines("""\
+error: at jamroot.jam:4
+error: Attempt to redeclare already registered project id '/foo'.
+error: Original project:
+error: Name: Jamfile<*>
+error: Module: Jamfile<*>
+error: Main id: /foo
+error: File: a*jamfile.jam
+error: Location: a
+error: New project:
+error: Module: Jamfile<*>
+error: File: jamroot.jam
+error: Location: .""")
+
+ t.cleanup()
+
+
+def test_repeated_ids_for_same_project():
+ t = BoostBuild.Tester()
+
+ t.write("jamroot.jam", "project foo ; project foo ;")
+ t.run_build_system()
+
+ t.write("jamroot.jam", "project foo ; use-project foo : . ;")
+ t.run_build_system()
+
+ t.write("jamroot.jam", "project foo ; use-project foo : ./. ;")
+ t.run_build_system()
+
+ t.write("jamroot.jam", """\
+project foo ;
+use-project foo : . ;
+use-project foo : ./aaa/.. ;
+use-project foo : ./. ;
+""")
+ t.run_build_system()
+
+ # On Windows we have a case-insensitive file system and we can use
+ # backslashes as path separators.
+ # FIXME: Make a similar test pass on Cygwin.
+ if sys.platform in ['win32']:
+ t.write("a/fOo bAr/b/jamfile.jam", "")
+ t.write("jamroot.jam", r"""
+use-project bar : "a/foo bar/b" ;
+use-project bar : "a/foO Bar/b" ;
+use-project bar : "a/foo BAR/b/" ;
+use-project bar : "a\\.\\FOO bar\\b\\" ;
+""")
+ t.run_build_system()
+ t.rm("a")
+
+ t.write("bar/jamfile.jam", "")
+ t.write("jamroot.jam", """\
+use-project bar : bar ;
+use-project bar : bar/ ;
+use-project bar : bar// ;
+use-project bar : bar/// ;
+use-project bar : bar//// ;
+use-project bar : bar/. ;
+use-project bar : bar/./ ;
+use-project bar : bar/////./ ;
+use-project bar : bar/../bar/xxx/.. ;
+use-project bar : bar/..///bar/xxx///////.. ;
+use-project bar : bar/./../bar/xxx/.. ;
+use-project bar : bar/.////../bar/xxx/.. ;
+use-project bar : bar/././../bar/xxx/.. ;
+use-project bar : bar/././//////////../bar/xxx/.. ;
+use-project bar : bar/.///.////../bar/xxx/.. ;
+use-project bar : bar/./././xxx/.. ;
+use-project bar : bar/xxx////.. ;
+use-project bar : bar/xxx/.. ;
+use-project bar : bar///////xxx/.. ;
+""")
+ t.run_build_system()
+ t.rm("bar")
+
+ # On Windows we have a case-insensitive file system and we can use
+ # backslashes as path separators.
+ # FIXME: Make a similar test pass on Cygwin.
+ if sys.platform in ['win32']:
+ t.write("baR/jamfile.jam", "")
+ t.write("jamroot.jam", r"""
+use-project bar : bar ;
+use-project bar : BAR ;
+use-project bar : bAr ;
+use-project bar : bAr/ ;
+use-project bar : bAr\\ ;
+use-project bar : bAr\\\\ ;
+use-project bar : bAr\\\\///// ;
+use-project bar : bAr/. ;
+use-project bar : bAr/./././ ;
+use-project bar : bAr\\.\\.\\.\\ ;
+use-project bar : bAr\\./\\/.\\.\\ ;
+use-project bar : bAr/.\\././ ;
+use-project bar : Bar ;
+use-project bar : BaR ;
+use-project bar : BaR/./../bAr/xxx/.. ;
+use-project bar : BaR/./..\\bAr\\xxx/.. ;
+use-project bar : BaR/xxx/.. ;
+use-project bar : BaR///\\\\\\//xxx/.. ;
+use-project bar : Bar\\xxx/.. ;
+use-project bar : BAR/xXx/.. ;
+use-project bar : BAR/xXx\\\\/\\/\\//\\.. ;
+""")
+ t.run_build_system()
+ t.rm("baR")
+
+ t.cleanup()
+
+
+def test_unresolved_project_references():
+ t = BoostBuild.Tester()
+
+ __write_appender(t, "appender.jam")
+ t.write("a/source._a", "")
+ t.write("a/jamfile.jam", "import alias ; alias target : source._a ;")
+ t.write("jamroot.jam", """\
+import type ;
+type.register AAA : _a ;
+type.register BBB : _b ;
+
+import appender ;
+appender.register aaa-to-bbb : AAA : BBB ;
+
+use-project foo : a ;
+
+bbb b1 : a//target ;
+bbb b2 : /foo//target ;
+bbb b-invalid : invalid//target ;
+bbb b-root-invalid : /invalid//target ;
+bbb b-missing-root : foo//target ;
+bbb b-invalid-target : /foo//invalid ;
+""")
+
+ t.run_build_system(["b1", "b2"])
+ t.expect_addition("bin/b%d._b" % x for x in range(1, 3))
+ t.expect_nothing_more()
+
+ t.run_build_system(["b-invalid"], status=1)
+ t.expect_output_lines("""\
+error: Unable to find file or target named
+error: 'invalid//target'
+error: referred to from project at
+error: '.'
+error: could not resolve project reference 'invalid'""")
+
+ t.run_build_system(["b-root-invalid"], status=1)
+ t.expect_output_lines("""\
+error: Unable to find file or target named
+error: '/invalid//target'
+error: referred to from project at
+error: '.'
+error: could not resolve project reference '/invalid'""")
+
+ t.run_build_system(["b-missing-root"], status=1)
+ t.expect_output_lines("""\
+error: Unable to find file or target named
+error: 'foo//target'
+error: referred to from project at
+error: '.'
+error: could not resolve project reference 'foo' - possibly missing a """
+ "leading slash ('/') character.")
+
+ t.run_build_system(["b-invalid-target"], status=1)
+ t.expect_output_lines("""\
+error: Unable to find file or target named
+error: '/foo//invalid'
+error: referred to from project at
+error: '.'""")
+ t.expect_output_lines("*could not resolve project reference*", False)
+
+ t.cleanup()
+
+
+def __write_appender(t, name):
+ t.write(name,
+r"""# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Support for registering test generators that construct their targets by
+# simply appending their given input data, e.g. list of sources & targets.
+
+import "class" : new ;
+import generators ;
+import modules ;
+import sequence ;
+
+rule register ( id composing ? : source-types + : target-types + )
+{
+ local caller-module = [ CALLER_MODULE ] ;
+ id = $(caller-module).$(id) ;
+ local g = [ new generator $(id) $(composing) : $(source-types) :
+ $(target-types) ] ;
+ $(g).set-rule-name $(__name__).appender ;
+ generators.register $(g) ;
+ return $(id) ;
+}
+
+if [ modules.peek : NT ]
+{
+ X = ")" ;
+ ECHO_CMD = (echo. ;
+}
+else
+{
+ X = \" ;
+ ECHO_CMD = "echo $(X)" ;
+}
+
+local appender-runs ;
+
+# We set up separate actions for building each target in order to avoid having
+# to iterate over them in action (i.e. shell) code. We have to be extra careful
+# though to achieve the exact same effect as if doing all the work in just one
+# action. Otherwise Boost Jam might, under some circumstances, run only some of
+# our actions. To achieve this we register a series of actions for all the
+# targets (since they all have the same target list - either all or none of them
+# get run independent of which target actually needs to get built), each
+# building only a single target. Since all our actions use the same targets, we
+# can not use 'on-target' parameters to pass data to a specific action so we
+# pass them using the second 'sources' parameter which our actions then know how
+# to interpret correctly. This works well since Boost Jam does not automatically
+# add dependency relations between specified action targets & sources and so the
+# second argument, even though most often used to pass in a list of sources, can
+# actually be used for passing in any type of information.
+rule appender ( targets + : sources + : properties * )
+{
+ appender-runs = [ CALC $(appender-runs:E=0) + 1 ] ;
+ local target-index = 0 ;
+ local target-count = [ sequence.length $(targets) ] ;
+ local original-targets ;
+ for t in $(targets)
+ {
+ target-index = [ CALC $(target-index) + 1 ] ;
+ local appender-run = $(appender-runs) ;
+ if $(targets[2])-defined
+ {
+ appender-run += [$(target-index)/$(target-count)] ;
+ }
+ append $(targets) : $(appender-run:J=" ") $(t) $(sources) ;
+ }
+}
+
+actions append
+{
+ $(ECHO_CMD)-------------------------------------------------$(X)
+ $(ECHO_CMD)Appender run: $(>[1])$(X)
+ $(ECHO_CMD)Appender run: $(>[1])$(X)>> "$(>[2])"
+ $(ECHO_CMD)Target group: $(<:J=' ')$(X)
+ $(ECHO_CMD)Target group: $(<:J=' ')$(X)>> "$(>[2])"
+ $(ECHO_CMD) Target: '$(>[2])'$(X)
+ $(ECHO_CMD) Target: '$(>[2])'$(X)>> "$(>[2])"
+ $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)
+ $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)>> "$(>[2])"
+ $(ECHO_CMD)=================================================$(X)
+ $(ECHO_CMD)-------------------------------------------------$(X)>> "$(>[2])"
+}
+""")
+
+
+test_assigning_project_ids()
+test_using_project_ids_in_target_references()
+test_repeated_ids_for_same_project()
+test_repeated_ids_for_different_projects()
+test_unresolved_project_references()
diff --git a/src/boost/tools/build/test/project_root_constants.py b/src/boost/tools/build/test/project_root_constants.py
new file mode 100644
index 000000000..865789935
--- /dev/null
+++ b/src/boost/tools/build/test/project_root_constants.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2004, 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester()
+
+# Create the needed files.
+t.write("jamroot.jam", """\
+constant FOO : foobar gee ;
+ECHO $(FOO) ;
+""")
+
+t.run_build_system()
+t.expect_output_lines("foobar gee")
+
+# Regression test: when absolute paths were passed to path-constant rule,
+# B2 failed to recognize path as absolute and prepended the current
+# dir.
+t.write("jamroot.jam", """\
+import path ;
+local here = [ path.native [ path.pwd ] ] ;
+path-constant HERE : $(here) ;
+if $(HERE) != $(here)
+{
+ ECHO "PWD =" $(here) ;
+ ECHO "path constant =" $(HERE) ;
+ EXIT ;
+}
+""")
+t.write("jamfile.jam", "")
+
+t.run_build_system()
+
+t.write("jamfile.jam", """\
+# This tests that rule 'hello' will be imported to children unlocalized, and
+# will still access variables in this Jamfile.
+x = 10 ;
+constant FOO : foo ;
+rule hello ( ) { ECHO "Hello $(x)" ; }
+""")
+
+t.write("d/jamfile.jam", """\
+ECHO "d: $(FOO)" ;
+constant BAR : bar ;
+""")
+
+t.write("d/d2/jamfile.jam", """\
+ECHO "d2: $(FOO)" ;
+ECHO "d2: $(BAR)" ;
+hello ;
+""")
+
+t.run_build_system(subdir="d/d2")
+t.expect_output_lines("d: foo\nd2: foo\nd2: bar\nHello 10")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/project_root_rule.py b/src/boost/tools/build/test/project_root_rule.py
new file mode 100644
index 000000000..850792662
--- /dev/null
+++ b/src/boost/tools/build/test/project_root_rule.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2005.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that we can declare a rule in Jamroot that will be can be called in
+# child Jamfile to declare a target. Specifically test for use of 'glob' in that
+# rule.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+
+t.write("jamroot.jam", """
+project : requirements <link>static ;
+rule my-lib ( name ) { lib $(name) : [ glob *.cpp ] ; }
+""")
+
+t.write("sub/a.cpp", """
+""")
+
+t.write("sub/jamfile.jam", """
+my-lib foo ;
+""")
+
+
+t.run_build_system(subdir="sub")
+
+t.expect_addition("sub/bin/$toolset/debug*/foo.lib")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/project_test3.py b/src/boost/tools/build/test/project_test3.py
new file mode 100644
index 000000000..72533f116
--- /dev/null
+++ b/src/boost/tools/build/test/project_test3.py
@@ -0,0 +1,135 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+
+t = BoostBuild.Tester(translate_suffixes=0)
+
+# First check some startup.
+t.set_tree("project-test3")
+os.remove("jamroot.jam")
+t.run_build_system(status=1)
+
+t.expect_output_lines("*.yfc-compile\" unknown in module*")
+
+t.set_tree("project-test3")
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/a.obj")
+t.expect_content("bin/$toolset/debug*/a.obj", """\
+$toolset/debug*
+a.cpp
+""")
+
+t.expect_addition("bin/$toolset/debug*/a.exe")
+t.expect_content("bin/$toolset/debug*/a.exe",
+"$toolset/debug*\n" +
+"bin/$toolset/debug*/a.obj lib/bin/$toolset/debug*/b.obj " +
+"lib2/bin/$toolset/debug*/c.obj lib2/bin/$toolset/debug*/d.obj " +
+"lib2/helper/bin/$toolset/debug*/e.obj " +
+"lib3/bin/$toolset/debug*/f.obj\n"
+)
+
+t.expect_addition("lib/bin/$toolset/debug*/b.obj")
+t.expect_content("lib/bin/$toolset/debug*/b.obj", """\
+$toolset/debug*
+lib/b.cpp
+""")
+
+t.expect_addition("lib/bin/$toolset/debug*/m.exe")
+t.expect_content("lib/bin/$toolset/debug*/m.exe", """\
+$toolset/debug*
+lib/bin/$toolset/debug*/b.obj lib2/bin/$toolset/debug*/c.obj
+""")
+
+t.expect_addition("lib2/bin/$toolset/debug*/c.obj")
+t.expect_content("lib2/bin/$toolset/debug*/c.obj", """\
+$toolset/debug*
+lib2/c.cpp
+""")
+
+t.expect_addition("lib2/bin/$toolset/debug*/d.obj")
+t.expect_content("lib2/bin/$toolset/debug*/d.obj", """\
+$toolset/debug*
+lib2/d.cpp
+""")
+
+t.expect_addition("lib2/bin/$toolset/debug*/l.exe")
+t.expect_content("lib2/bin/$toolset/debug*/l.exe", """\
+$toolset/debug*
+lib2/bin/$toolset/debug*/c.obj bin/$toolset/debug*/a.obj
+""")
+
+t.expect_addition("lib2/helper/bin/$toolset/debug*/e.obj")
+t.expect_content("lib2/helper/bin/$toolset/debug*/e.obj", """\
+$toolset/debug*
+lib2/helper/e.cpp
+""")
+
+t.expect_addition("lib3/bin/$toolset/debug*/f.obj")
+t.expect_content("lib3/bin/$toolset/debug*/f.obj", """\
+$toolset/debug*
+lib3/f.cpp lib2/helper/bin/$toolset/debug*/e.obj
+""")
+
+t.touch("a.cpp")
+t.run_build_system()
+t.expect_touch(["bin/$toolset/debug*/a.obj",
+ "bin/$toolset/debug*/a.exe",
+ "lib2/bin/$toolset/debug*/l.exe"])
+
+t.run_build_system(["release", "optimization=off,speed"])
+t.expect_addition(["bin/$toolset/release/optimization-off*/a.exe",
+ "bin/$toolset/release/optimization-off*/a.obj",
+ "bin/$toolset/release*/a.exe",
+ "bin/$toolset/release*/a.obj"])
+
+t.run_build_system(["--clean-all"])
+t.expect_removal(["bin/$toolset/debug*/a.obj",
+ "bin/$toolset/debug*/a.exe",
+ "lib/bin/$toolset/debug*/b.obj",
+ "lib/bin/$toolset/debug*/m.exe",
+ "lib2/bin/$toolset/debug*/c.obj",
+ "lib2/bin/$toolset/debug*/d.obj",
+ "lib2/bin/$toolset/debug*/l.exe",
+ "lib3/bin/$toolset/debug*/f.obj"])
+
+# Now test target ids in command line.
+t.set_tree("project-test3")
+t.run_build_system(["lib//b.obj"])
+t.expect_addition("lib/bin/$toolset/debug*/b.obj")
+t.expect_nothing_more()
+
+t.run_build_system(["--clean", "lib//b.obj"])
+t.expect_removal("lib/bin/$toolset/debug*/b.obj")
+t.expect_nothing_more()
+
+t.run_build_system(["lib//b.obj"])
+t.expect_addition("lib/bin/$toolset/debug*/b.obj")
+t.expect_nothing_more()
+
+t.run_build_system(["release", "lib2/helper//e.obj", "/lib3//f.obj"])
+t.expect_addition("lib2/helper/bin/$toolset/release*/e.obj")
+t.expect_addition("lib3/bin/$toolset/release*/f.obj")
+t.expect_nothing_more()
+
+# Test project ids in command line work as well.
+t.set_tree("project-test3")
+t.run_build_system(["/lib2"])
+t.expect_addition("lib2/bin/$toolset/debug*/" *
+ BoostBuild.List("c.obj d.obj l.exe"))
+t.expect_addition("bin/$toolset/debug*/a.obj")
+t.expect_nothing_more()
+
+t.run_build_system(["lib"])
+t.expect_addition("lib/bin/$toolset/debug*/" *
+ BoostBuild.List("b.obj m.exe"))
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/project_test4.py b/src/boost/tools/build/test/project_test4.py
new file mode 100644
index 000000000..816340c8c
--- /dev/null
+++ b/src/boost/tools/build/test/project_test4.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(translate_suffixes=0)
+
+
+t.set_tree("project-test4")
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/a.obj")
+t.expect_content("bin/$toolset/debug*/a.obj",
+"""$toolset/debug*/include-everything*
+a.cpp
+""")
+
+t.expect_addition("bin/$toolset/debug*/a.exe")
+t.expect_content("bin/$toolset/debug*/a.exe",
+"$toolset/debug*/include-everything*\n" +
+"bin/$toolset/debug*/a.obj lib/bin/$toolset/debug/optimization-speed*/b.obj\n"
+)
+
+t.expect_addition("lib/bin/$toolset/debug/optimization-speed*/b.obj")
+t.expect_content("lib/bin/$toolset/debug/optimization-speed*/b.obj",
+"""$toolset/debug/include-everything/optimization-speed*
+lib/b.cpp
+""")
+
+t.expect_addition("bin/$toolset/debug*/b.exe")
+t.expect_content("bin/$toolset/debug*/b.exe",
+"$toolset/debug/define-MACROS/include-everything*\n" +
+"bin/$toolset/debug*/a.obj\n"
+)
+
+t.copy("lib/jamfile3.jam", "lib/jamfile.jam")
+
+# Link-compatibility check for rtti is disabled...
+#t.run_build_system(status=None)
+#import string
+#t.fail_test(t.stdout().find(
+#"""warning: targets produced from b.obj are link incompatible
+#warning: with main target a.exe""") !=-0)
+
+# Test that if we specified composite property in target reference, everything
+# works OK.
+
+t.copy("lib/jamfile1.jam", "lib/jamfile.jam")
+t.copy("jamfile5.jam", "jamfile.jam")
+
+t.run_build_system()
+
+t.expect_addition("lib/bin/$toolset/release*/b.obj")
+
+t.expect_content("bin/$toolset/debug*/a.exe",
+"$toolset/debug/include-everything*\n" +
+"bin/$toolset/debug*/a.obj lib/bin/$toolset/release*/b.obj\n"
+)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/property_expansion.py b/src/boost/tools/build/test/property_expansion.py
new file mode 100644
index 000000000..48ece0c91
--- /dev/null
+++ b/src/boost/tools/build/test/property_expansion.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that free property inside.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """\
+variant debug-AA : debug : <define>AA ;
+alias all : hello ;
+exe hello : hello.cpp ;
+explicit hello ;
+""")
+
+t.write("hello.cpp", """\
+#ifdef AA
+int main() {}
+#endif
+""")
+
+t.run_build_system(["debug-AA"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/qt4.py b/src/boost/tools/build/test/qt4.py
new file mode 100755
index 000000000..85e5e6781
--- /dev/null
+++ b/src/boost/tools/build/test/qt4.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+# (c) Copyright Juergen Hunold 2008
+# Use, modification, and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE.txt or copy at https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+
+# Run test in real directory in order to find Boost.Test via Boost Top-Level
+# Jamroot.
+qt4_dir = os.getcwd() + "/qt4"
+
+t = BoostBuild.Tester(workdir=qt4_dir)
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/qt4/jamroot.jam b/src/boost/tools/build/test/qt4/jamroot.jam
new file mode 100644
index 000000000..7a4b1dd48
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/jamroot.jam
@@ -0,0 +1,82 @@
+# (c) Copyright Juergen Hunold 2008
+# Use, modification, and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE.txt or copy at https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import qt4 ;
+import testing ;
+import cast ;
+
+
+
+if [ qt4.initialized ]
+{
+ use-project /boost : ../../../.. ;
+
+ project qttest
+ : requirements
+ <library>/boost/test//boost_unit_test_framework
+ ;
+
+ alias qt-tests :
+ # Check for explicit libraries, <use>/qt should not link any lib
+ [ link-fail qtcorefail.cpp : <use>/qt ]
+
+ [ run qtcore.cpp /qt//QtCore ]
+ [ run qtsql.cpp /qt//QtSql ]
+ [ run qtxml.cpp /qt//QtXml ]
+ [ run qtnetwork.cpp /qt//QtNetwork ]
+ [ run qtscript.cpp /qt//QtScript ]
+ [ run qtscripttools.cpp /qt//QtScriptTools ]
+ [ run qtxmlpatterns.cpp /qt//QtXmlPatterns ]
+
+ # ToDo: runable example code
+ [ link qtsvg.cpp /qt//QtSvg ]
+ [ link qtgui.cpp /qt//QtGui ]
+
+ # Multimedia toolkits.
+ [ link qtwebkit.cpp /qt//QtWebKit ]
+ [ link phonon.cpp /qt//phonon ]
+ [ link qtmultimedia.cpp /qt//QtMultimedia ]
+
+ # QML
+ [ link qtdeclarative.cpp /qt//QtDeclarative ]
+
+ # Help systems.
+ [ link qthelp.cpp /qt//QtHelp ]
+ [ link qtassistant.cpp /qt//QtAssistantClient : <conditional>@check_for_assistant ]
+
+ # Check working and disabled Qt3Support
+ [ link qt3support.cpp /qt//Qt3Support : <qt3support>on ]
+ [ compile-fail qt3support.cpp /qt//Qt3Support : <qt3support>off ]
+
+ # Testing using QtTest. Simple sample
+ # ToDo: better support for "automoc" aka '#include "qttest.moc"'
+ [ run qttest.cpp [ cast _ moccable-cpp : qttest.cpp ] /qt//QtTest : : : <define>TEST_MOCK ]
+
+ # Test moc rule
+ [ run mock.cpp mock.h /qt//QtCore : : : <define>TEST_MOCK ]
+
+ # Test resource compiler
+ [ run rcc.cpp rcc.qrc /qt//QtCore : : : <rccflags>"-compress 9 -threshold 10" ]
+
+ : # requirements
+ : # default-build
+ : # usage-requirements
+ ;
+}
+
+# QtAssistant is removed from Qt >= 4.6
+rule check_for_assistant ( properties * )
+{
+ # Extract version number from toolset
+ local version = [ MATCH "<qt>([0-9.]+).*"
+ : $(properties) ] ;
+
+ if $(version) > "4.6.99"
+ {
+ result += <build>no ;
+ }
+}
+
+
diff --git a/src/boost/tools/build/test/qt4/mock.cpp b/src/boost/tools/build/test/qt4/mock.cpp
new file mode 100644
index 000000000..8465a4ccc
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/mock.cpp
@@ -0,0 +1,26 @@
+// (c) Copyright Juergen Hunold 2011
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtMoc
+
+#include "mock.h"
+
+#include <boost/test/unit_test.hpp>
+
+Mock::Mock()
+{
+}
+
+/*!
+ Check that the compiler get the correct #defines.
+ The logic to test the moc is in the header file "mock.h"
+ */
+BOOST_AUTO_TEST_CASE(construct_mock)
+{
+ delete new Mock();
+
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(TEST_MOCK), true);
+}
diff --git a/src/boost/tools/build/test/qt4/mock.h b/src/boost/tools/build/test/qt4/mock.h
new file mode 100644
index 000000000..1a6280461
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/mock.h
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2011
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#include <QtCore/QObject>
+
+class Mock : public QObject
+{
+ /*!
+ Test that the moc gets the necessary #defines
+ Else the moc will not see the Q_OBJECT macro, issue a warning
+ and linking will fail due to missing vtable symbols.
+ */
+#if defined(TEST_MOCK)
+ Q_OBJECT
+#endif
+ public:
+
+ Mock();
+};
diff --git a/src/boost/tools/build/test/qt4/phonon.cpp b/src/boost/tools/build/test/qt4/phonon.cpp
new file mode 100644
index 000000000..8ece7c0e3
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/phonon.cpp
@@ -0,0 +1,23 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtPhonon
+
+#include <phonon/MediaObject>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_PHONON_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( phonon_object)
+{
+ Phonon::MediaObject player;
+}
diff --git a/src/boost/tools/build/test/qt4/qt3support.cpp b/src/boost/tools/build/test/qt4/qt3support.cpp
new file mode 100644
index 000000000..1f48c05f6
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qt3support.cpp
@@ -0,0 +1,29 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE Qt3Support
+
+#include <Q3Table>
+
+#include <boost/test/unit_test.hpp>
+
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SQL_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_NETWORK_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_QT3SUPPORT_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT3_SUPPORT), true);
+}
+
+BOOST_AUTO_TEST_CASE( q3table )
+{
+ Q3Table q3table;
+
+}
+
diff --git a/src/boost/tools/build/test/qt4/qtassistant.cpp b/src/boost/tools/build/test/qt4/qtassistant.cpp
new file mode 100644
index 000000000..5df7ab35b
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtassistant.cpp
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtAssistant
+
+#include <QAssistantClient>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( empty_assistant)
+{
+ QAssistantClient client(QString());
+}
diff --git a/src/boost/tools/build/test/qt4/qtcore.cpp b/src/boost/tools/build/test/qt4/qtcore.cpp
new file mode 100644
index 000000000..f71dc4c83
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtcore.cpp
@@ -0,0 +1,22 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCore
+#include <QtCore>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( qstring_test)
+{
+ QString dummy;
+
+ BOOST_CHECK_EQUAL(dummy.isEmpty(), true);
+}
diff --git a/src/boost/tools/build/test/qt4/qtcorefail.cpp b/src/boost/tools/build/test/qt4/qtcorefail.cpp
new file mode 100644
index 000000000..edce90127
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtcorefail.cpp
@@ -0,0 +1,23 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCoreFail
+
+#include <QtCore>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( qstring_test)
+{
+ QString dummy;
+
+ BOOST_CHECK_EQUAL(dummy.isEmpty(), true);
+}
diff --git a/src/boost/tools/build/test/qt4/qtdeclarative.cpp b/src/boost/tools/build/test/qt4/qtdeclarative.cpp
new file mode 100644
index 000000000..f76fa47de
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtdeclarative.cpp
@@ -0,0 +1,27 @@
+// (c) Copyright Juergen Hunold 2011
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtMultimedia
+
+#include <QApplication>
+#include <QDeclarativeView>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_DECLARATIVE_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( declarative )
+{
+ QApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+ QDeclarativeView view;
+}
diff --git a/src/boost/tools/build/test/qt4/qtgui.cpp b/src/boost/tools/build/test/qt4/qtgui.cpp
new file mode 100644
index 000000000..279058c82
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtgui.cpp
@@ -0,0 +1,42 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtGui
+
+#include <QApplication>
+
+#include <boost/test/unit_test.hpp>
+
+struct Fixture
+{
+ Fixture()
+ : application(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv,
+ false)
+ {
+ BOOST_TEST_MESSAGE( "setup QApplication fixture" );
+ }
+
+ ~Fixture()
+ {
+ BOOST_TEST_MESSAGE( "teardown QApplication fixture" );
+ }
+
+ QApplication application;
+};
+
+BOOST_GLOBAL_FIXTURE( Fixture );
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( qtgui_test)
+{
+ BOOST_CHECK_EQUAL(true, true);
+}
diff --git a/src/boost/tools/build/test/qt4/qthelp.cpp b/src/boost/tools/build/test/qt4/qthelp.cpp
new file mode 100644
index 000000000..96900acdf
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qthelp.cpp
@@ -0,0 +1,22 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtHelp
+
+#include <QtHelp>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( empty_engine)
+{
+ QHelpEngine engine(QString());
+}
diff --git a/src/boost/tools/build/test/qt4/qtmultimedia.cpp b/src/boost/tools/build/test/qt4/qtmultimedia.cpp
new file mode 100644
index 000000000..f4d7fe0fb
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtmultimedia.cpp
@@ -0,0 +1,25 @@
+// (c) Copyright Juergen Hunold 2009
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtMultimedia
+
+#include <QAudioDeviceInfo>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_MULTIMEDIA_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( audiodevices)
+{
+ QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
+ for(int i = 0; i < devices.size(); ++i) {
+ BOOST_TEST_MESSAGE(QAudioDeviceInfo(devices.at(i)).deviceName().constData());
+ }
+}
diff --git a/src/boost/tools/build/test/qt4/qtnetwork.cpp b/src/boost/tools/build/test/qt4/qtnetwork.cpp
new file mode 100644
index 000000000..9e288d1c6
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtnetwork.cpp
@@ -0,0 +1,33 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtNetwork
+
+#include <QHostInfo>
+
+#include <QTextStream>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_NETWORK_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( hostname )
+{
+ QHostInfo info(QHostInfo::fromName("www.boost.org")); //blocking lookup
+
+ QTextStream stream(stdout, QIODevice::WriteOnly);
+
+ Q_FOREACH(QHostAddress address, info.addresses())
+ {
+ BOOST_CHECK_EQUAL(address.isNull(), false);
+ stream << address.toString() << endl;
+ }
+}
diff --git a/src/boost/tools/build/test/qt4/qtscript.cpp b/src/boost/tools/build/test/qt4/qtscript.cpp
new file mode 100644
index 000000000..0cdd9c08c
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtscript.cpp
@@ -0,0 +1,37 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtScript
+
+#include <QScriptEngine>
+
+#include <QCoreApplication>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+std::ostream&
+operator << (std::ostream& stream, QString const& string)
+{
+ stream << qPrintable(string);
+ return stream;
+}
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SCRIPT_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( script )
+{
+ QCoreApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+ QScriptEngine myEngine;
+ QScriptValue three = myEngine.evaluate("1 + 2");
+
+ BOOST_CHECK_EQUAL(three.toNumber(), 3);
+ BOOST_CHECK_EQUAL(three.toString(), QLatin1String("3"));
+}
diff --git a/src/boost/tools/build/test/qt4/qtscripttools.cpp b/src/boost/tools/build/test/qt4/qtscripttools.cpp
new file mode 100644
index 000000000..4defa30c6
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtscripttools.cpp
@@ -0,0 +1,47 @@
+// (c) Copyright Juergen Hunold 2009
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtScriptTools
+
+#include <QScriptEngine>
+
+#include <QScriptEngineDebugger>
+
+#include <QApplication>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+namespace utf = boost::unit_test::framework;
+
+std::ostream&
+operator << (std::ostream& stream, QString const& string)
+{
+ stream << qPrintable(string);
+ return stream;
+}
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SCRIPTTOOLS_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( script )
+{
+ QApplication app(utf::master_test_suite().argc,
+ utf::master_test_suite().argv);
+
+ QScriptEngine myEngine;
+ QScriptValue three = myEngine.evaluate("1 + 2");
+
+ QScriptEngineDebugger debugger;
+ debugger.attachTo(&myEngine);
+
+ BOOST_CHECK_EQUAL(three.toNumber(), 3);
+ BOOST_CHECK_EQUAL(three.toString(), QLatin1String("3"));
+
+ debugger.detach();
+}
diff --git a/src/boost/tools/build/test/qt4/qtsql.cpp b/src/boost/tools/build/test/qt4/qtsql.cpp
new file mode 100644
index 000000000..aef956e4f
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtsql.cpp
@@ -0,0 +1,37 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtSql
+
+#include <QSqlDatabase>
+
+#include <QTextStream>
+#include <QStringList>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SQL_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( drivers )
+{
+ QTextStream stream(stdout, QIODevice::WriteOnly);
+
+ Q_FOREACH(QString it, QSqlDatabase:: drivers())
+ {
+ stream << it << endl;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( construct )
+{
+ QSqlDatabase database;
+ BOOST_CHECK_EQUAL(database.isOpen(), false);
+}
diff --git a/src/boost/tools/build/test/qt4/qtsvg.cpp b/src/boost/tools/build/test/qt4/qtsvg.cpp
new file mode 100644
index 000000000..911e86782
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtsvg.cpp
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtSvg
+
+#include <QtSvg>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SVG_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( generator_construct)
+{
+ QSvgGenerator generator;
+}
diff --git a/src/boost/tools/build/test/qt4/qttest.cpp b/src/boost/tools/build/test/qt4/qttest.cpp
new file mode 100644
index 000000000..7ccd2c344
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qttest.cpp
@@ -0,0 +1,30 @@
+// (c) Copyright Juergen Hunold 2008-2011
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#include <QtTest>
+
+class QtTest: public QObject
+{
+ /*!
+ Test if the moc gets the #define
+ */
+#if defined(TEST_MOCK)
+ Q_OBJECT
+#endif
+
+private Q_SLOTS:
+ void toUpper();
+};
+
+void
+QtTest::toUpper()
+{
+ QString str = "Hello";
+ QCOMPARE(str.toUpper(), QString("HELLO"));
+}
+
+QTEST_MAIN(QtTest)
+#include "qttest.moc"
+
diff --git a/src/boost/tools/build/test/qt4/qtwebkit.cpp b/src/boost/tools/build/test/qt4/qtwebkit.cpp
new file mode 100644
index 000000000..961c1dd8f
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtwebkit.cpp
@@ -0,0 +1,24 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebKit
+
+#include <QWebPage>
+#include <QApplication>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBKIT_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( webkit )
+{
+ QWebPage page;
+ BOOST_CHECK_EQUAL(page.isModified(), false);
+}
diff --git a/src/boost/tools/build/test/qt4/qtxml.cpp b/src/boost/tools/build/test/qt4/qtxml.cpp
new file mode 100644
index 000000000..f3758da42
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtxml.cpp
@@ -0,0 +1,29 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtXml
+
+#include <QtXml>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( reader_construct)
+{
+ QXmlStreamReader reader;
+ BOOST_CHECK_EQUAL(reader.atEnd(), false);
+}
+
+BOOST_AUTO_TEST_CASE( writer_construct)
+{
+ QXmlStreamWriter writer;
+ BOOST_CHECK_EQUAL(writer.device(), static_cast<QIODevice*>(0));
+}
+
diff --git a/src/boost/tools/build/test/qt4/qtxmlpatterns.cpp b/src/boost/tools/build/test/qt4/qtxmlpatterns.cpp
new file mode 100644
index 000000000..eaf8eb94e
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/qtxmlpatterns.cpp
@@ -0,0 +1,76 @@
+// (c) Copyright Juergen Hunold 2008
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtXmlPatterns
+
+#include <QXmlQuery>
+#include <QXmlSerializer>
+
+#include <QCoreApplication>
+#include <QString>
+#include <QTextStream>
+#include <QBuffer>
+
+#include <boost/test/unit_test.hpp>
+
+
+struct Fixture
+{
+ Fixture()
+ : application(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv)
+ {
+ BOOST_TEST_MESSAGE( "setup QCoreApplication fixture" );
+ }
+
+ ~Fixture()
+ {
+ BOOST_TEST_MESSAGE( "teardown QCoreApplication fixture" );
+ }
+
+ QCoreApplication application;
+};
+
+BOOST_GLOBAL_FIXTURE( Fixture );
+
+QByteArray doc("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml/\" xml:lang=\"en\" lang=\"en\">"
+" <head>"
+" <title>Global variables report for globals.gccxml</title>"
+" </head>"
+"<body><p>Some Test text</p></body></html>");
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XMLPATTERNS_LIB), true);
+
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), false);
+}
+
+BOOST_AUTO_TEST_CASE( extract )
+{
+
+ QBuffer buffer(&doc); // This is a QIODevice.
+ buffer.open(QIODevice::ReadOnly);
+ QXmlQuery query;
+ query.bindVariable("myDocument", &buffer);
+ query.setQuery("declare variable $myDocument external;"
+ "doc($myDocument)");///p[1]");
+
+ BOOST_CHECK_EQUAL(query.isValid(), true);
+
+ QByteArray result;
+ QBuffer out(&result);
+ out.open(QIODevice::WriteOnly);
+
+ QXmlSerializer serializer(query, &out);
+ BOOST_CHECK_EQUAL(query.evaluateTo(&serializer), true);
+
+ QTextStream stream(stdout);
+ BOOST_CHECK_EQUAL(result.isEmpty(), false);
+ stream << "hallo" << result << endl;
+}
+
diff --git a/src/boost/tools/build/test/qt4/rcc.cpp b/src/boost/tools/build/test/qt4/rcc.cpp
new file mode 100644
index 000000000..85ffa1b14
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/rcc.cpp
@@ -0,0 +1,20 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCore
+#include <QtCore>
+
+#include <boost/test/unit_test.hpp>
+
+std::ostream& operator<<(std::ostream& out, QString const& text)
+{
+ out << text.toUtf8().constData();
+ return out;
+}
+
+BOOST_AUTO_TEST_CASE (check_exists)
+{
+ BOOST_CHECK(QFile::exists(":/test/rcc.cpp"));
+}
diff --git a/src/boost/tools/build/test/qt4/rcc.qrc b/src/boost/tools/build/test/qt4/rcc.qrc
new file mode 100644
index 000000000..13ca38a5d
--- /dev/null
+++ b/src/boost/tools/build/test/qt4/rcc.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+ <qresource prefix="/test/">
+ <file>rcc.cpp</file>
+ </qresource>
+</RCC>
diff --git a/src/boost/tools/build/test/qt5.py b/src/boost/tools/build/test/qt5.py
new file mode 100755
index 000000000..4712a79e8
--- /dev/null
+++ b/src/boost/tools/build/test/qt5.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+# (c) Copyright Juergen Hunold 2012
+# Use, modification, and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE.txt or copy at https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+
+# Run test in real directory in order to find Boost.Test via Boost Top-Level
+# Jamroot.
+qt5_dir = os.path.dirname(os.path.abspath(__file__)) + "/qt5"
+
+t = BoostBuild.Tester(workdir=qt5_dir)
+
+t.run_build_system()
+# Fails if a warning is thrown
+t.fail_test( t.stdout().find("warning") != -1 )
+t.cleanup()
diff --git a/src/boost/tools/build/test/qt5/initialization.cpp b/src/boost/tools/build/test/qt5/initialization.cpp
new file mode 100644
index 000000000..0a2fc1e7d
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/initialization.cpp
@@ -0,0 +1,7 @@
+#include <iostream>
+
+int main()
+{
+ // dummy file to test initialization of qt
+ return 0;
+} \ No newline at end of file
diff --git a/src/boost/tools/build/test/qt5/jamroot.jam b/src/boost/tools/build/test/qt5/jamroot.jam
new file mode 100644
index 000000000..989e17608
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/jamroot.jam
@@ -0,0 +1,118 @@
+# (c) Copyright Juergen Hunold 2008
+# Use, modification, and distribution are subject to the
+# Boost Software License, Version 1.0. (See accompanying file
+# LICENSE.txt or copy at https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import qt5 ;
+import testing ;
+import cast ;
+
+path-constant CWD : . ;
+
+if ! [ qt5.initialized ]
+{
+ # assuming qt5 from system
+ using qt5 : /usr/ ;
+
+ project qttest-initialization : ;
+
+ alias qt-tests-initialization :
+ [ run initialization.cpp /qt5//QtCore ]
+ : # requirements
+ : # default-build
+ : # usage-requirements
+ ;
+}
+
+if [ qt5.initialized ]
+{
+ use-project /boost : ../../../.. ;
+
+ project qttest
+ : requirements
+ <library>/boost/test//boost_unit_test_framework
+ ;
+
+ alias qt-tests :
+ # Check for explicit libraries, <use>/qt should not link any lib
+ [ link-fail qtcorefail.cpp : <use>/qt ]
+
+ [ run qtcore.cpp /qt5//QtCore ]
+ [ run qtsql.cpp /qt5//QtSql ]
+ [ run qtxml.cpp /qt5//QtXml ]
+ [ run qtnetwork.cpp /qt5//QtNetwork ]
+ [ run qtscript.cpp /qt5//QtScript ]
+ [ run qtscripttools.cpp /qt5//QtScriptTools ]
+ [ run qtxmlpatterns.cpp /qt5//QtXmlPatterns ]
+
+ [ run qtpositioning.cpp /qt5//QtPositioning ]
+
+ # ToDo: runable example code
+ [ link qtsvg.cpp /qt5//QtSvg ]
+ [ link qtwidgets.cpp /qt5//QtWidgets ]
+
+ # Multimedia toolkits.
+ [ link qtwebkit.cpp /qt5//QtWebKit ]
+ [ link qtwebkitwidgets.cpp /qt5//QtWebKitWidgets ]
+ [ link qtmultimedia.cpp /qt5//QtMultimedia ]
+
+ # QtQuick version1
+ [ link qtdeclarative.cpp /qt5//QtDeclarative ]
+
+ # QtQuick version2
+ [ run qtquick.cpp /qt5//QtQuick : "--" -platform offscreen : $(CWD)/qtquick.qml ]
+
+ [ run qtwebengine.cpp /qt5//QtWebEngine ]
+ [ run qtwebenginewidgets.cpp /qt5//QtWebEngineWidgets ]
+
+ # QtSerialPort
+ [ run qtserialport.cpp /qt5//QtSerialPort ]
+
+ [ run qtlocation.cpp /qt5//QtLocation ]
+
+ [ run qtwebchannel.cpp /qt5//QtWebChannel ]
+ [ run qtwebsockets.cpp /qt5//QtWebSockets ]
+ [ run qtwebview.cpp /qt5//QtWebView ]
+
+ [ run qtpurchasing.cpp /qt5//QtPurchasing ]
+
+ [ run qtcharts.cpp /qt5//QtCharts ]
+
+ [ run qt3dcore.cpp /qt5//Qt3DCore ]
+ [ run qt3drender.cpp /qt5//Qt3DRender ]
+ [ run qt3dinput.cpp /qt5//Qt3DInput ]
+ [ run qt3dlogic.cpp /qt5//Qt3DLogic ]
+
+ [ run qtdatavisualization.cpp /qt5//QtDataVisualization ]
+
+ # Qt Connectivity
+ [ run qtbluetooth.cpp /qt5//QtBluetooth ]
+ [ run qtnfc.cpp /qt5//QtNfc ]
+
+ [ run qtgamepad.cpp /qt5//QtGamepad ]
+
+ [ run qtscxml.cpp /qt5//QtScxml ]
+
+ [ run qtserialbus.cpp /qt5//QtSerialBus ]
+
+
+ # Help systems.
+ [ link qthelp.cpp /qt5//QtHelp ]
+
+ # Testing using QtTest. Simple sample
+ # ToDo: better support for "automoc" aka '#include "qttest.moc"'
+ [ run qttest.cpp [ cast _ moccable5-cpp : qttest.cpp ] /qt5//QtTest : : : <define>TEST_MOCK ]
+
+ # Test moc rule
+ [ run mock.cpp mock.h /qt5//QtCore : : : <define>TEST_MOCK ]
+
+ # Test resource compiler
+ [ run rcc.cpp rcc.qrc /qt5//QtCore : : : <rccflags>"-compress 9 -threshold 10" ]
+
+ : # requirements
+ : # default-build
+ : # usage-requirements
+ ;
+}
+
+
diff --git a/src/boost/tools/build/test/qt5/mock.cpp b/src/boost/tools/build/test/qt5/mock.cpp
new file mode 100644
index 000000000..3e8e4f683
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/mock.cpp
@@ -0,0 +1,26 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtMoc
+
+#include "mock.h"
+
+#include <boost/test/unit_test.hpp>
+
+Mock::Mock()
+{
+}
+
+/*!
+ Check that the compiler get the correct #defines.
+ The logic to test the moc is in the header file "mock.h"
+ */
+BOOST_AUTO_TEST_CASE(construct_mock)
+{
+ delete new Mock();
+
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(TEST_MOCK), true);
+}
diff --git a/src/boost/tools/build/test/qt5/mock.h b/src/boost/tools/build/test/qt5/mock.h
new file mode 100644
index 000000000..9536d3411
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/mock.h
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#include <QtCore/QObject>
+
+class Mock : public QObject
+{
+ /*!
+ Test that the moc gets the necessary #defines
+ Else the moc will not see the Q_OBJECT macro, issue a warning
+ and linking will fail due to missing vtable symbols.
+ */
+#if defined(TEST_MOCK)
+ Q_OBJECT
+#endif
+ public:
+
+ Mock();
+};
diff --git a/src/boost/tools/build/test/qt5/qt3dcore.cpp b/src/boost/tools/build/test/qt5/qt3dcore.cpp
new file mode 100644
index 000000000..dbf7de53d
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qt3dcore.cpp
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2015
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE Qt3DCore
+#include <Qt3DCore>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DCORE_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE ( sample_code )
+{
+ Qt3DCore::QTransform torusTransform;
+ torusTransform.setScale3D(QVector3D(1.5, 1, 0.5));
+ torusTransform.setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 45.0f));
+}
diff --git a/src/boost/tools/build/test/qt5/qt3dinput.cpp b/src/boost/tools/build/test/qt5/qt3dinput.cpp
new file mode 100644
index 000000000..d0358aa54
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qt3dinput.cpp
@@ -0,0 +1,24 @@
+// (c) Copyright Juergen Hunold 2015
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE Qt3DInput
+#include <Qt3DInput>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DINPUT_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DCORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DRENDER_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE ( sample_code )
+{
+ Qt3DCore::QEntity rootEntity;
+
+}
+
diff --git a/src/boost/tools/build/test/qt5/qt3dlogic.cpp b/src/boost/tools/build/test/qt5/qt3dlogic.cpp
new file mode 100644
index 000000000..4d766f6e5
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qt3dlogic.cpp
@@ -0,0 +1,20 @@
+// (c) Copyright Juergen Hunold 2015
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE Qt3DLogic
+#include <Qt3DLogic>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DCORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DLOGIC_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE ( sample_code )
+{
+ Qt3DLogic::QLogicAspect logicAspect;
+}
diff --git a/src/boost/tools/build/test/qt5/qt3drender.cpp b/src/boost/tools/build/test/qt5/qt3drender.cpp
new file mode 100644
index 000000000..133a18e45
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qt3drender.cpp
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2015
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE Qt3DRender
+#include <Qt3DRender>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DCORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_3DRENDER_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE ( sample_code )
+{
+ Qt3DCore::QEntity rootEntity;
+ Qt3DRender::QMaterial material(&rootEntity);
+}
diff --git a/src/boost/tools/build/test/qt5/qtassistant.cpp b/src/boost/tools/build/test/qt5/qtassistant.cpp
new file mode 100644
index 000000000..782262155
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtassistant.cpp
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtAssistant
+
+#include <QAssistantClient>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( empty_assistant)
+{
+ QAssistantClient client(QString());
+}
diff --git a/src/boost/tools/build/test/qt5/qtbluetooth.cpp b/src/boost/tools/build/test/qt5/qtbluetooth.cpp
new file mode 100644
index 000000000..5f3c0f8a9
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtbluetooth.cpp
@@ -0,0 +1,34 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtBluetooth
+
+#include <QtBluetooth>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_BLUETOOTH_LIB), true);
+}
+
+/*!
+ Try to detect a device
+ */
+BOOST_AUTO_TEST_CASE( bluetooth )
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+
+ if (!localAdapters.empty())
+ {
+ QBluetoothLocalDevice adapter(localAdapters.at(0).address());
+ adapter.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
+ }
+ else
+ {
+ BOOST_TEST(localAdapters.size() == 0);
+ }
+}
diff --git a/src/boost/tools/build/test/qt5/qtcharts.cpp b/src/boost/tools/build/test/qt5/qtcharts.cpp
new file mode 100644
index 000000000..a012a2ac2
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtcharts.cpp
@@ -0,0 +1,15 @@
+// (c) Copyright Juergen Hunold 2015
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCharts
+#include <QtCharts>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WIDGETS_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CHARTS_LIB), true);
+}
diff --git a/src/boost/tools/build/test/qt5/qtcore.cpp b/src/boost/tools/build/test/qt5/qtcore.cpp
new file mode 100644
index 000000000..f0834453b
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtcore.cpp
@@ -0,0 +1,22 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCore
+#include <QtCore>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( qstring_test)
+{
+ QString dummy;
+
+ BOOST_CHECK_EQUAL(dummy.isEmpty(), true);
+}
diff --git a/src/boost/tools/build/test/qt5/qtcorefail.cpp b/src/boost/tools/build/test/qt5/qtcorefail.cpp
new file mode 100644
index 000000000..9ff90ada1
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtcorefail.cpp
@@ -0,0 +1,23 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCoreFail
+
+#include <QtCore>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( qstring_test)
+{
+ QString dummy;
+
+ BOOST_CHECK_EQUAL(dummy.isEmpty(), true);
+}
diff --git a/src/boost/tools/build/test/qt5/qtdatavisualization.cpp b/src/boost/tools/build/test/qt5/qtdatavisualization.cpp
new file mode 100644
index 000000000..5fcbd7e27
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtdatavisualization.cpp
@@ -0,0 +1,31 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtDataVisualization
+
+#include <QtDataVisualization>
+
+#include <QGuiApplication>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_DATAVISUALIZATION_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( datavisualization )
+{
+ QGuiApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+
+ QtDataVisualization::Q3DBars graph;
+
+ graph.setShadowQuality(QtDataVisualization::QAbstract3DGraph::ShadowQualitySoftMedium);
+ graph.activeTheme()->setBackgroundEnabled(false);
+ graph.activeTheme()->setLabelBackgroundEnabled(true);
+}
diff --git a/src/boost/tools/build/test/qt5/qtdeclarative.cpp b/src/boost/tools/build/test/qt5/qtdeclarative.cpp
new file mode 100644
index 000000000..d5fd61562
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtdeclarative.cpp
@@ -0,0 +1,26 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtDeclarative
+
+#include <QCoreApplication>
+#include <QDeclarativeView>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_DECLARATIVE_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( declarative )
+{
+ QCoreApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+ QDeclarativeView view;
+}
diff --git a/src/boost/tools/build/test/qt5/qtgamepad.cpp b/src/boost/tools/build/test/qt5/qtgamepad.cpp
new file mode 100644
index 000000000..8f8bf7f47
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtgamepad.cpp
@@ -0,0 +1,29 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtGamepad
+
+#include <QtGamepad>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GAMEPAD_LIB), true);
+}
+
+/*!
+ Try to detect a device
+ */
+BOOST_AUTO_TEST_CASE( gamepad )
+{
+ auto gamepads = QGamepadManager::instance()->connectedGamepads();
+ if (gamepads.isEmpty()) {
+ return;
+ }
+
+ QGamepad gamepad(*gamepads.begin());
+}
diff --git a/src/boost/tools/build/test/qt5/qthelp.cpp b/src/boost/tools/build/test/qt5/qthelp.cpp
new file mode 100644
index 000000000..b8b10cef1
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qthelp.cpp
@@ -0,0 +1,22 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtHelp
+
+#include <QtHelp>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( empty_engine)
+{
+ QHelpEngine engine(QString());
+}
diff --git a/src/boost/tools/build/test/qt5/qtlocation.cpp b/src/boost/tools/build/test/qt5/qtlocation.cpp
new file mode 100644
index 000000000..762d1a023
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtlocation.cpp
@@ -0,0 +1,30 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtPositioning
+
+#include <QGeoAddress>
+#include <QGeoLocation>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_POSITIONING_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_NETWORK_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_LOCATION_LIB), true);
+}
+
+BOOST_TEST_DONT_PRINT_LOG_VALUE(QGeoAddress)
+
+BOOST_AUTO_TEST_CASE( geo_location )
+{
+ QGeoLocation geolocation;
+
+ QGeoAddress address;
+
+ BOOST_CHECK_EQUAL(geolocation.address(), address);
+}
diff --git a/src/boost/tools/build/test/qt5/qtmultimedia.cpp b/src/boost/tools/build/test/qt5/qtmultimedia.cpp
new file mode 100644
index 000000000..f4d7fe0fb
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtmultimedia.cpp
@@ -0,0 +1,25 @@
+// (c) Copyright Juergen Hunold 2009
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtMultimedia
+
+#include <QAudioDeviceInfo>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_MULTIMEDIA_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( audiodevices)
+{
+ QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
+ for(int i = 0; i < devices.size(); ++i) {
+ BOOST_TEST_MESSAGE(QAudioDeviceInfo(devices.at(i)).deviceName().constData());
+ }
+}
diff --git a/src/boost/tools/build/test/qt5/qtnetwork.cpp b/src/boost/tools/build/test/qt5/qtnetwork.cpp
new file mode 100644
index 000000000..9e658f4a2
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtnetwork.cpp
@@ -0,0 +1,33 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtNetwork
+
+#include <QHostInfo>
+
+#include <QTextStream>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_NETWORK_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( hostname )
+{
+ QHostInfo info(QHostInfo::fromName("www.boost.org")); //blocking lookup
+
+ QTextStream stream(stdout, QIODevice::WriteOnly);
+
+ Q_FOREACH(QHostAddress address, info.addresses())
+ {
+ BOOST_CHECK_EQUAL(address.isNull(), false);
+ stream << address.toString() << endl;
+ }
+}
diff --git a/src/boost/tools/build/test/qt5/qtnfc.cpp b/src/boost/tools/build/test/qt5/qtnfc.cpp
new file mode 100644
index 000000000..f452efafe
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtnfc.cpp
@@ -0,0 +1,28 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtNfc
+
+#include <QtNfc>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_NFC_LIB), true);
+}
+
+/*!
+ Try to detect a device
+ */
+BOOST_AUTO_TEST_CASE( nfc )
+{
+ QNearFieldManager manager;
+ if (!manager.isAvailable())
+ {
+ BOOST_TEST_MESSAGE("No Nfc");
+ }
+}
diff --git a/src/boost/tools/build/test/qt5/qtpositioning.cpp b/src/boost/tools/build/test/qt5/qtpositioning.cpp
new file mode 100644
index 000000000..38fd859ee
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtpositioning.cpp
@@ -0,0 +1,23 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtPositioning
+
+#include <QGeoCoordinate>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_POSITIONING_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( geo_coordinate )
+{
+ QGeoCoordinate geocoordinate;
+
+ BOOST_CHECK_EQUAL(geocoordinate.type(), QGeoCoordinate::InvalidCoordinate);
+}
diff --git a/src/boost/tools/build/test/qt5/qtpurchasing.cpp b/src/boost/tools/build/test/qt5/qtpurchasing.cpp
new file mode 100644
index 000000000..9ec8ee58d
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtpurchasing.cpp
@@ -0,0 +1,44 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtPurchasing
+
+#include <QtPurchasing>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_PURCHASING_LIB), true);
+}
+
+class DummyProduct : public QInAppProduct
+{
+public:
+
+ DummyProduct() : QInAppProduct{QStringLiteral("One"),
+ QString{},
+ QString{},
+ Consumable,
+ QStringLiteral("DummyProduct"),
+ nullptr} {};
+ void purchase() override {};
+};
+
+std::ostream&
+operator << (std::ostream& stream, QString const& string)
+{
+ stream << qPrintable(string);
+ return stream;
+}
+
+BOOST_AUTO_TEST_CASE (purchase)
+{
+ DummyProduct product;
+
+ BOOST_TEST(product.price() == QLatin1String("One"));
+ BOOST_TEST(product.identifier() == QLatin1String("DummyProduct"));
+}
diff --git a/src/boost/tools/build/test/qt5/qtquick.cpp b/src/boost/tools/build/test/qt5/qtquick.cpp
new file mode 100644
index 000000000..e1e8e100a
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtquick.cpp
@@ -0,0 +1,43 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtQuick
+#include <QDir>
+#include <QTimer>
+#include <QGuiApplication>
+#include <QQmlEngine>
+#include <QQuickView>
+#include <QDebug>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_QML_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_QUICK_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE (simple_test)
+{
+ QGuiApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+ QQuickView view;
+
+ QString fileName(boost::unit_test::framework::master_test_suite().argv[1]);
+
+ view.connect(view.engine(), SIGNAL(quit()), &app, SLOT(quit()));
+ view.setSource(QUrl::fromLocalFile(fileName)); \
+
+ QTimer::singleShot(2000, &app, SLOT(quit())); // Auto-close window
+
+ if (QGuiApplication::platformName() == QLatin1String("qnx") ||
+ QGuiApplication::platformName() == QLatin1String("eglfs")) {
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.showFullScreen();
+ } else {
+ view.show();
+ }
+ BOOST_CHECK_EQUAL(app.exec(), 0);
+}
diff --git a/src/boost/tools/build/test/qt5/qtquick.qml b/src/boost/tools/build/test/qt5/qtquick.qml
new file mode 100644
index 000000000..26b23eb2a
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtquick.qml
@@ -0,0 +1,20 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+import QtQuick 2.0
+
+Rectangle {
+ id: page
+ width: 400; height: 200
+ color: "#d6d6d6"
+ Text {
+ id: helloText
+ text: "B2 built!"
+ color: "darkgray"
+ anchors.horizontalCenter: page.horizontalCenter
+ anchors.verticalCenter: page.verticalCenter
+ font.pointSize: 30; font.italic: true ; font.bold: true
+ }
+}
diff --git a/src/boost/tools/build/test/qt5/qtscript.cpp b/src/boost/tools/build/test/qt5/qtscript.cpp
new file mode 100644
index 000000000..9c083a4d0
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtscript.cpp
@@ -0,0 +1,37 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtScript
+
+#include <QScriptEngine>
+
+#include <QCoreApplication>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+std::ostream&
+operator << (std::ostream& stream, QString const& string)
+{
+ stream << qPrintable(string);
+ return stream;
+}
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SCRIPT_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( script )
+{
+ QCoreApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+ QScriptEngine myEngine;
+ QScriptValue three = myEngine.evaluate("1 + 2");
+
+ BOOST_CHECK_EQUAL(three.toNumber(), 3);
+ BOOST_CHECK_EQUAL(three.toString(), QLatin1String("3"));
+}
diff --git a/src/boost/tools/build/test/qt5/qtscripttools.cpp b/src/boost/tools/build/test/qt5/qtscripttools.cpp
new file mode 100644
index 000000000..e5fda8d1a
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtscripttools.cpp
@@ -0,0 +1,47 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtScriptTools
+
+#include <QScriptEngine>
+
+#include <QScriptEngineDebugger>
+
+#include <QCoreApplication>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+namespace utf = boost::unit_test::framework;
+
+std::ostream&
+operator << (std::ostream& stream, QString const& string)
+{
+ stream << qPrintable(string);
+ return stream;
+}
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SCRIPTTOOLS_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( script )
+{
+ QCoreApplication app(utf::master_test_suite().argc,
+ utf::master_test_suite().argv);
+
+ QScriptEngine myEngine;
+ QScriptValue three = myEngine.evaluate("1 + 2");
+
+ QScriptEngineDebugger debugger;
+ debugger.attachTo(&myEngine);
+
+ BOOST_CHECK_EQUAL(three.toNumber(), 3);
+ BOOST_CHECK_EQUAL(three.toString(), QLatin1String("3"));
+
+ debugger.detach();
+}
diff --git a/src/boost/tools/build/test/qt5/qtscxml.cpp b/src/boost/tools/build/test/qt5/qtscxml.cpp
new file mode 100644
index 000000000..c2d81366a
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtscxml.cpp
@@ -0,0 +1,33 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtScxml
+
+#include <QtScxml>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SCXML_LIB), true);
+}
+
+std::ostream&
+operator << (std::ostream& stream, QString const& string)
+{
+ stream << qPrintable(string);
+ return stream;
+}
+
+/*!
+ */
+BOOST_AUTO_TEST_CASE( scxml )
+{
+ QString sessionId = QScxmlStateMachine::generateSessionId(QStringLiteral("dummy"));
+
+ BOOST_TEST(sessionId.isEmpty() == false);
+ BOOST_TEST(sessionId == QString{"dummy1"});
+}
diff --git a/src/boost/tools/build/test/qt5/qtserialbus.cpp b/src/boost/tools/build/test/qt5/qtserialbus.cpp
new file mode 100644
index 000000000..daa5aea14
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtserialbus.cpp
@@ -0,0 +1,25 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtSerialBus
+
+#include <QtSerialBus>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SERIALBUS_LIB), true);
+}
+
+/*!
+ create a canbus instance
+ */
+BOOST_AUTO_TEST_CASE( serialBus )
+{
+ auto canbus = QCanBus::instance();
+ Q_UNUSED(canbus);
+}
diff --git a/src/boost/tools/build/test/qt5/qtserialport.cpp b/src/boost/tools/build/test/qt5/qtserialport.cpp
new file mode 100644
index 000000000..d14fa9921
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtserialport.cpp
@@ -0,0 +1,22 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtSerialPort
+
+#include <QtSerialPort>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SERIALPORT_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( serialport )
+{
+ QSerialPort serialPort;
+ serialPort.setPortName(QStringLiteral("test serialport"));
+}
diff --git a/src/boost/tools/build/test/qt5/qtsql.cpp b/src/boost/tools/build/test/qt5/qtsql.cpp
new file mode 100644
index 000000000..7a3bcc447
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtsql.cpp
@@ -0,0 +1,37 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtSql
+
+#include <QSqlDatabase>
+
+#include <QTextStream>
+#include <QStringList>
+
+#include <boost/test/unit_test.hpp>
+
+#include <iostream>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SQL_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( drivers )
+{
+ QTextStream stream(stdout, QIODevice::WriteOnly);
+
+ Q_FOREACH(QString it, QSqlDatabase:: drivers())
+ {
+ stream << it << endl;
+ }
+}
+
+BOOST_AUTO_TEST_CASE( construct )
+{
+ QSqlDatabase database;
+ BOOST_CHECK_EQUAL(database.isOpen(), false);
+}
diff --git a/src/boost/tools/build/test/qt5/qtsvg.cpp b/src/boost/tools/build/test/qt5/qtsvg.cpp
new file mode 100644
index 000000000..629800bb0
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtsvg.cpp
@@ -0,0 +1,21 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtSvg
+
+#include <QtSvg>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_SVG_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( generator_construct)
+{
+ QSvgGenerator generator;
+}
diff --git a/src/boost/tools/build/test/qt5/qttest.cpp b/src/boost/tools/build/test/qt5/qttest.cpp
new file mode 100644
index 000000000..fd6a31d12
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qttest.cpp
@@ -0,0 +1,30 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#include <QtTest>
+
+class QtTest: public QObject
+{
+ /*!
+ Test if the moc gets the #define
+ */
+#if defined(TEST_MOCK)
+ Q_OBJECT
+#endif
+
+private Q_SLOTS:
+ void toUpper();
+};
+
+void
+QtTest::toUpper()
+{
+ QString str = "Hello";
+ QCOMPARE(str.toUpper(), QString("HELLO"));
+}
+
+QTEST_MAIN(QtTest)
+#include "qttest.moc"
+
diff --git a/src/boost/tools/build/test/qt5/qtwebchannel.cpp b/src/boost/tools/build/test/qt5/qtwebchannel.cpp
new file mode 100644
index 000000000..8b8270ee0
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebchannel.cpp
@@ -0,0 +1,29 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebChannel
+
+#include <QtWebChannel>
+
+#include <QGuiApplication>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBCHANNEL_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( webchannel )
+{
+ QGuiApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+
+ QWebChannel channel;
+ QObject dummy;
+ channel.registerObject(QStringLiteral("dummy"), &dummy);
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebengine.cpp b/src/boost/tools/build/test/qt5/qtwebengine.cpp
new file mode 100644
index 000000000..45c1c07dc
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebengine.cpp
@@ -0,0 +1,30 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebEngine
+
+#include <QtWebEngine>
+#include <QGuiApplication>
+
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINE_LIB), true);
+}
+
+/*!
+ Just call the global initialization function
+ */
+BOOST_AUTO_TEST_CASE( webengine )
+{
+ QGuiApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+
+ QtWebEngine::initialize();
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebenginewidgets.cpp b/src/boost/tools/build/test/qt5/qtwebenginewidgets.cpp
new file mode 100644
index 000000000..49cc9ed2f
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebenginewidgets.cpp
@@ -0,0 +1,40 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebEngineWidgets
+
+#include <QtWebEngineWidgets>
+
+#include <QWebEngineProfile>
+#include <QWebEngineSettings>
+#include <QWebEngineScript>
+
+#include <QApplication>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WIDGETS_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINECORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINEWIDGETS_LIB), true);
+}
+
+/*!
+ Also tests the core library
+ */
+BOOST_AUTO_TEST_CASE( webengine_widgets )
+{
+ QApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+
+ QWebEngineSettings *defaultSettings = QWebEngineSettings::globalSettings();
+ QWebEngineProfile *defaultProfile = QWebEngineProfile::defaultProfile();
+
+ defaultSettings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
+ defaultProfile->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebkit.cpp b/src/boost/tools/build/test/qt5/qtwebkit.cpp
new file mode 100644
index 000000000..1a87ba156
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebkit.cpp
@@ -0,0 +1,22 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebKit
+
+#include <QWebSettings>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBKIT_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( webkit )
+{
+ BOOST_CHECK(QWebSettings::globalSettings());
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebkitwidgets.cpp b/src/boost/tools/build/test/qt5/qtwebkitwidgets.cpp
new file mode 100644
index 000000000..b3d82ba8c
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebkitwidgets.cpp
@@ -0,0 +1,23 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebKitWidgets
+
+#include <QWebPage>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBKITWIDGETS_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( webkit )
+{
+ QWebPage page;
+ BOOST_CHECK_EQUAL(page.isModified(), false);
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebsocket.cpp b/src/boost/tools/build/test/qt5/qtwebsocket.cpp
new file mode 100644
index 000000000..a06702a1e
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebsocket.cpp
@@ -0,0 +1,26 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebSockets
+
+#include <QtWebSockets>
+
+#include <QCoreApplication>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBSOCKETS_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( websocket )
+{
+ QCoreApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+
+ QWebSocket socket;
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebsockets.cpp b/src/boost/tools/build/test/qt5/qtwebsockets.cpp
new file mode 100644
index 000000000..28284c9ff
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebsockets.cpp
@@ -0,0 +1,24 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebSockets
+
+#include <QtWebSockets>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE (defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBSOCKETS_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( websocket )
+{
+ QWebSocket socket;
+ socket.setPauseMode(QAbstractSocket::PauseNever);
+
+ BOOST_TEST(socket.isValid() == false);
+}
diff --git a/src/boost/tools/build/test/qt5/qtwebview.cpp b/src/boost/tools/build/test/qt5/qtwebview.cpp
new file mode 100644
index 000000000..2c38919de
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwebview.cpp
@@ -0,0 +1,31 @@
+// (c) Copyright Juergen Hunold 2016
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtWebView
+
+#include <QtWebView>
+
+#include <QGuiApplication>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBENGINECORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WEBVIEW_LIB), true);
+}
+
+/*!
+ Just call the global initialization function
+ */
+BOOST_AUTO_TEST_CASE( webview )
+{
+ QGuiApplication app(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv);
+
+ QtWebView::initialize();
+}
diff --git a/src/boost/tools/build/test/qt5/qtwidgets.cpp b/src/boost/tools/build/test/qt5/qtwidgets.cpp
new file mode 100644
index 000000000..3a7e6cf12
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtwidgets.cpp
@@ -0,0 +1,43 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtGui
+
+#include <QtWidgets/QApplication>
+
+#include <boost/test/unit_test.hpp>
+
+struct Fixture
+{
+ Fixture()
+ : application(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv,
+ false)
+ {
+ BOOST_TEST_MESSAGE( "setup QApplication fixture" );
+ }
+
+ ~Fixture()
+ {
+ BOOST_TEST_MESSAGE( "teardown QApplication fixture" );
+ }
+
+ QApplication application;
+};
+
+BOOST_GLOBAL_FIXTURE( Fixture );
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_GUI_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_WIDGETS_LIB), true);
+}
+
+
+BOOST_AUTO_TEST_CASE( qtgui_test)
+{
+ BOOST_CHECK_EQUAL(true, true);
+}
diff --git a/src/boost/tools/build/test/qt5/qtxml.cpp b/src/boost/tools/build/test/qt5/qtxml.cpp
new file mode 100644
index 000000000..bedcf0e74
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtxml.cpp
@@ -0,0 +1,29 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtXml
+
+#include <QtXml>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), true);
+}
+
+BOOST_AUTO_TEST_CASE( reader_construct)
+{
+ QXmlStreamReader reader;
+ BOOST_CHECK_EQUAL(reader.atEnd(), false);
+}
+
+BOOST_AUTO_TEST_CASE( writer_construct)
+{
+ QXmlStreamWriter writer;
+ BOOST_CHECK_EQUAL(writer.device(), static_cast<QIODevice*>(0));
+}
+
diff --git a/src/boost/tools/build/test/qt5/qtxmlpatterns.cpp b/src/boost/tools/build/test/qt5/qtxmlpatterns.cpp
new file mode 100644
index 000000000..1c38dc95a
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/qtxmlpatterns.cpp
@@ -0,0 +1,76 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtXmlPatterns
+
+#include <QXmlQuery>
+#include <QXmlSerializer>
+
+#include <QCoreApplication>
+#include <QString>
+#include <QTextStream>
+#include <QBuffer>
+
+#include <boost/test/unit_test.hpp>
+
+
+struct Fixture
+{
+ Fixture()
+ : application(boost::unit_test::framework::master_test_suite().argc,
+ boost::unit_test::framework::master_test_suite().argv)
+ {
+ BOOST_TEST_MESSAGE( "setup QCoreApplication fixture" );
+ }
+
+ ~Fixture()
+ {
+ BOOST_TEST_MESSAGE( "teardown QCoreApplication fixture" );
+ }
+
+ QCoreApplication application;
+};
+
+BOOST_GLOBAL_FIXTURE( Fixture );
+
+QByteArray doc("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml/\" xml:lang=\"en\" lang=\"en\">"
+" <head>"
+" <title>Global variables report for globals.gccxml</title>"
+" </head>"
+"<body><p>Some Test text</p></body></html>");
+
+BOOST_AUTO_TEST_CASE( defines)
+{
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_CORE_LIB), true);
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XMLPATTERNS_LIB), true);
+
+ BOOST_CHECK_EQUAL(BOOST_IS_DEFINED(QT_XML_LIB), false);
+}
+
+BOOST_AUTO_TEST_CASE( extract )
+{
+
+ QBuffer buffer(&doc); // This is a QIODevice.
+ buffer.open(QIODevice::ReadOnly);
+ QXmlQuery query;
+ query.bindVariable("myDocument", &buffer);
+ query.setQuery("declare variable $myDocument external;"
+ "doc($myDocument)");///p[1]");
+
+ BOOST_CHECK_EQUAL(query.isValid(), true);
+
+ QByteArray result;
+ QBuffer out(&result);
+ out.open(QIODevice::WriteOnly);
+
+ QXmlSerializer serializer(query, &out);
+ BOOST_CHECK_EQUAL(query.evaluateTo(&serializer), true);
+
+ QTextStream stream(stdout);
+ BOOST_CHECK_EQUAL(result.isEmpty(), false);
+ stream << "hallo" << result << endl;
+}
+
diff --git a/src/boost/tools/build/test/qt5/rcc.cpp b/src/boost/tools/build/test/qt5/rcc.cpp
new file mode 100644
index 000000000..85ffa1b14
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/rcc.cpp
@@ -0,0 +1,20 @@
+// (c) Copyright Juergen Hunold 2012
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+#define BOOST_TEST_MODULE QtCore
+#include <QtCore>
+
+#include <boost/test/unit_test.hpp>
+
+std::ostream& operator<<(std::ostream& out, QString const& text)
+{
+ out << text.toUtf8().constData();
+ return out;
+}
+
+BOOST_AUTO_TEST_CASE (check_exists)
+{
+ BOOST_CHECK(QFile::exists(":/test/rcc.cpp"));
+}
diff --git a/src/boost/tools/build/test/qt5/rcc.qrc b/src/boost/tools/build/test/qt5/rcc.qrc
new file mode 100644
index 000000000..13ca38a5d
--- /dev/null
+++ b/src/boost/tools/build/test/qt5/rcc.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+ <qresource prefix="/test/">
+ <file>rcc.cpp</file>
+ </qresource>
+</RCC>
diff --git a/src/boost/tools/build/test/readme.txt b/src/boost/tools/build/test/readme.txt
new file mode 100644
index 000000000..48459f805
--- /dev/null
+++ b/src/boost/tools/build/test/readme.txt
@@ -0,0 +1,6 @@
+# Copyright 2002 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+ See test_system.html for detailed information on using the Boost Build test
+system.
diff --git a/src/boost/tools/build/test/rebuilds.py b/src/boost/tools/build/test/rebuilds.py
new file mode 100644
index 000000000..d84fc96a3
--- /dev/null
+++ b/src/boost/tools/build/test/rebuilds.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+
+# Copyright 2005 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+
+def wait_for_bar(t):
+ """
+ Wait to make the test system correctly recognize the 'bar' file as
+ touched after the next build run. Without the wait, the next build run may
+ rebuild the 'bar' file with the new and the old file modification timestamp
+ too close to each other - which could, depending on the currently supported
+ file modification timestamp resolution, be detected as 'no change' by the
+ testing system.
+
+ """
+ t.wait_for_time_change("bar", touch=False)
+
+
+t = BoostBuild.Tester(["-ffile.jam", "-d+3", "-d+12", "-d+13"],
+ pass_toolset=0)
+
+t.write("file.jam", """\
+rule make
+{
+ DEPENDS $(<) : $(>) ;
+ DEPENDS all : $(<) ;
+}
+actions make
+{
+ echo "******" making $(<) from $(>) "******"
+ echo made from $(>) > $(<)
+}
+
+make aux1 : bar ;
+make foo : bar ;
+REBUILDS foo : bar ;
+make bar : baz ;
+make aux2 : bar ;
+""")
+
+t.write("baz", "nothing")
+
+t.run_build_system(["bar"])
+t.expect_addition("bar")
+t.expect_nothing_more()
+
+wait_for_bar(t)
+t.run_build_system(["foo"])
+t.expect_touch("bar")
+t.expect_addition("foo")
+t.expect_nothing_more()
+
+t.run_build_system()
+t.expect_addition(["aux1", "aux2"])
+t.expect_nothing_more()
+
+t.touch("bar")
+wait_for_bar(t)
+t.run_build_system()
+t.expect_touch(["foo", "bar", "aux1", "aux2"])
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/relative_sources.py b/src/boost/tools/build/test/relative_sources.py
new file mode 100644
index 000000000..12ce2e8a5
--- /dev/null
+++ b/src/boost/tools/build/test/relative_sources.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that we can specify sources using relative names.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Test that relative path to source, 'src', is preserved.
+t.write("jamroot.jam", "exe a : src/a.cpp ;")
+t.write("src/a.cpp", "int main() {}\n")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/src/a.obj")
+
+# Test that the relative path to source is preserved
+# when using 'glob'.
+t.rm("bin")
+t.write("jamroot.jam", "exe a : [ glob src/*.cpp ] ;")
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/src/a.obj")
+
+
+# Test that relative path with ".." is *not* added to
+# target path.
+t.rm(".")
+t.write("jamroot.jam", "")
+t.write("a.cpp", "int main() { return 0; }\n")
+t.write("build/Jamfile", "exe a : ../a.cpp ; ")
+t.run_build_system(subdir="build")
+t.expect_addition("build/bin/$toolset/debug*/a.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/remove_requirement.py b/src/boost/tools/build/test/remove_requirement.py
new file mode 100644
index 000000000..56133d6dc
--- /dev/null
+++ b/src/boost/tools/build/test/remove_requirement.py
@@ -0,0 +1,91 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+
+t.write("jamroot.jam", """
+project : requirements <threading>multi <variant>debug:<link>static ;
+# Force link to be relevant
+project : requirements <link>shared:<define>TEST_DLL ;
+
+build-project sub ;
+build-project sub2 ;
+build-project sub3 ;
+build-project sub4 ;
+""")
+
+t.write("sub/jamfile.jam", """
+exe hello : hello.cpp : -<threading>multi ;
+""")
+
+t.write("sub/hello.cpp", """
+int main() {}
+""")
+
+t.write("sub2/jamfile.jam", """
+project : requirements -<threading>multi ;
+exe hello : hello.cpp ;
+""")
+
+t.write("sub2/hello.cpp", """
+int main() {}
+""")
+
+t.write("sub3/hello.cpp", """
+int main() {}
+""")
+
+t.write("sub3/jamfile.jam", """
+exe hello : hello.cpp : "-<variant>debug:<link>static" ;
+""")
+
+t.write("sub4/hello.cpp", """
+int main() {}
+""")
+
+t.write("sub4/jamfile.jam", """
+project : requirements "-<variant>debug:<link>static" ;
+exe hello : hello.cpp ;
+""")
+
+t.run_build_system()
+
+t.expect_addition("sub/bin/$toolset/debug*/link-static*/hello.exe")
+t.expect_addition("sub2/bin/$toolset/debug*/link-static*/hello.exe")
+t.expect_addition("sub3/bin/$toolset/debug*/threading-multi*/hello.exe")
+t.expect_addition("sub4/bin/$toolset/debug*/threading-multi*/hello.exe")
+
+t.rm(".")
+
+# Now test that path requirements can be removed as well.
+t.write("jamroot.jam", """
+build-project sub ;
+""")
+
+t.write("sub/jamfile.jam", """
+project : requirements <include>broken ;
+exe hello : hello.cpp : -<include>broken ;
+""")
+
+t.write("sub/hello.cpp", """
+#include "math.h"
+int main() {}
+""")
+
+t.write("sub/broken/math.h", """
+Broken
+""")
+
+
+t.run_build_system()
+
+t.expect_addition("sub/bin/$toolset/debug*/hello.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/rescan_header.py b/src/boost/tools/build/test/rescan_header.py
new file mode 100755
index 000000000..6384e5abd
--- /dev/null
+++ b/src/boost/tools/build/test/rescan_header.py
@@ -0,0 +1,268 @@
+#!/usr/bin/python
+
+# Copyright 2012 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Test a header loop that depends on (but does not contain) a generated header.
+t.write("test.cpp", '#include "header1.h"\n')
+
+t.write("header1.h", """\
+#ifndef HEADER1_H
+#define HEADER1_H
+#include "header2.h"
+#endif
+""")
+
+t.write("header2.h", """\
+#ifndef HEADER2_H
+#define HEADER2_H
+#include "header1.h"
+#include "header3.h"
+#endif
+""")
+
+t.write("header3.in", "/* empty file */\n")
+
+t.write("jamroot.jam", """\
+import common ;
+make header3.h : header3.in : @common.copy ;
+obj test : test.cpp : <implicit-dependency>header3.h ;
+""")
+
+t.run_build_system(["-j2"])
+t.expect_addition("bin/header3.h")
+t.expect_addition("bin/$toolset/debug*/test.obj")
+t.expect_nothing_more()
+
+t.rm(".")
+
+# Test a linear sequence of generated headers.
+t.write("test.cpp", '#include "header1.h"\n')
+
+t.write("header1.in", """\
+#ifndef HEADER1_H
+#define HEADER1_H
+#include "header2.h"
+#endif
+""")
+
+t.write("header2.in", """\
+#ifndef HEADER2_H
+#define HEADER2_H
+#include "header3.h"
+#endif
+""")
+
+t.write("header3.in", "/* empty file */\n")
+
+t.write("jamroot.jam", """\
+import common ;
+make header1.h : header1.in : @common.copy ;
+make header2.h : header2.in : @common.copy ;
+make header3.h : header3.in : @common.copy ;
+obj test : test.cpp :
+ <implicit-dependency>header1.h
+ <implicit-dependency>header2.h
+ <implicit-dependency>header3.h ;
+""")
+
+t.run_build_system(["-j2", "test"])
+t.expect_addition("bin/header1.h")
+t.expect_addition("bin/header2.h")
+t.expect_addition("bin/header3.h")
+t.expect_addition("bin/$toolset/debug*/test.obj")
+t.expect_nothing_more()
+
+t.rm(".")
+
+# Test a loop in generated headers.
+t.write("test.cpp", '#include "header1.h"\n')
+
+t.write("header1.in", """\
+#ifndef HEADER1_H
+#define HEADER1_H
+#include "header2.h"
+#endif
+""")
+
+t.write("header2.in", """\
+#ifndef HEADER2_H
+#define HEADER2_H
+#include "header3.h"
+#endif
+""")
+
+t.write("header3.in", """\
+#ifndef HEADER2_H
+#define HEADER2_H
+#include "header1.h"
+#endif
+""")
+
+t.write("jamroot.jam", """\
+import common ;
+
+actions copy {
+ sleep 1
+ cp $(>) $(<)
+}
+
+make header1.h : header1.in : @common.copy ;
+make header2.h : header2.in : @common.copy ;
+make header3.h : header3.in : @common.copy ;
+obj test : test.cpp :
+ <implicit-dependency>header1.h
+ <implicit-dependency>header2.h
+ <implicit-dependency>header3.h ;
+""")
+
+t.run_build_system(["-j2", "test"])
+t.expect_addition("bin/header1.h")
+t.expect_addition("bin/header2.h")
+t.expect_addition("bin/header3.h")
+t.expect_addition("bin/$toolset/debug*/test.obj")
+t.expect_nothing_more()
+
+t.rm(".")
+
+# Test that all the dependencies of a loop are updated before any of the
+# dependents.
+t.write("test1.cpp", '#include "header1.h"\n')
+
+t.write("test2.cpp", """\
+#include "header2.h"
+int main() {}
+""")
+
+t.write("header1.h", """\
+#ifndef HEADER1_H
+#define HEADER1_H
+#include "header2.h"
+#endif
+""")
+
+t.write("header2.h", """\
+#ifndef HEADER2_H
+#define HEADER2_H
+#include "header1.h"
+#include "header3.h"
+#endif
+""")
+
+t.write("header3.in", "\n")
+
+t.write("sleep.bat", """\
+::@timeout /T %1 /NOBREAK >nul
+@ping 127.0.0.1 -n 2 -w 1000 >nul
+@ping 127.0.0.1 -n %1 -w 1000 >nul
+@exit /B 0
+""")
+
+t.write("jamroot.jam", """\
+import common ;
+import os ;
+
+if [ os.name ] = NT
+{
+ SLEEP = call sleep.bat ;
+}
+else
+{
+ SLEEP = sleep ;
+}
+
+rule copy { common.copy $(<) : $(>) ; }
+actions copy { $(SLEEP) 1 }
+
+make header3.h : header3.in : @copy ;
+exe test : test2.cpp test1.cpp : <implicit-dependency>header3.h ;
+""")
+
+t.run_build_system(["-j2", "test"])
+t.expect_addition("bin/header3.h")
+t.expect_addition("bin/$toolset/debug*/test1.obj")
+t.expect_addition("bin/$toolset/debug*/test2.obj")
+t.expect_addition("bin/$toolset/debug*/test.exe")
+t.ignore_addition("bin/*/test.rsp")
+t.expect_nothing_more()
+
+t.touch("header3.in")
+t.run_build_system(["-j2", "test"])
+t.expect_touch("bin/header3.h")
+t.expect_touch("bin/$toolset/debug*/test1.obj")
+t.expect_touch("bin/$toolset/debug*/test2.obj")
+t.expect_touch("bin/$toolset/debug*/test.exe")
+t.ignore_touch("bin/*/test.rsp")
+t.expect_nothing_more()
+
+t.rm(".")
+
+# Test a loop that includes a generated header
+t.write("test1.cpp", '#include "header1.h"\n')
+t.write("test2.cpp", """\
+#include "header2.h"
+int main() {}
+""")
+
+t.write("header1.h", """\
+#ifndef HEADER1_H
+#define HEADER1_H
+#include "header2.h"
+#endif
+""")
+
+t.write("header2.in", """\
+#ifndef HEADER2_H
+#define HEADER2_H
+#include "header3.h"
+#endif
+""")
+
+t.write("header3.h", """\
+#ifndef HEADER3_H
+#define HEADER3_H
+#include "header1.h"
+#endif
+""")
+
+t.write("sleep.bat", """\
+::@timeout /T %1 /NOBREAK >nul
+@ping 127.0.0.1 -n 2 -w 1000 >nul
+@ping 127.0.0.1 -n %1 -w 1000 >nul
+@exit /B 0
+""")
+
+t.write("jamroot.jam", """\
+import common ;
+import os ;
+
+if [ os.name ] = NT
+{
+ SLEEP = call sleep.bat ;
+}
+else
+{
+ SLEEP = sleep ;
+}
+
+rule copy { common.copy $(<) : $(>) ; }
+actions copy { $(SLEEP) 1 }
+
+make header2.h : header2.in : @copy ;
+exe test : test2.cpp test1.cpp : <implicit-dependency>header2.h <include>. ;
+""")
+
+t.run_build_system(["-j2", "test"])
+t.expect_addition("bin/header2.h")
+t.expect_addition("bin/$toolset/debug*/test1.obj")
+t.expect_addition("bin/$toolset/debug*/test2.obj")
+t.expect_addition("bin/$toolset/debug*/test.exe")
+t.ignore_addition("bin/*/test.rsp")
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/resolution.py b/src/boost/tools/build/test/resolution.py
new file mode 100644
index 000000000..cec8a4322
--- /dev/null
+++ b/src/boost/tools/build/test/resolution.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2006.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests for the target id resolution process.
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester(use_test_config=False)
+
+# Create the needed files
+t.write("jamroot.jam", """\
+exe hello : hello.cpp ;
+# This should use the 'hello' target, even if there is a 'hello' file in the
+# current dir.
+install s : hello : <location>. ;
+""")
+
+t.write("hello.cpp", "int main() {}\n")
+
+t.run_build_system()
+
+t.expect_addition("bin/$toolset/debug*/hello.obj")
+
+t.touch("hello.cpp")
+t.run_build_system(["s"])
+# If 'hello' in the 's' target resolved to file in the current dir, nothing
+# will be rebuilt.
+t.expect_touch("bin/$toolset/debug*/hello.obj")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/results-python.txt b/src/boost/tools/build/test/results-python.txt
new file mode 100644
index 000000000..83b351b2c
--- /dev/null
+++ b/src/boost/tools/build/test/results-python.txt
@@ -0,0 +1,132 @@
+Note: skipping extra tests
+unit_tests : FAILED
+module_actions : PASSED
+startup_v2 : PASSED
+core_d12 : PASSED
+core_typecheck : PASSED
+core_delete_module : PASSED
+core_language : PASSED
+core_arguments : PASSED
+core_varnames : PASSED
+core_import_module : PASSED
+absolute_sources : PASSED
+alias : PASSED
+alternatives : PASSED
+bad_dirname : PASSED
+build_dir : PASSED
+build_file : PASSED
+build_no : PASSED
+builtin_echo : PASSED
+builtin_exit : PASSED
+builtin_split_by_characters : FAILED
+c_file : PASSED
+chain : PASSED
+clean : PASSED
+composite : PASSED
+conditionals : PASSED
+conditionals2 : PASSED
+conditionals3 : PASSED
+conditionals_multiple : PASSED
+configuration : FAILED
+copy_time : PASSED
+core_action_output : PASSED
+core_action_status : PASSED
+core_actions_quietly : PASSED
+core_at_file : PASSED
+core_bindrule : PASSED
+core_multifile_actions : PASSED
+core_nt_cmd_line : PASSED
+core_option_d2 : PASSED
+core_option_l : PASSED
+core_option_n : PASSED
+core_parallel_actions : PASSED
+core_parallel_multifile_actions_1 : PASSED
+core_parallel_multifile_actions_2 : PASSED
+core_source_line_tracking : PASSED
+core_update_now : PASSED
+core_variables_in_actions : PASSED
+custom_generator : PASSED
+default_build : PASSED
+default_features : PASSED
+dependency_property : PASSED
+dependency_test : FAILED
+direct_request_test : PASSED
+disambiguation : PASSED
+dll_path : PASSED
+double_loading : PASSED
+duplicate : PASSED
+example_libraries : PASSED
+example_make : PASSED
+exit_status : PASSED
+expansion : PASSED
+explicit : PASSED
+free_features_request : PASSED
+generator_selection : FAILED
+generators_test : FAILED
+implicit_dependency : PASSED
+indirect_conditional : FAILED
+inherit_toolset : FAILED
+inherited_dependency : PASSED
+inline : PASSED
+lib_source_property : PASSED
+library_chain : PASSED
+library_property : PASSED
+link : FAILED
+load_order : FAILED
+loop : PASSED
+make_rule : PASSED
+message : FAILED
+ndebug : PASSED
+no_type : PASSED
+notfile : PASSED
+ordered_include : PASSED
+out_of_tree : PASSED
+path_features : FAILED
+prebuilt : PASSED
+print : FAILED
+project_dependencies : PASSED
+project_glob : PASSED
+project_id : FAILED
+project_root_constants : PASSED
+project_root_rule : PASSED
+project_test3 : FAILED
+project_test4 : FAILED
+property_expansion : PASSED
+rebuilds : PASSED
+regression : PASSED
+relative_sources : PASSED
+remove_requirement : PASSED
+rescan_header : PASSED
+resolution : PASSED
+scanner_causing_rebuilds : FAILED
+searched_lib : PASSED
+skipping : PASSED
+sort_rule : PASSED
+source_locations : PASSED
+source_order : FAILED
+space_in_path : PASSED
+stage : PASSED
+standalone : PASSED
+static_and_shared_library : PASSED
+suffix : PASSED
+tag : PASSED
+test_result_dumping : PASSED
+test_rc : FAILED
+testing_support : PASSED
+timedata : FAILED
+unit_test : PASSED
+unused : FAILED
+use_requirements : PASSED
+using : PASSED
+wrapper : PASSED
+wrong_project : PASSED
+zlib : PASSED
+symlink : PASSED
+library_order : FAILED
+gcc_runtime : FAILED
+pch : PASSED
+
+ === Test summary ===
+ PASS: 103
+ FAIL: 23
+
diff --git a/src/boost/tools/build/test/rootless.py b/src/boost/tools/build/test/rootless.py
new file mode 100644
index 000000000..9f407d934
--- /dev/null
+++ b/src/boost/tools/build/test/rootless.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# Copyright 2018 Rene Rivera
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os
+
+t = BoostBuild.Tester(translate_suffixes=0)
+
+t.set_tree("rootless/test1")
+t.run_build_system(status=1)
+t.expect_output_lines("error: no Jamfile in current directory*")
+
+t.set_tree("rootless/test1")
+t.run_build_system(subdir="sub_root")
+t.expect_addition("sub_root/bin/a.txt")
+
+t.set_tree("rootless/test1")
+t.run_build_system(subdir="sub_root", extra_args=["--build-dir=../bin"])
+t.expect_output_lines("warning: the --build-dir option will be ignored")
+
+t.set_tree("rootless/test2")
+t.run_build_system(subdir="sub_root", extra_args=["--build-dir=../bin"])
+t.expect_addition("bin/foo/a.txt")
+
+t.set_tree("rootless/test3")
+t.run_build_system()
+
+t.set_tree("rootless/test3")
+t.run_build_system(subdir="sub/inner")
+t.expect_addition("bins/sub/inner/a.txt")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/rootless/test1/sub_root/a.cpp b/src/boost/tools/build/test/rootless/test1/sub_root/a.cpp
new file mode 100644
index 000000000..a22c6c6a6
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test1/sub_root/a.cpp
@@ -0,0 +1,6 @@
+// Copyright 2018 Rene Rivera
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+int main() {}
diff --git a/src/boost/tools/build/test/rootless/test1/sub_root/jamfile.jam b/src/boost/tools/build/test/rootless/test1/sub_root/jamfile.jam
new file mode 100644
index 000000000..ae9ab4be6
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test1/sub_root/jamfile.jam
@@ -0,0 +1,10 @@
+# Copyright 2018 Rene Rivera
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+actions foo
+{
+ echo "$(>)" > "$(<)"
+}
+make a.txt : a.cpp : @foo ;
diff --git a/src/boost/tools/build/test/rootless/test2/sub_root/a.cpp b/src/boost/tools/build/test/rootless/test2/sub_root/a.cpp
new file mode 100644
index 000000000..a22c6c6a6
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test2/sub_root/a.cpp
@@ -0,0 +1,6 @@
+// Copyright 2018 Rene Rivera
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+int main() {}
diff --git a/src/boost/tools/build/test/rootless/test2/sub_root/jamfile.jam b/src/boost/tools/build/test/rootless/test2/sub_root/jamfile.jam
new file mode 100644
index 000000000..f15a77566
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test2/sub_root/jamfile.jam
@@ -0,0 +1,13 @@
+# Copyright 2018 Rene Rivera
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+project foo ;
+
+actions foo
+{
+ echo "$(>)" > "$(<)"
+}
+
+make a.txt : a.cpp : @foo ;
diff --git a/src/boost/tools/build/test/rootless/test3/jamfile.jam b/src/boost/tools/build/test/rootless/test3/jamfile.jam
new file mode 100644
index 000000000..521edfd8a
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test3/jamfile.jam
@@ -0,0 +1,6 @@
+# Copyright 2018 Rene Rivera
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+project root-foo : build-dir bins ;
diff --git a/src/boost/tools/build/test/rootless/test3/sub/inner/a.cpp b/src/boost/tools/build/test/rootless/test3/sub/inner/a.cpp
new file mode 100644
index 000000000..a22c6c6a6
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test3/sub/inner/a.cpp
@@ -0,0 +1,6 @@
+// Copyright 2018 Rene Rivera
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+int main() {}
diff --git a/src/boost/tools/build/test/rootless/test3/sub/inner/jamfile.jam b/src/boost/tools/build/test/rootless/test3/sub/inner/jamfile.jam
new file mode 100644
index 000000000..464f7631b
--- /dev/null
+++ b/src/boost/tools/build/test/rootless/test3/sub/inner/jamfile.jam
@@ -0,0 +1,11 @@
+# Copyright 2018 Rene Rivera
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+actions foo
+{
+ echo "$(>)" > "$(<)"
+}
+
+make a.txt : a.cpp : @foo ;
diff --git a/src/boost/tools/build/test/scanner_causing_rebuilds.py b/src/boost/tools/build/test/scanner_causing_rebuilds.py
new file mode 100755
index 000000000..d0b55ee16
--- /dev/null
+++ b/src/boost/tools/build/test/scanner_causing_rebuilds.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+
+# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests for a bug causing Boost Build's scanner targets to be rebuilt.
+# unnecessarily in the following scenario:
+# * We want to build target X requiring target A.
+# * We have a multi-file action generating targets A & B.
+# * Out action generates target B with a more recent timestamp than target A.
+# * Target A includes target B.
+# * Target A has a registered include scanner.
+# Now even if our targets A & B have already been built and are up-to-date
+# (e.g. in a state left by a previous successful build run), our scanner target
+# tasked with scanning target A will be marked for updating, thus causing any
+# targets depending on it to be updated/rebuilt as well.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("foo.jam", r"""
+import common ;
+import generators ;
+import modules ;
+import type ;
+import types/cpp ;
+
+type.register FOO : foo ;
+type.register BAR : bar ;
+generators.register-standard foo.foo : FOO : CPP BAR ;
+
+local rule sleep-cmd ( delay )
+{
+ if [ modules.peek : NT ]
+ {
+ return ping 127.0.0.1 -n $(delay) -w 1000 >NUL ;
+ }
+ else
+ {
+ return sleep $(delay) ;
+ }
+}
+
+.touch = [ common.file-creation-command ] ;
+.sleep = [ sleep-cmd 2 ] ;
+
+rule foo ( cpp bar : foo : properties * )
+{
+ # We add the INCLUDE relationship between our generated CPP & BAR targets
+ # explicitly instead of relying on Boost Jam's internal implementation
+ # detail - automatically adding such relationships between all files
+ # generated by the same action. This way our test will continue to function
+ # correctly even if the related Boost Jam implementation detail changes.
+ # Note that adding this relationship by adding an #include directive in our
+ # generated CPP file is not good enough as such a relationship would get
+ # added only after the scanner target's relationships have already been
+ # established and they (as affected by our initial INCLUDE relationship) are
+ # the original reason for this test failing.
+ INCLUDES $(cpp) : $(bar) ;
+}
+
+actions foo
+{
+ $(.touch) "$(<[1])"
+ $(.sleep)
+ $(.touch) "$(<[2])"
+}
+""")
+
+t.write(
+ 'foo.py',
+"""
+import os
+
+from b2.build import type as type_, generators
+from b2.tools import common
+from b2.manager import get_manager
+
+MANAGER = get_manager()
+ENGINE = MANAGER.engine()
+
+type_.register('FOO', ['foo'])
+type_.register('BAR', ['bar'])
+generators.register_standard('foo.foo', ['FOO'], ['CPP', 'BAR'])
+
+def sleep_cmd(delay):
+ if os.name == 'nt':
+ return 'ping 127.0.0.1 -n {} -w 1000 >NUL'.format(delay)
+ return 'sleep {}'.format(delay)
+
+def foo(targets, sources, properties):
+ cpp, bar = targets
+ foo = sources[0]
+ # We add the INCLUDE relationship between our generated CPP & BAR targets
+ # explicitly instead of relying on Boost Jam's internal implementation
+ # detail - automatically adding such relationships between all files
+ # generated by the same action. This way our test will continue to function
+ # correctly even if the related Boost Jam implementation detail changes.
+ # Note that adding this relationship by adding an #include directive in our
+ # generated CPP file is not good enough as such a relationship would get
+ # added only after the scanner target's relationships have already been
+ # established and they (as affected by our initial INCLUDE relationship) are
+ # the original reason for this test failing.
+ bjam.call('INCLUDES', cpp, bar)
+
+ENGINE.register_action(
+ 'foo.foo',
+ '''
+ {touch} "$(<[1])"
+ {sleep}
+ {touch} "$(<[2])"
+ '''.format(touch=common.file_creation_command(), sleep=sleep_cmd(2))
+)
+"""
+)
+
+t.write("x.foo", "")
+t.write("jamroot.jam", """\
+import foo ;
+lib x : x.foo : <link>static ;
+""")
+
+
+# Get everything built once.
+t.run_build_system()
+
+# Simply rerunning the build without touching any of its source target files
+# should not cause any files to be affected.
+t.run_build_system()
+t.expect_nothing_more()
diff --git a/src/boost/tools/build/test/searched_lib.py b/src/boost/tools/build/test/searched_lib.py
new file mode 100644
index 000000000..67fef4630
--- /dev/null
+++ b/src/boost/tools/build/test/searched_lib.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2003, 2004, 2005, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test usage of searched-libs: one which are found via -l
+# switch to the linker/compiler.
+
+import BoostBuild
+import os
+import string
+
+t = BoostBuild.Tester(use_test_config=False)
+
+
+# To start with, we have to prepare a library to link with.
+t.write("lib/jamroot.jam", "")
+t.write("lib/jamfile.jam", "lib test_lib : test_lib.cpp ;")
+t.write("lib/test_lib.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void foo() {}
+""");
+
+t.run_build_system(subdir="lib")
+t.expect_addition("lib/bin/$toolset/debug*/test_lib.dll")
+
+
+# Auto adjusting of suffixes does not work, since we need to
+# change dll to lib.
+if ( ( os.name == "nt" ) or os.uname()[0].lower().startswith("cygwin") ) and \
+ ( BoostBuild.get_toolset() != "gcc" ):
+ t.copy("lib/bin/$toolset/debug*/test_lib.implib", "lib/test_lib.implib")
+ t.copy("lib/bin/$toolset/debug*/test_lib.dll", "lib/test_lib.dll")
+else:
+ t.copy("lib/bin/$toolset/debug*/test_lib.dll", "lib/test_lib.dll")
+
+
+# Test that the simplest usage of searched library works.
+t.write("jamroot.jam", "")
+
+t.write("jamfile.jam", """\
+import path ;
+import project ;
+exe main : main.cpp helper ;
+lib helper : helper.cpp test_lib ;
+lib test_lib : : <name>test_lib <search>lib ;
+""")
+
+t.write("main.cpp", """\
+void helper();
+int main() { helper(); }
+""")
+
+t.write("helper.cpp", """\
+void foo();
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+helper() { foo(); }
+""")
+
+t.run_build_system(["-d2"])
+t.expect_addition("bin/$toolset/debug*/main.exe")
+t.rm("bin/$toolset/debug/main.exe")
+t.rm("bin/$toolset/debug/*/main.exe")
+
+
+# Test that 'unit-test' will correctly add runtime paths to searched libraries.
+t.write("jamfile.jam", """\
+import path ;
+import project ;
+import testing ;
+
+project : requirements <hardcode-dll-paths>false ;
+
+unit-test main : main.cpp helper ;
+lib helper : helper.cpp test_lib ;
+lib test_lib : : <name>test_lib <search>lib ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/main.passed")
+t.rm("bin/$toolset/debug/main.exe")
+t.rm("bin/$toolset/debug/*/main.exe")
+
+
+# Now try using searched lib from static lib. Request shared version of searched
+# lib, since we do not have a static one handy.
+t.write("jamfile.jam", """\
+exe main : main.cpp helper ;
+lib helper : helper.cpp test_lib/<link>shared : <link>static ;
+lib test_lib : : <name>test_lib <search>lib ;
+""")
+
+t.run_build_system(stderr=None)
+t.expect_addition("bin/$toolset/debug*/main.exe")
+t.expect_addition("bin/$toolset/debug/link-static*/helper.lib")
+t.rm("bin/$toolset/debug/main.exe")
+t.rm("bin/$toolset/debug/*/main.exe")
+
+# A regression test: <library>property referring to searched-lib was being
+# mishandled. As the result, we were putting target name to the command line!
+# Note that
+# g++ ...... <.>z
+# works nicely in some cases, sending output from compiler to file 'z'. This
+# problem shows up when searched libs are in usage requirements.
+t.write("jamfile.jam", "exe main : main.cpp d/d2//a ;")
+t.write("main.cpp", """\
+void foo();
+int main() { foo(); }
+""")
+
+t.write("d/d2/jamfile.jam", """\
+lib test_lib : : <name>test_lib <search>../../lib ;
+lib a : a.cpp : : : <library>test_lib ;
+""")
+
+t.write("d/d2/a.cpp", """\
+#ifdef _WIN32
+__declspec(dllexport) int force_library_creation_for_a;
+#endif
+""")
+
+t.run_build_system()
+
+
+# A regression test. Searched targets were not associated with any properties.
+# For that reason, if the same searched lib is generated with two different
+# properties, we had an error saying they are actualized to the same Jam target
+# name.
+t.write("jamroot.jam", "")
+
+t.write("a.cpp", "")
+
+# The 'l' library will be built in two variants: 'debug' (directly requested)
+# and 'release' (requested from 'a').
+t.write("jamfile.jam", """\
+exe a : a.cpp l/<variant>release ;
+lib l : : <name>l_d <variant>debug ;
+lib l : : <name>l_r <variant>release ;
+""")
+
+t.run_build_system(["-n"])
+
+
+# A regression test. Two virtual target with the same properties were created
+# for 'l' target, which caused and error to be reported when actualizing
+# targets. The final error is correct, but we should not create two duplicated
+# targets. Thanks to Andre Hentz for finding this bug.
+t.write("jamroot.jam", "")
+t.write("a.cpp", "")
+t.write("jamfile.jam", """\
+project a : requirements <runtime-link>static ;
+static-lib a : a.cpp l ;
+lib l : : <name>l_f ;
+""")
+
+t.run_build_system(["-n"])
+
+
+# A regression test. Virtual targets distinguished by their search paths were
+# not differentiated when registered, which caused search paths to be selected
+# incorrectly for build requests with multiple feature values.
+t.write("jamroot.jam", "")
+t.write("a.cpp", "")
+t.write("jamfile.jam", """\
+exe a : a.cpp l ;
+lib l : : <name>l <search>lib32 <address-model>32 ;
+lib l : : <name>l <search>lib64 <address-model>64 ;
+""")
+
+t.run_build_system(["-n","address-model=32,64"])
+t.fail_test(t.stdout().find("lib32") == -1)
+t.fail_test(t.stdout().find("lib64") == -1)
+
+
+# Make sure plain "lib foobar ; " works.
+t.write("jamfile.jam", """\
+exe a : a.cpp foobar ;
+lib foobar ;
+""")
+
+t.run_build_system(["-n", "-d2"])
+t.fail_test(t.stdout().find("foobar") == -1)
+
+
+# Make sure plain "lib foo bar ; " works.
+t.write("jamfile.jam", """\
+exe a : a.cpp foo bar ;
+lib foo bar ;
+""")
+
+t.run_build_system(["-n", "-d2"])
+t.fail_test(t.stdout().find("foo") == -1)
+t.fail_test(t.stdout().find("bar") == -1)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/skipping.py b/src/boost/tools/build/test/skipping.py
new file mode 100644
index 000000000..a3eda7a0a
--- /dev/null
+++ b/src/boost/tools/build/test/skipping.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that V2 does not fail gracelessy when any target is skipped.
+
+import BoostBuild
+
+# Create a temporary working directory.
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("a.cpp", "int main() {}\n")
+t.write("b.cpp", "int main() {}\n")
+t.write("c.cpp", "int main() {}\n")
+t.write("jamroot.jam", """\
+import feature ;
+feature.feature foo : 1 2 : link-incompatible ;
+exe a : a.cpp : <foo>1 ;
+exe b : b.cpp : <foo>2 ;
+exe c : c.cpp ;
+""")
+
+t.run_build_system(["foo=1"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/sort_rule.py b/src/boost/tools/build/test/sort_rule.py
new file mode 100755
index 000000000..82f7d82e6
--- /dev/null
+++ b/src/boost/tools/build/test/sort_rule.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+# Copyright (C) 2008. Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests for the Boost Jam builtin SORT rule.
+
+from __future__ import print_function
+
+import BoostBuild
+
+
+###############################################################################
+#
+# testSORTCorrectness()
+# ---------------------
+#
+###############################################################################
+
+def testSORTCorrectness():
+ """Testing that Boost Jam's SORT builtin rule actually sorts correctly."""
+ t = BoostBuild.Tester(["-ftest.jam", "-d1"], pass_toolset=False,
+ use_test_config=False)
+
+ t.write("test.jam", """\
+NOCARE all ;
+source-data = 1 8 9 2 7 3 4 7 1 27 27 9 98 98 1 1 4 5 6 2 3 4 8 1 -2 -2 0 0 0 ;
+target-data = -2 -2 0 0 0 1 1 1 1 1 2 2 27 27 3 3 4 4 4 5 6 7 7 8 8 9 9 98 98 ;
+ECHO "starting up" ;
+sorted-data = [ SORT $(source-data) ] ;
+ECHO "done" ;
+if $(sorted-data) != $(target-data)
+{
+ ECHO "Source :" $(source-data) ;
+ ECHO "Expected :" $(target-data) ;
+ ECHO "SORT returned:" $(sorted-data) ;
+ EXIT "SORT error" : -2 ;
+}
+""")
+
+ t.run_build_system()
+ t.expect_output_lines("starting up")
+ t.expect_output_lines("done")
+ t.expect_output_lines("SORT error", False)
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# testSORTDuration()
+# ------------------
+#
+###############################################################################
+
+def testSORTDuration():
+ """
+ Regression test making sure Boost Jam's SORT builtin rule does not get
+ quadratic behaviour again in this use case.
+
+ """
+ t = BoostBuild.Tester(["-ftest.jam", "-d1"], pass_toolset=False,
+ use_test_config=False)
+
+ f = open(t.workpath("test.jam"), "w")
+ print("data = ", file=f)
+ for i in range(0, 20000):
+ if i % 2:
+ print('"aaa"', file=f)
+ else:
+ print('"bbb"', file=f)
+ print(""";
+
+ECHO "starting up" ;
+sorted = [ SORT $(data) ] ;
+ECHO "done" ;
+NOCARE all ;
+""", file=f)
+ f.close()
+
+ t.run_build_system(expected_duration=1)
+ t.expect_output_lines("starting up")
+ t.expect_output_lines("done")
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+testSORTCorrectness()
+testSORTDuration()
diff --git a/src/boost/tools/build/test/source_locations.py b/src/boost/tools/build/test/source_locations.py
new file mode 100644
index 000000000..11422d43a
--- /dev/null
+++ b/src/boost/tools/build/test/source_locations.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+# Copyright (C) Craig Rodrigues 2005.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that projects with multiple source-location directories are handled OK.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", """
+path-constant SRC1 : "./src1" ;
+path-constant SRC2 : "./src2" ;
+path-constant SRC3 : "./src3" ;
+path-constant BUILD : "build" ;
+
+project : requirements <include>$(SRC1)/include <threading>multi
+ : build-dir $(BUILD) ;
+
+build-project project1 ;
+""")
+
+t.write("project1/jamfile.jam", """
+project project1 : source-location $(SRC1) $(SRC2) $(SRC3) ;
+SRCS = s1.cpp s2.cpp testfoo.cpp ;
+exe test : $(SRCS) ;
+""")
+
+t.write("src1/s1.cpp", "int main() {}\n")
+t.write("src2/s2.cpp", "void hello() {}\n")
+t.write("src3/testfoo.cpp", "void testfoo() {}\n")
+
+# This file should not be picked up, because "src2" is before "src3" in the list
+# of source directories.
+t.write("src3/s2.cpp", "void hello() {}\n")
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/source_order.py b/src/boost/tools/build/test/source_order.py
new file mode 100755
index 000000000..90011fe5b
--- /dev/null
+++ b/src/boost/tools/build/test/source_order.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+# Copyright 2013 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests that action sources are not reordered
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("check-order.jam", """\
+import type ;
+import generators ;
+
+type.register ORDER_TEST : order-test ;
+
+SPACE = " " ;
+nl = "\n" ;
+actions check-order
+{
+ echo$(SPACE)$(>[1])>$(<[1])
+ echo$(SPACE)$(>[2-])>>$(<[1])$(nl)
+}
+
+generators.register-composing check-order.check-order : C : ORDER_TEST ;
+""")
+
+t.write(
+ 'check-order.py',
+"""
+import bjam
+
+from b2.build import type as type_, generators
+from b2.tools import common
+from b2.manager import get_manager
+
+MANAGER = get_manager()
+ENGINE = MANAGER.engine()
+
+type_.register('ORDER_TEST', ['order-test'])
+
+generators.register_composing('check-order.check-order', ['C'], ['ORDER_TEST'])
+
+def check_order(targets, sources, properties):
+ ENGINE.set_target_variable(targets, 'SPACE', ' ')
+ ENGINE.set_target_variable(targets, 'nl', '\\n')
+
+ENGINE.register_action(
+ 'check-order.check-order',
+ function=check_order,
+ command='''
+ echo$(SPACE)$(>[1])>$(<[1])
+ echo$(SPACE)$(>[2-])>>$(<[1])$(nl)
+ '''
+)
+"""
+)
+
+# The aliases are necessary for this test, since
+# the targets were sorted by virtual target
+# id, not by file name.
+t.write("jamroot.jam", """\
+import check-order ;
+alias file1 : file1.c ;
+alias file2 : file2.c ;
+alias file3 : file3.c ;
+order-test check : file2 file1 file3 ;
+""")
+
+t.write("file1.c", "")
+t.write("file2.c", "")
+t.write("file3.c", "")
+
+t.run_build_system()
+t.expect_addition("bin/check.order-test")
+t.expect_content("bin/check.order-test", """\
+file2.c
+file1.c
+file3.c
+""", True)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/space_in_path.py b/src/boost/tools/build/test/space_in_path.py
new file mode 100755
index 000000000..bfcba4894
--- /dev/null
+++ b/src/boost/tools/build/test/space_in_path.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Copyright 2012 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that paths containing spaces are handled correctly by actions.
+
+import BoostBuild
+import os
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("has space/jamroot.jam", """\
+import testing ;
+unit-test test : test.cpp ;
+actions write-file
+{
+ @(STDOUT:E=okay) >"$(<)"
+}
+make test.txt : : @write-file ;
+""")
+t.write("has space/test.cpp", "int main() {}\n")
+
+tmpdir = t.workpath("has space")
+try:
+ oldtmp = os.environ["TMP"]
+except:
+ oldtmp = None
+try:
+ oldtmpdir = os.environ["TMPDIR"]
+except:
+ oldtmpdir = None
+os.environ["TMP"] = tmpdir; # Windows
+os.environ["TMPDIR"] = tmpdir; # *nix
+
+try:
+ t.run_build_system(["has space"])
+ t.expect_addition("has space/bin/test.txt")
+ t.expect_addition("has space/bin/$toolset/debug*/test.passed")
+finally:
+ if oldtmp is not None:
+ os.environ["TMP"] = oldtmp
+ else:
+ del os.environ["TMP"]
+ if oldtmpdir is not None:
+ os.environ["TMPDIR"] = oldtmpdir
+ else:
+ del os.environ["TMPDIR"]
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/stage.py b/src/boost/tools/build/test/stage.py
new file mode 100644
index 000000000..8b31b4b0f
--- /dev/null
+++ b/src/boost/tools/build/test/stage.py
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test staging.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "")
+t.write("jamfile.jam", """\
+lib a : a.cpp ;
+stage dist : a a.h auxilliary/1 ;
+""")
+t.write("a.cpp", """\
+int
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+must_export_something;
+""")
+t.write("a.h", "")
+t.write("auxilliary/1", "")
+
+t.run_build_system()
+t.expect_addition(["dist/a.dll", "dist/a.h", "dist/1"])
+
+
+# Regression test: the following was causing a "duplicate target name" error.
+t.write("jamfile.jam", """\
+project : requirements <hardcode-dll-paths>true ;
+lib a : a.cpp ;
+stage dist : a a.h auxilliary/1 ;
+alias dist-alias : dist ;
+""")
+
+t.run_build_system()
+
+
+# Test the <location> property.
+t.write("jamfile.jam", """\
+lib a : a.cpp ;
+stage dist : a : <variant>debug:<location>ds <variant>release:<location>rs ;
+""")
+
+t.run_build_system()
+t.expect_addition("ds/a.dll")
+
+t.run_build_system(["release"])
+t.expect_addition("rs/a.dll")
+
+
+# Test the <location> property in subprojects. Thanks to Kirill Lapshin for the
+# bug report.
+
+t.write("jamroot.jam", "path-constant DIST : dist ;")
+t.write("jamfile.jam", "build-project d ;")
+t.write("d/jamfile.jam", """\
+exe a : a.cpp ;
+stage dist : a : <location>$(DIST) ;
+""")
+t.write("d/a.cpp", "int main() {}\n")
+
+t.run_build_system()
+t.expect_addition("dist/a.exe")
+
+t.rm("dist")
+
+# Workaround a BIG BUG: the response file is not deleted, even if application
+# *is* deleted. We will try to use the same response file when building from
+# subdir, with very bad results.
+t.rm("d/bin")
+t.run_build_system(subdir="d")
+t.expect_addition("dist/a.exe")
+
+
+# Check that 'stage' does not incorrectly reset target suffixes.
+t.write("a.cpp", "int main() {}\n")
+t.write("jamroot.jam", """\
+import type ;
+type.register MYEXE : : EXE ;
+type.set-generated-target-suffix MYEXE : <optimization>off : myexe ;
+""")
+
+# Since <optimization>off is in properties when 'a' is built and staged, its
+# suffix should be "myexe".
+t.write("jamfile.jam", """\
+stage dist : a ;
+myexe a : a.cpp ;
+""")
+
+t.run_build_system()
+t.expect_addition("dist/a.myexe")
+
+# Test 'stage's ability to traverse dependencies.
+t.write("a.cpp", "int main() {}\n")
+t.write("l.cpp", """\
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+t.write("jamfile.jam", """\
+lib l : l.cpp ;
+exe a : a.cpp l ;
+stage dist : a : <install-dependencies>on <install-type>EXE <install-type>LIB ;
+""")
+t.write("jamroot.jam", "")
+t.rm("dist")
+
+t.run_build_system()
+t.expect_addition("dist/a.exe")
+t.expect_addition("dist/l.dll")
+
+# Check that <use> properties are ignored the traversing target for staging.
+t.copy("l.cpp", "l2.cpp")
+t.copy("l.cpp", "l3.cpp")
+t.write("jamfile.jam", """\
+lib l2 : l2.cpp ;
+lib l3 : l3.cpp ;
+lib l : l.cpp : <use>l2 <dependency>l3 ;
+exe a : a.cpp l ;
+stage dist : a : <install-dependencies>on <install-type>EXE <install-type>LIB ;
+""")
+t.rm("dist")
+
+t.run_build_system()
+t.expect_addition("dist/l3.dll")
+t.expect_nothing("dist/l2.dll")
+
+# Check if <dependency> on 'stage' works.
+t.rm(".")
+t.write("jamroot.jam", """\
+stage a1 : a1.txt : <location>dist ;
+stage a2 : a2.txt : <location>dist <dependency>a1 ;
+""")
+t.write("a1.txt", "")
+t.write("a2.txt", "")
+t.run_build_system(["a2"])
+t.expect_addition(["dist/a1.txt", "dist/a2.txt"])
+
+# Regression test: check that <location>. works.
+t.rm(".")
+t.write("jamroot.jam", "stage a1 : d/a1.txt : <location>. ;")
+t.write("d/a1.txt", "")
+
+t.run_build_system()
+t.expect_addition("a1.txt")
+
+# Test that relative paths of sources can be preserved.
+t.rm(".")
+t.write("jamroot.jam", "install dist : a/b/c.h : <install-source-root>. ;")
+t.write("a/b/c.h", "")
+
+t.run_build_system()
+t.expect_addition("dist/a/b/c.h")
+
+t.write("jamroot.jam", "install dist : a/b/c.h : <install-source-root>a ;")
+t.write("a/b/c.h", "")
+
+t.run_build_system()
+t.expect_addition("dist/b/c.h")
+
+t.rm(".")
+t.write("build/jamroot.jam", """\
+install dist : ../a/b/c.h : <location>../dist <install-source-root>../a ;
+""")
+t.write("a/b/c.h", "")
+
+t.run_build_system(subdir="build")
+t.expect_addition("dist/b/c.h")
+
+t.write("jamroot.jam", "install dist2 : a/b/c.h : <install-source-root>a ;")
+t.write("a/b/c.h", "")
+t.write("sub/jamfile.jam", "alias h : ..//dist2 ;")
+
+t.run_build_system(subdir="sub")
+t.expect_addition("dist2/b/c.h")
+
+# Test that when installing .cpp files, we do not scan include dependencies.
+t.rm(".")
+t.write("jamroot.jam", "install dist : a.cpp ;")
+t.write("a.cpp", '#include "a.h"')
+t.write("a.h", "")
+
+t.run_build_system()
+t.expect_addition("dist/a.cpp")
+
+t.touch("a.h")
+
+t.run_build_system()
+t.expect_nothing("dist/a.cpp")
+
+# Test that <name> property works, when there is just one file in sources.
+t.rm(".")
+t.write("jamroot.jam", "install dist : a.cpp : <name>b.cpp ;")
+t.write("a.cpp", "test file")
+
+t.run_build_system()
+t.expect_addition("dist/b.cpp")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/standalone.py b/src/boost/tools/build/test/standalone.py
new file mode 100644
index 000000000..f13ed59db
--- /dev/null
+++ b/src/boost/tools/build/test/standalone.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+
+# Regression tests: standalone project were not able to refer to targets
+# declared in themselves.
+
+t.write("a.cpp", "int main() {}\n")
+t.write("jamroot.jam", "import standalone ;")
+t.write("standalone.jam", """\
+import alias ;
+import project ;
+
+project.initialize $(__name__) ;
+project standalone ;
+
+local pwd = [ PWD ] ;
+
+alias x : $(pwd)/../a.cpp ;
+alias runtime : x ;
+""")
+
+t.write("standalone.py", """\
+from b2.manager import get_manager
+
+# FIXME: this is ugly as death
+get_manager().projects().initialize(__name__)
+
+import os ;
+
+# This use of list as parameter is also ugly.
+project(['standalone'])
+
+pwd = os.getcwd()
+alias('x', [os.path.join(pwd, '../a.cpp')])
+alias('runtime', ['x'])
+""")
+
+
+t.write("sub/jamfile.jam", "stage bin : /standalone//runtime ;")
+
+t.run_build_system(subdir="sub")
+t.expect_addition("sub/bin/a.cpp")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/startup/boost-root/boost-build.jam b/src/boost/tools/build/test/startup/boost-root/boost-build.jam
new file mode 100644
index 000000000..19f9df3c5
--- /dev/null
+++ b/src/boost/tools/build/test/startup/boost-root/boost-build.jam
@@ -0,0 +1,7 @@
+# Copyright 2002 Dave Abrahams
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Emulate v1 behavior; with the boost-build file in the boost root directory.
+boost-build build ;
diff --git a/src/boost/tools/build/test/startup/boost-root/build/boost-build.jam b/src/boost/tools/build/test/startup/boost-root/build/boost-build.jam
new file mode 100644
index 000000000..eb2f7970b
--- /dev/null
+++ b/src/boost/tools/build/test/startup/boost-root/build/boost-build.jam
@@ -0,0 +1,6 @@
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# The presence of this file emulates the Boost 1.27.0 release
+include $(BOOST_ROOT)/tools/build/bootstrap.jam ;
diff --git a/src/boost/tools/build/test/startup/boost-root/build/bootstrap.jam b/src/boost/tools/build/test/startup/boost-root/build/bootstrap.jam
new file mode 100644
index 000000000..982be4a6e
--- /dev/null
+++ b/src/boost/tools/build/test/startup/boost-root/build/bootstrap.jam
@@ -0,0 +1,7 @@
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+ECHO build system bootstrapped ;
+DEPENDS all : nothing ;
+NOTFILE nothing ;
diff --git a/src/boost/tools/build/test/startup/bootstrap-env/boost-build.jam b/src/boost/tools/build/test/startup/bootstrap-env/boost-build.jam
new file mode 100644
index 000000000..1835ce1ba
--- /dev/null
+++ b/src/boost/tools/build/test/startup/bootstrap-env/boost-build.jam
@@ -0,0 +1,5 @@
+# Copyright 2002 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+boost-build ;
diff --git a/src/boost/tools/build/test/startup/bootstrap-explicit/boost-build.jam b/src/boost/tools/build/test/startup/bootstrap-explicit/boost-build.jam
new file mode 100644
index 000000000..571077851
--- /dev/null
+++ b/src/boost/tools/build/test/startup/bootstrap-explicit/boost-build.jam
@@ -0,0 +1,6 @@
+# Copyright 2002 Dave Abrahams
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+boost-build ../boost-root/build ;
diff --git a/src/boost/tools/build/test/startup/bootstrap-implicit/readme.txt b/src/boost/tools/build/test/startup/bootstrap-implicit/readme.txt
new file mode 100644
index 000000000..0278716e5
--- /dev/null
+++ b/src/boost/tools/build/test/startup/bootstrap-implicit/readme.txt
@@ -0,0 +1,5 @@
+Copyright 2002 Dave Abrahams
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+This file is only here so that cvs update -P won't fail to create a directory
diff --git a/src/boost/tools/build/test/startup/no-bootstrap1/boost-build.jam b/src/boost/tools/build/test/startup/no-bootstrap1/boost-build.jam
new file mode 100644
index 000000000..d98b5cdfa
--- /dev/null
+++ b/src/boost/tools/build/test/startup/no-bootstrap1/boost-build.jam
@@ -0,0 +1,6 @@
+# Copyright 2002 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Bootstrap file not found via implicit lookup in BOOST_BUILD_PATH
+boost-build ;
diff --git a/src/boost/tools/build/test/startup/no-bootstrap1/subdir/readme.txt b/src/boost/tools/build/test/startup/no-bootstrap1/subdir/readme.txt
new file mode 100644
index 000000000..00f428d44
--- /dev/null
+++ b/src/boost/tools/build/test/startup/no-bootstrap1/subdir/readme.txt
@@ -0,0 +1,5 @@
+Copyright 2002 Dave Abrahams
+Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
+
+This file is only here so cvs update -P will create the directory.
diff --git a/src/boost/tools/build/test/startup/no-bootstrap2/boost-build.jam b/src/boost/tools/build/test/startup/no-bootstrap2/boost-build.jam
new file mode 100644
index 000000000..dffe883e2
--- /dev/null
+++ b/src/boost/tools/build/test/startup/no-bootstrap2/boost-build.jam
@@ -0,0 +1,6 @@
+# Copyright 2002 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Bootstrap file not found via explicit lookup in .
+boost-build . ;
diff --git a/src/boost/tools/build/test/startup/no-bootstrap3/boost-build.jam b/src/boost/tools/build/test/startup/no-bootstrap3/boost-build.jam
new file mode 100644
index 000000000..9b4dceb22
--- /dev/null
+++ b/src/boost/tools/build/test/startup/no-bootstrap3/boost-build.jam
@@ -0,0 +1,5 @@
+# Copyright 2002 Dave Abrahams
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Call to boost-build is intentionally missing
diff --git a/src/boost/tools/build/test/startup_v2.py b/src/boost/tools/build/test/startup_v2.py
new file mode 100644
index 000000000..0cd7ef3c3
--- /dev/null
+++ b/src/boost/tools/build/test/startup_v2.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+
+# Copyright 2002 Dave Abrahams
+# Copyright 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+import os.path
+import re
+
+
+def check_for_existing_boost_build_jam(t):
+ """
+ This test depends on no boost-build.jam file existing in any of the
+ folders along the current folder's path. If it does exist, not only would
+ this test fail but it could point to a completely wrong Boost Build
+ installation, thus causing headaches when attempting to diagnose the
+ problem. That is why we explicitly check for this scenario.
+
+ """
+ problem = find_up_to_root(t.workdir, "boost-build.jam")
+ if problem:
+ BoostBuild.annotation("misconfiguration", """\
+This test expects to be run from a folder with no 'boost-build.jam' file in any
+of the folders along its path.
+
+Working folder:
+ '%s'
+
+Problematic boost-build.jam found at:
+ '%s'
+
+Please remove this file or change the test's working folder and rerun the test.
+""" % (t.workdir, problem))
+ t.fail_test(1, dump_stdio=False, dump_stack=False)
+
+
+def find_up_to_root(folder, name):
+ last = ""
+ while last != folder:
+ candidate = os.path.join(folder, name)
+ if os.path.exists(candidate):
+ return candidate
+ last = folder
+ folder = os.path.dirname(folder)
+
+
+def match_re(actual, expected):
+ return re.match(expected, actual, re.DOTALL) != None
+
+
+t = BoostBuild.Tester(match=match_re, boost_build_path="", pass_toolset=0)
+t.set_tree("startup")
+check_for_existing_boost_build_jam(t)
+
+t.run_build_system(status=1, stderr=
+r"""Unable to load B2: could not find 'boost-build\.jam'
+.*Attempted search from .* up to the root at '.*'""")
+
+t.run_build_system(status=1, subdir="no-bootstrap1",
+ stderr=
+r"""Unable to load B2: could not find build system\.
+-----------------------------------------------
+.*attempted to load the build system by invoking
+.*'boost-build ;'
+.*but we were unable to find 'bootstrap\.jam' in the specified directory or in BOOST_BUILD_PATH:""")
+
+# Descend to a subdirectory which /does not/ contain a boost-build.jam file,
+# and try again to test the crawl-up behavior.
+t.run_build_system(status=1, subdir=os.path.join("no-bootstrap1", "subdir"),
+ stderr=r"Unable to load B2: could not find build system\."
+ r".*attempted to load the build system by invoking"
+ r".*'boost-build ;'"
+ r".*but we were unable to find 'bootstrap\.jam' in the specified directory or in BOOST_BUILD_PATH:")
+
+t.run_build_system(status=1, subdir="no-bootstrap2",
+ stderr=r"Unable to load B2: could not find build system\."
+ r".*attempted to load the build system by invoking"
+ r".*'boost-build \. ;'"
+ r".*but we were unable to find 'bootstrap\.jam' in the specified directory or in BOOST_BUILD_PATH:")
+
+t.run_build_system(status=1, subdir='no-bootstrap3', stderr=
+r"""Unable to load B2
+.*boost-build\.jam' was found.*
+However, it failed to call the 'boost-build' rule to indicate the location of the build system.""")
+
+# Test bootstrapping based on BOOST_BUILD_PATH.
+t.run_build_system(["-sBOOST_BUILD_PATH=../boost-root/build"],
+ subdir="bootstrap-env", stdout="build system bootstrapped")
+
+# Test bootstrapping based on an explicit path in boost-build.jam.
+t.run_build_system(subdir="bootstrap-explicit",
+ stdout="build system bootstrapped")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/static_and_shared_library.py b/src/boost/tools/build/test/static_and_shared_library.py
new file mode 100755
index 000000000..e30a5465d
--- /dev/null
+++ b/src/boost/tools/build/test/static_and_shared_library.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Dave Abrahams
+# Copyright 2002, 2003, 2005 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+t.write("jamroot.jam", "")
+t.write("lib/c.cpp", "int bar() { return 0; }\n")
+t.write("lib/jamfile.jam", """\
+static-lib auxilliary1 : c.cpp ;
+lib auxilliary2 : c.cpp ;
+""")
+
+def reset():
+ t.rm("lib/bin")
+
+t.run_build_system(subdir='lib')
+t.expect_addition("lib/bin/$toolset/debug*/" * BoostBuild.List("c.obj "
+ "auxilliary1.lib auxilliary2.dll"))
+
+reset()
+t.run_build_system(["link=shared"], subdir="lib")
+t.expect_addition("lib/bin/$toolset/debug*/" * BoostBuild.List("c.obj "
+ "auxilliary1.lib auxilliary2.dll"))
+
+reset()
+t.run_build_system(["link=static"], subdir="lib")
+t.expect_addition("lib/bin/$toolset/debug*/" * BoostBuild.List(
+ "c.obj auxilliary1.lib auxilliary2.lib"))
+t.expect_nothing_more()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/suffix.py b/src/boost/tools/build/test/suffix.py
new file mode 100644
index 000000000..e831ece20
--- /dev/null
+++ b/src/boost/tools/build/test/suffix.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+# Regression test: when staging V2 used to change suffixes on targets
+# corresponding to real files.
+t.write("jamfile.jam", """
+import type : register ;
+register A : a1 a2 a3 ;
+stage a : a.a3 ;
+""")
+
+t.write("jamroot.jam", "")
+t.write("a.a3", "")
+
+t.run_build_system()
+t.expect_addition("a/a.a3");
+
+# Regression test: we should be able to specify empty suffix for derived target
+# type, even if base type has non-empty suffix.
+t.write("a.cpp", "")
+
+t.write("suffixes.jam", """
+import type ;
+import generators ;
+import common ;
+
+type.register First : first : ;
+type.register Second : "" : First ;
+
+generators.register-standard $(__name__).second : CPP : Second ;
+
+rule second
+{
+ TOUCH on $(<) = [ common.file-creation-command ] ;
+}
+
+actions second
+{
+ $(TOUCH) $(<)
+}
+""")
+
+t.write("suffixes.py", """
+import b2.build.type as type
+import b2.build.generators as generators
+import b2.tools.common as common
+
+from b2.manager import get_manager
+
+type.register("First", ["first"])
+type.register("Second", [""], "First")
+
+generators.register_standard("suffixes.second", ["CPP"], ["Second"])
+
+get_manager().engine().register_action("suffixes.second",
+ "%s $(<)" % common.file_creation_command())
+
+""")
+
+t.write("jamroot.jam", """
+import suffixes ;
+""")
+
+t.write("jamfile.jam", """
+second a : a.cpp ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/a")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/symlink.py b/src/boost/tools/build/test/symlink.py
new file mode 100644
index 000000000..b84309afe
--- /dev/null
+++ b/src/boost/tools/build/test/symlink.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the 'symlink' rule.
+
+from __future__ import print_function
+
+import os
+import BoostBuild
+
+
+if os.name != 'posix':
+ print("The symlink tests can be run on posix only.")
+ import sys
+ sys.exit(1)
+
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "import gcc ;")
+
+t.write("jamfile.jam", """
+exe hello : hello.cpp ;
+symlink hello_release : hello/<variant>release ;
+symlink hello_debug : hello/<variant>debug ;
+symlink links/hello_release : hello/<variant>release ;
+""")
+
+t.write("hello.cpp", """
+int main() {}
+""")
+
+t.run_build_system()
+t.expect_addition([
+ 'hello_debug.exe',
+ 'hello_release.exe',
+ 'links/hello_release.exe'])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/tag.py b/src/boost/tools/build/test/tag.py
new file mode 100644
index 000000000..260bb385f
--- /dev/null
+++ b/src/boost/tools/build/test/tag.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+
+# Copyright (C) 2003. Pedro Ferreira
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+
+###############################################################################
+#
+# test_folder_with_dot_in_name()
+# ------------------------------
+#
+###############################################################################
+
+def test_folder_with_dot_in_name(t):
+ """
+ Regression test: the 'tag' feature did not work in directories that had a
+ dot in their name.
+
+ """
+ t.write("version-1.32.0/jamroot.jam", """\
+project test : requirements <tag>@$(__name__).tag ;
+
+rule tag ( name : type ? : property-set )
+{
+ # Do nothing, just make sure the rule is invoked OK.
+ ECHO The tag rule has been invoked. ;
+}
+exe a : a.cpp ;
+""")
+ t.write("version-1.32.0/a.cpp", "int main() {}\n")
+
+ t.run_build_system(subdir="version-1.32.0")
+ t.expect_addition("version-1.32.0/bin/$toolset/debug*/a.exe")
+ t.expect_output_lines("The tag rule has been invoked.")
+
+
+###############################################################################
+#
+# test_tag_property()
+# -------------------
+#
+###############################################################################
+
+def test_tag_property(t):
+ """Basic tag property test."""
+
+ t.write("jamroot.jam", """\
+import virtual-target ;
+
+rule tag ( name : type ? : property-set )
+{
+ local tags ;
+ switch [ $(property-set).get <variant> ]
+ {
+ case debug : tags += d ;
+ case release : tags += r ;
+ }
+ switch [ $(property-set).get <link> ]
+ {
+ case shared : tags += s ;
+ case static : tags += t ;
+ }
+ if $(tags)
+ {
+ return [ virtual-target.add-prefix-and-suffix $(name)_$(tags:J="")
+ : $(type) : $(property-set) ] ;
+ }
+}
+
+# Test both fully-qualified and local name of the rule
+exe a : a.cpp : <tag>@$(__name__).tag ;
+lib b : a.cpp : <tag>@tag ;
+stage c : a ;
+""")
+
+ t.write("a.cpp", """\
+int main() {}
+#ifdef _MSC_VER
+__declspec (dllexport) void x () {}
+#endif
+""")
+
+ file_list = (
+ BoostBuild.List("bin/$toolset/debug*/a_ds.exe") +
+ BoostBuild.List("bin/$toolset/debug*/b_ds.dll") +
+ BoostBuild.List("c/a_ds.exe") +
+ BoostBuild.List("bin/$toolset/release*/a_rs.exe") +
+ BoostBuild.List("bin/$toolset/release*/b_rs.dll") +
+ BoostBuild.List("c/a_rs.exe") +
+ BoostBuild.List("bin/$toolset/debug*/a_dt.exe") +
+ BoostBuild.List("bin/$toolset/debug*/b_dt.lib") +
+ BoostBuild.List("c/a_dt.exe") +
+ BoostBuild.List("bin/$toolset/release*/a_rt.exe") +
+ BoostBuild.List("bin/$toolset/release*/b_rt.lib") +
+ BoostBuild.List("c/a_rt.exe"))
+
+ variants = ["debug", "release", "link=static,shared"]
+
+ t.run_build_system(variants)
+ t.expect_addition(file_list)
+
+ t.run_build_system(variants + ["clean"])
+ t.expect_removal(file_list)
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+t = BoostBuild.Tester(use_test_config=False)
+
+test_tag_property(t)
+test_folder_with_dot_in_name(t)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/template.py b/src/boost/tools/build/test/template.py
new file mode 100644
index 000000000..180a82303
--- /dev/null
+++ b/src/boost/tools/build/test/template.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+# Copyright (C) FILL SOMETHING HERE 2006.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# This file is template for B2 tests. It creates a simple project that
+# builds one exe from one source, and checks that the exe is really created.
+
+import BoostBuild
+
+
+# Create a temporary working directory.
+t = BoostBuild.Tester()
+
+# Create the needed files.
+t.write("jamroot.jam", """
+exe hello : hello.cpp ;
+""")
+
+t.write("hello.cpp", """
+int main() {}
+"""
+
+# Run the build.
+t.run_build_system()
+
+# First, create a list of three pathnames.
+file_list = BoostBuild.List("bin/$toolset/debug*/") * \
+ BoostBuild.List("hello.exe hello.obj")
+# Second, assert that those files were added as result of the last build system
+# invocation.
+t.expect_addition(file_list)
+
+# Invoke the build system once again.
+t.run_build_system("clean")
+# Check if the files added previously were removed.
+t.expect_removal(file_list)
+
+# Remove temporary directories.
+t.cleanup()
diff --git a/src/boost/tools/build/test/test-config-example.jam b/src/boost/tools/build/test/test-config-example.jam
new file mode 100644
index 000000000..269683a69
--- /dev/null
+++ b/src/boost/tools/build/test/test-config-example.jam
@@ -0,0 +1,19 @@
+# Copyright 2004, 2005, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+
+# Skeleton for test configuration. If your local configuration
+# interferes with testing, rename this files to 'test-system.jam'
+# and tweak it. When tests are run, only this file will be loaded,
+# while site-config.jam and user-config.jam will be ignored.
+
+using gcc ;
+
+using boostbook
+ : /home/ghost/Store/docbook/xsl
+ : /home/ghost/Store/docbook/dtd
+ : /home/ghost/Work/Boost/boost-svn/tools/boostbook
+ ;
+using doxygen ;
+using qt4 : /usr/share/qt4 ;
diff --git a/src/boost/tools/build/test/test.jam b/src/boost/tools/build/test/test.jam
new file mode 100644
index 000000000..d27ef2817
--- /dev/null
+++ b/src/boost/tools/build/test/test.jam
@@ -0,0 +1,39 @@
+# Copyright 2001, 2002, 2003 Dave Abrahams
+# Copyright 2002 Rene Rivera
+# Copyright 2002, 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# util
+import assert ;
+import container ;
+import indirect ;
+import numbers ;
+import order ;
+import os ;
+import path ;
+import print ;
+import regex ;
+import sequence ;
+import set ;
+import string ;
+import utility ;
+
+# kernel
+import "class" ;
+import errors ;
+import modules ;
+
+# build
+import build-request ;
+import feature ;
+import property ;
+import toolset ;
+import type ;
+import version ;
+
+# tools
+import common ;
+
+actions nothing { }
+nothing all ;
diff --git a/src/boost/tools/build/test/test1.py b/src/boost/tools/build/test/test1.py
new file mode 100644
index 000000000..4341deece
--- /dev/null
+++ b/src/boost/tools/build/test/test1.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+
+t.write("test.jam", """
+actions unbuilt { }
+unbuilt all ;
+ECHO "Hi" ;
+""")
+
+t.run_build_system(["-ftest.jam", "-d0"], stdout="Hi\n")
+t.cleanup()
diff --git a/src/boost/tools/build/test/test2.py b/src/boost/tools/build/test/test2.py
new file mode 100644
index 000000000..df42a2c54
--- /dev/null
+++ b/src/boost/tools/build/test/test2.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Dave Abrahams
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.set_tree("test2")
+
+file_list = 'bin/$toolset/debug*/' * \
+ BoostBuild.List("foo.exe foo.obj")
+
+t.run_build_system(["-sBOOST_BUILD_PATH=" + t.original_workdir + "/.."])
+t.expect_addition(file_list)
+
+
+t.write("foo.cpp", "int main() {}\n")
+t.run_build_system(["-d2", "-sBOOST_BUILD_PATH=" + t.original_workdir + "/.."])
+t.expect_touch(file_list)
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/test2/foo.cpp b/src/boost/tools/build/test/test2/foo.cpp
new file mode 100644
index 000000000..e347939db
--- /dev/null
+++ b/src/boost/tools/build/test/test2/foo.cpp
@@ -0,0 +1,7 @@
+// Copyright (c) 2003 Vladimir Prus
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+int main() { return 0; }
diff --git a/src/boost/tools/build/test/test2/jamroot.jam b/src/boost/tools/build/test/test2/jamroot.jam
new file mode 100644
index 000000000..0790a86d9
--- /dev/null
+++ b/src/boost/tools/build/test/test2/jamroot.jam
@@ -0,0 +1,5 @@
+# Copyright 2002 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+exe foo : foo.cpp ;
diff --git a/src/boost/tools/build/test/test_all.py b/src/boost/tools/build/test/test_all.py
new file mode 100644
index 000000000..651034f79
--- /dev/null
+++ b/src/boost/tools/build/test/test_all.py
@@ -0,0 +1,373 @@
+#!/usr/bin/python
+
+# Copyright 2002-2005 Dave Abrahams.
+# Copyright 2002-2006 Vladimir Prus.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from __future__ import print_function
+
+import BoostBuild
+
+import os
+import os.path
+import sys
+
+xml = "--xml" in sys.argv
+toolset = BoostBuild.get_toolset()
+
+
+# Clear environment for testing.
+#
+for s in ("BOOST_ROOT", "BOOST_BUILD_PATH", "JAM_TOOLSET", "BCCROOT",
+ "MSVCDir", "MSVC", "MSVCNT", "MINGW", "watcom"):
+ try:
+ del os.environ[s]
+ except:
+ pass
+
+BoostBuild.set_defer_annotations(1)
+
+
+def run_tests(critical_tests, other_tests):
+ """
+ Runs first the critical_tests and then the other_tests.
+
+ Writes the name of the first failed test to test_results.txt. Critical
+ tests are run in the specified order, other tests are run starting with the
+ one that failed first on the last test run.
+
+ """
+ last_failed = last_failed_test()
+ other_tests = reorder_tests(other_tests, last_failed)
+ all_tests = critical_tests + other_tests
+
+ invocation_dir = os.getcwd()
+ max_test_name_len = 10
+ for x in all_tests:
+ if len(x) > max_test_name_len:
+ max_test_name_len = len(x)
+
+ pass_count = 0
+ failures_count = 0
+
+ for test in all_tests:
+ if not xml:
+ s = "%%-%ds :" % max_test_name_len % test
+ print(s, end='')
+
+ passed = 0
+ try:
+ __import__(test)
+ passed = 1
+ except KeyboardInterrupt:
+ """This allows us to abort the testing manually using Ctrl-C."""
+ raise
+ except SystemExit as e:
+ """This is the regular way our test scripts are supposed to report
+ test failures."""
+ if e.code is None or e.code == 0:
+ passed = 1
+ except:
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ try:
+ BoostBuild.annotation("failure - unhandled exception", "%s - "
+ "%s" % (exc_type.__name__, exc_value))
+ BoostBuild.annotate_stack_trace(exc_tb)
+ finally:
+ # Explicitly clear a hard-to-garbage-collect traceback
+ # related reference cycle as per documented sys.exc_info()
+ # usage suggestion.
+ del exc_tb
+
+ if passed:
+ pass_count += 1
+ else:
+ failures_count += 1
+ if failures_count == 1:
+ f = open(os.path.join(invocation_dir, "test_results.txt"), "w")
+ try:
+ f.write(test)
+ finally:
+ f.close()
+
+ # Restore the current directory, which might have been changed by the
+ # test.
+ os.chdir(invocation_dir)
+
+ if not xml:
+ if passed:
+ print("PASSED")
+ else:
+ print("FAILED")
+ BoostBuild.flush_annotations()
+ else:
+ rs = "succeed"
+ if not passed:
+ rs = "fail"
+ print('''
+<test-log library="build" test-name="%s" test-type="run" toolset="%s" test-program="%s" target-directory="%s">
+<run result="%s">''' % (test, toolset, "tools/build/v2/test/" + test + ".py",
+ "boost/bin.v2/boost.build.tests/" + toolset + "/" + test, rs))
+ if not passed:
+ BoostBuild.flush_annotations(1)
+ print('''
+</run>
+</test-log>
+''')
+ sys.stdout.flush() # Makes testing under emacs more entertaining.
+ BoostBuild.clear_annotations()
+
+ # Erase the file on success.
+ if failures_count == 0:
+ open("test_results.txt", "w").close()
+
+ if not xml:
+ print('''
+ === Test summary ===
+ PASS: %d
+ FAIL: %d
+ ''' % (pass_count, failures_count))
+
+ # exit with failure with failures
+ if failures_count > 0:
+ sys.exit(1)
+
+def last_failed_test():
+ "Returns the name of the last failed test or None."
+ try:
+ f = open("test_results.txt")
+ try:
+ return f.read().strip()
+ finally:
+ f.close()
+ except Exception:
+ return None
+
+
+def reorder_tests(tests, first_test):
+ try:
+ n = tests.index(first_test)
+ return [first_test] + tests[:n] + tests[n + 1:]
+ except ValueError:
+ return tests
+
+
+critical_tests = ["unit_tests", "module_actions", "startup_v2", "core_d12",
+ "core_typecheck", "core_delete_module", "core_language", "core_arguments",
+ "core_varnames", "core_import_module"]
+
+# We want to collect debug information about the test site before running any
+# of the tests, but only when not running the tests interactively. Then the
+# user can easily run this always-failing test directly to see what it would
+# have returned and there is no need to have it spoil a possible 'all tests
+# passed' result.
+if xml:
+ critical_tests.insert(0, "collect_debug_info")
+
+tests = ["abs_workdir",
+ "absolute_sources",
+ "alias",
+ "alternatives",
+ "always",
+ "bad_dirname",
+ "build_dir",
+ "build_file",
+ "build_hooks",
+ "build_no",
+ "builtin_echo",
+ "builtin_exit",
+ "builtin_glob",
+ "builtin_readlink",
+ "builtin_split_by_characters",
+ "bzip2",
+ "c_file",
+ "chain",
+ "clean",
+ "cli_property_expansion",
+ "command_line_properties",
+ "composite",
+ "conditionals",
+ "conditionals2",
+ "conditionals3",
+ "conditionals4",
+ "conditionals_multiple",
+ "configuration",
+ "configure",
+ "copy_time",
+ "core_action_output",
+ "core_action_status",
+ "core_actions_quietly",
+ "core_at_file",
+ "core_bindrule",
+ "core_dependencies",
+ "core_syntax_error_exit_status",
+ "core_fail_expected",
+ "core_jamshell",
+ "core_modifiers",
+ "core_multifile_actions",
+ "core_nt_cmd_line",
+ "core_option_d2",
+ "core_option_l",
+ "core_option_n",
+ "core_parallel_actions",
+ "core_parallel_multifile_actions_1",
+ "core_parallel_multifile_actions_2",
+ "core_scanner",
+ "core_source_line_tracking",
+ "core_update_now",
+ "core_variables_in_actions",
+ "custom_generator",
+ "debugger",
+# Newly broken?
+# "debugger-mi",
+ "default_build",
+ "default_features",
+# This test is known to be broken itself.
+# "default_toolset",
+ "dependency_property",
+ "dependency_test",
+ "disambiguation",
+ "dll_path",
+ "double_loading",
+ "duplicate",
+ "example_libraries",
+ "example_make",
+ "exit_status",
+ "expansion",
+ "explicit",
+ "feature_cxxflags",
+ "feature_implicit_dependency",
+ "feature_relevant",
+ "feature_suppress_import_lib",
+ "file_types",
+ "flags",
+ "generator_selection",
+ "generators_test",
+ "implicit_dependency",
+ "indirect_conditional",
+ "inherit_toolset",
+ "inherited_dependency",
+ "inline",
+ "install_build_no",
+ "libjpeg",
+ "liblzma",
+ "libpng",
+ "libtiff",
+ "libzstd",
+ "lib_source_property",
+ "lib_zlib",
+ "library_chain",
+ "library_property",
+ "link",
+ "load_order",
+ "loop",
+ "make_rule",
+ "message",
+ "ndebug",
+ "no_type",
+ "notfile",
+ "ordered_include",
+# FIXME: Disabled due to bug in B2
+# "ordered_properties",
+ "out_of_tree",
+ "package",
+ "param",
+ "path_features",
+ "prebuilt",
+ "preprocessor",
+ "print",
+ "project_dependencies",
+ "project_glob",
+ "project_id",
+ "project_root_constants",
+ "project_root_rule",
+ "project_test3",
+ "project_test4",
+ "property_expansion",
+# FIXME: Disabled due lack of qt5 detection
+# "qt5",
+ "rebuilds",
+ "relative_sources",
+ "remove_requirement",
+ "rescan_header",
+ "resolution",
+ "rootless",
+ "scanner_causing_rebuilds",
+ "searched_lib",
+ "skipping",
+ "sort_rule",
+ "source_locations",
+ "source_order",
+ "space_in_path",
+ "stage",
+ "standalone",
+ "static_and_shared_library",
+ "suffix",
+ "tag",
+ "test_rc",
+ "test1",
+ "test2",
+ "testing",
+ "timedata",
+ "toolset_clang_darwin",
+ "toolset_clang_linux",
+ "toolset_clang_vxworks",
+ "toolset_darwin",
+ "toolset_defaults",
+ "toolset_gcc",
+ "toolset_intel_darwin",
+ "toolset_msvc",
+ "toolset_requirements",
+ "transitive_skip",
+ "unit_test",
+ "unused",
+ "use_requirements",
+ "using",
+ "wrapper",
+ "wrong_project",
+ ]
+
+if os.name == "posix":
+ tests.append("symlink")
+ # On Windows, library order is not important, so skip this test. Besides,
+ # it fails ;-). Further, the test relies on the fact that on Linux, one can
+ # build a shared library with unresolved symbols. This is not true on
+ # Windows, even with cygwin gcc.
+
+# Disable this test until we figure how to address failures due to --as-needed being default now.
+# if "CYGWIN" not in os.uname()[0]:
+# tests.append("library_order")
+
+if toolset.startswith("gcc") and os.name != "nt":
+ # On Windows it's allowed to have a static runtime with gcc. But this test
+ # assumes otherwise. Hence enable it only when not on Windows.
+ tests.append("gcc_runtime")
+
+if toolset.startswith("clang") or toolset.startswith("gcc") or toolset.startswith("msvc"):
+ tests.append("pch")
+ if sys.platform != "darwin": # clang-darwin does not yet support
+ tests.append("feature_force_include")
+
+# Clang includes Objective-C driver everywhere, but GCC usually in a separate gobj package
+if toolset.startswith("clang") or "darwin" in toolset:
+ tests.append("lang_objc")
+
+# Disable on OSX as it doesn't seem to work for unknown reasons.
+if sys.platform != 'darwin':
+ tests.append("builtin_glob_archive")
+
+if "--extras" in sys.argv:
+ tests.append("boostbook")
+ tests.append("qt4")
+ tests.append("qt5")
+ tests.append("example_qt4")
+ # Requires ./whatever.py to work, so is not guaranteed to work everywhere.
+ tests.append("example_customization")
+ # Requires gettext tools.
+ tests.append("example_gettext")
+elif not xml:
+ print("Note: skipping extra tests")
+
+run_tests(critical_tests, tests)
diff --git a/src/boost/tools/build/test/test_rc.py b/src/boost/tools/build/test/test_rc.py
new file mode 100755
index 000000000..0c0224f91
--- /dev/null
+++ b/src/boost/tools/build/test/test_rc.py
@@ -0,0 +1,148 @@
+#!/usr/bin/python
+
+# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests rc toolset behaviour.
+
+import BoostBuild
+
+
+def included_resource_newer_than_rc_script():
+ """
+ When a .rc script file includes another resource file - the resource file
+ being newer than the .rc script file should not cause the .rc script file
+ to be considered old and force all of its dependents to rebuild.
+
+ """
+ toolsetName = "__myDummyResourceCompilerToolset__"
+
+ # Used options rationale:
+ #
+ # -d4 & --debug-configuration
+ # Display additional information in case of test failure. In the past
+ # we have had testing system issues causing this test to fail
+ # sporadically for which -d+3 output had been instrumental in getting to
+ # the root cause (a touched file's timestamp was not as new as it should
+ # have been).
+ #
+ # --ignore-site-config --user-config=
+ # Disable reading any external Boost Build configuration. This test is
+ # self sufficient so these options protect it from being adversly
+ # affected by any local (mis)configuration..
+ t = BoostBuild.Tester(["-d4", "--debug-configuration",
+ "--ignore-site-config", "--user-config=", "toolset=%s" % toolsetName],
+ pass_toolset=False, use_test_config=False,
+ translate_suffixes=False)
+
+ # Prepare a dummy toolset so we do not get errors in case the default one
+ # is not found and that we can test rc.jam functionality without having to
+ # depend on the externally specified toolset actually supporting it exactly
+ # the way it is required for this test, e.g. gcc toolset, under some
+ # circumstances, uses a quiet action for generating its null RC targets.
+ t.write(toolsetName + ".jam", """\
+import feature ;
+import rc ;
+import type ;
+local toolset-name = "%s" ;
+feature.extend toolset : $(toolset-name) ;
+rule init ( ) { }
+rc.configure dummy-rc-command : <toolset>$(toolset-name) : <rc-type>dummy ;
+module rc
+{
+ rule compile.resource.dummy ( targets * : sources * : properties * )
+ {
+ import common ;
+ .TOUCH on $(targets) = [ common.file-touch-command ] ;
+ }
+ actions compile.resource.dummy { $(.TOUCH) "$(<)" }
+}
+# Make OBJ files generated by our toolset use the "obj" suffix on all
+# platforms. We need to do this explicitly for <target-os> windows & cygwin to
+# override the default OBJ type configuration (otherwise we would get
+# 'ambiguous key' errors on those platforms).
+local rule set-generated-obj-suffix ( target-os ? )
+{
+ type.set-generated-target-suffix OBJ : <toolset>$(toolset-name)
+ <target-os>$(target-os) : obj ;
+}
+set-generated-obj-suffix ;
+set-generated-obj-suffix windows ;
+set-generated-obj-suffix cygwin ;
+""" % toolsetName)
+
+ t.write(
+ toolsetName + '.py',
+"""
+from b2.build import feature, type as type_
+from b2.manager import get_manager
+from b2.tools import rc, common
+
+MANAGER = get_manager()
+ENGINE = MANAGER.engine()
+
+toolset_name = "{0}"
+
+feature.extend('toolset', [toolset_name])
+
+def init(*args):
+ pass
+
+rc.configure(['dummy-rc-command'], ['<toolset>' + toolset_name], ['<rc-type>dummy'])
+
+ENGINE.register_action(
+ 'rc.compile.resource.dummy',
+ '''
+ %s "$(<)"
+ ''' % common.file_creation_command()
+)
+
+def set_generated_obj_suffix(target_os=''):
+ requirements = ['<toolset>' + toolset_name]
+ if target_os:
+ requirements.append('<target-os>' + target_os)
+ type_.set_generated_target_suffix('OBJ', requirements, 'obj')
+
+set_generated_obj_suffix()
+set_generated_obj_suffix('windows')
+set_generated_obj_suffix('cygwin')
+""".format(toolsetName)
+ )
+
+ # Prepare project source files.
+ t.write("jamroot.jam", """\
+ECHO "{{{" [ modules.peek : XXX ] [ modules.peek : NOEXEC ] "}}}" ;
+obj xxx : xxx.rc ;
+""")
+ t.write("xxx.rc", '1 MESSAGETABLE "xxx.bin"\n')
+ t.write("xxx.bin", "foo")
+
+ def test1(n, expect, noexec=False):
+ params = ["-sXXX=%d" % n]
+ if noexec:
+ params.append("-n")
+ params.append("-sNOEXEC=NOEXEC")
+ t.run_build_system(params)
+ t.expect_output_lines("*NOEXEC*", noexec)
+ obj_file = "xxx_res.obj"
+ t.expect_output_lines("compile.resource.dummy *%s" % obj_file, expect)
+ if expect and not noexec:
+ expect("bin/%s/debug/%s" % (toolsetName, obj_file))
+ t.expect_nothing_more()
+
+ def test(n, expect):
+ test1(n, expect, noexec=True)
+ test1(n, expect)
+
+ test(1, t.expect_addition)
+ test(2, None)
+ t.touch("xxx.bin")
+ test(3, t.expect_touch)
+ test(4, None)
+
+ t.cleanup()
+
+
+included_resource_newer_than_rc_script()
diff --git a/src/boost/tools/build/test/test_system.html b/src/boost/tools/build/test/test_system.html
new file mode 100644
index 000000000..e7f5694e1
--- /dev/null
+++ b/src/boost/tools/build/test/test_system.html
@@ -0,0 +1,623 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+ <head>
+ <meta name="generator" content=
+ "HTML Tidy for Linux/x86 (vers 1st March 2002), see www.w3.org">
+ <!--tidy options: -i -wrap 78 -->
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+
+ <title>A testing system for B2</title>
+<style type="text/css">
+ hr { color: black }
+ p.revision { text-align: right; font-style: italic }
+ pre.code { margin-left: 2em }
+ pre.example { margin-left: 2em; border: solid black thin }
+ pre.output { margin-left: 2em }
+ img.banner { border: 0; float: left }
+ h1 { text-align: right }
+ br.clear { clear: left }
+ div.attention { color: red }
+
+</style>
+ </head>
+
+ <body>
+ <p><a href="../../../../index.htm"><img class="banner" height="86" width=
+ "277" alt="C++ Boost" src="../../../../boost.png"></a></p>
+
+ <h1>A testing system for B2<br class="clear">
+ </h1>
+ <hr>
+
+ <dl class="page-index">
+ <dt><a href="#sec-intro">Introduction for users</a></dt>
+
+ <dd>
+ <dl class="page-index">
+ <dt><a href="#sec-command-line-options">Command line options</a></dt>
+ </dl>
+ </dd>
+
+ <dt><a href="#sec-developers">Introduction for developers</a></dt>
+
+ <dd>
+ <dl class="page-index">
+ <dt><a href="#sec-intro-changing">Changing the working
+ directory</a></dt>
+
+ <dt><a href="#sec-intro-examining">Examining the working directory and
+ changing it</a></dt>
+
+ <dt><a href="#sec-intro-results">Test result</a></dt>
+ </dl>
+ </dd>
+
+ <dt><a href="#sec-reference">Reference documentation</a></dt>
+
+ <dd>
+ <dl class="page-index">
+ <dt><a href="#method-__init__">Method __init__</a></dt>
+
+ <dt><a href="#method-set_tree">Method <tt>set_tree</tt></a></dt>
+
+ <dt><a href="#method-write">Method <tt>write</tt></a></dt>
+
+ <dt><a href="#method-copy">Method <tt>copy</tt></a></dt>
+
+ <dt><a href="#method-touch">Method <tt>touch</tt></a></dt>
+
+ <dt><a href="#method-run_build_system">Method
+ <tt>run_build_system</tt></a></dt>
+
+ <dt><a href="#method-read">Method <tt>read</tt></a></dt>
+
+ <dt><a href="#method-read_and_strip">Method
+ <tt>read_and_strip</tt></a></dt>
+
+ <dt><a href="#methods-expectations">Methods for declaring
+ expectations</a></dt>
+
+ <dt><a href="#methods-ignoring">Methods for ignoring
+ changes</a></dt>
+
+ <dt><a href="#methods-result">Methods for explicitly specifying
+ results</a></dt>
+
+ <dt><a href="#class-list">Helper class <tt>List</tt></a></dt>
+ </dl>
+ </dd>
+ </dl>
+ <hr>
+
+ <h2><a name="sec-intro">Introduction for users</a></h2>
+
+ <p>The testing system for B2 is a small set of Python modules and
+ scripts for automatically testing user-obversable behaviour. It uses
+ components from testing systems of <a href="http://www.scons.org">Scons</a>
+ and <a href="http://subversion.tigris.org">Subversion</a>, together with
+ some additional functionality.</p>
+
+ <p>To run the tests you need to:</p>
+
+ <ol>
+ <li>Get the source tree of B2 (located at <tt>tools/build</tt>
+ in Boost)</li>
+
+ <li>Have <a href="http://www.python.org">Python</a> installed. Version
+ 2.1 is known to work.</li>
+
+ <li>Build Boost.Jam. See <a href=
+ "../engine/index.html">$boost_build_root/engine/index.html</a> for
+ instructions.</li>
+
+ <li>Configure at least one toolset. You can edit <tt>site-config.jam</tt>
+ or <tt>user-config.jam</tt> to add new toolsets. Or you can create file
+ <tt>test-config.jam</tt> in <tt>$boost_build_root/test</tt> directory. In
+ this case, <tt>site-config.jam</tt> and <tt>user-config.jam</tt> will be
+ ignored for testing.</li>
+
+ <li>Make sure that in the configuration jam file(s) that you use you generate
+ no console output, ie. with the Boost Build 'ECHO' rule. Such console output
+ in the configuration jam file(s) will cause a number of tests to automatically
+ fail which would otherwise succeed.</li>
+ </ol>
+
+ <p>When all is set, you can run all the tests using the <tt>test_all.py</tt>
+ script or you can run a specific test by starting its Python script
+ directly.</p>
+
+ <p>Examples:</p>
+
+<pre class="code">
+python test_all.py
+python generators_test.py
+</pre>
+
+ <p>If everything is OK, you will see a list of passed tests. Otherwise, a
+ failure will be reported.</p>
+
+ <h3><a name="sec-command-line-options">Command line options</a></h3>
+
+ <p>Test scripts will use the toolset you configured to be the default or
+ you can specify a specific one on the command line:</p>
+
+<pre class="code">
+python test_all.py borland
+python generators_test.py msvc-7.1
+</pre>
+
+ <p>Other test script flags you can specify on the command line are:</p>
+
+ <ul>
+ <li><tt>--default-bjam</tt> -- By default the test system will use the
+ Boost Jam executable found built in its default development build
+ location. This option makes it use the default one available on your
+ system, i.e. the one found in the system path.</li>
+
+ <li><tt>--preserve</tt> -- In case of a failed test its working
+ directory will be copied to the "failed_test" directory under the
+ current directory.</li>
+
+ <li><tt>--verbose</tt> -- Makes the test system and the run build system
+ display additional output. Note though that this may cause tests that
+ check the build system output to fail.</li>
+ </ul>
+
+ <h2><a name="sec-developers">Introduction for developers</a></h2>
+
+ <p>It is suggested that every new functionality come together with tests,
+ and that bugfixes are accompanied by tests. There's no need to say that
+ tests are good, but two points are extremely important:</p>
+
+ <ul>
+ <li>For an interpreted language like Jam, without any static checks,
+ testing is simply the only sefeguard we can have.</li>
+
+ <li>Good tests allow us to change internal design much more safely, and we
+ have not gotten everything nailed down yet.</li>
+ </ul>
+
+ <p>Adding a new test is simple:</p>
+
+ <ol>
+ <li>Go to <tt>$boost_build_root/test/test_all.py</tt> and add new test
+ name to the list at the end of the file. Suppose the test name is "hello".
+ </li>
+
+ <li>Add a new python module, in this example "hello.py", to do the actual
+ testing.</li>
+ </ol>
+
+ <p>The module, in general will perform these basic actions:</p>
+
+ <ol>
+ <li>Set up the initial working directory state</li>
+
+ <li>
+ Run the build system and check the results:
+
+ <ol>
+ <li>generated output,</li>
+
+ <li>changes made to the working directory,</li>
+
+ <li>new content of the working directory.</li>
+ </ol>
+ </li>
+
+ <li>Add, remove or touch files or change their content and then repeat
+ the previous step until satisfied.</li>
+
+ <li>Clean up</li>
+ </ol>
+
+ <p>The "hello.py" module might contain:</p>
+<pre class="example">
+from BoostBuild import List
+
+# Create a temporary working directory
+t = BoostBuild.Tester()
+
+# Create the needed files
+t.write("jamroot.jam", "")
+t.write("jamfile.jam", """
+exe hello : hello.cpp ;
+""")
+t.write("hello.cpp", """
+int main()
+{
+ return 0;
+}
+
+""")
+
+t.run_build_system()
+
+# First, create a list of three pathnames.
+file_list = List("bin/$toolset/debug/") * List("hello.exe hello.obj")
+# Second, assert that those files were added as result of the last build system invocation.
+t.expect_addition(file_list)
+
+# Invoke the build system once again.
+t.run_build_system("clean")
+# Check if the files added previously were removed.
+t.expect_removal(file_list)
+
+# Remove temporary directories
+t.cleanup()
+</pre>
+
+ <p>The <tt>test</tt> directory contains a file "template.py" which can be
+ used as a start for your own tests.</p>
+
+ <p>Overview of the most important methods of class <tt>Tester</tt> follows.
+ </p>
+
+ <h3><a name="sec-intro-changing">Changing the working directory</a></h3>
+
+ <p>The class <tt>Tester</tt> creates a temporary directory in its
+ constructor and changes to that directory. It can be modified by calling
+ these methods:</p>
+
+ <ul>
+ <li><tt>set_tree</tt> -- sets the content of the working directory to be
+ equal to the content of the specified directory. This method is
+ preferable when directory tree for testing is large.</li>
+
+ <li><tt>write</tt> -- sets the content of file in a working directory.
+ This is optimal if you want to create a directory tree with 3-4 small
+ files.</li>
+
+ <li><tt>touch</tt> -- changes the modification times of a file</li>
+ </ul>
+
+ <h3><a name="sec-intro-examining">Examining the working directory and
+ changing it</a></h3>
+
+ <p>The method <tt>read</tt>, inherited from the <tt>TestCmd</tt> class, can
+ be used to read any file in the working directory and check its content.
+ <tt>Tester</tt> adds another method for tracking changes. Whenever the build
+ system is run (using <a href="#method-run_build_system"><tt>run_build_system
+ </tt></a>), the working dir state before and after running is recorded. In
+ addition, difference between the two states -- i.e. lists of files that were
+ added, removed, modified or touched -- are stored in two member variables -
+ <tt>tree_difference</tt> and <tt>unexpected_difference</tt>.</p>
+
+ <p>After than, the test author may specify that some change is expected, for
+ example, by calling <tt>expect_addition("foo")</tt>. This call will check if
+ the file was indeed added, and if so, will remove its name from the list of
+ added files in <tt>unexpected_difference</tt>. Likewise, it is possible to
+ specify that some changes are not interesting, for example a call to
+ <tt>ignore("*.obj")</tt> will just remove every file with the ".obj"
+ extension from <tt>unexpected_difference</tt>.</p>
+
+ <p>When test has finished with expectations and ignoring, the member
+ <tt>unexpected_difference</tt> will contain the list of all changes not yet
+ accounted for. It is possible to assure that this list is empty by calling
+ the <tt>expect_nothing_more</tt> member function.</p>
+
+ <h3><a name="sec-intro-results">Test result</a></h3>
+
+ <p>Any of the <tt>expect*</tt> methods below will fail the test if the
+ expectation is not met. It is also possible to perform manually arbitrary
+ test and explicitly cause the test to either pass or fail. Ordinary
+ filesystem functions can be used to work with the directory tree. Methods
+ <tt>pass_test</tt> and <tt>fail_test</tt> are used to explicitly give the
+ test outcome.</p>
+
+ <p>Typically, after test termination, the working directory is erased. See
+ the <a href="#sec-command-line-options">"--preserve" command line option</a>
+ for information on how to preserve the working directory content for failed
+ tests for debugging purposes.</p>
+
+ <h2 id="sec-reference">Reference documentation</h2>
+
+ <p>The test system is composed of class <tt>Tester</tt>, derived form
+ <tt>TestCmd.TestCmd</tt>, and helper class <tt>List</tt>. <tt>Tester</tt>
+ and <tt>List</tt> methods are described below.</p>
+
+ <p>The documentation frequently refers to <tt>filename</tt>. In all cases,
+ files are specified in unix style: a sequence of components, separated by
+ "/". This is true on all platforms. In some contexts a list of files is
+ allowed. In those cases any object with a sequence interface is allowed.</p>
+
+ <h3><a name="method-__init__">Method <tt>__init__(self, arguments="",
+ executable="bjam", match=TestCmd.match_exact, boost_build_path=None,
+ translate_suffixes=True, pass_toolset=True, use_test_config=True,
+ ignore_toolset_requirements=True, workdir="", **keywords)</tt></a></h3>
+
+ <p><b>Optional arguments:</b></p>
+
+ <ul>
+ <li><tt>arguments</tt>
+ -- Arguments passed to the run executable.</li>
+ <li><tt>executable</tt>
+ -- Name of the executable to invoke.</li>
+ <li><tt>match</tt>
+ -- Function to use for compating actual and expected file contents.
+ </li>
+ <li><tt>boost_build_path</tt>
+ -- Boost build path to be passed to the run executable.</li>
+ <li><tt>translate_suffixes</tt>
+ -- Whether to update suffixes on the the file names passed from the
+ test script so they match those actually created by the current
+ toolset. For example, static library files are specified by using
+ the .lib suffix but when the 'gcc' toolset is used it actually
+ creates them using the .a suffix.</li>
+ <li><tt>pass_toolset</tt>
+ -- Whether the test system should pass the specified toolset to the
+ run executable.</li>
+ <li><tt>use_test_config</tt>
+ -- Whether the test system should tell the run executable to read in
+ the test_config.jam configuration file.</li>
+ <li><tt>ignore_toolset_requirements</tt>
+ -- Whether the test system should tell the run executable to ignore
+ toolset requirements.</li>
+ <li><tt>workdir</tt>
+ -- Indicates an absolute directory where the test will be run from.
+ </li>
+ </ul>
+
+ <p><b>Optional arguments inherited from the base class:</b></p>
+
+ <ul>
+ <li><tt>description</tt>
+ -- Test description string displayed in case of a failed test.</li>
+ <li><tt>subdir</tt>
+ -- List of subdirectories to automatically create under the working
+ directory. Each subdirectory needs to be specified separately
+ parent coming before its child.</li>
+ <li><tt>verbose</tt>
+ -- Flag that may be used to enable more verbose test system output.
+ Note that it does not also enable more verbose build system output
+ like the <a href="#sec-command-line-options">"--verbose" command
+ line option</a> does.</li>
+ </ul>
+
+ <p><b>Effects:</b></p>
+
+ <ol>
+ <li>Remembers the current working directory in member
+ <tt>original_workdir</tt>.</li>
+
+ <li>Determines the location of the executable (<code>bjam</code> by
+ default) and build system files, assuming that the current directory is
+ <tt>tools/build/test</tt>. Formulates jam invocation command, which
+ will include explicit setting for the <tt>BOOST_BUILD_PATH</tt> variable
+ and arguments passed to this methods, if any. This command will be used
+ by subsequent invocation of <a href="#method-run_build_system"><tt>
+ run_build_system</tt></a>. Finally, initializes the base class.</li>
+
+ <li>Changes the current working directory to the temporary working
+ directory created by the base constructor.</li>
+
+ <li>If you want to run a test in an existing directory, pass it as
+ <tt>workdir</tt>.</li>
+
+ <li> Most parameters passed to this constructor function may be overruled
+ for each specific test system run using <a href=
+ "#method-run_build_system"><tt>run_build_system</tt></a> parameters.
+ </ol>
+
+ <h3><a name="method-set_tree">Method <tt>set_tree(self,
+ tree_location)</tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <p>Replaces the content of the current working directory with the content
+ of directory at <tt>tree_location</tt>. If <tt>tree_location</tt> is not
+ absolute pathname, it will be treated as relative to
+ <tt>self.original_workdir</tt>. This methods also explicitly makes the
+ copied files writeable.</p>
+
+ <h3><a name="method-write">Method <tt>write(self, name,
+ content)</tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <p>Writes the specified content to the file given by <tt>name</tt> under
+ the temporary working directory. If the file already exists, it is
+ overwritten. Any required directories are automatically created.</p>
+
+ <h3><a name="method-copy">Method <tt>copy(self, src, dst)</tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <p>Equvivalent to <tt>self.write(self.read(src), dst)</tt>.</p>
+
+ <h3><a name="method-touch">Method <tt>touch(self, names)</tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <p>Sets the access and modification times for all files in <tt>names</tt> to
+ the current time. All the elements in <tt>names</tt> should be relative
+ paths.</p>
+
+ <h3><a name="method-run_build_system">Method <tt>run_build_system(self,
+ extra_args="", subdir="", stdout=None, stderr="", status=0, match=None,
+ pass_toolset=None, use_test_config=None, ignore_toolset_requirements=None,
+ expected_duration=None, **kw)</tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <ol>
+ <li>Stores the state of the working directory in
+ <tt>self.previous_tree</tt>.</li>
+
+ <li>Changes to <tt>subdir</tt>, if it is specified. It is relative to
+ the <tt>original_workdir</tt> or the workdir specified in
+ <tt>__init</tt>.</li>
+
+ <li>Invokes the <tt>bjam</tt> executable, passing <tt>extra_args</tt>
+ to it. The binary should be located under
+ <tt>&lt;test_invocation_dir&gt;/../jam/src/bin.&lt;platform&gt;</tt>.
+ This is to make sure tests use the version of jam build from CVS.</li>
+
+ <li>Compares the stdout, stderr and exit status of build system
+ invocation with values to appropriate parameters, if they are not
+ <tt>None</tt>. If any difference is found, the test fails.</li>
+
+ <li>If the <tt>expected_duration</tt> parameter is specified then it
+ represents the maximal allowed time in seconds for the test to run. The
+ test will be marked as failed if its duration is greater than the given
+ <tt>expected_duration</tt> parameter value.</li>
+
+ <li>Stores the new state of the working directory in <tt>self.tree</tt>.
+ Computes the difference between previous and current trees and stores them
+ in variables <tt>self.tree_difference</tt> and
+ <tt>self.unexpected_difference</tt>. Both variables are instances of class
+ <tt>tree.Trees_different</tt>, which have four attributes:
+ <tt>added_files</tt>, <tt>removed_files</tt>, <tt>modified_files</tt> and
+ <tt>touched_files</tt>. Each is a list of strings.</p></li>
+ </ol>
+
+ <h3><a name="method-read">Method <tt>read(self, name)</tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <p>Read the specified file and returns it content. Raises an exception is
+ the file is absent.</p>
+
+ <h3><a name="method-read_and_strip">Method <tt>read_and_strip(self, name)
+ </tt></a></h3>
+
+ <p><b>Effects:</b></p>
+
+ <p>Read the specified file and returns it content, after removing trailing
+ whitespace from every line. Raises an exception is the file is absent.</p>
+
+ <p><b>Rationale:</b></p>
+
+ <p>Although this method is questionable, there are a lot of cases when jam
+ or shells it uses insert spaces. It seems that introducing this method is
+ much simpler than dealing with all those cases.</p>
+
+ <h3><a name="methods-expectations">Methods for declaring expectations</a>
+ </h3>
+
+ <p>Accordingly to the number of changes kinds that are detected, there are
+ four methods that specify that test author expects a specific change to
+ occur. They check <tt>self.unexpected_difference</tt>, and if the change is
+ present there, it is removed. Otherwise, test fails.</p>
+
+ <p>Each method accepts a list of names. Those names use <tt>/</tt> path
+ separator on all systems. Additionally, the test system translates suffixes
+ appropriately. For the test to be portable, suffixes should use Windows
+ convention: <tt>exe</tt> for executables, <tt>dll</tt> for dynamic libraries
+ and <tt>lib</tt> for static libraries. Lastly, the string "$toolset" in file
+ names is replaced by the name of tested toolset.</p>
+
+ <p><b>Note:</b> The <tt>List</tt> helper class might be useful to create
+ lists of names.</p>
+
+ <p><b>Note:</b> The file content can be examined using the
+ <tt>TestCmd.read</tt> function.</p>
+
+ <p>The members are:</p>
+
+ <ul>
+ <li>expect_addition</li>
+ <li>expect_removal</li>
+ <li>expect_modification</li>
+ <li>expect_nothing</li>
+ </ul>
+
+ <p>Note that <tt>expect_modification</tt> is used to check that a either
+ file content or timestamp has changed. The rationale is that some compilers
+ change content even if sources does not change, and it's easier to have a
+ method which checks for both content and time changes.</p>
+
+ <p>There's also a member <tt>expect_nothing_more</tt>, which checks that all
+ the changes are either expected or ignored, in other words that
+ <tt>unexpected_difference</tt> is empty by now.</p>
+
+ <p>Lastly, there's a method to compare file content with expected content:
+ </p>
+ <tt>expect_content(self, name, content, exact=0)</tt>
+
+ <p>The method fails the test if the content of file identified by 'name' is
+ different from 'content'. If 'exact' is true, the file content is used
+ as-is, otherwise, two transformations are applied:</p>
+
+ <ul>
+ <li>The <tt>read_and_strip</tt> method is used to read the file, which
+ removes trailing whitespace</li>
+
+ <li>Each backslash in the file content is converted to forward slash.</li>
+ </ul>
+
+ <h3><a name="methods-ignoring">Methods for ignoring changes</a></h3>
+
+ <p>There are five methods which ignore changes made to the working tree.
+ They silently remove elements from <tt>self.unexpected_difference</tt>, and
+ don't generate error if element is not found. They accept shell style
+ wildcard.</p>
+
+ <p>The following methods correspond to four kinds of changes:</p>
+
+ <ul>
+ <li>ignore_addition(self, wildcard)</li>
+ <li>ignore_removal(self, wildcard)</li>
+ <li>ignore_modification(self, wildcard)</li>
+ <li>ignore_touch(self, wildcard)</li>
+ </ul>
+
+ <p>The method <tt>ignore(self, wildcard)</tt> ignores all the changes made
+ to files that match a wildcard.</p>
+
+ <h3><a name="methods-result">Methods for explicitly specifying results</a>
+ </h3>
+
+ <h4>Method <tt>pass_test(self, condition=1)</tt></h4>
+
+ <div class="attention">
+ At this moment, the method should not be used.
+ </div>
+
+ <h4>Method <tt>fail_test(self, condition=1)</tt></h4>
+
+ <p><b>Effects:</b> Cause the test to fail if <tt>condition</tt> is true.</p>
+
+ <h3><a name="class-list">Helper class <tt>List</tt></a></h3>
+ The class has sequence interface and two additional methods.
+
+ <h4>Method <tt>__init__(self, string)</tt></h4>
+
+ <p><b>Effects:</b> Splits the string on unescaped spaces and tabs. The split
+ components can further be retrieved using standard sequence access.</p>
+
+ <h4>Method <tt>__mul__(self, other)</tt></h4>
+
+ <p><b>Effects:</b> Returns an <tt>List</tt> instance, which elements are all
+ possible concatenations of two string, first of which is from <tt>self</tt>,
+ and second of which is from <tt>other</tt>.</p>
+
+ <p>The class also defines <tt>__str__</tt> and <tt>__repr__</tt> methods.
+ Finally, there's <tt>__coerce__</tt> method which allows to convert strings
+ to instances of <tt>List</tt>.</p>
+
+ <p><b>Example:</b></p>
+<pre>
+ l = "a b" * List("c d")
+ for e in l:
+ print e
+</pre>
+
+ <p>will output:</p>
+<pre>
+ ac
+ ad
+ bc
+ bd
+
+</pre>
+ <hr>
+ <p class="revision">Last modified: May 02, 2008</p>
+ <p>&copy; Copyright Vladimir Prus 2002, 2003, 2004, 2005.<br>
+ &copy; Copyright Jurko Gospodnetic 2008.<br>
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)</p>
+ </body>
+</html>
diff --git a/src/boost/tools/build/test/testing.py b/src/boost/tools/build/test/testing.py
new file mode 100755
index 000000000..c3d5f83a1
--- /dev/null
+++ b/src/boost/tools/build/test/testing.py
@@ -0,0 +1,556 @@
+#!/usr/bin/python
+
+# Copyright 2008 Jurko Gospodnetic
+# Copyright 2017 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests different aspects of Boost Builds automated testing support.
+
+import BoostBuild
+import TestCmd
+
+def test_run():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+run pass.cpp ;
+run fail-compile.cpp ;
+run fail-link.cpp ;
+run fail-run.cpp ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.output")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.run")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj")
+
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.exe")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.output")
+
+ t.ignore_addition("bin/pass.test/*/pass.rsp")
+ t.ignore_addition("bin/fail-link.test/*/fail-link.rsp")
+ t.ignore_addition("bin/fail-run.test/*/fail-run.rsp")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_run_fail():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+run-fail pass.cpp ;
+run-fail fail-compile.cpp ;
+run-fail fail-link.cpp ;
+run-fail fail-run.cpp ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.output")
+
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj")
+
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.exe")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.output")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.run")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test")
+
+ t.ignore_addition("bin/pass.test/*/pass.rsp")
+ t.ignore_addition("bin/fail-link.test/*/fail-link.rsp")
+ t.ignore_addition("bin/fail-run.test/*/fail-run.rsp")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_run_change():
+ """Tests that the test file is removed when a test fails after it
+ previously passed."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() { return 1; }\n")
+ t.write("fail-compile.cpp", "int main() {}\n")
+ t.write("fail-link.cpp", "int main() {}\n")
+ t.write("fail-run.cpp", "int main() {}\n")
+
+ t.write("Jamroot.jam", """import testing ;
+run-fail pass.cpp ;
+run fail-compile.cpp ;
+run fail-link.cpp ;
+run fail-run.cpp ;
+""")
+ t.run_build_system()
+ # Sanity check
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+ t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test")
+ t.expect_output_lines("...failed*", False)
+
+ # Now make them fail
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+ t.run_build_system(status=1)
+
+ t.expect_removal("bin/pass.test/$toolset/debug*/pass.test")
+ t.expect_removal("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+ t.expect_removal("bin/fail-link.test/$toolset/debug*/fail-link.test")
+ t.expect_removal("bin/fail-run.test/$toolset/debug*/fail-run.test")
+
+ t.cleanup()
+
+def test_run_path():
+ """Tests that run can find shared libraries even without
+ hardcode-dll-paths. Important: The library is in neither the
+ current working directory, nor any system path, nor the same
+ directory as the executable, so it should never be found without
+ help from B2."""
+ t = BoostBuild.Tester(["hardcode-dll-paths=false"], use_test_config=False)
+
+ t.write("l.cpp", """
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+f() {}
+""")
+ t.write("pass.cpp", "void f(); int main() { f(); }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+lib l : l.cpp : <link>shared ;
+run pass.cpp l ;
+""")
+
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/l.obj")
+ t.expect_addition("bin/$toolset/debug*/l.dll")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.output")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.run")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+
+ t.cleanup()
+
+def test_run_args():
+ """Tests the handling of args and input-files"""
+ t = BoostBuild.Tester(use_test_config=False)
+ t.write("test.cpp", """
+#include <iostream>
+#include <fstream>
+int main(int argc, const char ** argv)
+{
+ for(int i = 1; i < argc; ++i)
+ {
+ if(argv[i][0] == '-')
+ {
+ std::cout << argv[i] << std::endl;
+ }
+ else
+ {
+ std::ifstream ifs(argv[i]);
+ std::cout << ifs.rdbuf();
+ }
+ }
+}
+""")
+ t.write("input1.in", "first input\n")
+ t.write("input2.in", "second input\n")
+ t.write("Jamroot.jam", """import testing ;
+import common ;
+# FIXME: The order actually depends on the lexigraphical
+# ordering of the virtual target objects, which is just
+# crazy. Switch the order of input1.txt and input2.txt
+# to make this fail. Joining the arguments with && might
+# work, but might get a bit complicated to implement as
+# dependency properties do not currently support &&.
+make input1.txt : input1.in : @common.copy ;
+make input2.txt : input2.in : @common.copy ;
+run test.cpp : -y -a : input1.txt input2.txt ;
+""")
+ t.run_build_system()
+ t.expect_content("bin/test.test/$toolset/debug*/test.output", """\
+-y
+-a
+first input
+second input
+
+EXIT STATUS: 0
+""")
+ t.cleanup()
+
+def test_link():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+link pass.cpp ;
+link fail-compile.cpp ;
+link fail-link.cpp ;
+link fail-run.cpp ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj")
+
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.exe")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test")
+
+ t.ignore_addition("bin/pass.test/*/pass.rsp")
+ t.ignore_addition("bin/fail-link.test/*/fail-link.rsp")
+ t.ignore_addition("bin/fail-run.test/*/fail-run.rsp")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_link_fail():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+link-fail pass.cpp ;
+link-fail fail-compile.cpp ;
+link-fail fail-link.cpp ;
+link-fail fail-run.cpp ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj")
+
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj")
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.exe")
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test")
+
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj")
+
+ t.ignore_addition("bin/pass.test/*/pass.rsp")
+ t.ignore_addition("bin/fail-link.test/*/fail-link.rsp")
+ t.ignore_addition("bin/fail-run.test/*/fail-run.rsp")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_link_change():
+ """Tests that the test file is removed when a test fails after it
+ previously passed."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-compile.cpp", "int main() {}\n")
+ t.write("fail-link.cpp", "int main() {}\n")
+
+ t.write("Jamroot.jam", """import testing ;
+link-fail pass.cpp ;
+link fail-compile.cpp ;
+link fail-link.cpp ;
+""")
+ t.run_build_system()
+ # Sanity check
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+ t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test")
+ t.expect_output_lines("...failed*", False)
+
+ # Now make them fail
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.run_build_system(status=1)
+
+ t.expect_removal("bin/pass.test/$toolset/debug*/pass.test")
+ t.expect_removal("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+ t.expect_removal("bin/fail-link.test/$toolset/debug*/fail-link.test")
+
+ t.cleanup()
+
+def test_compile():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+compile pass.cpp ;
+compile fail-compile.cpp ;
+compile fail-link.cpp ;
+compile fail-run.cpp ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj")
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj")
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test")
+
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_compile_fail():
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+compile-fail pass.cpp ;
+compile-fail fail-compile.cpp ;
+compile-fail fail-link.cpp ;
+compile-fail fail-run.cpp ;
+""")
+
+ t.run_build_system(status=1)
+ t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.obj")
+ t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_compile_change():
+ """Tests that the test file is removed when a test fails after it
+ previously passed."""
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass.cpp", "#error expected to fail\n")
+ t.write("fail-compile.cpp", "int main() {}\n")
+
+ t.write("Jamroot.jam", """import testing ;
+compile-fail pass.cpp ;
+compile fail-compile.cpp ;
+""")
+ t.run_build_system()
+ # Sanity check
+ t.expect_addition("bin/pass.test/$toolset/debug*/pass.test")
+ t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+ t.expect_output_lines("...failed*", False)
+
+ # Now make them fail
+ t.write("pass.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.run_build_system(status=1)
+
+ t.expect_removal("bin/pass.test/$toolset/debug*/pass.test")
+ t.expect_removal("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+
+ t.cleanup()
+
+def test_remove_test_targets(option):
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass-compile.cpp", "int main() {}\n")
+ t.write("pass-link.cpp", "int main() {}\n")
+ t.write("pass-run.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+ t.write("source.cpp", "int f();\n")
+
+ t.write("Jamroot.jam", """import testing ;
+obj source.o : source.cpp ;
+compile pass-compile.cpp ;
+link pass-link.cpp source.o ;
+run pass-run.cpp source.o ;
+compile-fail fail-compile.cpp ;
+link-fail fail-link.cpp ;
+run-fail fail-run.cpp ;
+""")
+
+ t.run_build_system([option])
+
+ t.expect_addition("bin/$toolset/debug*/source.obj")
+
+ t.expect_addition("bin/pass-compile.test/$toolset/debug*/pass-compile.test")
+
+ t.expect_addition("bin/pass-link.test/$toolset/debug*/pass-link.test")
+
+ t.expect_addition("bin/pass-run.test/$toolset/debug*/pass-run.output")
+ t.expect_addition("bin/pass-run.test/$toolset/debug*/pass-run.run")
+ t.expect_addition("bin/pass-run.test/$toolset/debug*/pass-run.test")
+
+ t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test")
+
+ t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test")
+
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.output")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.run")
+ t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test")
+
+ t.ignore_addition("bin/pass-link.test/*/pass-link.rsp")
+ t.ignore_addition("bin/pass-run.test/*/pass-run.rsp")
+ t.ignore_addition("bin/fail-link.test/*/fail-link.rsp")
+ t.ignore_addition("bin/fail-run.test/*/fail-run.rsp")
+
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+def test_dump_tests():
+ """Tests the output of the --dump-tests option"""
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("pass-compile.cpp", "int main() {}\n")
+ t.write("pass-link.cpp", "int main() {}\n")
+ t.write("pass-run.cpp", "int main() {}\n")
+ t.write("fail-compile.cpp", "#error expected to fail\n")
+ t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n")
+ t.write("fail-run.cpp", "int main() { return 1; }\n")
+
+ t.write("Jamroot.jam", """import testing ;
+run pass-run.cpp ;
+run-fail fail-run.cpp ;
+link pass-link.cpp ;
+link-fail fail-link.cpp ;
+compile pass-compile.cpp ;
+compile-fail fail-compile.cpp ;
+build-project libs/any/test ;
+build-project libs/any/example ;
+build-project libs/any ;
+build-project tools/bcp/test ;
+build-project tools/bcp/example ;
+build-project subdir/test ;
+build-project status ;
+build-project outside/project ;
+""")
+ def write_subdir(dir):
+ t.write(dir + "/test.cpp", "int main() {}\n")
+ t.write(dir + "/Jamfile", "run test.cpp ;")
+ write_subdir("libs/any/test")
+ write_subdir("libs/any/example")
+ write_subdir("libs/any")
+ write_subdir("tools/bcp/test")
+ write_subdir("tools/bcp/example")
+ write_subdir("status")
+ write_subdir("subdir/test")
+ t.write("outside/other/test.cpp", "int main() {}\n")
+ t.write("outside/project/Jamroot", "run ../other/test.cpp ;")
+ t.run_build_system(["--dump-tests", "-n", "-d0"],
+ match=TestCmd.match_re, stdout=
+"""boost-test\(RUN\) ".*/pass-run" : "pass-run\.cpp"
+boost-test\(RUN_FAIL\) ".*/fail-run" : "fail-run\.cpp"
+boost-test\(LINK\) ".*/pass-link" : "pass-link\.cpp"
+boost-test\(LINK_FAIL\) ".*/fail-link" : "fail-link\.cpp"
+boost-test\(COMPILE\) ".*/pass-compile" : "pass-compile\.cpp"
+boost-test\(COMPILE_FAIL\) ".*/fail-compile" : "fail-compile\.cpp"
+boost-test\(RUN\) "any/test" : "libs/any/test\.cpp"
+boost-test\(RUN\) "any/test" : "libs/any/test/test\.cpp"
+boost-test\(RUN\) "any/test" : "libs/any/example/test\.cpp"
+boost-test\(RUN\) "bcp/test" : "tools/bcp/test/test\.cpp"
+boost-test\(RUN\) "bcp/test" : "tools/bcp/example/test\.cpp"
+boost-test\(RUN\) ".*/subdir/test/test" : "subdir/test/test\.cpp"
+boost-test\(RUN\) "test" : "status/test\.cpp"
+boost-test\(RUN\) ".*/outside/project/test" : "../other/test.cpp"
+""")
+ t.cleanup()
+
+################################################################################
+#
+# test_files_with_spaces_in_their_name()
+# --------------------------------------
+#
+################################################################################
+
+def test_files_with_spaces_in_their_name():
+ """Regression test making sure test result files get created correctly when
+ testing files with spaces in their name.
+ """
+
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("valid source.cpp", "int main() {}\n");
+
+ t.write("invalid source.cpp", "this is not valid source code");
+
+ t.write("jamroot.jam", """
+import testing ;
+testing.compile "valid source.cpp" ;
+testing.compile-fail "invalid source.cpp" ;
+""")
+
+ t.run_build_system(status=0)
+ t.expect_addition("bin/invalid source.test/$toolset/debug*/invalid source.obj")
+ t.expect_addition("bin/invalid source.test/$toolset/debug*/invalid source.test")
+ t.expect_addition("bin/valid source.test/$toolset/debug*/valid source.obj")
+ t.expect_addition("bin/valid source.test/$toolset/debug*/valid source.test")
+
+ t.expect_content("bin/valid source.test/$toolset/debug*/valid source.test", \
+ "passed" )
+ t.expect_content( \
+ "bin/invalid source.test/$toolset/debug*/invalid source.test", \
+ "passed" )
+ t.expect_content( \
+ "bin/invalid source.test/$toolset/debug*/invalid source.obj", \
+ "failed as expected" )
+
+ t.cleanup()
+
+
+################################################################################
+#
+# main()
+# ------
+#
+################################################################################
+
+test_run()
+test_run_fail()
+test_run_change()
+test_run_path()
+test_run_args()
+test_link()
+test_link_fail()
+test_link_change()
+test_compile()
+test_compile_fail()
+test_compile_change()
+test_remove_test_targets("--remove-test-targets")
+test_remove_test_targets("preserve-test-targets=off")
+test_dump_tests()
+test_files_with_spaces_in_their_name()
diff --git a/src/boost/tools/build/test/timedata.py b/src/boost/tools/build/test/timedata.py
new file mode 100644
index 000000000..007a409c8
--- /dev/null
+++ b/src/boost/tools/build/test/timedata.py
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+
+# Copyright 2005 David Abrahams
+# Copyright 2008, 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Tests the build step timing facilities.
+
+# TODO: Missing tests:
+# 1. 'time' target with a source target representing more than one virtual
+# target. This happens in practice, e.g. when using the time rule on a msvc
+# exe target whose generator actually constructs an EXE and a PDB target.
+# When this is done - only the main virtual target's constructing action
+# should be timed.
+# 2. 'time' target with a source target representing a virtual target that
+# actually gets built by multiple actions run in sequence. In that case a
+# separate timing result should be reported for each of those actions. This
+# happens in practice, e.g. when using the time rule on a msvc exe target
+# which first gets created as a result of some link action and then its
+# manifest gets embedded inside it as a resource using a separate action
+# (assuming an appropriate property has been set for this target - see the
+# msvc module for details).
+
+import BoostBuild
+import re
+
+
+###############################################################################
+#
+# basic_jam_action_test()
+# -----------------------
+#
+###############################################################################
+
+def basic_jam_action_test():
+ """Tests basic Jam action timing support."""
+
+ t = BoostBuild.Tester(pass_toolset=0)
+
+ t.write("file.jam", """\
+rule time
+{
+ DEPENDS $(<) : $(>) ;
+ __TIMING_RULE__ on $(>) = record_time $(<) ;
+ DEPENDS all : $(<) ;
+}
+
+actions time
+{
+ echo $(>) user: $(__USER_TIME__) system: $(__SYSTEM_TIME__) clock: $(__CLOCK_TIME__)
+ echo timed from $(>) >> $(<)
+}
+
+rule record_time ( target : source : start end user system clock )
+{
+ __USER_TIME__ on $(target) = $(user) ;
+ __SYSTEM_TIME__ on $(target) = $(system) ;
+ __CLOCK_TIME__ on $(target) = $(clock) ;
+}
+
+rule make
+{
+ DEPENDS $(<) : $(>) ;
+}
+
+actions make
+{
+ echo made from $(>) >> $(<)
+}
+
+time foo : bar ;
+make bar : baz ;
+""")
+
+ t.write("baz", "nothing")
+
+ expected_output = """\
+\.\.\.found 4 targets\.\.\.
+\.\.\.updating 2 targets\.\.\.
+make bar
+time foo
+bar +user: [0-9\.]+ +system: +[0-9\.]+ +clock: +[0-9\.]+ *
+\.\.\.updated 2 targets\.\.\.$
+"""
+
+ t.run_build_system(["-ffile.jam", "-d+1"], stdout=expected_output,
+ match=lambda actual, expected: re.search(expected, actual, re.DOTALL))
+ t.expect_addition("foo")
+ t.expect_addition("bar")
+ t.expect_nothing_more()
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# boost_build_testing_support_timing_rule():
+# ------------------------------------------
+#
+###############################################################################
+
+def boost_build_testing_support_timing_rule():
+ """
+ Tests the target build timing rule provided by the Boost Build testing
+ support system.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("aaa.cpp", "int main() {}\n")
+
+ t.write("jamroot.jam", """\
+import testing ;
+exe my-exe : aaa.cpp ;
+time my-time : my-exe ;
+""")
+
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/aaa.obj")
+ t.expect_addition("bin/$toolset/debug*/my-exe.exe")
+ t.expect_addition("bin/$toolset/debug*/my-time.time")
+
+ t.expect_content_lines("bin/$toolset/debug*/my-time.time",
+ "user: *[0-9] seconds")
+ t.expect_content_lines("bin/$toolset/debug*/my-time.time",
+ "system: *[0-9] seconds")
+ t.expect_content_lines("bin/$toolset/debug*/my-time.time",
+ "clock: *[0-9] seconds")
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# boost_build_testing_support_timing_rule_with_spaces_in_names()
+# --------------------------------------------------------------
+#
+###############################################################################
+
+def boost_build_testing_support_timing_rule_with_spaces_in_names():
+ """
+ Tests the target build timing rule provided by the Boost Build testing
+ support system when used with targets contining spaces in their names.
+
+ """
+ t = BoostBuild.Tester(use_test_config=False)
+
+ t.write("aaa bbb.cpp", "int main() {}\n")
+
+ t.write("jamroot.jam", """\
+import testing ;
+exe "my exe" : "aaa bbb.cpp" ;
+time "my time" : "my exe" ;
+""")
+
+ t.run_build_system()
+ t.expect_addition("bin/$toolset/debug*/aaa bbb.obj")
+ t.expect_addition("bin/$toolset/debug*/my exe.exe")
+ t.expect_addition("bin/$toolset/debug*/my time.time")
+
+ t.expect_content_lines("bin/$toolset/debug*/my time.time", "user: *")
+ t.expect_content_lines("bin/$toolset/debug*/my time.time", "system: *")
+
+ t.cleanup()
+
+
+###############################################################################
+#
+# main()
+# ------
+#
+###############################################################################
+
+basic_jam_action_test()
+boost_build_testing_support_timing_rule()
+boost_build_testing_support_timing_rule_with_spaces_in_names() \ No newline at end of file
diff --git a/src/boost/tools/build/test/toolset-mock/Jamroot.jam b/src/boost/tools/build/test/toolset-mock/Jamroot.jam
new file mode 100644
index 000000000..b9ce8e500
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/Jamroot.jam
@@ -0,0 +1,8 @@
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+lib l1 : lib.cpp ;
+exe test : main.cpp l1 ;
diff --git a/src/boost/tools/build/test/toolset-mock/lib.cpp b/src/boost/tools/build/test/toolset-mock/lib.cpp
new file mode 100644
index 000000000..6c6352a29
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/lib.cpp
@@ -0,0 +1,7 @@
+// Copyright (c) 2017 Steven Watanabe
+//
+// Distributed under the Boost Software License Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+int f() {}
diff --git a/src/boost/tools/build/test/toolset-mock/main.cpp b/src/boost/tools/build/test/toolset-mock/main.cpp
new file mode 100644
index 000000000..7b21686c4
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/main.cpp
@@ -0,0 +1,7 @@
+// Copyright (c) 2017 Steven Watanabe
+//
+// Distributed under the Boost Software License Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+int main() {}
diff --git a/src/boost/tools/build/test/toolset-mock/project-config.jam b/src/boost/tools/build/test/toolset-mock/project-config.jam
new file mode 100644
index 000000000..36c5337e1
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/project-config.jam
@@ -0,0 +1,48 @@
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import modules ;
+import os ;
+import toolset ;
+
+path-constant here : . ;
+
+local PYTHON = [ os.environ PYTHON_CMD ] ;
+
+using gcc : 4.8.3 : $(PYTHON) $(here)/src/gcc-4.8.3-linux.py : : <target-os>linux ;
+using gcc : 4.2.1 : $(PYTHON) $(here)/src/gcc-4.2.1-darwin.py : : <target-os>darwin ;
+
+# hard-code this to make the test work on other platforms
+modules.poke darwin : .host-osx-version : 10.11.0 ;
+using darwin : 4.2.1 : $(PYTHON) $(here)/src/darwin-4.2.1.py
+ : <archiver>$(here)/src/bin/libtool
+ <striper>$(here)/src/bin/strip
+ : <target-os>darwin
+ ;
+
+using clang-darwin : 3.9.0 : $(PYTHON) $(here)/src/clang-3.9.0-darwin.py
+ : <archiver>$(here)/src/bin/ar
+ ;
+
+using clang-linux : 3.9.0 : $(PYTHON) $(here)/src/clang-linux-3.9.0.py
+ : <archiver>$(here)/src/bin/ar
+ ;
+
+using clang-vxworks : 4.0.1 : $(PYTHON) $(here)/src/clang-vxworks-4.0.1.py
+ : <linker>$(here)/src/bin/ld
+ <archiver>$(here)/src/bin/ar
+ ;
+
+using intel-darwin : 10.2 : $(PYTHON) $(here)/src/intel-darwin-10.2.py
+ : <archiver>$(here)/src/bin/ar
+ ;
+
+# TODO: msvc toolset should not require this env variable to be presented
+modules.poke .ENVIRON : PROCESSOR_ARCHITEW6432 : amd64 ;
+using msvc : 14.3 : :
+ "<compiler>$(PYTHON) $(here)/src/msvc-14.3.py"
+ "<linker>$(PYTHON) $(here)/src/linkx.py"
+ ;
diff --git a/src/boost/tools/build/test/toolset-mock/src/Jamroot.jam b/src/boost/tools/build/test/toolset-mock/src/Jamroot.jam
new file mode 100644
index 000000000..172e26db7
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/Jamroot.jam
@@ -0,0 +1,57 @@
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import os ;
+import print ;
+import regex ;
+import feature ;
+
+.PYTHON = [ os.environ PYTHON_CMD ] ;
+path-constant .AR : ar.py ;
+path-constant .LIBTOOL : libtool.py ;
+path-constant .STRIP : strip.py ;
+path-constant .LD : ld.py ;
+
+rule c-escape ( str )
+{
+ return [ regex.replace $(str) \\\\ \\\\ ] ;
+}
+
+rule cfg-header ( target : : properties * )
+{
+ local PYTHON = [ c-escape $(.PYTHON) ] ;
+ local AR = [ c-escape $(.AR) ] ;
+ local LIBTOOL = [ c-escape $(.LIBTOOL) ] ;
+ local STRIP = [ c-escape $(.STRIP) ] ;
+ local LD = [ c-escape $(.LD) ] ;
+ print.output $(target) ;
+ print.text "#define PYTHON_CMD "\"$(PYTHON)\" : true ;
+ print.text "#define AR_CMD "\"$(AR)\" : true ;
+ print.text "#define LIBTOOL_CMD "\"$(LIBTOOL)\" : true ;
+ print.text "#define STRIP_CMD "\"$(STRIP)\" : true ;
+ print.text "#define LD_CMD "\"$(LD)\" : true ;
+}
+
+# We can only build one variant at a time and we need to have a fixed path
+project : requirements <location>bin ;
+
+make config.h : : @cfg-header ;
+
+project : requirements <implicit-dependency>config.h ;
+
+rule write-target-os ( target : : properties * )
+{
+ local target-os = [ feature.defaults <target-os> ] ;
+ print.output $(target) ;
+ print.text $(target-os:G=) : true ;
+}
+
+make target-os.txt : : @write-target-os ;
+
+exe ar : [ obj ar.obj : mock-program.cpp : <define>PY_SCRIPT=AR_CMD ] ;
+exe libtool : [ obj libtool.obj : mock-program.cpp : <define>PY_SCRIPT=LIBTOOL_CMD ] ;
+exe strip : [ obj strip.obj : mock-program.cpp : <define>PY_SCRIPT=STRIP_CMD ] ;
+exe ld : [ obj ld.obj : mock-program.cpp : <define>PY_SCRIPT=LD_CMD ] ;
diff --git a/src/boost/tools/build/test/toolset-mock/src/MockProgram.py b/src/boost/tools/build/test/toolset-mock/src/MockProgram.py
new file mode 100644
index 000000000..f0cc858d7
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/MockProgram.py
@@ -0,0 +1,287 @@
+# coding: utf-8
+# Copyright 2017 Steven Watanabe
+# Copyright 2020 René Ferdinand Rivera Morell
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from __future__ import print_function
+
+import sys
+import os
+import re
+import fnmatch
+
+# Represents a sequence of arguments that must appear
+# in a fixed order.
+class ordered:
+ def __init__(self, *args):
+ self.args = args
+ def match(self, command_line, pos, outputs):
+ for p in self.args:
+ res = try_match(command_line, pos, p, outputs)
+ if res is None:
+ return
+ pos = res
+ return pos
+
+# Represents a sequence of arguments that can appear
+# in any order.
+class unordered:
+ def __init__(self, *args):
+ self.args = list(args)
+ def match(self, command_line, pos, outputs):
+ unmatched = self.args[:]
+ while len(unmatched) > 0:
+ res = try_match_one(command_line, pos, unmatched, outputs)
+ if res is None:
+ return
+ pos = res
+ return pos
+
+# Represents a single input file.
+# If id is set, then the file must have been created
+# by a prior use of output_file.
+# If source is set, then the file must be that source file.
+class input_file:
+ def __init__(self, id=None, source=None):
+ assert((id is None) ^ (source is None))
+ self.id = id
+ self.source = source
+ def check(self, path):
+ if path.startswith("-"):
+ return
+ if self.id is not None:
+ try:
+ with open(path, "r") as f:
+ data = f.read()
+ if data == make_file_contents(self.id):
+ return True
+ else:
+ return
+ except:
+ return
+ elif self.source is not None:
+ if self.source == path:
+ return True
+ else:
+ return
+ assert(False)
+ def match(self, command_line, pos, outputs):
+ if self.check(command_line[pos]):
+ return pos + 1
+
+# Matches an output file.
+# If the full pattern is matched, The
+# file will be created.
+class output_file:
+ def __init__(self, id):
+ self.id = id
+ def match(self, command_line, pos, outputs):
+ if command_line[pos].startswith("-"):
+ return
+ outputs.append((command_line[pos], self.id))
+ return pos + 1
+
+class arg_file:
+ def __init__(self, id):
+ self.id = id
+ def match(self, command_line, pos, outputs):
+ if command_line[pos].startswith("-"):
+ return
+ if fnmatch.fnmatch(command_line[pos], self.id):
+ return pos + 1
+ else:
+ return
+
+# Matches the directory containing an input_file
+class target_path(object):
+ def __init__(self, id):
+ self.tester = input_file(id=id)
+ def match(self, command_line, pos, outputs):
+ arg = command_line[pos]
+ if arg.startswith("-"):
+ return
+ try:
+ for path in os.listdir(arg):
+ if self.tester.check(os.path.join(arg, path)):
+ return pos + 1
+ except:
+ return
+
+# Matches a single argument, which is composed of a prefix and a path
+# for example arguments of the form -ofilename.
+class arg(object):
+ def __init__(self, prefix, a):
+ # The prefix should be a string, a should be target_path or input_file.
+ self.prefix = prefix
+ self.a = a
+ def match(self, command_line, pos, outputs):
+ s = command_line[pos]
+ if s.startswith(self.prefix) and try_match([s[len(self.prefix):]], 0, self.a, outputs) == 1:
+ return pos + 1
+
+#
+class opt(object):
+ def __init__(self, *args):
+ self.args = args
+ def match(self, command_line, pos, outputs):
+ for p in self.args:
+ res = try_match_one(command_line, pos, p, outputs)
+ if res is not None:
+ pos = res
+ return pos
+
+# Given a file id, returns a string that will be
+# written to the file to allow it to be recognized.
+def make_file_contents(id):
+ return id
+
+# Matches a single pattern from a list.
+# If it succeeds, the matching pattern
+# is removed from the list.
+# Returns the index after the end of the match
+def try_match_one(command_line, pos, patterns, outputs):
+ for p in patterns:
+ tmp = outputs[:]
+ res = try_match(command_line, pos, p, tmp)
+ if res is not None:
+ outputs[:] = tmp
+ patterns.remove(p)
+ return res
+
+# returns the end of the match if any
+def try_match(command_line, pos, pattern, outputs):
+ if pos == len(command_line):
+ return
+ elif type(pattern) is str:
+ if pattern == command_line[pos]:
+ return pos + 1
+ else:
+ return pattern.match(command_line, pos, outputs)
+
+known_patterns = []
+program_name = None
+
+# Registers a command
+# The arguments should be a sequence of:
+# str, ordered, unordered, arg, input_file, output_file, target_path
+# kwarg: stdout is text that will be printed on success.
+def command(*args, **kwargs):
+ global known_patterns
+ global program_name
+ stdout = kwargs.get("stdout", None)
+ pattern = ordered(*args)
+ known_patterns += [(pattern, stdout)]
+ if program_name is None:
+ program_name = args[0]
+ else:
+ assert(program_name == args[0])
+
+# Use this to filter the recognized commands, based on the properties
+# passed to b2.
+def allow_properties(*args):
+ try:
+ return all(a in os.environ["B2_PROPERTIES"].split(" ") for a in args)
+ except KeyError:
+ return True
+
+# Use this in the stdout argument of command to print the command
+# for running another script.
+def script(name):
+ return os.path.join(os.path.dirname(__file__), "bin", re.sub('\.py$', '', name))
+
+def match(command_line):
+ for (p, stdout) in known_patterns:
+ outputs = []
+ if try_match(command_line, 0, p, outputs) == len(command_line):
+ return (stdout, outputs)
+
+# Every mock program should call this after setting up all the commands.
+def main():
+ command_line = [program_name] + sys.argv[1:]
+ result = match(command_line)
+ if result is not None:
+ (stdout, outputs) = result
+ if stdout is not None:
+ print(stdout)
+ for (file,id) in outputs:
+ with open(file, "w") as f:
+ f.write(make_file_contents(id))
+ exit(0)
+ else:
+ print("ERROR on command: %s"%(" ".join(command_line)))
+ exit(1)
+
+# file should be the name of a file in the same directory
+# as this. Must be called after verify_setup
+def verify_file(filename):
+ global known_files
+ if filename not in known_files:
+ known_files.add(filename)
+ srcdir = os.path.dirname(__file__)
+ execfile(os.path.join(srcdir, filename), {})
+
+def verify_setup():
+ """Override the behavior of most module components
+ in order to detect whether they are being used correctly."""
+ global main
+ global allow_properties
+ global output_file
+ global input_file
+ global target_path
+ global script
+ global command
+ global verify_errors
+ global output_ids
+ global input_ids
+ global known_files
+ def allow_properties(*args):
+ return True
+ def main():
+ pass
+ def output_file(id):
+ global output_ids
+ global verify_error
+ if id in output_ids:
+ verify_error("duplicate output_file: %s" % id)
+ output_ids.add(id)
+ def input_file(id=None, source=None):
+ if id is not None:
+ input_ids.add(id)
+ def target_path(id):
+ input_ids.add(id)
+ def script(filename):
+ verify_file(filename)
+ def command(*args, **kwargs):
+ pass
+ verify_errors = []
+ output_ids = set()
+ input_ids = set()
+ known_files = set()
+
+def verify_error(message):
+ global verify_errors
+ verify_errors += [message]
+
+def verify_finalize():
+ for id in input_ids:
+ if not id in output_ids:
+ verify_error("Input file does not exist: %s" % id)
+ for error in verify_errors:
+ print("error: %s" % error)
+ if len(verify_errors) != 0:
+ return 1
+ else:
+ return 0
+
+def verify():
+ srcdir = os.path.dirname(__file__)
+ if srcdir == '':
+ srcdir = '.'
+ verify_setup()
+ for f in os.listdir(srcdir):
+ if re.match(r"(gcc|clang|darwin|intel)-.*\.py", f):
+ verify_file(f)
+ exit(verify_finalize())
diff --git a/src/boost/tools/build/test/toolset-mock/src/ar.py b/src/boost/tools/build/test/toolset-mock/src/ar.py
new file mode 100644
index 000000000..2ecb7d7e7
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/ar.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+# coding: utf-8
+#
+# Copyright 2017-2018 Steven Watanabe
+# Copyright 2020 René Ferdinand Rivera Morell
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('ar', 'rsc', output_file('bin/gcc-gnu-4.8.3/debug/link-static/libl1.a'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/lib.o'))
+command('ar', 'rsc', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/libl1.a'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/lib.o'))
+command('ar', 'rsc', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/lib.o'))
+command('ar', 'rsc', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o'))
+command('ar', 'rsc', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/lib.o'))
+command('ar', 'rsc', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/lib.o'))
+command('ar', 'rsc', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/lib.o'))
+command('ar', 'rsc', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/lib.o'))
+command('ar', 'rsc', output_file('bin/clang-linux-3.9.0/debug/link-static/libl1.a'), input_file('bin/clang-linux-3.9.0/debug/link-static/lib.o'))
+command('ar', 'rsc', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/libl1.a'), input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/lib.o'))
+command('ar', 'rcu', output_file('bin/clang-vxworks-4.0.1/debug/link-static/libl1.a'), input_file('bin/clang-vxworks-4.0.1/debug/link-static/lib.o'))
+command('ar', 'rcu', output_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/libl1.a'), input_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/lib.o'))
+command('ar', 'rsc', output_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/libl1.lib'), input_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/lib.obj'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/clang-3.9.0-darwin.py b/src/boost/tools/build/test/toolset-mock/src/clang-3.9.0-darwin.py
new file mode 100644
index 000000000..85bf38610
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/clang-3.9.0-darwin.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('clang++', '-print-prog-name=ar', stdout=script('ar.py'))
+
+# all builds are multi-threaded for darwin
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared"):
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), unordered('-g', '-fPIC'))
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC'))
+
+if allow_properties("variant=release", "link=shared", "runtime-link=shared"):
+ command('clang++', '-x', 'c++', unordered('-O3', '-Wno-inline', '-Wall', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-v', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/release/target-os-darwin/lib.o'), '-fPIC')
+ command('clang++', '-x', 'c++', unordered('-O3', '-Wno-inline', '-Wall', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-v', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/release/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/release/target-os-darwin/libl1.dylib'), '-fPIC')
+
+if allow_properties("variant=debug", "link=static", "runtime-link=shared"):
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/libl1.a'), '-g')
+
+if allow_properties("variant=debug", "link=static", "runtime-link=static"):
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', '-static'))
+
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared", "architecture=x86", "address-model=32"):
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/lib.o'), unordered('-g', '-march=i686', '-fPIC', '-m32'))
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/libl1.dylib'), unordered('-g', '-march=i686', '-fPIC', '-m32'))
+
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared", "cxxstd=latest"):
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-std=c++1z'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), unordered('-g', '-fPIC', '-std=c++1z'))
+ command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-std=c++1z'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC', '-std=c++1z'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/clang-linux-3.9.0.py b/src/boost/tools/build/test/toolset-mock/src/clang-linux-3.9.0.py
new file mode 100644
index 000000000..183d1b7fa
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/clang-linux-3.9.0.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+# coding: utf-8
+#
+# Copyright 2017 Steven Watanabe
+# Copyright 2020 René Ferdinand Rivera Morell
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('clang++', '-print-prog-name=ar', stdout=script('ar.py'))
+
+# target-os=linux ..
+
+if allow_properties('target-os=linux', 'variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-fPIC'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/test'), '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/main.o'), input_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-fPIC'))
+
+if allow_properties('target-os=linux', 'variant=release', 'link=shared', 'threading=single', 'runtime-link=shared', 'strip=on'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-linux-3.9.0/release/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/release/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/release/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-fPIC', '-Wl,--strip-all'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-linux-3.9.0/release/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/release/test'), '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/release/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/release/libl1.so')), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/release/main.o'), input_file('bin/clang-linux-3.9.0/release/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-fPIC', '-Wl,--strip-all'))
+
+if allow_properties('target-os=linux', 'variant=debug', 'link=shared', 'threading=multi', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/threading-multi/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/test'), '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so')), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/threading-multi/main.o'), input_file('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC'))
+
+if allow_properties('target-os=linux', 'variant=debug', 'link=static', 'threading=single', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/link-static/main.o'), input_file('bin/clang-linux-3.9.0/debug/link-static/libl1.a'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-g')
+
+if allow_properties('target-os=linux', 'variant=debug', 'link=static', 'threading=single', 'runtime-link=static'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/main.o'), input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/libl1.a'), '-Wl,--end-group', unordered('-g', '-static'))
+
+if allow_properties('target-os=linux', 'variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared', 'architecture=x86', 'address-model=32'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-march=i686', '-fPIC', '-m32'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/main.o'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/test'), '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/main.o'), input_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-march=i686', '-fPIC', '-m32'))
+
+# target-os=windows ..
+
+if allow_properties('target-os=windows', 'variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/lib.obj'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/l1.dll'), '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/target-os-windows/lib.obj'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/main.obj'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/test.exe'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/target-os-windows/main.obj'), input_file('bin/clang-linux-3.9.0/debug/target-os-windows/l1.dll'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g'))
+
+if allow_properties('target-os=windows', 'variant=release', 'link=shared', 'threading=single', 'runtime-link=shared', 'strip=on'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-linux-3.9.0/release/target-os-windows/lib.obj'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/release/strip-on/target-os-windows/l1.dll'), '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/release/target-os-windows/lib.obj'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-Wl,--strip-all'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-linux-3.9.0/release/strip-on/target-os-windows/main.obj'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/release/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/release/strip-on/target-os-windows/main.obj'), input_file('bin/clang-linux-3.9.0/release/strip-on/target-os-windows/l1.dll'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-Wl,--strip-all'))
+
+if allow_properties('target-os=windows', 'variant=debug', 'link=shared', 'threading=multi', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/threading-multi/lib.obj'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/threading-multi/l1.dll'), '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/target-os-windows/threading-multi/lib.obj'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-pthread'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/target-os-windows/threading-multi/main.obj'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/target-os-windows/threading-multi/main.obj'), input_file('bin/clang-linux-3.9.0/debug/target-os-windows/threading-multi/l1.dll'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-pthread'))
+
+if allow_properties('target-os=windows', 'variant=debug', 'link=static', 'threading=single', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/lib.obj'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/main.obj'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/test.exe'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/main.obj'), input_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/libl1.lib'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-g')
+
+if allow_properties('target-os=windows', 'variant=debug', 'link=static', 'threading=single', 'runtime-link=static'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/target-os-windows/lib.obj'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/target-os-windows/main.obj'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/target-os-windows/test.exe'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/main.obj'), input_file('bin/clang-linux-3.9.0/debug/link-static/target-os-windows/libl1.lib'), '-Wl,--end-group', unordered('-g', '-static'))
+
+if allow_properties('target-os=windows', 'variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared', 'architecture=x86', 'address-model=32'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/lib.obj'), input_file(source='lib.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/l1.dll'), '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/lib.obj'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-march=i686', '-m32'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/main.obj'), input_file(source='main.cpp'))
+ command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/test.exe'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/main.obj'), input_file('bin/clang-linux-3.9.0/debug/address-model-32/architecture-x86/target-os-windows/l1.dll'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-march=i686', '-m32'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/clang-vxworks-4.0.1.py b/src/boost/tools/build/test/toolset-mock/src/clang-vxworks-4.0.1.py
new file mode 100644
index 000000000..b6a13b4fd
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/clang-vxworks-4.0.1.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+#
+# Copyright 2018 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('clang++', '-print-prog-name=ar', stdout=script('ar.py'))
+
+if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/main.o'), input_file(source='main.cpp'))
+
+if allow_properties('variant=release', 'link=shared', 'threading=single', 'runtime-link=shared', 'strip=on'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/release/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/release/main.o'), input_file(source='main.cpp'))
+
+if allow_properties('variant=debug', 'link=shared', 'threading=multi', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/threading-multi/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/threading-multi/main.o'), input_file(source='main.cpp'))
+
+if allow_properties('variant=debug', 'link=static', 'threading=single', 'runtime-link=shared'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/link-static/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/link-static/main.o'), input_file(source='main.cpp'))
+
+if allow_properties('variant=debug', 'link=static', 'threading=single', 'runtime-link=static'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/main.o'), input_file(source='main.cpp'))
+
+if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared', 'architecture=x86', 'address-model=32'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-fPIC', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-fPIC', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/main.o'), input_file(source='main.cpp'))
+
+if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared', 'rtti=off', 'exception-handling=off'):
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-fno-rtti', '-fno-exceptions', '-Wall', '-g', '-fPIC', '-D_NO_RTTI', '-D_NO_EX=1', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/lib.o'), input_file(source='lib.cpp'))
+ command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-fno-rtti', '-fno-exceptions', '-Wall', '-g', '-fPIC', '-D_NO_RTTI', '-D_NO_EX=1', '-c'), '-o', output_file('bin/clang-vxworks-4.0.1/debug/main.o'), input_file(source='main.cpp'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/darwin-4.2.1.py b/src/boost/tools/build/test/toolset-mock/src/darwin-4.2.1.py
new file mode 100644
index 000000000..1c12e00b3
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/darwin-4.2.1.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+script("libtool.py")
+
+command('g++', '-dumpversion', stdout='4.2.1')
+
+# all builds are multi-threaded for darwin
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', '-dynamiclib', '-Wl,-single_module', '-install_name', 'libl1.dylib', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), input_file('bin/darwin-4.2.1/debug/target-os-darwin/lib.o'), '-headerpad_max_install_names', unordered('-g', '-fPIC'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/test'), input_file('bin/darwin-4.2.1/debug/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC'))
+
+if allow_properties("variant=release", "link=shared", "runtime-link=shared"):
+ command('g++', unordered('-O3', '-Wno-inline', '-Wall', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', '-dynamiclib', '-Wl,-single_module', '-install_name', 'libl1.dylib', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/libl1.dylib'), input_file('bin/darwin-4.2.1/release/target-os-darwin/lib.o'), '-headerpad_max_install_names', unordered(ordered('-Wl,-dead_strip', '-no_dead_strip_inits_and_terms'), '-fPIC'))
+ command('g++', unordered('-O3', '-Wno-inline', '-Wall', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/test'), input_file('bin/darwin-4.2.1/release/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/release/target-os-darwin/libl1.dylib'), unordered(ordered('-Wl,-dead_strip', '-no_dead_strip_inits_and_terms'), '-fPIC'))
+
+if allow_properties("variant=debug", "link=static", "runtime-link=shared"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/test'), input_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), '-g')
+
+if allow_properties("variant=debug", "link=static", "runtime-link=static"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', ordered('-nodefaultlibs', '-shared-libgcc', '-lstdc++-static', '-lgcc_eh', '-lgcc', '-lSystem'), '-static'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/gcc-4.2.1-darwin.py b/src/boost/tools/build/test/toolset-mock/src/gcc-4.2.1-darwin.py
new file mode 100644
index 000000000..ae5230eb1
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/gcc-4.2.1-darwin.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('g++', '-print-prog-name=ar', stdout=script('ar.py'))
+
+# all builds are multi-threaded for darwin
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), '-shared', input_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/lib.o'), unordered('-g', '-fPIC'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-darwin-4.2.1/debug/target-os-darwin/libl1.dylib')), '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC'))
+
+if allow_properties("variant=release", "link=shared", "runtime-link=shared"):
+ command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/libl1.dylib'), '-shared', input_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/lib.o'), '-fPIC')
+ command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-darwin-4.2.1/release/target-os-darwin/libl1.dylib')), '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/libl1.dylib'), '-fPIC')
+
+if allow_properties("variant=debug", "link=static", "runtime-link=shared"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), '-g')
+
+if allow_properties("variant=debug", "link=static", "runtime-link=static"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', '-static'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/gcc-4.8.3-linux.py b/src/boost/tools/build/test/toolset-mock/src/gcc-4.8.3-linux.py
new file mode 100644
index 000000000..d454301e4
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/gcc-4.8.3-linux.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('g++', '-print-prog-name=ar', stdout=script('ar.py'))
+
+if allow_properties("variant=debug", "link=shared", "threading=single", "runtime-link=shared"):
+ command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/lib.o"), input_file(source="lib.cpp"))
+ command("g++", "-o", output_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-h", "-Wl,libl1.so", "-shared", "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/lib.o"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC"))
+ command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file(source="main.cpp"))
+ command("g++", "-Wl,-rpath", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-Wl,-rpath-link", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-o", output_file("bin/gcc-gnu-4.8.3/debug/test"), "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC"))
+
+if allow_properties("variant=release", "link=shared", "threading=single", "runtime-link=shared"):
+ command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/release/lib.o'), input_file(source='lib.cpp'))
+ command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/release/libl1.so'), '-Wl,-h', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/release/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-fPIC')
+ command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/release/main.o'), input_file(source='main.cpp'))
+ command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/release/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/release/libl1.so')), '-o', output_file('bin/gcc-gnu-4.8.3/release/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/release/main.o'), input_file('bin/gcc-gnu-4.8.3/release/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-fPIC')
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/lib.o'), input_file(source='lib.cpp'))
+ command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so'), '-Wl,-h', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/threading-multi/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/main.o'), input_file(source='main.cpp'))
+ command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so')), '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/threading-multi/main.o'), input_file('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC'))
+
+if allow_properties("variant=debug", "link=static", "threading=single", "runtime-link=shared"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/lib.o'), input_file(source='lib.cpp'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/link-static/main.o'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/libl1.a'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-g')
+
+if allow_properties("variant=debug", "link=static", "threading=single", "runtime-link=static"):
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/lib.o'), input_file(source='lib.cpp'))
+ command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/main.o'), input_file(source='main.cpp'))
+ command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/main.o'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/libl1.a'), '-Wl,--end-group', unordered('-g', '-static'))
+
+
+if allow_properties("variant=debug", "link=shared", "threading=single", "runtime-link=shared"):
+ command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC", "-std=c++1y"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/lib.o"), input_file(source="lib.cpp"))
+ command("g++", "-o", output_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-h", "-Wl,libl1.so", "-shared", "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/lib.o"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC", "-std=c++1y"))
+ command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC", "-std=c++1y"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file(source="main.cpp"))
+ command("g++", "-Wl,-rpath", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-Wl,-rpath-link", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-o", output_file("bin/gcc-gnu-4.8.3/debug/test"), "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC", "-std=c++1y"))
+
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/intel-darwin-10.2.py b/src/boost/tools/build/test/toolset-mock/src/intel-darwin-10.2.py
new file mode 100644
index 000000000..23b9ad9d7
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/intel-darwin-10.2.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('icc', '-print-prog-name=ar', stdout=script('ar.py'))
+
+# all builds are multi-threaded for darwin
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared"):
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-fPIC'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', 'libl1.dylib', input_file('bin/intel-darwin-10.2/debug/target-os-darwin/lib.o'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC'))
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-fPIC'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/target-os-darwin/libl1.dylib'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC'))
+
+if allow_properties("variant=release", "link=shared", "runtime-link=shared"):
+ command('icc', '-xc++', unordered('-O3', '-inline-level=2', '-w1', '-vec-report0', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', 'libl1.dylib', input_file('bin/intel-darwin-10.2/release/target-os-darwin/lib.o'), unordered(ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC'))
+ command('icc', '-xc++', unordered('-O3', '-inline-level=2', '-w1', '-vec-report0', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/release/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/release/target-os-darwin/libl1.dylib'), unordered(ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC'))
+
+if allow_properties("variant=debug", "link=static", "runtime-link=shared"):
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/libl1.a'), '-g', ordered('-shared-intel', '-lstdc++', '-lpthread'))
+
+if allow_properties("variant=debug", "link=static", "runtime-link=static"):
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', ordered('-static', '-static-intel', '-lstdc++', '-lpthread'), '-static'))
+
+if allow_properties("variant=debug", "link=shared", "runtime-link=shared", "architecture=x86", "address-model=32"):
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/lib.o'), input_file(source='lib.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', 'libl1.dylib', input_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/lib.o'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-march=i686', '-fPIC', '-m32'))
+ command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/main.o'), input_file(source='main.cpp'))
+ command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/libl1.dylib'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-march=i686', '-fPIC', '-m32'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/ld.py b/src/boost/tools/build/test/toolset-mock/src/ld.py
new file mode 100644
index 000000000..040c23191
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/ld.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+#
+# Copyright 2018 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared'):
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/libl1.so'), input_file('bin/clang-vxworks-4.0.1/debug/lib.o'), unordered('-g', '-fPIC'), '-fpic', '-shared', '-non-static')
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/test'), input_file('bin/clang-vxworks-4.0.1/debug/main.o'), input_file('bin/clang-vxworks-4.0.1/debug/libl1.so'), unordered('-g', '-fPIC'))
+
+if allow_properties('variant=release', 'link=shared', 'threading=single', 'runtime-link=shared', 'strip=on'):
+ command('ld', '-t', '-o', output_file('bin/clang-vxworks-4.0.1/release/libl1.so'), input_file('bin/clang-vxworks-4.0.1/release/lib.o'), unordered('-fPIC', '-Wl,--strip-all'), '-fpic', '-shared', '-non-static')
+ command('ld', '-t', '-o', output_file('bin/clang-vxworks-4.0.1/release/test'), input_file('bin/clang-vxworks-4.0.1/release/main.o'), input_file('bin/clang-vxworks-4.0.1/release/libl1.so'), unordered('-fPIC', '-Wl,--strip-all'))
+
+if allow_properties('variant=debug', 'link=shared', 'threading=multi', 'runtime-link=shared'):
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/threading-multi/libl1.so'), input_file('bin/clang-vxworks-4.0.1/debug/threading-multi/lib.o'), unordered('-g', '-fPIC'), '-fpic', '-shared', '-non-static')
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/threading-multi/test'), input_file('bin/clang-vxworks-4.0.1/debug/threading-multi/main.o'), input_file('bin/clang-vxworks-4.0.1/debug/threading-multi/libl1.so'), unordered('-g', '-fPIC'))
+
+if allow_properties('variant=debug', 'link=static', 'threading=single', 'runtime-link=shared'):
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/link-static/test'), input_file('bin/clang-vxworks-4.0.1/debug/link-static/main.o'), input_file('bin/clang-vxworks-4.0.1/debug/link-static/libl1.a'), '-g')
+
+if allow_properties('variant=debug', 'link=static', 'threading=single', 'runtime-link=static'):
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/test'), input_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/main.o'), input_file('bin/clang-vxworks-4.0.1/debug/link-static/runtime-link-static/libl1.a'), unordered('-g'))
+
+if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared', 'architecture=x86', 'address-model=32'):
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/libl1.so'), input_file('bin/clang-vxworks-4.0.1/debug/lib.o'), unordered('-g', '-march=i686', '-fPIC', '-m32'), '-fpic', '-shared', '-non-static')
+ command('ld', '-o', output_file('bin/clang-vxworks-4.0.1/debug/test'), input_file('bin/clang-vxworks-4.0.1/debug/main.o'), input_file('bin/clang-vxworks-4.0.1/debug/libl1.so'), unordered('-g', '-march=i686', '-fPIC', '-m32'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/libtool.py b/src/boost/tools/build/test/toolset-mock/src/libtool.py
new file mode 100644
index 000000000..35b6051cc
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/libtool.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('libtool', '-static', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/lib.o'))
+command('libtool', '-static', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/linkx.py b/src/boost/tools/build/test/toolset-mock/src/linkx.py
new file mode 100644
index 000000000..2c7cd518f
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/linkx.py
@@ -0,0 +1,33 @@
+# Copyright 2022 Nikita Kniazev
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=desktop"):
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\threading-multi\lib.obj'), '/DEBUG', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\threading-multi\l1.dll')), '/DLL', arg('/IMPLIB:', output_file(r'bin\msvc-14.3\debug\threading-multi\l1.implib')))
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\threading-multi\main.obj'), input_file(r'bin\msvc-14.3\debug\threading-multi\l1.implib'), '/DEBUG', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\threading-multi\test.exe')))
+
+if allow_properties("variant=release", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=desktop"):
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\release\threading-multi\lib.obj'), '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\release\threading-multi\l1.dll')), '/DLL', arg('/IMPLIB:', output_file(r'bin\msvc-14.3\release\threading-multi\l1.implib')))
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\release\threading-multi\main.obj'), input_file(r'bin\msvc-14.3\release\threading-multi\l1.implib'), '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\release\threading-multi\test.exe')))
+
+if allow_properties("variant=debug", "link=static", "threading=multi", "runtime-link=shared", "windows-api=desktop"):
+ command('link', '/lib', '/NOLOGO', arg('/out:', output_file(r'bin\msvc-14.3\debug\link-static\threading-multi\l1.lib')), input_file(r'bin\msvc-14.3\debug\link-static\threading-multi\lib.obj'))
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\link-static\threading-multi\main.obj'), input_file(r'bin\msvc-14.3\debug\link-static\threading-multi\l1.lib'), '/DEBUG', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\link-static\threading-multi\test.exe')))
+
+if allow_properties("variant=debug", "link=static", "threading=single", "runtime-link=static", "windows-api=desktop"):
+ command('link', '/lib', '/NOLOGO', arg('/out:', output_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\l1.lib')), input_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\lib.obj'))
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\main.obj'), input_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\l1.lib'), '/DEBUG', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\test.exe')))
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=store"):
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\lib.obj'), '/DEBUG', '/APPCONTAINER', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\l1.dll')), '/DLL', arg('/IMPLIB:', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\l1.implib')))
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\main.obj'), input_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\l1.implib'), '/DEBUG', '/APPCONTAINER', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\test.exe')))
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=phone"):
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\lib.obj'), '/DEBUG', '/APPCONTAINER', '/NODEFAULTLIB:kernel32.lib', '/NODEFAULTLIB:ole32.lib', 'WindowsPhoneCore.lib', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\l1.dll')), '/DLL', arg('/IMPLIB:', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\l1.implib')))
+ command('link', '/NOLOGO', '/INCREMENTAL:NO', input_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\main.obj'), input_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\l1.implib'), '/DEBUG', '/APPCONTAINER', '/NODEFAULTLIB:kernel32.lib', '/NODEFAULTLIB:ole32.lib', 'WindowsPhoneCore.lib', '/MACHINE:X64', '/MANIFEST:EMBED', '/subsystem:console', arg('/out:', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\test.exe')))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/mock-program.cpp b/src/boost/tools/build/test/toolset-mock/src/mock-program.cpp
new file mode 100644
index 000000000..e4a4523a5
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/mock-program.cpp
@@ -0,0 +1,42 @@
+// mock-program.cpp
+//
+// Copyright (c) 2017 Steven Watanabe
+//
+// Distributed under the Boost Software License Version 1.0. (See
+// accompanying file LICENSE.txt or copy at
+// https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+// This program does nothing except to exec a python script
+
+#include <vector>
+#include <iostream>
+#include <stdio.h>
+#include "config.h"
+
+#if defined(_WIN32)
+ #include <process.h>
+ #define execv _execv
+#else
+ #include <unistd.h>
+#endif
+
+#ifndef PY_SCRIPT
+#error PY_SCRIPT must be defined to the absolute path to the script to run
+#endif
+
+#ifndef PYTHON_CMD
+#error PYTHON_CMD must be defined to the absolute path to the python interpreter
+#endif
+
+int main(int argc, char ** argv)
+{
+ std::vector<char *> args;
+ char python_cmd[] = PYTHON_CMD;
+ char script[] = PY_SCRIPT;
+ args.push_back(python_cmd);
+ args.push_back(script);
+ args.insert(args.end(), argv + 1, argv + argc);
+ args.push_back(NULL);
+ execv(python_cmd, &args[0]);
+ perror("exec");
+}
diff --git a/src/boost/tools/build/test/toolset-mock/src/msvc-14.3.py b/src/boost/tools/build/test/toolset-mock/src/msvc-14.3.py
new file mode 100644
index 000000000..200c31275
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/msvc-14.3.py
@@ -0,0 +1,33 @@
+# Copyright 2022 Nikita Kniazev
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=desktop"):
+ command('cl', '/Zm800', '-nologo', input_file(source='lib.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\threading-multi\lib.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend')
+ command('cl', '/Zm800', '-nologo', input_file(source='main.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\threading-multi\main.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend')
+
+if allow_properties("variant=release", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=desktop"):
+ command('cl', '/Zm800', '-nologo', input_file(source='lib.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\release\threading-multi\lib.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/O2', '/Ob2', '/W3', '/MD', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/Gw', '/favor:blend', '-DNDEBUG')
+ command('cl', '/Zm800', '-nologo', input_file(source='main.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\release\threading-multi\main.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/O2', '/Ob2', '/W3', '/MD', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/Gw', '/favor:blend', '-DNDEBUG')
+
+if allow_properties("variant=debug", "link=static", "threading=multi", "runtime-link=shared", "windows-api=desktop"):
+ command('cl', '/Zm800', '-nologo', input_file(source='lib.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\link-static\threading-multi\lib.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend')
+ command('cl', '/Zm800', '-nologo', input_file(source='main.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\link-static\threading-multi\main.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend')
+
+if allow_properties("variant=debug", "link=static", "threading=single", "runtime-link=static", "windows-api=desktop"):
+ command('cl', '/Zm800', '-nologo', input_file(source='lib.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\lib.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend', '/MTd')
+ command('cl', '/Zm800', '-nologo', input_file(source='main.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\link-static\runtime-link-static\main.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend', '/MTd')
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=store"):
+ command('cl', '/Zm800', '/ZW', '/EHsc', '-nologo', input_file(source=r'lib.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\lib.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend', '-DWINAPI_FAMILY=WINAPI_FAMILY_APP', '-D_WIN32_WINNT=0x0602')
+ command('cl', '/Zm800', '/ZW', '/EHsc', '-nologo', input_file(source=r'main.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-store\main.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend', '-DWINAPI_FAMILY=WINAPI_FAMILY_APP', '-D_WIN32_WINNT=0x0602')
+
+if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared", "windows-api=phone"):
+ command('cl', '/Zm800', '/ZW', '/EHsc', '-nologo', input_file(source=r'lib.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\lib.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend', '-DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP', '-D_WIN32_WINNT=0x0602')
+ command('cl', '/Zm800', '/ZW', '/EHsc', '-nologo', input_file(source=r'main.cpp'), '-c', arg('-Fo', output_file(r'bin\msvc-14.3\debug\threading-multi\windows-api-phone\main.obj')), '-TP', '/wd4675', '/EHs', '/GR', '/Zc:throwingNew', '/Z7', '/Od', '/Ob0', '/W3', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/Zc:inline', '/favor:blend', '-DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP', '-D_WIN32_WINNT=0x0602')
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/project-config.jam b/src/boost/tools/build/test/toolset-mock/src/project-config.jam
new file mode 100644
index 000000000..4d57faa45
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/project-config.jam
@@ -0,0 +1,5 @@
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
diff --git a/src/boost/tools/build/test/toolset-mock/src/strip.py b/src/boost/tools/build/test/toolset-mock/src/strip.py
new file mode 100644
index 000000000..09b074307
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/strip.py
@@ -0,0 +1,13 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+command('strip', '-S', '-x', input_file('bin/darwin-4.2.1/release/target-os-darwin/test'))
+
+main()
diff --git a/src/boost/tools/build/test/toolset-mock/src/verify.py b/src/boost/tools/build/test/toolset-mock/src/verify.py
new file mode 100644
index 000000000..9181d08fb
--- /dev/null
+++ b/src/boost/tools/build/test/toolset-mock/src/verify.py
@@ -0,0 +1,9 @@
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from MockProgram import *
+
+verify()
diff --git a/src/boost/tools/build/test/toolset_clang_darwin.py b/src/boost/tools/build/test/toolset_clang_darwin.py
new file mode 100644
index 000000000..e261fa9a6
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_clang_darwin.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates the clang-darwin toolset using a mock of clang
+
+from TestToolset import test_toolset
+
+test_toolset("clang-darwin", "3.9.0", [
+ ["target-os=darwin"],
+ ["target-os=darwin", "release", "strip=on", "linkflags=-v"],
+ ["target-os=darwin", "threading=multi"],
+ ["target-os=darwin", "link=static"],
+ ["target-os=darwin", "link=static", "runtime-link=static"],
+ ["target-os=darwin", "architecture=x86", "address-model=32"],
+ ["target-os=darwin", "cxxstd=latest"]])
diff --git a/src/boost/tools/build/test/toolset_clang_linux.py b/src/boost/tools/build/test/toolset_clang_linux.py
new file mode 100644
index 000000000..eed96875f
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_clang_linux.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+# coding: utf-8
+#
+# Copyright 2017 Steven Watanabe
+# Copyright 2020 René Ferdinand Rivera Morell
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates the clang_linux toolset using a mock of clang
+
+from TestToolset import test_toolset
+
+test_toolset("clang-linux", "3.9.0", [
+ ["target-os=linux"],
+ ["target-os=linux", "release", "strip=on"],
+ ["target-os=linux", "threading=multi"],
+ ["target-os=linux", "link=static"],
+ ["target-os=linux", "link=static", "runtime-link=static"],
+ ["target-os=linux", "architecture=x86", "address-model=32"]])
+
+test_toolset("clang-linux", "3.9.0", [
+ ["target-os=windows"],
+ ["target-os=windows", "release", "strip=on"],
+ ["target-os=windows", "threading=multi"],
+ ["target-os=windows", "link=static"],
+ ["target-os=windows", "architecture=x86", "address-model=32"]
+ ])
diff --git a/src/boost/tools/build/test/toolset_clang_vxworks.py b/src/boost/tools/build/test/toolset_clang_vxworks.py
new file mode 100644
index 000000000..66ed4d8f2
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_clang_vxworks.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+#
+# Copyright 2018 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates the clang_vxworks toolset using a mock of clang
+
+from TestToolset import test_toolset
+
+test_toolset("clang-vxworks", "4.0.1", [
+ ["target-os=vxworks"],
+ ["target-os=vxworks", "release", "strip=on", "linkflags=-t"],
+ ["target-os=vxworks", "threading=multi"],
+ ["target-os=vxworks", "link=static"],
+ ["target-os=vxworks", "link=static", "runtime-link=static"],
+ ["target-os=vxworks", "architecture=x86", "address-model=32"],
+ ["target-os=vxworks", "rtti=off", "exception-handling=off"]])
diff --git a/src/boost/tools/build/test/toolset_darwin.py b/src/boost/tools/build/test/toolset_darwin.py
new file mode 100644
index 000000000..246c27305
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_darwin.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates the darwin toolset using a mock of gcc
+
+from TestToolset import test_toolset
+
+test_toolset("darwin", "4.2.1", [
+ ["target-os=darwin"],
+ ["target-os=darwin", "release", "strip=on"],
+ ["target-os=darwin", "threading=multi"],
+ ["target-os=darwin", "link=static"],
+ ["target-os=darwin", "link=static", "runtime-link=static"],
+# Address-model handling is quite broken
+# ["target-os=darwin", "architecture=x86", "address-model=32"]
+])
diff --git a/src/boost/tools/build/test/toolset_defaults.py b/src/boost/tools/build/test/toolset_defaults.py
new file mode 100644
index 000000000..912b8a05d
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_defaults.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+
+# Copyright 2018 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the handling of toolset.add-defaults
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0, ignore_toolset_requirements=False)
+
+t.write('jamroot.jam', '''
+import toolset ;
+import errors ;
+import feature : feature ;
+import set ;
+
+feature f1 : a b ;
+feature f2 : c d ;
+feature f3 : e f ;
+feature f4 : g h ;
+feature f5 : i j ;
+feature f6 : k l m ;
+
+rule test-rule ( properties * )
+{
+ if <f1>a in $(properties)
+ {
+ return <f2>d ;
+ }
+}
+
+toolset.add-defaults
+ <conditional>@test-rule
+ <f3>e:<f4>h
+ <f5>i:<f6>l
+;
+
+rule check-requirements ( target : sources * : properties * )
+{
+ local expected = <f2>d <f4>h <f6>m ;
+ local unexpected = <f2>c <f4>g <f6>k <f6>l ;
+ local missing = [ set.difference $(expected) : $(properties) ] ;
+ if $(missing)
+ {
+ errors.error $(missing) not present ;
+ }
+ local extra = [ set.intersection $(unexpected) : $(properties) ] ;
+ if $(extra)
+ {
+ errors.error $(extra) present ;
+ }
+}
+make test : : @check-requirements : <f6>m ;
+''')
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/toolset_gcc.py b/src/boost/tools/build/test/toolset_gcc.py
new file mode 100644
index 000000000..948de9e42
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_gcc.py
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates the gcc toolset using a mock of gcc
+
+from TestToolset import test_toolset
+
+test_toolset("gcc", "4.8.3", [
+ ["target-os=linux"],
+ ["target-os=linux", "release"],
+ ["target-os=linux", "threading=multi"],
+ ["target-os=linux", "link=static"],
+ ["target-os=linux", "link=static", "runtime-link=static"],
+ ["target-os=linux", "cxxstd=latest"]])
+
+test_toolset("gcc", "4.2.1", [
+ ["target-os=darwin"],
+ ["target-os=darwin", "release"],
+ ["target-os=darwin", "threading=multi"],
+ ["target-os=darwin", "link=static"],
+ ["target-os=darwin", "link=static", "runtime-link=static"]])
diff --git a/src/boost/tools/build/test/toolset_intel_darwin.py b/src/boost/tools/build/test/toolset_intel_darwin.py
new file mode 100644
index 000000000..53df4744e
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_intel_darwin.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Steven Watanabe
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# validates the intel-darwin toolset using a mock of icc
+
+from TestToolset import test_toolset
+
+test_toolset("intel-darwin", "10.2", [
+ ["target-os=darwin"],
+ ["target-os=darwin", "release", "strip=on"],
+ ["target-os=darwin", "threading=multi"],
+ ["target-os=darwin", "link=static"],
+ ["target-os=darwin", "link=static", "runtime-link=static"],
+ ["target-os=darwin", "architecture=x86", "address-model=32"]])
diff --git a/src/boost/tools/build/test/toolset_msvc.py b/src/boost/tools/build/test/toolset_msvc.py
new file mode 100644
index 000000000..9dc43a156
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_msvc.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+#
+# Copyright 2022 Nikita Kniazev
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+from TestToolset import test_toolset
+
+test_toolset("msvc", "14.3", [
+ ["target-os=windows"],
+ ["target-os=windows", "release"],
+ ["target-os=windows", "threading=single"],
+ ["target-os=windows", "link=static"],
+ ["target-os=windows", "link=static", "runtime-link=static"],
+ ["target-os=windows", "windows-api=store"],
+ ["target-os=windows", "windows-api=phone"],
+])
diff --git a/src/boost/tools/build/test/toolset_requirements.py b/src/boost/tools/build/test/toolset_requirements.py
new file mode 100644
index 000000000..a911ba0e3
--- /dev/null
+++ b/src/boost/tools/build/test/toolset_requirements.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+# Copyright 2014 Steven Watanabe
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the handling of toolset.add-requirements
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0, ignore_toolset_requirements=False)
+
+t.write('jamroot.jam', '''
+import toolset ;
+import errors ;
+
+rule test-rule ( properties * )
+{
+ return <define>TEST_INDIRECT_CONDITIONAL ;
+}
+
+toolset.add-requirements
+ <define>TEST_MACRO
+ <conditional>@test-rule
+ <link>shared:<define>TEST_CONDITIONAL
+;
+
+rule check-requirements ( target : sources * : properties * )
+{
+ local macros = TEST_MACRO TEST_CONDITIONAL TEST_INDIRECT_CONDITIONAL ;
+ for local m in $(macros)
+ {
+ if ! <define>$(m) in $(properties)
+ {
+ errors.error $(m) not defined ;
+ }
+ }
+}
+make test : : @check-requirements ;
+''')
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/transitive_skip.py b/src/boost/tools/build/test/transitive_skip.py
new file mode 100644
index 000000000..85d73de30
--- /dev/null
+++ b/src/boost/tools/build/test/transitive_skip.py
@@ -0,0 +1,30 @@
+#!/usr/bin/python
+
+# Copyright 2021 Dmitry Arkhipov (grisumbras@gmail.com)
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Regression test: it was possible that due to evaluation of conditional
+# requirements, two different values of non-free features were present in a
+# property set.
+
+import BoostBuild
+
+t = BoostBuild.Tester()
+
+t.write("a.cpp", "")
+
+t.write("jamroot.jam", """
+alias dep : : <build>no ;
+make a : dep : maker ;
+
+actions maker
+{
+ this-command-is-unlikely-to-succeed ;
+}
+
+""")
+
+t.run_build_system()
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/tree.py b/src/boost/tools/build/test/tree.py
new file mode 100644
index 000000000..18215d672
--- /dev/null
+++ b/src/boost/tools/build/test/tree.py
@@ -0,0 +1,245 @@
+# Copyright 2003 Dave Abrahams
+# Copyright 2001, 2002 Vladimir Prus
+# Copyright 2012 Jurko Gospodnetic
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+###############################################################################
+#
+# Based in part on an old Subversion tree.py source file (tools for comparing
+# directory trees). See http://subversion.tigris.org for more information.
+#
+# Copyright (c) 2001 Sam Tobin-Hochstadt. All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which you should
+# have received as part of this distribution. The terms are also available at
+# http://subversion.tigris.org/license-1.html. If newer versions of this
+# license are posted there, you may use a newer version instead, at your
+# option.
+#
+###############################################################################
+
+from __future__ import print_function
+
+import os
+import os.path
+import stat
+import sys
+
+
+class TreeNode:
+ """
+ Fundamental data type used to build file system tree structures.
+
+ If CHILDREN is None, then the node represents a file. Otherwise, CHILDREN
+ is a list of the nodes representing that directory's children.
+
+ NAME is simply the name of the file or directory. CONTENTS is a string
+ holding the file's contents (if a file).
+
+ """
+
+ def __init__(self, name, children=None, contents=None):
+ assert children is None or contents is None
+ self.name = name
+ self.mtime = 0
+ self.children = children
+ self.contents = contents
+ self.path = name
+
+ def add_child(self, newchild):
+ assert not self.is_file()
+ for a in self.children:
+ if a.name == newchild.name:
+ if newchild.is_file():
+ a.contents = newchild.contents
+ a.path = os.path.join(self.path, newchild.name)
+ else:
+ for i in newchild.children:
+ a.add_child(i)
+ break
+ else:
+ self.children.append(newchild)
+ newchild.path = os.path.join(self.path, newchild.name)
+
+ def get_child(self, name):
+ """
+ If the given TreeNode directory NODE contains a child named NAME,
+ return the child; else, return None.
+
+ """
+ for n in self.children:
+ if n.name == name:
+ return n
+
+ def is_file(self):
+ return self.children is None
+
+ def pprint(self):
+ print(" * Node name: %s" % self.name)
+ print(" Path: %s" % self.path)
+ print(" Contents: %s" % self.contents)
+ if self.is_file():
+ print(" Children: is a file.")
+ else:
+ print(" Children: %d" % len(self.children))
+
+
+class TreeDifference:
+ def __init__(self):
+ self.added_files = []
+ self.removed_files = []
+ self.modified_files = []
+ self.touched_files = []
+
+ def append(self, other):
+ self.added_files.extend(other.added_files)
+ self.removed_files.extend(other.removed_files)
+ self.modified_files.extend(other.modified_files)
+ self.touched_files.extend(other.touched_files)
+
+ def ignore_directories(self):
+ """Removes directories from our lists of found differences."""
+ not_dir = lambda x : x[-1] != "/"
+ self.added_files = list(filter(not_dir, self.added_files))
+ self.removed_files = list(filter(not_dir, self.removed_files))
+ self.modified_files = list(filter(not_dir, self.modified_files))
+ self.touched_files = list(filter(not_dir, self.touched_files))
+
+ def pprint(self, file=sys.stdout):
+ file.write("Added files : %s\n" % self.added_files)
+ file.write("Removed files : %s\n" % self.removed_files)
+ file.write("Modified files: %s\n" % self.modified_files)
+ file.write("Touched files : %s\n" % self.touched_files)
+
+ def empty(self):
+ return not (self.added_files or self.removed_files or
+ self.modified_files or self.touched_files)
+
+
+def build_tree(path):
+ """
+ Takes PATH as the folder path, walks the file system below that path, and
+ creates a tree structure based on any files and folders found there.
+ Returns the prepared tree structure plus the maximum file modification
+ timestamp under the given folder.
+
+ """
+ return _handle_dir(os.path.normpath(path))
+
+
+def tree_difference(a, b):
+ """Compare TreeNodes A and B, and create a TreeDifference instance."""
+ return _do_tree_difference(a, b, "", True)
+
+
+def _do_tree_difference(a, b, parent_path, root=False):
+ """Internal recursive worker function for tree_difference()."""
+
+ # We do not want to list root node names.
+ if root:
+ assert not parent_path
+ assert not a.is_file()
+ assert not b.is_file()
+ full_path = ""
+ else:
+ assert a.name == b.name
+ full_path = parent_path + a.name
+ result = TreeDifference()
+
+ # A and B are both files.
+ if a.is_file() and b.is_file():
+ if a.contents != b.contents:
+ result.modified_files.append(full_path)
+ elif a.mtime != b.mtime:
+ result.touched_files.append(full_path)
+ return result
+
+ # Directory converted to file.
+ if not a.is_file() and b.is_file():
+ result.removed_files.extend(_traverse_tree(a, parent_path))
+ result.added_files.append(full_path)
+
+ # File converted to directory.
+ elif a.is_file() and not b.is_file():
+ result.removed_files.append(full_path)
+ result.added_files.extend(_traverse_tree(b, parent_path))
+
+ # A and B are both directories.
+ else:
+ if full_path:
+ full_path += "/"
+ accounted_for = [] # Children present in both trees.
+ for a_child in a.children:
+ b_child = b.get_child(a_child.name)
+ if b_child:
+ accounted_for.append(b_child)
+ result.append(_do_tree_difference(a_child, b_child, full_path))
+ else:
+ result.removed_files.append(full_path + a_child.name)
+ for b_child in b.children:
+ if b_child not in accounted_for:
+ result.added_files.extend(_traverse_tree(b_child, full_path))
+
+ return result
+
+
+def _traverse_tree(t, parent_path):
+ """Returns a list of all names in a tree."""
+ assert not parent_path or parent_path[-1] == "/"
+ full_node_name = parent_path + t.name
+ if t.is_file():
+ result = [full_node_name]
+ else:
+ name_prefix = full_node_name + "/"
+ result = [name_prefix]
+ for i in t.children:
+ result.extend(_traverse_tree(i, name_prefix))
+ return result
+
+
+def _get_text(path):
+ """Return a string with the textual contents of a file at PATH."""
+ fp = open(path, 'rb')
+ try:
+ return fp.read()
+ finally:
+ fp.close()
+
+
+def _handle_dir(path):
+ """
+ Main recursive worker function for build_tree(). Returns a newly created
+ tree node representing the given normalized folder path as well as the
+ maximum file/folder modification time detected under the same path.
+
+ """
+ files = []
+ dirs = []
+ node = TreeNode(os.path.basename(path), children=[])
+ max_mtime = node.mtime = os.stat(path).st_mtime
+
+ # List files & folders.
+ for f in os.listdir(path):
+ f = os.path.join(path, f)
+ if os.path.isdir(f):
+ dirs.append(f)
+ elif os.path.isfile(f):
+ files.append(f)
+
+ # Add a child node for each file.
+ for f in files:
+ fcontents = _get_text(f)
+ new_file_node = TreeNode(os.path.basename(f), contents=fcontents)
+ new_file_node.mtime = os.stat(f).st_mtime
+ max_mtime = max(max_mtime, new_file_node.mtime)
+ node.add_child(new_file_node)
+
+ # For each subdir, create a node, walk its tree, add it as a child.
+ for d in dirs:
+ new_dir_node, new_max_mtime = _handle_dir(d)
+ max_mtime = max(max_mtime, new_max_mtime)
+ node.add_child(new_dir_node)
+
+ return node, max_mtime
diff --git a/src/boost/tools/build/test/unit_test.py b/src/boost/tools/build/test/unit_test.py
new file mode 100644
index 000000000..dde205ff9
--- /dev/null
+++ b/src/boost/tools/build/test/unit_test.py
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+# Copyright 2003, 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test the unit_test rule.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+# Create the needed files.
+t.write("jamroot.jam", """
+using testing ;
+lib helper : helper.cpp ;
+unit-test test : test.cpp : <library>helper ;
+""")
+
+t.write("test.cpp", """
+void helper();
+int main() { helper(); }
+""")
+
+t.write("helper.cpp", """
+void
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+helper() {}
+""")
+
+t.run_build_system(["link=static"])
+t.expect_addition("bin/$toolset/debug/link-static*/test.passed")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/unit_tests.py b/src/boost/tools/build/test/unit_tests.py
new file mode 100644
index 000000000..323e31c0e
--- /dev/null
+++ b/src/boost/tools/build/test/unit_tests.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+
+# Copyright 2002, 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(pass_toolset=0)
+t.run_build_system(["--debug", "--build-system=test/test"])
+t.cleanup()
diff --git a/src/boost/tools/build/test/unused.py b/src/boost/tools/build/test/unused.py
new file mode 100644
index 000000000..8599dd3b0
--- /dev/null
+++ b/src/boost/tools/build/test/unused.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+# Copyright 2003 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that unused sources are at least reported.
+
+import BoostBuild
+
+t = BoostBuild.Tester(["-d+2"], use_test_config=False)
+
+t.write("a.cpp", "int main() {}\n")
+t.write("b.cpp", "\n")
+t.write("b.xyz", "")
+t.write("jamroot.jam", """\
+import "class" : new ;
+import modules ;
+import project ;
+import targets ;
+import type ;
+import virtual-target ;
+
+type.register X : xyz ;
+
+class test-target-class : basic-target
+{
+ rule construct ( name : source-targets * : property-set )
+ {
+ local result = [ property-set.empty ] ;
+ if ! [ modules.peek : GENERATE_NOTHING ]
+ {
+ result += [ virtual-target.from-file b.xyz : . : $(self.project) ] ;
+ if ! [ modules.peek : GENERATE_ONLY_UNUSABLE ]
+ {
+ result += [ virtual-target.from-file b.cpp : . : $(self.project)
+ ] ;
+ }
+ }
+ return $(result) ;
+ }
+
+ rule compute-usage-requirements ( rproperties : targets * )
+ {
+ return [ property-set.create <define>FOO ] ;
+ }
+}
+
+rule make-b-main-target
+{
+ local project = [ project.current ] ;
+ targets.main-target-alternative [ new test-target-class b : $(project) ] ;
+}
+
+exe a : a.cpp b c ;
+make-b-main-target ;
+alias c ; # Expands to nothing, intentionally.
+""")
+
+t.run_build_system()
+
+# The second invocation should do nothing, and produce no warning. The previous
+# invocation might have printed executed actions and other things, so it is not
+# easy to check if a warning was issued or not.
+t.run_build_system(stdout="")
+
+t.run_build_system(["-sGENERATE_ONLY_UNUSABLE=1"], stdout="")
+
+# Check that even if main target generates nothing, its usage requirements are
+# still propagated to dependants.
+t.write("a.cpp", """\
+#ifndef FOO
+ #error We refuse to compile without FOO being defined!
+ We_refuse_to_compile_without_FOO_being_defined
+#endif
+int main() {}
+""")
+t.run_build_system(["-sGENERATE_NOTHING=1"])
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/use_requirements.py b/src/boost/tools/build/test/use_requirements.py
new file mode 100644
index 000000000..575a3e5a9
--- /dev/null
+++ b/src/boost/tools/build/test/use_requirements.py
@@ -0,0 +1,283 @@
+#!/usr/bin/python
+
+# Copyright 2003 Dave Abrahams
+# Copyright 2002, 2003, 2004, 2006 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+
+# Test that usage requirements on main targets work (and are propagated all the
+# way up, and not only to direct dependants).
+t.write("jamroot.jam", "")
+
+# Note: 'lib cc ..', not 'lib c'. If using 'lib c: ...' the HP-CXX linker will
+# confuse it with the system C runtime.
+t.write("jamfile.jam", """\
+lib b : b.cpp : <link>shared:<define>SHARED_B : :
+ <define>FOO <link>shared:<define>SHARED_B ;
+lib cc : c.cpp b ;
+exe a : a.cpp cc ;
+""")
+
+t.write("b.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.write("c.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+#endif
+create_lib_please() {}
+""")
+
+t.write("a.cpp", """\
+#ifdef FOO
+void
+# if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+# endif
+foo() {}
+#endif
+int main() { foo(); }
+""")
+
+t.run_build_system()
+t.run_build_system(["--clean"])
+
+
+# Test that use requirements on main target work, when they are referred using
+# 'dependency' features.
+
+t.write("jamfile.jam", """\
+lib b : b.cpp : <link>shared:<define>SHARED_B : : <define>FOO
+ <link>shared:<define>SHARED_B ;
+exe a : a.cpp : <use>b ;
+""")
+
+t.write("b.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.write("a.cpp", """\
+#ifdef FOO
+int main() {}
+#endif
+""")
+
+t.run_build_system()
+t.run_build_system(["--clean"])
+
+
+# Test that usage requirements on a project work.
+t.write("jamfile.jam", "exe a : a.cpp lib//b ;")
+
+t.write("lib/jamfile.jam", """\
+project
+ : requirements <link>shared:<define>SHARED_B
+ : usage-requirements <define>FOO <link>shared:<define>SHARED_B ;
+lib b : b.cpp ;
+""")
+
+t.write("lib/b.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.run_build_system()
+
+
+# Test that use requirements are inherited correctly.
+t.write("jamfile.jam", "exe a : a.cpp lib/1//b ;")
+
+t.write("a.cpp", """\
+#if defined(FOO) && defined(ZOO)
+void foo() {}
+#endif
+int main() { foo(); }
+""")
+
+t.write("lib/jamfile.jam", """\
+project : requirements : usage-requirements <define>FOO ;
+""")
+
+t.write("lib/1/jamfile.jam", """\
+project
+ : requirements <link>shared:<define>SHARED_B
+ : usage-requirements <define>ZOO <link>shared:<define>SHARED_B ;
+lib b : b.cpp ;
+""")
+
+t.write("lib/1/b.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.run_build_system()
+t.run_build_system(["--clean"])
+
+
+# Test that we correctly handle dependency features in usage requirements on
+# target.
+t.write("jamfile.jam", """\
+lib b : b.cpp : <link>shared:<define>SHARED_B : : <define>FOO
+ <link>shared:<define>SHARED_B ;
+
+# Here's the test: we should correctly handle dependency feature and get usage
+# requirements from 'b'.
+lib cc : c.cpp : <link>shared:<define>SHARED_C : : <library>b ;
+
+# This will build only if <define>FOO was propagated from 'c'.
+exe a : a.cpp cc ;
+""")
+
+t.write("a.cpp", """\
+#ifdef FOO
+void
+# if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+# endif
+foo();
+#endif
+
+int main() { foo(); }
+""")
+
+t.write("c.cpp", """\
+int
+#if defined(_WIN32) && defined(SHARED_C)
+__declspec(dllexport)
+#endif
+must_export_something;
+""")
+
+t.run_build_system()
+t.run_build_system(["--clean"])
+
+
+# Test correct handling of dependency features in project requirements.
+t.write("jamfile.jam", "exe a : a.cpp lib1//cc ;")
+
+t.write("lib1/jamfile.jam", """\
+project
+ : requirements <link>shared:<define>SHARED_C
+ : usage-requirements <library>../lib2//b <link>shared:<define>SHARED_C ;
+lib cc : c.cpp ;
+""")
+
+t.write("lib1/c.cpp", """\
+int
+#if defined(_WIN32) && defined(SHARED_C)
+__declspec(dllexport)
+#endif
+must_export_something;
+""")
+
+t.write("lib2/jamfile.jam", """\
+lib b : b.cpp : <link>shared:<define>SHARED_B : : <define>FOO
+ <link>shared:<define>SHARED_B ;
+""")
+
+t.copy("b.cpp", "lib2/b.cpp")
+
+t.run_build_system()
+
+
+# Test that targets listed in dependency features in usage requirements are
+# built with the correct properties.
+t.rm(".")
+
+t.write("jamroot.jam", "")
+t.write("jamfile.jam", """\
+lib main : main.cpp : <use>libs//lib1 : : <library>libs//lib1 ;
+exe hello : hello.cpp main : ;
+""")
+
+t.write("main.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_LIB1)
+__declspec(dllimport)
+#endif
+foo();
+
+int main() { foo(); }
+""")
+
+t.write("hello.cpp", "\n")
+t.write("libs/a.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_LIB1)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+
+# This library should be built with the same properties as 'main'. This is a
+# regression test for a bug when they were generated with empty properties, and
+# there were ambiguities between variants.
+t.write("libs/jamfile.jam", """\
+lib lib1 : a_d.cpp : <variant>debug <link>shared:<define>SHARED_LIB1 : :
+ <link>shared:<define>SHARED_LIB1 ;
+lib lib1 : a.cpp : <variant>release <link>shared:<define>SHARED_LIB1 : :
+ <link>shared:<define>SHARED_LIB1 ;
+""")
+
+t.write("libs/a_d.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_LIB1)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.run_build_system(["link=static"])
+t.expect_addition("libs/bin/$toolset/debug/link-static*/a_d.obj")
+
+
+# Test that indirect conditionals are respected in usage requirements.
+t.rm(".")
+
+t.write("jamroot.jam", """\
+rule has-foo ( properties * ) { return <define>HAS_FOO ; }
+exe a : a.cpp b ;
+lib b : b.cpp : <link>static : : <conditional>@has-foo ;
+""")
+
+t.write("a.cpp", """\
+#ifdef HAS_FOO
+void foo();
+int main() { foo(); }
+#endif
+""")
+
+t.write("b.cpp", """\
+void
+#if defined(_WIN32) && defined(SHARED_B)
+__declspec(dllexport)
+#endif
+foo() {}
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/using.py b/src/boost/tools/build/test/using.py
new file mode 100644
index 000000000..1ee3dffa0
--- /dev/null
+++ b/src/boost/tools/build/test/using.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+# Copyright (C) Vladimir Prus 2005.
+# Distributed under the Boost Software License, Version 1.0. (See
+# accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamroot.jam", "using some_tool ;")
+t.write("some_tool.jam", """\
+import project ;
+project.initialize $(__name__) ;
+rule init ( ) { }
+""")
+
+t.write("some_tool.py", """\
+from b2.manager import get_manager
+get_manager().projects().initialize(__name__)
+def init():
+ pass
+""")
+
+t.write("sub/a.cpp", "int main() {}\n")
+t.write("sub/jamfile.jam", "exe a : a.cpp ;")
+
+t.run_build_system(subdir="sub")
+t.expect_addition("sub/bin/$toolset/debug*/a.exe")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/wrapper.py b/src/boost/tools/build/test/wrapper.py
new file mode 100644
index 000000000..f99e26169
--- /dev/null
+++ b/src/boost/tools/build/test/wrapper.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# Copyright 2004 Vladimir Prus
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Test that the user can define his own rule that will call built-in main target
+# rule and that this will work.
+
+import BoostBuild
+
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("jamfile.jam", """
+my-test : test.cpp ;
+""")
+
+t.write("test.cpp", """
+int main() {}
+""")
+
+t.write("jamroot.jam", """
+using testing ;
+
+rule my-test ( name ? : sources + )
+{
+ name ?= test ;
+ unit-test $(name) : $(sources) ; # /site-config//cppunit /util//testMain ;
+}
+
+IMPORT $(__name__) : my-test : : my-test ;
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/test.passed")
+
+t.cleanup()
diff --git a/src/boost/tools/build/test/wrong_project.py b/src/boost/tools/build/test/wrong_project.py
new file mode 100644
index 000000000..bacd01b70
--- /dev/null
+++ b/src/boost/tools/build/test/wrong_project.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+# Copyright Vladimir Prus 2005.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE.txt or copy at
+# https://www.bfgroup.xyz/b2/LICENSE.txt)
+
+# Regression test. When Jamfile contained "using whatever ; " and the 'whatever'
+# module declared a project, then all targets in Jamfile were considered to be
+# declared in the project associated with 'whatever', not with the Jamfile.
+
+import BoostBuild
+
+t = BoostBuild.Tester(use_test_config=False)
+
+t.write("a.cpp", "int main() {}\n")
+
+t.write("jamroot.jam", """\
+using some_tool ;
+exe a : a.cpp ;
+""")
+
+t.write("some_tool.jam", """\
+import project ;
+project.initialize $(__name__) ;
+rule init ( ) { }
+""")
+
+t.write("some_tool.py", """\
+from b2.manager import get_manager
+get_manager().projects().initialize(__name__)
+def init():
+ pass
+""")
+
+t.run_build_system()
+t.expect_addition("bin/$toolset/debug*/a.exe")
+
+t.cleanup()