[Cmake-commits] CMake branch, master, updated. v3.15.4-1304-g3247506
Kitware Robot
kwrobot at kitware.com
Thu Oct 3 14:11:37 EDT 2019
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".
The branch, master has been updated
via 3247506662684003e19d9552ab5ce70643808f4d (commit)
via e9500271a3acdc0d1ee448cae5912f768491f429 (commit)
via d1f100a41582fecc05c3e04a87c72de94b0e6316 (commit)
via b741ee820d9d5e81a7ec73bda5515465359782af (commit)
via 3c8a5aad46ddbd1d0e4c02f09bd403601c6c94b3 (commit)
via 2d74e54661953a02efa155ffe8c093400e5389c3 (commit)
via e34de0691b3bd94720c44c1efad47c3d39ff4134 (commit)
via aee09648511433160f7fd660eb3c746719810216 (commit)
via c494b2973a1557c3373a2841ba39da6b8d4bdb5a (commit)
via c8f48069430cb286ca593a967e0c3b8e5ea842c4 (commit)
via bb4a1410592342a824b1dd755b7ca8897deac65c (commit)
from d918b4a545fefd1593753189d83ec8590f430579 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3247506662684003e19d9552ab5ce70643808f4d
commit 3247506662684003e19d9552ab5ce70643808f4d
Merge: d918b4a e950027
Author: Brad King <brad.king at kitware.com>
AuthorDate: Thu Oct 3 18:07:37 2019 +0000
Commit: Kitware Robot <kwrobot at kitware.com>
CommitDate: Thu Oct 3 14:07:52 2019 -0400
Merge topic 'ctest-hardware-allocation'
e9500271a3 Help: Add documentation for CTest hardware allocation
d1f100a415 CTest: Add Json output for PROCESSES property
b741ee820d Tests: Add test for CTest hardware allocation feature
3c8a5aad46 Tests: Write tests for cthwalloc helper tool
2d74e54661 Tests: Write cthwalloc helper tool
e34de0691b CTest: Allocate hardware to tests
aee0964851 CTest: Add bin-packing algorithm
c494b2973a CTest: Add cmCTestHardwareAllocator class
...
Acked-by: Kitware Robot <kwrobot at kitware.com>
Merge-request: !3858
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=e9500271a3acdc0d1ee448cae5912f768491f429
commit e9500271a3acdc0d1ee448cae5912f768491f429
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Thu Jul 11 17:14:01 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
Help: Add documentation for CTest hardware allocation
diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst
index 4a69491..0a33da3 100644
--- a/Help/command/ctest_test.rst
+++ b/Help/command/ctest_test.rst
@@ -17,6 +17,7 @@ Perform the :ref:`CTest Test Step` as a :ref:`Dashboard Client`.
[EXCLUDE_FIXTURE_SETUP <regex>]
[EXCLUDE_FIXTURE_CLEANUP <regex>]
[PARALLEL_LEVEL <level>]
+ [HARDWARE_SPEC_FILE <file>]
[TEST_LOAD <threshold>]
[SCHEDULE_RANDOM <ON|OFF>]
[STOP_TIME <time-of-day>]
@@ -82,6 +83,11 @@ The options are:
Specify a positive number representing the number of tests to
be run in parallel.
+``HARDWARE_SPEC_FILE <file>``
+ Specify a
+ :ref:`hardware specification file <ctest-hardware-specification-file>`. See
+ :ref:`ctest-hardware-allocation` for more information.
+
``TEST_LOAD <threshold>``
While running tests in parallel, try not to start tests when they
may cause the CPU load to pass above a given threshold. If not
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 3fe609b..1369aa3 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -414,6 +414,7 @@ Properties on Tests
/prop_test/LABELS
/prop_test/MEASUREMENT
/prop_test/PASS_REGULAR_EXPRESSION
+ /prop_test/PROCESSES
/prop_test/PROCESSOR_AFFINITY
/prop_test/PROCESSORS
/prop_test/REQUIRED_FILES
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 9d93bb8..a18d43f 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -90,6 +90,15 @@ Options
See `Label and Subproject Summary`_.
+``--hardware-spec-file <file>``
+ Run CTest with :ref:`hardware allocation <ctest-hardware-allocation>` enabled,
+ using the
+ :ref:`hardware specification file <ctest-hardware-specification-file>`
+ specified in ``<file>``.
+
+ When ``ctest`` is run as a `Dashboard Client`_ this sets the
+ ``HardwareSpecFile`` option of the `CTest Test Step`_.
+
``--test-load <level>``
While running tests in parallel (e.g. with ``-j``), try not to start
tests when they may cause the CPU load to pass above a given threshold.
@@ -958,6 +967,11 @@ Arguments to the command may specify some of the step settings.
Configuration settings include:
+``HardwareSpecFile``
+ Specify a
+ :ref:`hardware specification file <ctest-hardware-specification-file>`. See
+ :ref:`ctest-hardware-allocation` for more information.
+
``LabelsForSubprojects``
Specify a semicolon-separated list of labels that will be treated as
subprojects. This mapping will be passed on to CDash when configure, test or
@@ -1267,6 +1281,221 @@ model is defined as follows:
Test properties.
Can contain keys for each of the supported test properties.
+.. _`ctest-hardware-allocation`:
+
+Hardware Allocation
+===================
+
+CTest provides a mechanism for tests to specify the hardware that they need and
+how much of it they need, and for users to specify the hardware availiable on
+the running machine. This allows CTest to internally keep track of which
+hardware is in use and which is free, scheduling tests in a way that prevents
+them from trying to claim hardware that is not available.
+
+A common use case for this feature is for tests that require the use of a GPU.
+Multiple tests can simultaneously allocate memory from a GPU, but if too many
+tests try to do this at once, some of them will fail to allocate, resulting in
+a failed test, even though the test would have succeeded if it had the memory
+it needed. By using the hardware allocation feature, each test can specify how
+much memory it requires from a GPU, allowing CTest to schedule tests in a way
+that running several of these tests at once does not exhaust the GPU's memory
+pool.
+
+Please note that CTest has no concept of what a GPU is or how much memory it
+has, nor does it have any way of communicating with a GPU to retrieve this
+information or perform any memory management. CTest simply keeps track of a
+list of abstract resource types, each of which has a certain number of slots
+available for tests to use. Each test specifies the number of slots that it
+requires from a certain resource, and CTest then schedules them in a way that
+prevents the total number of slots in use from exceeding the listed capacity.
+When a test is executed, and slots from a resource are allocated to that test,
+tests may assume that they have exclusive use of those slots for the duration
+of the test's process.
+
+The CTest hardware allocation feature consists of two inputs:
+
+* The :ref:`hardware specification file <ctest-hardware-specification-file>`,
+ described below, which describes the hardware resources available on the
+ system, and
+* The :prop_test:`PROCESSES` property of tests, which describes the resources
+ required by the test
+
+When CTest runs a test, the hardware allocated to that test is passed in the
+form of a set of
+:ref:`environment variables <ctest-hardware-environment-variables>` as
+described below. Using this information to decide which resource to connect to
+is left to the test writer.
+
+Please note that these processes are not spawned by CTest. The ``PROCESSES``
+property merely tells CTest what processes the test expects to launch. It is up
+to the test itself to do this process spawning, and read the :ref:`environment
+variables <ctest-hardware-environment-variables>` to determine which resources
+each process has been allocated.
+
+.. _`ctest-hardware-specification-file`:
+
+Hardware Specification File
+---------------------------
+
+The hardware specification file is a JSON file which is passed to CTest, either
+on the :manual:`ctest(1)` command line as ``--hardware-spec-file``, or as the
+``HARDWARE_SPEC_FILE`` argument of :command:`ctest_test`. The hardware
+specification file must be a JSON object. All examples in this document assume
+the following hardware specification file:
+
+.. code-block:: json
+
+ {
+ "local": [
+ {
+ "gpus": [
+ {
+ "id": "0",
+ "slots": 2
+ },
+ {
+ "id": "1",
+ "slots": 4
+ },
+ {
+ "id": "2",
+ "slots": 2
+ },
+ {
+ "id": "3"
+ }
+ ],
+ "crypto_chips": [
+ {
+ "id": "card0",
+ "slots": 4
+ }
+ ]
+ }
+ ]
+ }
+
+The members are:
+
+``local``
+ A JSON array consisting of CPU sockets present on the system. Currently, only
+ one socket is supported.
+
+ Each socket is a JSON object with members whose names are equal to the
+ desired resource types, such as ``gpu``. These names must start with a
+ lowercase letter or an underscore, and subsequent characters can be a
+ lowercase letter, a digit, or an underscore. Uppercase letters are not
+ allowed, because certain platforms have case-insensitive environment
+ variables. See the `Environment Variables`_ section below for
+ more information. It is recommended that the resource type name be the plural
+ of a noun, such as ``gpus`` or ``crypto_chips`` (and not ``gpu`` or
+ ``crypto_chip``.)
+
+ Please note that the names ``gpus`` and ``crypto_chips`` are just examples,
+ and CTest does not interpret them in any way. You are free to make up any
+ resource type you want to meet your own requirements.
+
+ The value for each resource type is a JSON array consisting of JSON objects,
+ each of which describe a specific instance of the specified resource. These
+ objects have the following members:
+
+ ``id``
+ A string consisting of an identifier for the resource. Each character in
+ the identifier can be a lowercase letter, a digit, or an underscore.
+ Uppercase letters are not allowed.
+
+ Identifiers must be unique within a resource type. However, they do not
+ have to be unique across resource types. For example, it is valid to have a
+ ``gpus`` resource named ``0`` and a ``crypto_chips`` resource named ``0``,
+ but not two ``gpus`` resources both named ``0``.
+
+ Please note that the IDs ``0``, ``1``, ``2``, ``3``, and ``card0`` are just
+ examples, and CTest does not interpret them in any way. You are free to
+ make up any IDs you want to meet your own requirements.
+
+ ``slots``
+ An optional unsigned number specifying the number of slots available on the
+ resource. For example, this could be megabytes of RAM on a GPU, or
+ cryptography units available on a cryptography chip. If ``slots`` is not
+ specified, a default value of ``1`` is assumed.
+
+In the example file above, there are four GPUs with ID's 0 through 3. GPU 0 has
+2 slots, GPU 1 has 4, GPU 2 has 2, and GPU 3 has a default of 1 slot. There is
+also one cryptography chip with 4 slots.
+
+``PROCESSES`` Property
+----------------------
+
+See :prop_test:`PROCESSES` for a description of this property.
+
+.. _`ctest-hardware-environment-variables`:
+
+Environment Variables
+---------------------
+
+Once CTest has decided which resources to allocate to a test, it passes this
+information to the test executable as a series of environment variables. For
+each example below, we will assume that the test in question has a
+:prop_test:`PROCESSES` property of ``2,gpus:2;gpus:4,gpus:1,crypto_chips:2``.
+
+The following variables are passed to the test process:
+
+.. envvar:: CTEST_PROCESS_COUNT
+
+ The total number of processes specified by the :prop_test:`PROCESSES`
+ property. For example:
+
+ * ``CTEST_PROCESS_COUNT=3``
+
+ This variable will only be defined if :manual:`ctest(1)` has been given a
+ ``--hardware-spec-file``, or if :command:`ctest_test` has been given a
+ ``HARDWARE_SPEC_FILE``. If no hardware specification file has been given,
+ this variable will not be defined.
+
+.. envvar:: CTEST_PROCESS_<num>
+
+ The list of resource types allocated to each process, with each item
+ separated by a comma. ``<num>`` is a number from zero to
+ ``CTEST_PROCESS_COUNT`` minus one. ``CTEST_PROCESS_<num>`` is defined for
+ each ``<num>`` in this range. For example:
+
+ * ``CTEST_PROCESS_0=gpus``
+ * ``CTEST_PROCESS_1=gpus``
+ * ``CTEST_PROCESS_2=crypto_chips,gpus``
+
+.. envvar:: CTEST_PROCESS_<num>_<resource-type>
+
+ The list of resource IDs and number of slots from each ID allocated to each
+ process for a given resource type. This variable consists of a series of
+ pairs, each pair separated by a semicolon, and with the two items in the pair
+ separated by a comma. The first item in each pair is ``id:`` followed by the
+ ID of a resource of type ``<resource-type>``, and the second item is
+ ``slots:`` followed by the number of slots from that resource allocated to
+ the given process. For example:
+
+ * ``CTEST_PROCESS_0_GPUS=id:0,slots:2``
+ * ``CTEST_PROCESS_1_GPUS=id:2,slots:2``
+ * ``CTEST_PROCESS_2_GPUS=id:1,slots:4;id:3,slots:1``
+ * ``CTEST_PROCESS_2_CRYPTO_CHIPS=id:card0,slots:2``
+
+ In this example, process 0 gets 2 slots from GPU ``0``, process 1 gets 2 slots
+ from GPU ``2``, and process 2 gets 4 slots from GPU ``1`` and 2 slots from
+ cryptography chip ``card0``.
+
+ ``<num>`` is a number from zero to ``CTEST_PROCESS_COUNT`` minus one.
+ ``<resource-type>`` is the name of a resource type, converted to uppercase.
+ ``CTEST_PROCESS_<num>_<resource-type>`` is defined for the product of each
+ ``<num>`` in the range listed above and each resource type listed in
+ ``CTEST_PROCESS_<num>``.
+
+ Because some platforms have case-insensitive names for environment variables,
+ the names of resource types may not clash in a case-insensitive environment.
+ Because of this, for the sake of simplicity, all resource types must be
+ listed in all lowercase in the
+ :ref:`hardware specification file <ctest-hardware-specification-file>` and in
+ the :prop_test:`PROCESSES` property, and they are converted to all uppercase
+ in the ``CTEST_PROCESS_<num>_<resource-type>`` environment variable.
+
See Also
========
diff --git a/Help/prop_test/PROCESSES.rst b/Help/prop_test/PROCESSES.rst
new file mode 100644
index 0000000..d09c6d1
--- /dev/null
+++ b/Help/prop_test/PROCESSES.rst
@@ -0,0 +1,54 @@
+PROCESSES
+----------
+
+Set to specify the number of processes spawned by a test, and the resources
+that they require. See :ref:`hardware allocation <ctest-hardware-allocation>`
+for more information on how this property integrates into the CTest hardware
+allocation feature.
+
+The ``PROCESSES`` property is a :ref:`semicolon-separated list <CMake Language
+Lists>` of process descriptions. Each process description consists of an
+optional number of processes for the description followed by a series of
+resource requirements for those processes. These requirements (and the number
+of processes) are separated by commas. The resource requirements consist of the
+name of a resource type, followed by a colon, followed by an unsigned integer
+specifying the number of slots required on one resource of the given type.
+
+Please note that these processes are not spawned by CTest. The ``PROCESSES``
+property merely tells CTest what processes the test expects to launch. It is up
+to the test itself to do this process spawning, and read the :ref:`environment
+variables <ctest-hardware-environment-variables>` to determine which resources
+each process has been allocated.
+
+Consider the following example:
+
+.. code-block:: cmake
+
+ add_test(NAME MyTest COMMAND MyExe)
+ set_property(TEST MyTest PROPERTY PROCESSES
+ "2,gpus:2"
+ "gpus:4,crypto_chips:2")
+
+In this example, there are two process descriptions (implicitly separated by a
+semicolon.) The content of the first description is ``2,gpus:2``. This
+description spawns 2 processes, each of which requires 2 slots from a single
+GPU. The content of the second description is ``gpus:4,crypto_chips:2``. This
+description does not specify a process count, so a default of 1 is assumed.
+This single process requires 4 slots from a single GPU and 2 slots from a
+single cryptography chip. In total, 3 processes are spawned from this test,
+each with their own unique requirements.
+
+When CTest sets the :ref:`environment variables
+<ctest-hardware-environment-variables>` for a test, it assigns a process number
+based on the process description, starting at 0 on the left and the number of
+processes minus 1 on the right. For example, in the example above, the two
+processes in the first description would have IDs of 0 and 1, and the single
+process in the second description would have an ID of 2.
+
+Both the ``PROCESSES`` and :prop_test:`RESOURCE_LOCK` properties serve similar
+purposes, but they are distinct and orthogonal. Resources specified by
+``PROCESSES`` do not affect :prop_test:`RESOURCE_LOCK`, and vice versa. Whereas
+:prop_test:`RESOURCE_LOCK` is a simpler property that is used for locking one
+global resource, ``PROCESSES`` is a more advanced property that allows multiple
+tests to simultaneously use multiple resources of the same type, specifying
+their requirements in a fine-grained manner.
diff --git a/Help/prop_test/RESOURCE_LOCK.rst b/Help/prop_test/RESOURCE_LOCK.rst
index 755e0aa..7d61f77 100644
--- a/Help/prop_test/RESOURCE_LOCK.rst
+++ b/Help/prop_test/RESOURCE_LOCK.rst
@@ -8,3 +8,11 @@ not to run concurrently.
See also :prop_test:`FIXTURES_REQUIRED` if the resource requires any setup or
cleanup steps.
+
+Both the :prop_test:`PROCESSES` and ``RESOURCE_LOCK`` properties serve similar
+purposes, but they are distinct and orthogonal. Resources specified by
+:prop_test:`PROCESSES` do not affect ``RESOURCE_LOCK``, and vice versa. Whereas
+``RESOURCE_LOCK`` is a simpler property that is used for locking one global
+resource, :prop_test:`PROCESSES` is a more advanced property that allows
+multiple tests to simultaneously use multiple resources of the same type,
+specifying their requirements in a fine-grained manner.
diff --git a/Help/release/dev/ctest-hardware-allocation.rst b/Help/release/dev/ctest-hardware-allocation.rst
new file mode 100644
index 0000000..875dbdc
--- /dev/null
+++ b/Help/release/dev/ctest-hardware-allocation.rst
@@ -0,0 +1,6 @@
+ctest-hardware-allocation
+-------------------------
+
+* :manual:`ctest(1)` now has the ability to serialize tests based on hardware
+ requirements for each test. See :ref:`ctest-hardware-allocation` for
+ details.
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=d1f100a41582fecc05c3e04a87c72de94b0e6316
commit d1f100a41582fecc05c3e04a87c72de94b0e6316
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Wed Jul 24 10:56:05 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
CTest: Add Json output for PROCESSES property
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index aee6d67..7e8d548 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -912,6 +912,28 @@ static Json::Value DumpTimeoutAfterMatch(
return timeoutAfterMatch;
}
+static Json::Value DumpProcessesToJsonArray(
+ const std::vector<
+ std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+ processes)
+{
+ Json::Value jsonProcesses = Json::arrayValue;
+ for (auto const& it : processes) {
+ Json::Value jsonProcess = Json::objectValue;
+ Json::Value requirements = Json::arrayValue;
+ for (auto const& it2 : it) {
+ Json::Value res = Json::objectValue;
+ res[".type"] = it2.ResourceType;
+ // res[".units"] = it2.UnitsNeeded; // Intentionally commented out
+ res["slots"] = it2.SlotsNeeded;
+ requirements.append(res);
+ }
+ jsonProcess["requirements"] = requirements;
+ jsonProcesses.append(jsonProcess);
+ }
+ return jsonProcesses;
+}
+
static Json::Value DumpCTestProperty(std::string const& name,
Json::Value value)
{
@@ -983,6 +1005,10 @@ static Json::Value DumpCTestProperties(
"PASS_REGULAR_EXPRESSION",
DumpRegExToJsonArray(testProperties.RequiredRegularExpressions)));
}
+ if (!testProperties.Processes.empty()) {
+ properties.append(DumpCTestProperty(
+ "PROCESSES", DumpProcessesToJsonArray(testProperties.Processes)));
+ }
if (testProperties.WantAffinity) {
properties.append(
DumpCTestProperty("PROCESSOR_AFFINITY", testProperties.WantAffinity));
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
index ca2975a..fd2c97f 100644
--- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -258,6 +258,7 @@ function(run_ShowOnly)
add_test(ShowOnly \"${CMAKE_COMMAND}\" -E echo)
set_tests_properties(ShowOnly PROPERTIES
WILL_FAIL true
+ PROCESSES \"2,threads:2,gpus:4;gpus:2,threads:4\"
REQUIRED_FILES RequiredFileDoesNotExist
_BACKTRACE_TRIPLES \"file1;1;add_test;file0;;\"
)
diff --git a/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py b/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
index 3ad5768..6eb8624 100644
--- a/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
+++ b/Tests/RunCMake/CTestCommandLine/show-only_json-v1_check.py
@@ -80,6 +80,62 @@ def check_willfail_property(p):
assert p["name"] == "WILL_FAIL"
assert p["value"] == True
+def check_processes_property(p):
+ assert is_dict(p)
+ assert sorted(p.keys()) == ["name", "value"]
+ assert is_string(p["name"])
+ assert is_list(p["value"])
+ assert p["name"] == "PROCESSES"
+ assert len(p["value"]) == 3
+
+ assert is_dict(p["value"][0])
+ assert sorted(p["value"][0].keys()) == ["requirements"]
+ assert is_list(p["value"][0]["requirements"])
+ assert len(p["value"][0]["requirements"]) == 2
+ assert is_dict(p["value"][0]["requirements"][0])
+ assert sorted(p["value"][0]["requirements"][0].keys()) == \
+ [".type", "slots"]
+ assert is_string(p["value"][0]["requirements"][0][".type"])
+ assert p["value"][0]["requirements"][0][".type"] == "threads"
+ assert is_int(p["value"][0]["requirements"][0]["slots"])
+ assert p["value"][0]["requirements"][0]["slots"] == 2
+ assert is_string(p["value"][0]["requirements"][1][".type"])
+ assert p["value"][0]["requirements"][1][".type"] == "gpus"
+ assert is_int(p["value"][0]["requirements"][1]["slots"])
+ assert p["value"][0]["requirements"][1]["slots"] == 4
+
+ assert is_dict(p["value"][1])
+ assert sorted(p["value"][1].keys()) == ["requirements"]
+ assert is_list(p["value"][1]["requirements"])
+ assert len(p["value"][1]["requirements"]) == 2
+ assert is_dict(p["value"][1]["requirements"][0])
+ assert sorted(p["value"][1]["requirements"][0].keys()) == \
+ [".type", "slots"]
+ assert is_string(p["value"][1]["requirements"][0][".type"])
+ assert p["value"][1]["requirements"][0][".type"] == "threads"
+ assert is_int(p["value"][1]["requirements"][0]["slots"])
+ assert p["value"][1]["requirements"][0]["slots"] == 2
+ assert is_string(p["value"][1]["requirements"][1][".type"])
+ assert p["value"][1]["requirements"][1][".type"] == "gpus"
+ assert is_int(p["value"][1]["requirements"][1]["slots"])
+ assert p["value"][1]["requirements"][1]["slots"] == 4
+
+ assert is_dict(p["value"][2])
+ assert sorted(p["value"][2].keys()) == ["requirements"]
+ assert is_list(p["value"][2]["requirements"])
+ assert len(p["value"][2]["requirements"]) == 2
+ assert is_dict(p["value"][2]["requirements"][0])
+ assert sorted(p["value"][2]["requirements"][0].keys()) == \
+ [".type", "slots"]
+ assert is_string(p["value"][2]["requirements"][0][".type"])
+ assert p["value"][2]["requirements"][0][".type"] == "gpus"
+ assert is_int(p["value"][2]["requirements"][0]["slots"])
+ assert p["value"][2]["requirements"][0]["slots"] == 2
+ assert is_string(p["value"][2]["requirements"][1][".type"])
+ assert p["value"][2]["requirements"][1][".type"] == "threads"
+ assert is_int(p["value"][2]["requirements"][1]["slots"])
+ assert p["value"][2]["requirements"][1]["slots"] == 4
+
def check_workingdir_property(p):
assert is_dict(p)
assert sorted(p.keys()) == ["name", "value"]
@@ -90,10 +146,11 @@ def check_workingdir_property(p):
def check_properties(p):
assert is_list(p)
- assert len(p) == 3
- check_reqfiles_property(p[0])
- check_willfail_property(p[1])
- check_workingdir_property(p[2])
+ assert len(p) == 4
+ check_processes_property(p[0])
+ check_reqfiles_property(p[1])
+ check_willfail_property(p[2])
+ check_workingdir_property(p[3])
def check_tests(t):
assert is_list(t)
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=b741ee820d9d5e81a7ec73bda5515465359782af
commit b741ee820d9d5e81a7ec73bda5515465359782af
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Wed Jul 10 16:33:21 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
Tests: Add test for CTest hardware allocation feature
diff --git a/Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in b/Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in
new file mode 100644
index 0000000..d6cff63
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.15)
+set(CASE_NAME "@CASE_NAME@")
+if(CASE_NAME MATCHES "^(.*)-ctest-s")
+ set(projname "${CMAKE_MATCH_1}")
+ project(${projname} NONE)
+ include(CTest)
+ include("@RunCMake_SOURCE_DIR@/HardwareCommon.cmake")
+ include("@RunCMake_SOURCE_DIR@/${projname}.cmake")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake b/Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake
new file mode 100644
index 0000000..3893d40
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake
@@ -0,0 +1,23 @@
+function(setup_hardware_tests)
+ if(CTEST_HARDWARE_ALLOC_ENABLED)
+ add_test(NAME HardwareSetup COMMAND "${CMAKE_COMMAND}" -E remove -f "${CMAKE_BINARY_DIR}/cthwalloc.log")
+ endif()
+endfunction()
+
+function(add_hardware_test name sleep_time proc)
+ if(CTEST_HARDWARE_ALLOC_ENABLED)
+ add_test(NAME "${name}" COMMAND "${CTHWALLOC_COMMAND}" write "${CMAKE_BINARY_DIR}/cthwalloc.log" "${name}" "${sleep_time}" "${proc}")
+ set_property(TEST "${name}" PROPERTY DEPENDS HardwareSetup)
+ else()
+ add_test(NAME "${name}" COMMAND "${CTHWALLOC_COMMAND}" write "${CMAKE_BINARY_DIR}/cthwalloc.log" "${name}" "${sleep_time}")
+ endif()
+ set_property(TEST "${name}" PROPERTY PROCESSES "${proc}")
+ list(APPEND HARDWARE_TESTS "${name}")
+ set(HARDWARE_TESTS "${HARDWARE_TESTS}" PARENT_SCOPE)
+endfunction()
+
+function(cleanup_hardware_tests)
+ if(CTEST_HARDWARE_ALLOC_ENABLED)
+ file(WRITE "${CMAKE_BINARY_DIR}/hwtests.txt" "${HARDWARE_TESTS}")
+ endif()
+endfunction()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake b/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
index aa249d4..d666922 100644
--- a/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+include(RunCTest)
###############################################################################
# Test cthwalloc itself - we want to make sure it's not just rubber-stamping
@@ -47,7 +48,7 @@ alloc widgets 0 1
dealloc widgets 0 1
end test1
]])
-run_cthwalloc_write_proc_nodel(cthwalloc-write-proc-good1 "1,widgets=2,transmogrifiers=1;2,widgets=1,widgets=2"
+run_cthwalloc_write_proc_nodel(cthwalloc-write-proc-good1 "1,widgets:2,transmogrifiers:1;2,widgets:1,widgets:2"
CTEST_PROCESS_COUNT=3
CTEST_PROCESS_0=widgets,transmogrifiers
CTEST_PROCESS_0_WIDGETS=id:0,slots:2
@@ -58,57 +59,57 @@ run_cthwalloc_write_proc_nodel(cthwalloc-write-proc-good1 "1,widgets=2,transmogr
"CTEST_PROCESS_2_WIDGETS=id:0,slots:1\\;id:2,slots:2"
)
set(RunCMake_TEST_NO_CLEAN 0)
-run_cthwalloc_write_proc(cthwalloc-write-proc-good2 "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-good2 "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
CTEST_PROCESS_0_WIDGETS=id:3,slots:8
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-nocount "widgets=8")
-run_cthwalloc_write_proc(cthwalloc-write-proc-badcount "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-nocount "widgets:8")
+run_cthwalloc_write_proc(cthwalloc-write-proc-badcount "widgets:8"
CTEST_PROCESS_COUNT=2
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-nores "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-nores "widgets:8"
CTEST_PROCESS_COUNT=1
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badres "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badres "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets,transmogrifiers
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-nowidgets "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-nowidgets "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets1 "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets1 "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
CTEST_PROCESS_0_WIDGETS=
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets2 "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets2 "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
"CTEST_PROCESS_0_WIDGETS=id:3,slots:8\\;id:0,slots:1"
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets3 "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets3 "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
CTEST_PROCESS_0_WIDGETS=id:3,slots:7
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets4 "widgets=8"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets4 "widgets:8"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
CTEST_PROCESS_0_WIDGETS=invalid
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets5 "widgets=2,widgets=2"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets5 "widgets:2,widgets:2"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
"CTEST_PROCESS_0_WIDGETS=id:0,slots:2\\;id:0,slots:1"
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets6 "widgets=2"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets6 "widgets:2"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
"CTEST_PROCESS_0_WIDGETS=id:0,slots:2\\;id:0,slots:1"
)
-run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets7 "widgets=2,widgets=2"
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets7 "widgets:2,widgets:2"
CTEST_PROCESS_COUNT=1
CTEST_PROCESS_0=widgets
CTEST_PROCESS_0_WIDGETS=id:0,slots:2
@@ -134,3 +135,33 @@ run_cthwalloc_verify(cthwalloc-verify-badtest4 "test1")
run_cthwalloc_verify(cthwalloc-verify-badtest5 "test1")
run_cthwalloc_verify(cthwalloc-verify-nobegin "test1")
run_cthwalloc_verify(cthwalloc-verify-noend "test1")
+
+###############################################################################
+# Now test the hardware allocation feature of CTest
+###############################################################################
+
+function(run_ctest_hardware name parallel random)
+ run_ctest("${name}-ctest-s-hw" "-DCTEST_HARDWARE_ALLOC_ENABLED=1" "-DCTHWALLOC_COMMAND=${CTHWALLOC_COMMAND}" "-DCTEST_PARALLEL=${parallel}" "-DCTEST_RANDOM=${random}")
+ run_ctest("${name}-ctest-s-nohw" "-DCTEST_HARDWARE_ALLOC_ENABLED=0" "-DCTHWALLOC_COMMAND=${CTHWALLOC_COMMAND}" "-DCTEST_PARALLEL=${parallel}" "-DCTEST_RANDOM=${random}")
+endfunction()
+
+function(verify_ctest_hardware)
+ file(READ "${RunCMake_TEST_BINARY_DIR}/hwtests.txt" hwtests)
+ execute_process(COMMAND "${CTHWALLOC_COMMAND}" verify "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log" "${CMAKE_CURRENT_LIST_DIR}/hwspec.json" "${hwtests}"
+ OUTPUT_VARIABLE output ERROR_QUIET RESULT_VARIABLE result)
+ if(result)
+ string(APPEND RunCMake_TEST_FAILED "${output}")
+ set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+run_ctest_hardware(lotsoftests 10 1)
+run_ctest_hardware(checkfree1 2 0)
+run_ctest_hardware(checkfree2 1 0)
+run_ctest_hardware(notenough1 1 0)
+run_ctest_hardware(notenough2 1 0)
+run_ctest_hardware(ensure_parallel 2 0)
+
+set(ENV{CTEST_PROCESS_COUNT} 2)
+run_ctest_hardware(process_count 1 0)
+unset(ENV{CTEST_PROCESS_COUNT})
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake
new file mode 100644
index 0000000..0e997b5
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake
@@ -0,0 +1,7 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "widgets:8")
+add_hardware_test(Test2 1 "fluxcapacitors:50;fluxcapacitors:50,widgets:8")
+add_hardware_test(Test3 1 "fluxcapacitors:121")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake b/Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake
new file mode 100644
index 0000000..3c2b666
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake
@@ -0,0 +1,8 @@
+setup_hardware_tests()
+
+# This test is an attack on the hardware scheduling algorithm. It has been
+# carefully crafted to fool the algorithm into thinking there isn't sufficient
+# hardware for it.
+add_hardware_test(Test1 1 "widgets:2;4,widgets:4")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..e5f6828
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake
@@ -0,0 +1,16 @@
+verify_ctest_hardware()
+
+set(expected_contents [[
+begin Test1
+alloc transmogrifiers calvin 2
+begin Test2
+alloc transmogrifiers hobbes 2
+dealloc transmogrifiers calvin 2
+end Test1
+dealloc transmogrifiers hobbes 2
+end Test2
+]])
+file(READ "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log" actual_contents)
+if(NOT actual_contents STREQUAL expected_contents)
+ string(APPEND RunCMake_TEST_FAILED "cthwalloc.log contents did not match expected\n")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake
new file mode 100644
index 0000000..1dafb8f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake
@@ -0,0 +1,11 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 4 "transmogrifiers:2")
+
+# Mitigate possible race conditions to ensure that the events are logged in the
+# exact order we want
+add_test(NAME Test2Sleep COMMAND "${CMAKE_COMMAND}" -E sleep 2)
+add_hardware_test(Test2 4 "transmogrifiers:2")
+set_property(TEST Test2 APPEND PROPERTY DEPENDS Test2Sleep)
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/hwspec.json b/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
index bf8daf6..c67fcca 100644
--- a/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
+++ b/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
@@ -4,51 +4,50 @@
"widgets": [
{
"id": "0",
- "capacity": 4
+ "slots": 4
},
{
"id": "1",
- "capacity": 2
+ "slots": 2
},
{
"id": "2",
- "capacity": 4
+ "slots": 4
},
{
"id": "3",
- "capacity": 8
+ "slots": 8
},
{
"id": "4",
- "capacity": 1
+ "slots": 1
},
{
"id": "5",
- "capacity": 1
+ "slots": 1
},
{
"id": "6",
- "capacity": 1
+ "slots": 1
},
{
- "id": "7",
- "capacity": 1
+ "id": "7"
}
],
"transmogrifiers": [
{
"id": "calvin",
- "capacity": 2
+ "slots": 2
},
{
"id": "hobbes",
- "capacity": 2
+ "slots": 2
}
],
"fluxcapacitors": [
{
"id": "outatime",
- "capacity": 121
+ "slots": 121
}
]
}
diff --git a/Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake
new file mode 100644
index 0000000..c684434
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake
@@ -0,0 +1,16 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 2 "widgets:8;2,widgets:2")
+add_hardware_test(Test2 5 "fluxcapacitors:40")
+add_hardware_test(Test3 1 "10,widgets:1,fluxcapacitors:2")
+add_hardware_test(Test4 4 "fluxcapacitors:121")
+
+foreach(i RANGE 5 50)
+ add_hardware_test(Test${i} 1 "2,widgets:1")
+endforeach()
+
+foreach(i RANGE 51 100)
+ add_hardware_test(Test${i} 1 "2,transmogrifiers:2")
+endforeach()
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..9c730be
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake
@@ -0,0 +1,3 @@
+if(EXISTS "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log")
+ set(RunCMake_TEST_FAILED "cthwalloc.log should not exist")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt
new file mode 100644
index 0000000..d465cd3
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt
@@ -0,0 +1,4 @@
+^Insufficient hardware
+CMake Error at [^
+]*/Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw/test\.cmake:[0-9]+ \(message\):
+ Tests did not pass$
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake
new file mode 100644
index 0000000..3e1f620
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake
@@ -0,0 +1,5 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "fluxcapacitors:200")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..9c730be
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake
@@ -0,0 +1,3 @@
+if(EXISTS "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log")
+ set(RunCMake_TEST_FAILED "cthwalloc.log should not exist")
+endif()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt
new file mode 100644
index 0000000..912f0fb
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt
@@ -0,0 +1,4 @@
+^Insufficient hardware
+CMake Error at [^
+]*/Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw/test\.cmake:[0-9]+ \(message\):
+ Tests did not pass$
diff --git a/Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake b/Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake
new file mode 100644
index 0000000..8205c95
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake
@@ -0,0 +1,5 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "terminators:2")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake
new file mode 100644
index 0000000..94b1fa7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake
@@ -0,0 +1 @@
+verify_ctest_hardware()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/process_count.cmake b/Tests/RunCMake/CTestHardwareAllocation/process_count.cmake
new file mode 100644
index 0000000..c969648
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/process_count.cmake
@@ -0,0 +1,5 @@
+setup_hardware_tests()
+
+add_hardware_test(Test1 1 "widgets:1")
+
+cleanup_hardware_tests()
diff --git a/Tests/RunCMake/CTestHardwareAllocation/test.cmake.in b/Tests/RunCMake/CTestHardwareAllocation/test.cmake.in
new file mode 100644
index 0000000..5ba3587
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/test.cmake.in
@@ -0,0 +1,23 @@
+set(CTEST_SITE "test-site")
+set(CTEST_BUILD_NAME "test-build-name")
+set(CTEST_SOURCE_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY "@RunCMake_BINARY_DIR@/@CASE_NAME at -build")
+set(CTEST_CMAKE_GENERATOR "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}")
+set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
+
+ctest_start(Experimental QUIET)
+ctest_configure(OPTIONS
+ "-DCTEST_HARDWARE_ALLOC_ENABLED=${CTEST_HARDWARE_ALLOC_ENABLED};-DCTHWALLOC_COMMAND=${CTHWALLOC_COMMAND}"
+ )
+ctest_build()
+
+if(CTEST_HARDWARE_ALLOC_ENABLED)
+ set(hwspec HARDWARE_SPEC_FILE "@RunCMake_SOURCE_DIR@/hwspec.json")
+endif()
+ctest_test(${hwspec} RETURN_VALUE retval PARALLEL_LEVEL ${CTEST_PARALLEL} SCHEDULE_RANDOM ${CTEST_RANDOM})
+if(retval)
+ message(FATAL_ERROR "Tests did not pass")
+endif()
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3c8a5aad46ddbd1d0e4c02f09bd403601c6c94b3
commit 3c8a5aad46ddbd1d0e4c02f09bd403601c6c94b3
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Wed Jul 10 15:18:37 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
Tests: Write tests for cthwalloc helper tool
This helper tool is sufficiently complex that it warrants having its
own test suite.
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 45c549b..0925c0e 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -367,6 +367,12 @@ else()
set_property(TARGET cthwalloc PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMake_BIN_DIR})
endif()
+if(TARGET cthwalloc)
+ add_RunCMake_test(CTestHardwareAllocation -DCTHWALLOC_COMMAND=$<TARGET_FILE:cthwalloc>)
+else()
+ message(WARNING "Could not find or build cthwalloc")
+endif()
+
find_package(Qt4 QUIET)
find_package(Qt5Core QUIET)
if (QT4_FOUND AND Qt5Core_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.1.0)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake b/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
new file mode 100644
index 0000000..aa249d4
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
@@ -0,0 +1,136 @@
+include(RunCMake)
+
+###############################################################################
+# Test cthwalloc itself - we want to make sure it's not just rubber-stamping
+# the test results
+###############################################################################
+
+function(cthwalloc_verify_log expected_contents)
+ if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log")
+ string(APPEND RunCMake_TEST_FAILED "Log file was not written\n")
+ set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+ return()
+ endif()
+ file(READ "${RunCMake_TEST_BINARY_DIR}/cthwalloc.log" actual_contents)
+ if(NOT actual_contents STREQUAL expected_contents)
+ string(APPEND RunCMake_TEST_FAILED "Actual log did not match expected log\n")
+ set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(run_cthwalloc_write_proc name proc)
+ file(REMOVE "${RunCMake_BINARY_DIR}/${name}-build/cthwalloc.log")
+ run_cthwalloc_write_proc_nodel("${name}" "${proc}" "${ARGN}")
+endfunction()
+
+function(run_cthwalloc_write_proc_nodel name proc)
+ string(REPLACE ";" "\\;" proc "${proc}")
+ run_cmake_command(${name} "${CMAKE_COMMAND}" -E env "${ARGN}" "${CTHWALLOC_COMMAND}" write "${RunCMake_BINARY_DIR}/${name}-build/cthwalloc.log" "${name}" 0 "${proc}")
+endfunction()
+
+function(run_cthwalloc_write_noproc name)
+ run_cmake_command(${name} "${CMAKE_COMMAND}" -E env "${ARGN}" "${CTHWALLOC_COMMAND}" write "${RunCMake_BINARY_DIR}/${name}-build/cthwalloc.log" "${name}" 0)
+endfunction()
+
+function(run_cthwalloc_verify name tests)
+ string(REPLACE ";" "\\;" tests "${tests}")
+ run_cmake_command(${name} "${CTHWALLOC_COMMAND}" verify "${RunCMake_SOURCE_DIR}/${name}.log" "${CMAKE_CURRENT_LIST_DIR}/hwspec.json" "${tests}")
+endfunction()
+
+unset(ENV{CTEST_PROCESS_COUNT})
+set(RunCMake_TEST_NO_CLEAN 1)
+file(REMOVE_RECURSE "${RunCMake_BINARY_DIR}/cthwalloc-write-proc-good1-build")
+file(MAKE_DIRECTORY "${RunCMake_BINARY_DIR}/cthwalloc-write-proc-good1-build")
+file(WRITE "${RunCMake_BINARY_DIR}/cthwalloc-write-proc-good1-build/cthwalloc.log"
+[[begin test1
+alloc widgets 0 1
+dealloc widgets 0 1
+end test1
+]])
+run_cthwalloc_write_proc_nodel(cthwalloc-write-proc-good1 "1,widgets=2,transmogrifiers=1;2,widgets=1,widgets=2"
+ CTEST_PROCESS_COUNT=3
+ CTEST_PROCESS_0=widgets,transmogrifiers
+ CTEST_PROCESS_0_WIDGETS=id:0,slots:2
+ CTEST_PROCESS_0_TRANSMOGRIFIERS=id:calvin,slots:1
+ CTEST_PROCESS_1=widgets
+ "CTEST_PROCESS_1_WIDGETS=id:0,slots:1\\;id:2,slots:2"
+ CTEST_PROCESS_2=widgets
+ "CTEST_PROCESS_2_WIDGETS=id:0,slots:1\\;id:2,slots:2"
+ )
+set(RunCMake_TEST_NO_CLEAN 0)
+run_cthwalloc_write_proc(cthwalloc-write-proc-good2 "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ CTEST_PROCESS_0_WIDGETS=id:3,slots:8
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-nocount "widgets=8")
+run_cthwalloc_write_proc(cthwalloc-write-proc-badcount "widgets=8"
+ CTEST_PROCESS_COUNT=2
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-nores "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badres "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets,transmogrifiers
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-nowidgets "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets1 "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ CTEST_PROCESS_0_WIDGETS=
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets2 "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ "CTEST_PROCESS_0_WIDGETS=id:3,slots:8\\;id:0,slots:1"
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets3 "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ CTEST_PROCESS_0_WIDGETS=id:3,slots:7
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets4 "widgets=8"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ CTEST_PROCESS_0_WIDGETS=invalid
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets5 "widgets=2,widgets=2"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ "CTEST_PROCESS_0_WIDGETS=id:0,slots:2\\;id:0,slots:1"
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets6 "widgets=2"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ "CTEST_PROCESS_0_WIDGETS=id:0,slots:2\\;id:0,slots:1"
+ )
+run_cthwalloc_write_proc(cthwalloc-write-proc-badwidgets7 "widgets=2,widgets=2"
+ CTEST_PROCESS_COUNT=1
+ CTEST_PROCESS_0=widgets
+ CTEST_PROCESS_0_WIDGETS=id:0,slots:2
+ )
+
+run_cthwalloc_write_noproc(cthwalloc-write-noproc-good1)
+run_cthwalloc_write_noproc(cthwalloc-write-noproc-count
+ CTEST_PROCESS_COUNT=1
+ )
+
+run_cthwalloc_verify(cthwalloc-verify-good1 "test1;test2")
+run_cthwalloc_verify(cthwalloc-verify-good2 "")
+run_cthwalloc_verify(cthwalloc-verify-nolog "")
+run_cthwalloc_verify(cthwalloc-verify-nores "")
+run_cthwalloc_verify(cthwalloc-verify-noid "")
+run_cthwalloc_verify(cthwalloc-verify-notenough "")
+run_cthwalloc_verify(cthwalloc-verify-baddealloc "")
+run_cthwalloc_verify(cthwalloc-verify-leak "")
+run_cthwalloc_verify(cthwalloc-verify-badtest1 "")
+run_cthwalloc_verify(cthwalloc-verify-badtest2 "test1")
+run_cthwalloc_verify(cthwalloc-verify-badtest3 "test1")
+run_cthwalloc_verify(cthwalloc-verify-badtest4 "test1")
+run_cthwalloc_verify(cthwalloc-verify-badtest5 "test1")
+run_cthwalloc_verify(cthwalloc-verify-nobegin "test1")
+run_cthwalloc_verify(cthwalloc-verify-noend "test1")
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log
new file mode 100644
index 0000000..abd6bad
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log
@@ -0,0 +1,2 @@
+alloc widgets 0 1
+dealloc widgets 0 2
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log
new file mode 100644
index 0000000..605104b
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log
@@ -0,0 +1 @@
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log
new file mode 100644
index 0000000..1ff1b0d
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log
@@ -0,0 +1,2 @@
+begin test1
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log
new file mode 100644
index 0000000..1925e6a
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log
@@ -0,0 +1,3 @@
+begin test1
+end test1
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log
new file mode 100644
index 0000000..3fe7da1
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log
@@ -0,0 +1,3 @@
+begin test1
+end test1
+end test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log
new file mode 100644
index 0000000..3a2e7e3
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log
@@ -0,0 +1 @@
+end test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log
new file mode 100644
index 0000000..2cca0c3
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log
@@ -0,0 +1,14 @@
+begin test1
+alloc widgets 3 4
+alloc widgets 4 1
+alloc transmogrifiers calvin 2
+alloc fluxcapacitors outatime 121
+begin test2
+alloc widgets 3 4
+dealloc widgets 3 4
+dealloc widgets 4 1
+dealloc transmogrifiers calvin 2
+dealloc fluxcapacitors outatime 121
+end test1
+dealloc widgets 3 4
+end test2
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good2.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good2.log
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log
new file mode 100644
index 0000000..b900d86
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log
@@ -0,0 +1 @@
+alloc widgets 0 1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin.log
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log
new file mode 100644
index 0000000..605104b
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log
@@ -0,0 +1 @@
+begin test1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log
new file mode 100644
index 0000000..c718975
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log
@@ -0,0 +1,2 @@
+alloc fluxcapacitors train 1
+dealloc fluxcapacitors train 1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log
new file mode 100644
index 0000000..a18202b
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log
@@ -0,0 +1,2 @@
+alloc gpus 0 1
+dealloc gpus 0 1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log
new file mode 100644
index 0000000..ac78d5a
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log
@@ -0,0 +1,2 @@
+alloc widgets 0 8
+dealloc widgets 0 8
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake
new file mode 100644
index 0000000..949d2d7
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake
@@ -0,0 +1,20 @@
+cthwalloc_verify_log(
+[[begin test1
+alloc widgets 0 1
+dealloc widgets 0 1
+end test1
+begin cthwalloc-write-proc-good1
+alloc transmogrifiers calvin 1
+alloc widgets 0 2
+alloc widgets 0 1
+alloc widgets 2 2
+alloc widgets 0 1
+alloc widgets 2 2
+dealloc transmogrifiers calvin 1
+dealloc widgets 0 2
+dealloc widgets 0 1
+dealloc widgets 2 2
+dealloc widgets 0 1
+dealloc widgets 2 2
+end cthwalloc-write-proc-good1
+]])
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake
new file mode 100644
index 0000000..ca0c6b8
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake
@@ -0,0 +1,6 @@
+cthwalloc_verify_log(
+[[begin cthwalloc-write-proc-good2
+alloc widgets 3 8
+dealloc widgets 3 8
+end cthwalloc-write-proc-good2
+]])
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTestHardwareAllocation/hwspec.json b/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
new file mode 100644
index 0000000..bf8daf6
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/hwspec.json
@@ -0,0 +1,56 @@
+{
+ "local": [
+ {
+ "widgets": [
+ {
+ "id": "0",
+ "capacity": 4
+ },
+ {
+ "id": "1",
+ "capacity": 2
+ },
+ {
+ "id": "2",
+ "capacity": 4
+ },
+ {
+ "id": "3",
+ "capacity": 8
+ },
+ {
+ "id": "4",
+ "capacity": 1
+ },
+ {
+ "id": "5",
+ "capacity": 1
+ },
+ {
+ "id": "6",
+ "capacity": 1
+ },
+ {
+ "id": "7",
+ "capacity": 1
+ }
+ ],
+ "transmogrifiers": [
+ {
+ "id": "calvin",
+ "capacity": 2
+ },
+ {
+ "id": "hobbes",
+ "capacity": 2
+ }
+ ],
+ "fluxcapacitors": [
+ {
+ "id": "outatime",
+ "capacity": 121
+ }
+ ]
+ }
+ ]
+}
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=2d74e54661953a02efa155ffe8c093400e5389c3
commit 2d74e54661953a02efa155ffe8c093400e5389c3
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Tue Jul 9 18:33:49 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
Tests: Write cthwalloc helper tool
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index bd068fd..45c549b 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -335,6 +335,38 @@ add_RunCMake_test(no_install_prefix)
add_RunCMake_test(configure_file)
add_RunCMake_test(CTestTimeoutAfterMatch)
+# cthwalloc links against CMakeLib and CTestLib, which means it can't be built
+# if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.)
+# So, it has to be provided in the original build tree.
+if(CMake_TEST_EXTERNAL_CMAKE)
+ set(no_package_root_path)
+ if(NOT CMAKE_VERSION VERSION_LESS 3.12)
+ set(no_package_root_path NO_PACKAGE_ROOT_PATH)
+ endif()
+ find_program(cthwalloc cthwalloc PATHS ${CMake_TEST_EXTERNAL_CMAKE}
+ NO_DEFAULT_PATH
+ ${no_package_root_path}
+ NO_CMAKE_PATH
+ NO_CMAKE_ENVIRONMENT_PATH
+ NO_SYSTEM_ENVIRONMENT_PATH
+ NO_CMAKE_SYSTEM_PATH
+ NO_CMAKE_FIND_ROOT_PATH
+ )
+ if(cthwalloc)
+ add_executable(cthwalloc IMPORTED)
+ set_property(TARGET cthwalloc PROPERTY IMPORTED_LOCATION ${cthwalloc})
+ endif()
+else()
+ add_executable(cthwalloc CTestHardwareAllocation/cthwalloc.cxx)
+ target_link_libraries(cthwalloc CTestLib)
+ target_include_directories(cthwalloc PRIVATE
+ ${CMake_BINARY_DIR}/Source
+ ${CMake_SOURCE_DIR}/Source
+ ${CMake_SOURCE_DIR}/Source/CTest
+ )
+ set_property(TARGET cthwalloc PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMake_BIN_DIR})
+endif()
+
find_package(Qt4 QUIET)
find_package(Qt5Core QUIET)
if (QT4_FOUND AND Qt5Core_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.1.0)
diff --git a/Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx
new file mode 100644
index 0000000..eee2c7f
--- /dev/null
+++ b/Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx
@@ -0,0 +1,396 @@
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+#include "cmsys/Encoding.hxx"
+#include "cmsys/FStream.hxx"
+
+#include "cmCTestHardwareAllocator.h"
+#include "cmCTestHardwareSpec.h"
+#include "cmCTestMultiProcessHandler.h"
+#include "cmCTestTestHandler.h"
+#include "cmFileLock.h"
+#include "cmFileLockResult.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+/*
+ * This helper program is used to verify that the CTest hardware allocation
+ * feature is working correctly. It consists of two stages:
+ *
+ * 1) write - This stage receives the PROCESSES property of the test and
+ * compares it with the values passed in the CTEST_PROCESS_* environment
+ * variables. If it received all of the resources it expected, then it
+ * writes this information to a log file, which will be read in the verify
+ * stage.
+ * 2) verify - This stage compares the log file with the hardware spec file to
+ * make sure that no resources were over-subscribed, deallocated without
+ * being allocated, or allocated without being deallocated.
+ */
+
+static int usage(const char* argv0)
+{
+ std::cout << "Usage: " << argv0 << " (write|verify) <args...>" << std::endl;
+ return 1;
+}
+
+static int usageWrite(const char* argv0)
+{
+ std::cout << "Usage: " << argv0
+ << " write <log-file> <test-name> <sleep-time-secs>"
+ " [<processes-property>]"
+ << std::endl;
+ return 1;
+}
+
+static int usageVerify(const char* argv0)
+{
+ std::cout << "Usage: " << argv0
+ << " verify <log-file> <hardware-spec-file> [<test-names>]"
+ << std::endl;
+ return 1;
+}
+
+static int doWrite(int argc, char const* const* argv)
+{
+ if (argc < 5 || argc > 6) {
+ return usageWrite(argv[0]);
+ }
+ std::string logFile = argv[2];
+ std::string testName = argv[3];
+ unsigned int sleepTime = std::atoi(argv[4]);
+ std::vector<std::map<
+ std::string, std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>>
+ hardware;
+ if (argc == 6) {
+ // Parse processes property
+ std::string processesProperty = argv[5];
+ std::vector<
+ std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>
+ processes;
+ bool result =
+ cmCTestTestHandler::ParseProcessesProperty(processesProperty, processes);
+ (void)result;
+ assert(result);
+
+ // Verify process count
+ const char* processCountEnv = cmSystemTools::GetEnv("CTEST_PROCESS_COUNT");
+ if (!processCountEnv) {
+ std::cout << "CTEST_PROCESS_COUNT should be defined" << std::endl;
+ return 1;
+ }
+ int processCount = std::atoi(processCountEnv);
+ if (processes.size() != std::size_t(processCount)) {
+ std::cout << "CTEST_PROCESS_COUNT does not match expected processes"
+ << std::endl
+ << "Expected: " << processes.size() << std::endl
+ << "Actual: " << processCount << std::endl;
+ return 1;
+ }
+
+ if (!cmSystemTools::Touch(logFile + ".lock", true)) {
+ std::cout << "Could not create lock file" << std::endl;
+ return 1;
+ }
+ cmFileLock lock;
+ auto lockResult =
+ lock.Lock(logFile + ".lock", static_cast<unsigned long>(-1));
+ if (!lockResult.IsOk()) {
+ std::cout << "Could not lock file" << std::endl;
+ return 1;
+ }
+ std::size_t i = 0;
+ cmsys::ofstream fout(logFile.c_str(), std::ios::app);
+ fout << "begin " << testName << std::endl;
+ for (auto& process : processes) {
+ try {
+ // Build and verify set of expected resources
+ std::set<std::string> expectedResources;
+ for (auto const& it : process) {
+ expectedResources.insert(it.ResourceType);
+ }
+
+ std::string prefix = "CTEST_PROCESS_";
+ prefix += std::to_string(i);
+ const char* actualResourcesCStr = cmSystemTools::GetEnv(prefix);
+ if (!actualResourcesCStr) {
+ std::cout << prefix << " should be defined" << std::endl;
+ return 1;
+ }
+
+ auto actualResourcesVec =
+ cmSystemTools::SplitString(actualResourcesCStr, ',');
+ std::set<std::string> actualResources;
+ for (auto const& r : actualResourcesVec) {
+ if (!r.empty()) {
+ actualResources.insert(r);
+ }
+ }
+
+ if (actualResources != expectedResources) {
+ std::cout << prefix << " did not list expected resources"
+ << std::endl;
+ return 1;
+ }
+
+ // Verify that we got what we asked for and write it to the log
+ prefix += '_';
+ std::map<std::string,
+ std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>
+ hwEntry;
+ for (auto const& type : actualResources) {
+ auto it = process.begin();
+
+ std::string varName = prefix;
+ varName += cmSystemTools::UpperCase(type);
+ const char* varVal = cmSystemTools::GetEnv(varName);
+ if (!varVal) {
+ std::cout << varName << " should be defined" << std::endl;
+ return 1;
+ }
+
+ auto received = cmSystemTools::SplitString(varVal, ';');
+ for (auto const& r : received) {
+ while (it->ResourceType != type || it->UnitsNeeded == 0) {
+ ++it;
+ if (it == process.end()) {
+ std::cout << varName << " did not list expected resources"
+ << std::endl;
+ return 1;
+ }
+ }
+ auto split = cmSystemTools::SplitString(r, ',');
+ if (split.size() != 2) {
+ std::cout << varName << " was ill-formed" << std::endl;
+ return 1;
+ }
+ if (!cmHasLiteralPrefix(split[0], "id:")) {
+ std::cout << varName << " was ill-formed" << std::endl;
+ return 1;
+ }
+ auto id = split[0].substr(3);
+ if (!cmHasLiteralPrefix(split[1], "slots:")) {
+ std::cout << varName << " was ill-formed" << std::endl;
+ return 1;
+ }
+ auto slots = split[1].substr(6);
+ unsigned int amount = std::atoi(slots.c_str());
+ if (amount != static_cast<unsigned int>(it->SlotsNeeded)) {
+ std::cout << varName << " did not list expected resources"
+ << std::endl;
+ return 1;
+ }
+ --it->UnitsNeeded;
+
+ fout << "alloc " << type << " " << id << " " << amount
+ << std::endl;
+ hwEntry[type].push_back({ id, amount });
+ }
+
+ bool ended = false;
+ while (it->ResourceType != type || it->UnitsNeeded == 0) {
+ ++it;
+ if (it == process.end()) {
+ ended = true;
+ break;
+ }
+ }
+
+ if (!ended) {
+ std::cout << varName << " did not list expected resources"
+ << std::endl;
+ return 1;
+ }
+ }
+ hardware.push_back(hwEntry);
+
+ ++i;
+ } catch (...) {
+ std::cout << "Unknown error while processing resources" << std::endl;
+ return 1;
+ }
+ }
+
+ auto unlockResult = lock.Release();
+ if (!unlockResult.IsOk()) {
+ std::cout << "Could not unlock file" << std::endl;
+ return 1;
+ }
+ } else {
+ if (cmSystemTools::GetEnv("CTEST_PROCESS_COUNT")) {
+ std::cout << "CTEST_PROCESS_COUNT should not be defined" << std::endl;
+ return 1;
+ }
+ }
+
+ std::this_thread::sleep_for(std::chrono::seconds(sleepTime));
+
+ if (argc == 6) {
+ if (!cmSystemTools::Touch(logFile + ".lock", true)) {
+ std::cout << "Could not create lock file" << std::endl;
+ return 1;
+ }
+ cmFileLock lock;
+ auto lockResult =
+ lock.Lock(logFile + ".lock", static_cast<unsigned long>(-1));
+ if (!lockResult.IsOk()) {
+ std::cout << "Could not lock file" << std::endl;
+ return 1;
+ }
+ cmsys::ofstream fout(logFile.c_str(), std::ios::app);
+ for (auto const& process : hardware) {
+ for (auto const& it : process) {
+ for (auto const& it2 : it.second) {
+ fout << "dealloc " << it.first << " " << it2.Id << " " << it2.Slots
+ << std::endl;
+ }
+ }
+ }
+
+ fout << "end " << testName << std::endl;
+
+ auto unlockResult = lock.Release();
+ if (!unlockResult.IsOk()) {
+ std::cout << "Could not unlock file" << std::endl;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int doVerify(int argc, char const* const* argv)
+{
+ if (argc < 4 || argc > 5) {
+ return usageVerify(argv[0]);
+ }
+ std::string logFile = argv[2];
+ std::string hwFile = argv[3];
+ std::string testNames;
+ if (argc == 5) {
+ testNames = argv[4];
+ }
+ auto testNameList = cmExpandedList(testNames, false);
+ std::set<std::string> testNameSet(testNameList.begin(), testNameList.end());
+
+ cmCTestHardwareSpec spec;
+ if (!spec.ReadFromJSONFile(hwFile)) {
+ std::cout << "Could not read hardware spec " << hwFile << std::endl;
+ return 1;
+ }
+
+ cmCTestHardwareAllocator allocator;
+ allocator.InitializeFromHardwareSpec(spec);
+
+ cmsys::ifstream fin(logFile.c_str(), std::ios::in);
+ if (!fin) {
+ std::cout << "Could not open log file " << logFile << std::endl;
+ return 1;
+ }
+
+ std::string command;
+ std::string resourceName;
+ std::string resourceId;
+ std::string testName;
+ unsigned int amount;
+ std::set<std::string> inProgressTests;
+ std::set<std::string> completedTests;
+ try {
+ while (fin >> command) {
+ if (command == "begin") {
+ if (!(fin >> testName)) {
+ std::cout << "Could not read begin line" << std::endl;
+ return 1;
+ }
+ if (!testNameSet.count(testName) || inProgressTests.count(testName) ||
+ completedTests.count(testName)) {
+ std::cout << "Could not begin test" << std::endl;
+ return 1;
+ }
+ inProgressTests.insert(testName);
+ } else if (command == "alloc") {
+ if (!(fin >> resourceName) || !(fin >> resourceId) ||
+ !(fin >> amount)) {
+ std::cout << "Could not read alloc line" << std::endl;
+ return 1;
+ }
+ if (!allocator.AllocateResource(resourceName, resourceId, amount)) {
+ std::cout << "Could not allocate resources" << std::endl;
+ return 1;
+ }
+ } else if (command == "dealloc") {
+ if (!(fin >> resourceName) || !(fin >> resourceId) ||
+ !(fin >> amount)) {
+ std::cout << "Could not read dealloc line" << std::endl;
+ return 1;
+ }
+ if (!allocator.DeallocateResource(resourceName, resourceId, amount)) {
+ std::cout << "Could not deallocate resources" << std::endl;
+ return 1;
+ }
+ } else if (command == "end") {
+ if (!(fin >> testName)) {
+ std::cout << "Could not read end line" << std::endl;
+ return 1;
+ }
+ if (!inProgressTests.erase(testName)) {
+ std::cout << "Could not end test" << std::endl;
+ return 1;
+ }
+ if (!completedTests.insert(testName).second) {
+ std::cout << "Could not end test" << std::endl;
+ return 1;
+ }
+ }
+ }
+ } catch (...) {
+ std::cout << "Unknown error while reading log file" << std::endl;
+ return 1;
+ }
+
+ auto const& avail = allocator.GetResources();
+ for (auto const& it : avail) {
+ for (auto const& it2 : it.second) {
+ if (it2.second.Locked != 0) {
+ std::cout << "Resource was not unlocked" << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ if (completedTests != testNameSet) {
+ std::cout << "Tests were not ended" << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char const* const* argv)
+{
+ cmsys::Encoding::CommandLineArguments args =
+ cmsys::Encoding::CommandLineArguments::Main(argc, argv);
+ argc = args.argc();
+ argv = args.argv();
+
+ if (argc < 2) {
+ return usage(argv[0]);
+ }
+
+ std::string argv1 = argv[1];
+ if (argv1 == "write") {
+ return doWrite(argc, argv);
+ }
+ if (argv1 == "verify") {
+ return doVerify(argc, argv);
+ }
+ return usage(argv[0]);
+}
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=e34de0691b3bd94720c44c1efad47c3d39ff4134
commit e34de0691b3bd94720c44c1efad47c3d39ff4134
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Tue Jul 9 16:06:17 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
CTest: Allocate hardware to tests
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 1902500..aee6d67 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -3,8 +3,10 @@
#include "cmCTestMultiProcessHandler.h"
#include <algorithm>
+#include <cassert>
#include <chrono>
#include <cmath>
+#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <iomanip>
@@ -27,6 +29,7 @@
#include "cmAffinity.h"
#include "cmAlgorithms.h"
#include "cmCTest.h"
+#include "cmCTestBinPacker.h"
#include "cmCTestRunTest.h"
#include "cmCTestTestHandler.h"
#include "cmDuration.h"
@@ -133,6 +136,12 @@ void cmCTestMultiProcessHandler::RunTests()
uv_run(&this->Loop, UV_RUN_DEFAULT);
uv_loop_close(&this->Loop);
+ if (!this->StopTimePassed) {
+ assert(this->Completed == this->Total);
+ assert(this->Tests.empty());
+ }
+ assert(this->AllHardwareAvailable());
+
this->MarkFinished();
this->UpdateCostData();
}
@@ -168,6 +177,10 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
}
testRun->SetIndex(test);
testRun->SetTestProperties(this->Properties[test]);
+ if (this->TestHandler->UseHardwareSpec) {
+ testRun->SetUseAllocatedHardware(true);
+ testRun->SetAllocatedHardware(this->AllocatedHardware[test]);
+ }
// Find any failed dependencies for this test. We assume the more common
// scenario has no failed tests, so make it the outer loop.
@@ -179,7 +192,13 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
// Always lock the resources we'll be using, even if we fail to set the
// working directory because FinishTestProcess() will try to unlock them
- this->LockResources(test);
+ this->AllocateResources(test);
+
+ if (!this->TestsHaveSufficientHardware[test]) {
+ testRun->StartFailure("Insufficient hardware");
+ this->FinishTestProcess(testRun, false);
+ return false;
+ }
cmWorkingDirectory workdir(this->Properties[test]->Directory);
if (workdir.Failed()) {
@@ -199,6 +218,110 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
return false;
}
+bool cmCTestMultiProcessHandler::AllocateHardware(int index)
+{
+ if (!this->TestHandler->UseHardwareSpec) {
+ return true;
+ }
+
+ std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
+ if (!this->TryAllocateHardware(index, allocations)) {
+ return false;
+ }
+
+ auto& allocatedHardware = this->AllocatedHardware[index];
+ allocatedHardware.resize(this->Properties[index]->Processes.size());
+ for (auto const& it : allocations) {
+ for (auto const& alloc : it.second) {
+ bool result = this->HardwareAllocator.AllocateResource(
+ it.first, alloc.Id, alloc.SlotsNeeded);
+ (void)result;
+ assert(result);
+ allocatedHardware[alloc.ProcessIndex][it.first].push_back(
+ { alloc.Id, static_cast<unsigned int>(alloc.SlotsNeeded) });
+ }
+ }
+
+ return true;
+}
+
+bool cmCTestMultiProcessHandler::TryAllocateHardware(
+ int index,
+ std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations)
+{
+ allocations.clear();
+
+ std::size_t processIndex = 0;
+ for (auto const& process : this->Properties[index]->Processes) {
+ for (auto const& requirement : process) {
+ for (int i = 0; i < requirement.UnitsNeeded; ++i) {
+ allocations[requirement.ResourceType].push_back(
+ { processIndex, requirement.SlotsNeeded, "" });
+ }
+ }
+ ++processIndex;
+ }
+
+ auto const& availableHardware = this->HardwareAllocator.GetResources();
+ for (auto& it : allocations) {
+ if (!availableHardware.count(it.first)) {
+ return false;
+ }
+ if (!cmAllocateCTestHardwareRoundRobin(availableHardware.at(it.first),
+ it.second)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void cmCTestMultiProcessHandler::DeallocateHardware(int index)
+{
+ if (!this->TestHandler->UseHardwareSpec) {
+ return;
+ }
+
+ {
+ auto& allocatedHardware = this->AllocatedHardware[index];
+ for (auto const& processAlloc : allocatedHardware) {
+ for (auto const& it : processAlloc) {
+ auto resourceType = it.first;
+ for (auto const& it2 : it.second) {
+ bool success = this->HardwareAllocator.DeallocateResource(
+ resourceType, it2.Id, it2.Slots);
+ (void)success;
+ assert(success);
+ }
+ }
+ }
+ }
+ this->AllocatedHardware.erase(index);
+}
+
+bool cmCTestMultiProcessHandler::AllHardwareAvailable()
+{
+ for (auto const& it : this->HardwareAllocator.GetResources()) {
+ for (auto const& it2 : it.second) {
+ if (it2.second.Locked != 0) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void cmCTestMultiProcessHandler::CheckHardwareAvailable()
+{
+ for (auto test : this->SortedTests) {
+ std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
+ this->TestsHaveSufficientHardware[test] =
+ !this->TestHandler->UseHardwareSpec ||
+ this->TryAllocateHardware(test, allocations);
+ }
+}
+
bool cmCTestMultiProcessHandler::CheckStopTimePassed()
{
if (!this->StopTimePassed) {
@@ -223,7 +346,7 @@ void cmCTestMultiProcessHandler::SetStopTimePassed()
}
}
-void cmCTestMultiProcessHandler::LockResources(int index)
+void cmCTestMultiProcessHandler::AllocateResources(int index)
{
this->LockedResources.insert(
this->Properties[index]->LockedResources.begin(),
@@ -234,7 +357,7 @@ void cmCTestMultiProcessHandler::LockResources(int index)
}
}
-void cmCTestMultiProcessHandler::UnlockResources(int index)
+void cmCTestMultiProcessHandler::DeallocateResources(int index)
{
for (std::string const& i : this->Properties[index]->LockedResources) {
this->LockedResources.erase(i);
@@ -281,12 +404,20 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
}
}
+ // Allocate hardware
+ if (this->TestsHaveSufficientHardware[test] &&
+ !this->AllocateHardware(test)) {
+ this->DeallocateHardware(test);
+ return false;
+ }
+
// if there are no depends left then run this test
if (this->Tests[test].empty()) {
return this->StartTestProcess(test);
}
// This test was not able to start because it is waiting
// on depends to run
+ this->DeallocateHardware(test);
return false;
}
@@ -471,7 +602,8 @@ void cmCTestMultiProcessHandler::FinishTestProcess(cmCTestRunTest* runner,
this->TestFinishMap[test] = true;
this->TestRunningMap[test] = false;
this->WriteCheckpoint(test);
- this->UnlockResources(test);
+ this->DeallocateHardware(test);
+ this->DeallocateResources(test);
this->RunningCount -= GetProcessorsUsed(test);
for (auto p : properties->Affinity) {
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index be31c75..da716f0 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -14,10 +14,13 @@
#include "cm_uv.h"
+#include "cmCTestHardwareAllocator.h"
#include "cmCTestTestHandler.h"
#include "cmUVHandlePtr.h"
class cmCTest;
+struct cmCTestBinPackerAllocation;
+class cmCTestHardwareSpec;
class cmCTestRunTest;
/** \class cmCTestMultiProcessHandler
@@ -44,6 +47,11 @@ public:
: public std::map<int, cmCTestTestHandler::cmCTestTestProperties*>
{
};
+ struct HardwareAllocation
+ {
+ std::string Id;
+ unsigned int Slots;
+ };
cmCTestMultiProcessHandler();
virtual ~cmCTestMultiProcessHandler();
@@ -79,6 +87,13 @@ public:
void SetQuiet(bool b) { this->Quiet = b; }
+ void InitHardwareAllocator(const cmCTestHardwareSpec& spec)
+ {
+ this->HardwareAllocator.InitializeFromHardwareSpec(spec);
+ }
+
+ void CheckHardwareAvailable();
+
protected:
// Start the next test or tests as many as are allowed by
// ParallelLevel
@@ -119,8 +134,17 @@ protected:
bool CheckStopTimePassed();
void SetStopTimePassed();
- void LockResources(int index);
- void UnlockResources(int index);
+ void AllocateResources(int index);
+ void DeallocateResources(int index);
+
+ bool AllocateHardware(int index);
+ bool TryAllocateHardware(
+ int index,
+ std::map<std::string, std::vector<cmCTestBinPackerAllocation>>&
+ allocations);
+ void DeallocateHardware(int index);
+ bool AllHardwareAvailable();
+
// map from test number to set of depend tests
TestMap Tests;
TestList SortedTests;
@@ -141,6 +165,11 @@ protected:
std::vector<std::string>* Failed;
std::vector<std::string> LastTestsFailed;
std::set<std::string> LockedResources;
+ std::map<int,
+ std::vector<std::map<std::string, std::vector<HardwareAllocation>>>>
+ AllocatedHardware;
+ std::map<int, bool> TestsHaveSufficientHardware;
+ cmCTestHardwareAllocator HardwareAllocator;
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
size_t ParallelLevel; // max number of process that can be run at once
unsigned long TestLoad;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 0188fe0..7f7f736 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -3,6 +3,7 @@
#include "cmCTestRunTest.h"
#include <chrono>
+#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
@@ -689,10 +690,52 @@ bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout,
cmSystemTools::AppendEnv(*environment);
}
+ if (this->UseAllocatedHardware) {
+ this->SetupHardwareEnvironment();
+ } else {
+ cmSystemTools::UnsetEnv("CTEST_PROCESS_COUNT");
+ }
+
return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
affinity);
}
+void cmCTestRunTest::SetupHardwareEnvironment()
+{
+ std::string processCount = "CTEST_PROCESS_COUNT=";
+ processCount += std::to_string(this->AllocatedHardware.size());
+ cmSystemTools::PutEnv(processCount);
+
+ std::size_t i = 0;
+ for (auto const& process : this->AllocatedHardware) {
+ std::string prefix = "CTEST_PROCESS_";
+ prefix += std::to_string(i);
+ std::string resourceList = prefix + '=';
+ prefix += '_';
+ bool firstType = true;
+ for (auto const& it : process) {
+ if (!firstType) {
+ resourceList += ',';
+ }
+ firstType = false;
+ auto resourceType = it.first;
+ resourceList += resourceType;
+ std::string var = prefix + cmSystemTools::UpperCase(resourceType) + '=';
+ bool firstName = true;
+ for (auto const& it2 : it.second) {
+ if (!firstName) {
+ var += ';';
+ }
+ firstName = false;
+ var += "id:" + it2.Id + ",slots:" + std::to_string(it2.Slots);
+ }
+ cmSystemTools::PutEnv(var);
+ }
+ cmSystemTools::PutEnv(resourceList);
+ ++i;
+ }
+}
+
void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
{
std::ostringstream outputStream;
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index c770bac..085a6b8 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#include <map>
#include <memory>
#include <set>
#include <string>
@@ -12,12 +13,12 @@
#include <stddef.h>
+#include "cmCTestMultiProcessHandler.h"
#include "cmCTestTestHandler.h"
#include "cmDuration.h"
#include "cmProcess.h"
class cmCTest;
-class cmCTestMultiProcessHandler;
/** \class cmRunTest
* \brief represents a single test to be run
@@ -83,6 +84,16 @@ public:
bool TimedOutForStopTime() const { return this->TimeoutIsForStopTime; }
+ void SetUseAllocatedHardware(bool use) { this->UseAllocatedHardware = use; }
+ void SetAllocatedHardware(
+ const std::vector<
+ std::map<std::string,
+ std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>>&
+ hardware)
+ {
+ this->AllocatedHardware = hardware;
+ }
+
private:
bool NeedsToRerun();
void DartProcessing();
@@ -94,6 +105,8 @@ private:
// Run post processing of the process output for MemCheck
void MemCheckPostProcess();
+ void SetupHardwareEnvironment();
+
// Returns "completed/total Test #Index: "
std::string GetTestPrefix(size_t completed, size_t total) const;
@@ -112,6 +125,10 @@ private:
std::string StartTime;
std::string ActualCommand;
std::vector<std::string> Arguments;
+ bool UseAllocatedHardware = false;
+ std::vector<std::map<
+ std::string, std::vector<cmCTestMultiProcessHandler::HardwareAllocation>>>
+ AllocatedHardware;
bool RunUntilFail;
int NumberOfRunsLeft;
bool RunAgain;
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index d200b40..5496353 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -32,6 +32,7 @@ void cmCTestTestCommand::BindArguments()
this->Bind("SCHEDULE_RANDOM"_s, this->ScheduleRandom);
this->Bind("STOP_TIME"_s, this->StopTime);
this->Bind("TEST_LOAD"_s, this->TestLoad);
+ this->Bind("HARDWARE_SPEC_FILE"_s, this->HardwareSpecFile);
}
cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
@@ -87,6 +88,9 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
if (!this->ScheduleRandom.empty()) {
handler->SetOption("ScheduleRandom", this->ScheduleRandom.c_str());
}
+ if (!this->HardwareSpecFile.empty()) {
+ handler->SetOption("HardwareSpecFile", this->HardwareSpecFile.c_str());
+ }
if (!this->StopTime.empty()) {
this->CTest->SetStopTime(this->StopTime);
}
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h
index cb65c0b..dc15279 100644
--- a/Source/CTest/cmCTestTestCommand.h
+++ b/Source/CTest/cmCTestTestCommand.h
@@ -58,6 +58,7 @@ protected:
std::string ScheduleRandom;
std::string StopTime;
std::string TestLoad;
+ std::string HardwareSpecFile;
};
#endif
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 927e086..2be62ae 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -289,6 +289,7 @@ cmCTestTestHandler::cmCTestTestHandler()
this->UseIncludeRegExpFlag = false;
this->UseExcludeRegExpFlag = false;
this->UseExcludeRegExpFirst = false;
+ this->UseHardwareSpec = false;
this->CustomMaximumPassedTestOutputSize = 1 * 1024;
this->CustomMaximumFailedTestOutputSize = 300 * 1024;
@@ -509,6 +510,16 @@ bool cmCTestTestHandler::ProcessOptions()
}
this->SetRerunFailed(cmIsOn(this->GetOption("RerunFailed")));
+ val = this->GetOption("HardwareSpecFile");
+ if (val) {
+ this->UseHardwareSpec = true;
+ if (!this->HardwareSpec.ReadFromJSONFile(val)) {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "Could not read hardware spec file: " << val << std::endl);
+ return false;
+ }
+ }
+
return true;
}
@@ -1226,6 +1237,9 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed,
} else {
parallel->SetTestLoad(this->CTest->GetTestLoad());
}
+ if (this->UseHardwareSpec) {
+ parallel->InitHardwareAllocator(this->HardwareSpec);
+ }
*this->LogFile
<< "Start testing: " << this->CTest->CurrentTime() << std::endl
@@ -1269,6 +1283,7 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed,
parallel->SetPassFailVectors(&passed, &failed);
this->TestResults.clear();
parallel->SetTestResults(&this->TestResults);
+ parallel->CheckHardwareAvailable();
if (this->CTest->ShouldPrintLabels()) {
parallel->PrintLabels();
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 2602c30..525215c 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -19,6 +19,7 @@
#include "cmsys/RegularExpression.hxx"
#include "cmCTestGenericHandler.h"
+#include "cmCTestHardwareSpec.h"
#include "cmDuration.h"
#include "cmListFileCache.h"
@@ -335,6 +336,9 @@ private:
cmsys::RegularExpression IncludeTestsRegularExpression;
cmsys::RegularExpression ExcludeTestsRegularExpression;
+ bool UseHardwareSpec;
+ cmCTestHardwareSpec HardwareSpec;
+
void GenerateRegressionImages(cmXMLWriter& xml, const std::string& dart);
cmsys::RegularExpression DartStuff1;
void CheckLabelFilter(cmCTestTestProperties& it);
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 0d1ecca..10b7646 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -2090,6 +2090,15 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
"ExcludeFixtureCleanupRegularExpression", args[i].c_str());
}
+ if (this->CheckArgument(arg, "--hardware-spec-file") &&
+ i < args.size() - 1) {
+ i++;
+ this->GetTestHandler()->SetPersistentOption("HardwareSpecFile",
+ args[i].c_str());
+ this->GetMemCheckHandler()->SetPersistentOption("HardwareSpecFile",
+ args[i].c_str());
+ }
+
if (this->CheckArgument(arg, "--rerun-failed")) {
this->GetTestHandler()->SetPersistentOption("RerunFailed", "true");
this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true");
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index 54549a1..91ee598 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -103,6 +103,7 @@ static const char* cmDocumentationOptions[][2] = {
"times without failing in order to pass" },
{ "--max-width <width>", "Set the max width for a test name to output" },
{ "--interactive-debug-mode [0|1]", "Set the interactive mode to 0 or 1." },
+ { "--hardware-spec-file <file>", "Set the hardware spec file to use." },
{ "--no-label-summary", "Disable timing summary information for labels." },
{ "--no-subproject-summary",
"Disable timing summary information for "
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=aee09648511433160f7fd660eb3c746719810216
commit aee09648511433160f7fd660eb3c746719810216
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Mon Jul 15 16:58:34 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
CTest: Add bin-packing algorithm
This algorithm is used to determine whether or not a test can
execute with the available resources. It uses a recursive largest-
first algorithm to try to place the tests into their respective
slots.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 57229db..63e08de 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -899,6 +899,7 @@ include_directories(
#
set(CTEST_SRCS cmCTest.cxx
CTest/cmProcess.cxx
+ CTest/cmCTestBinPacker.cxx
CTest/cmCTestBuildAndTestHandler.cxx
CTest/cmCTestBuildCommand.cxx
CTest/cmCTestBuildHandler.cxx
diff --git a/Source/CTest/cmCTestBinPacker.cxx b/Source/CTest/cmCTestBinPacker.cxx
new file mode 100644
index 0000000..e9e3bee
--- /dev/null
+++ b/Source/CTest/cmCTestBinPacker.cxx
@@ -0,0 +1,201 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCTestBinPacker.h"
+
+#include <algorithm>
+#include <utility>
+
+bool cmCTestBinPackerAllocation::operator==(
+ const cmCTestBinPackerAllocation& other) const
+{
+ return this->ProcessIndex == other.ProcessIndex &&
+ this->SlotsNeeded == other.SlotsNeeded && this->Id == other.Id;
+}
+
+bool cmCTestBinPackerAllocation::operator!=(
+ const cmCTestBinPackerAllocation& other) const
+{
+ return !(*this == other);
+}
+
+namespace {
+
+/*
+ * The following algorithm is used to do two things:
+ *
+ * 1) Determine if a test's hardware requirements can fit within the hardware
+ * present on the system, and
+ * 2) Do the actual allocation
+ *
+ * This algorithm performs a recursive search, looking for a bin pack that will
+ * fit the specified requirements. It has a template to specify different
+ * optimization strategies. If it ever runs out of room, it backtracks as far
+ * down the stack as it needs to and tries a different combination until no
+ * more combinations can be tried.
+ */
+template <typename AllocationStrategy>
+static bool AllocateCTestHardware(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ const std::vector<std::string>& hardwareSorted, std::size_t currentIndex,
+ std::vector<cmCTestBinPackerAllocation*>& allocations)
+{
+ // Iterate through all large enough resources until we find a solution
+ std::size_t hardwareIndex = 0;
+ while (hardwareIndex < hardwareSorted.size()) {
+ auto const& resource = hardware.at(hardwareSorted[hardwareIndex]);
+ if (resource.Free() >=
+ static_cast<unsigned int>(allocations[currentIndex]->SlotsNeeded)) {
+ // Preemptively allocate the resource
+ allocations[currentIndex]->Id = hardwareSorted[hardwareIndex];
+ if (currentIndex + 1 >= allocations.size()) {
+ // We have a solution
+ return true;
+ }
+
+ // Move the resource up the list until it is sorted again
+ auto hardware2 = hardware;
+ auto hardwareSorted2 = hardwareSorted;
+ hardware2[hardwareSorted2[hardwareIndex]].Locked +=
+ allocations[currentIndex]->SlotsNeeded;
+ AllocationStrategy::IncrementalSort(hardware2, hardwareSorted2,
+ hardwareIndex);
+
+ // Recurse one level deeper
+ if (AllocateCTestHardware<AllocationStrategy>(
+ hardware2, hardwareSorted2, currentIndex + 1, allocations)) {
+ return true;
+ }
+ }
+
+ // No solution found here, deallocate the resource and try the next one
+ allocations[currentIndex]->Id.clear();
+ auto freeSlots = hardware.at(hardwareSorted.at(hardwareIndex)).Free();
+ do {
+ ++hardwareIndex;
+ } while (hardwareIndex < hardwareSorted.size() &&
+ hardware.at(hardwareSorted.at(hardwareIndex)).Free() ==
+ freeSlots);
+ }
+
+ // No solution was found
+ return false;
+}
+
+template <typename AllocationStrategy>
+static bool AllocateCTestHardware(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<cmCTestBinPackerAllocation>& allocations)
+{
+ // Sort the resource requirements in descending order by slots needed
+ std::vector<cmCTestBinPackerAllocation*> allocationsPtr;
+ allocationsPtr.reserve(allocations.size());
+ for (auto& allocation : allocations) {
+ allocationsPtr.push_back(&allocation);
+ }
+ std::stable_sort(
+ allocationsPtr.rbegin(), allocationsPtr.rend(),
+ [](cmCTestBinPackerAllocation* a1, cmCTestBinPackerAllocation* a2) {
+ return a1->SlotsNeeded < a2->SlotsNeeded;
+ });
+
+ // Sort the resources according to sort strategy
+ std::vector<std::string> hardwareSorted;
+ hardwareSorted.reserve(hardware.size());
+ for (auto const& hw : hardware) {
+ hardwareSorted.push_back(hw.first);
+ }
+ AllocationStrategy::InitialSort(hardware, hardwareSorted);
+
+ // Do the actual allocation
+ return AllocateCTestHardware<AllocationStrategy>(
+ hardware, hardwareSorted, std::size_t(0), allocationsPtr);
+}
+
+class RoundRobinAllocationStrategy
+{
+public:
+ static void InitialSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted);
+
+ static void IncrementalSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex);
+};
+
+void RoundRobinAllocationStrategy::InitialSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted)
+{
+ std::stable_sort(
+ hardwareSorted.rbegin(), hardwareSorted.rend(),
+ [&hardware](const std::string& id1, const std::string& id2) {
+ return hardware.at(id1).Free() < hardware.at(id2).Free();
+ });
+}
+
+void RoundRobinAllocationStrategy::IncrementalSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex)
+{
+ auto tmp = hardwareSorted[lastAllocatedIndex];
+ std::size_t i = lastAllocatedIndex;
+ while (i < hardwareSorted.size() - 1 &&
+ hardware.at(hardwareSorted[i + 1]).Free() > hardware.at(tmp).Free()) {
+ hardwareSorted[i] = hardwareSorted[i + 1];
+ ++i;
+ }
+ hardwareSorted[i] = tmp;
+}
+
+class BlockAllocationStrategy
+{
+public:
+ static void InitialSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted);
+
+ static void IncrementalSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex);
+};
+
+void BlockAllocationStrategy::InitialSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<std::string>& hardwareSorted)
+{
+ std::stable_sort(
+ hardwareSorted.rbegin(), hardwareSorted.rend(),
+ [&hardware](const std::string& id1, const std::string& id2) {
+ return hardware.at(id1).Free() < hardware.at(id2).Free();
+ });
+}
+
+void BlockAllocationStrategy::IncrementalSort(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>&,
+ std::vector<std::string>& hardwareSorted, std::size_t lastAllocatedIndex)
+{
+ auto tmp = hardwareSorted[lastAllocatedIndex];
+ std::size_t i = lastAllocatedIndex;
+ while (i > 0) {
+ hardwareSorted[i] = hardwareSorted[i - 1];
+ --i;
+ }
+ hardwareSorted[i] = tmp;
+}
+}
+
+bool cmAllocateCTestHardwareRoundRobin(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<cmCTestBinPackerAllocation>& allocations)
+{
+ return AllocateCTestHardware<RoundRobinAllocationStrategy>(hardware,
+ allocations);
+}
+
+bool cmAllocateCTestHardwareBlock(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<cmCTestBinPackerAllocation>& allocations)
+{
+ return AllocateCTestHardware<BlockAllocationStrategy>(hardware, allocations);
+}
diff --git a/Source/CTest/cmCTestBinPacker.h b/Source/CTest/cmCTestBinPacker.h
new file mode 100644
index 0000000..54f03d7
--- /dev/null
+++ b/Source/CTest/cmCTestBinPacker.h
@@ -0,0 +1,31 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCTestBinPacker_h
+#define cmCTestBinPacker_h
+
+#include <cstddef>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmCTestHardwareAllocator.h"
+
+struct cmCTestBinPackerAllocation
+{
+ std::size_t ProcessIndex;
+ int SlotsNeeded;
+ std::string Id;
+
+ bool operator==(const cmCTestBinPackerAllocation& other) const;
+ bool operator!=(const cmCTestBinPackerAllocation& other) const;
+};
+
+bool cmAllocateCTestHardwareRoundRobin(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<cmCTestBinPackerAllocation>& allocations);
+
+bool cmAllocateCTestHardwareBlock(
+ const std::map<std::string, cmCTestHardwareAllocator::Resource>& hardware,
+ std::vector<cmCTestBinPackerAllocation>& allocations);
+
+#endif
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index c24b64c..bc2079f 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -7,6 +7,7 @@ include_directories(
set(CMakeLib_TESTS
testArgumentParser.cxx
+ testCTestBinPacker.cxx
testCTestProcesses.cxx
testCTestHardwareAllocator.cxx
testCTestHardwareSpec.cxx
diff --git a/Tests/CMakeLib/testCTestBinPacker.cxx b/Tests/CMakeLib/testCTestBinPacker.cxx
new file mode 100644
index 0000000..62ea55b
--- /dev/null
+++ b/Tests/CMakeLib/testCTestBinPacker.cxx
@@ -0,0 +1,300 @@
+#include <cstddef>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmCTestBinPacker.h"
+#include "cmCTestHardwareAllocator.h"
+
+struct ExpectedPackResult
+{
+ std::vector<int> SlotsNeeded;
+ std::map<std::string, cmCTestHardwareAllocator::Resource> Hardware;
+ bool ExpectedReturnValue;
+ std::vector<cmCTestBinPackerAllocation> ExpectedRoundRobinAllocations;
+ std::vector<cmCTestBinPackerAllocation> ExpectedBlockAllocations;
+};
+
+static const std::vector<ExpectedPackResult> expectedResults
+{
+ /* clang-format off */
+ {
+ { 2, 2, 2, 2 },
+ { { "0", { 4, 0 } }, { "1", { 4, 0 } }, { "2", { 4, 0 } },
+ { "3", { 4, 0 } } },
+ true,
+ {
+ { 0, 2, "0" },
+ { 1, 2, "1" },
+ { 2, 2, "2" },
+ { 3, 2, "3" },
+ },
+ {
+ { 0, 2, "0" },
+ { 1, 2, "0" },
+ { 2, 2, "1" },
+ { 3, 2, "1" },
+ },
+ },
+ {
+ { 2, 3, 2 },
+ { { "0", { 5, 0 } }, { "1", { 2, 0 } } },
+ true,
+ {
+ { 0, 2, "0" },
+ { 1, 3, "0" },
+ { 2, 2, "1" },
+ },
+ {
+ { 0, 2, "0" },
+ { 1, 3, "0" },
+ { 2, 2, "1" },
+ },
+ },
+ {
+ { 1, 2, 3 },
+ { { "0", { 1, 0 } }, { "1", { 2, 0 } }, { "2", { 2, 0 } } },
+ false,
+ { },
+ { },
+ },
+ {
+ { 48, 21, 31, 10, 40 },
+ { { "0", { 81, 0 } }, { "1", { 68, 0 } }, { "2", { 20, 0 } },
+ { "3", { 13, 0 } } },
+ true,
+ {
+ { 0, 48, "0" },
+ { 1, 21, "1" },
+ { 2, 31, "0" },
+ { 3, 10, "2" },
+ { 4, 40, "1" },
+ },
+ {
+ { 0, 48, "0" },
+ { 1, 21, "1" },
+ { 2, 31, "0" },
+ { 3, 10, "2" },
+ { 4, 40, "1" },
+ },
+ },
+ {
+ { 30, 31, 39, 67 },
+ { { "0", { 16, 0 } }, { "1", { 81, 0 } }, { "2", { 97, 0 } } },
+ true,
+ {
+ { 0, 30, "2" },
+ { 1, 31, "1" },
+ { 2, 39, "1" },
+ { 3, 67, "2" },
+ },
+ {
+ { 0, 30, "2" },
+ { 1, 31, "1" },
+ { 2, 39, "1" },
+ { 3, 67, "2" },
+ },
+ },
+ {
+ { 63, 47, 1, 9 },
+ { { "0", { 18, 0 } }, { "1", { 29, 0 } }, { "2", { 9, 0 } },
+ { "3", { 52, 0 } } },
+ false,
+ { },
+ { },
+ },
+ {
+ { 22, 29, 46, 85 },
+ { { "0", { 65, 0 } }, { "1", { 85, 0 } }, { "2", { 65, 0 } },
+ { "3", { 78, 0 } } },
+ true,
+ {
+ { 0, 22, "2" },
+ { 1, 29, "0" },
+ { 2, 46, "3" },
+ { 3, 85, "1" },
+ },
+ {
+ { 0, 22, "0" },
+ { 1, 29, "3" },
+ { 2, 46, "3" },
+ { 3, 85, "1" },
+ },
+ },
+ {
+ { 66, 11, 34, 21 },
+ { { "0", { 24, 0 } }, { "1", { 57, 0 } }, { "2", { 61, 0 } },
+ { "3", { 51, 0 } } },
+ false,
+ { },
+ { },
+ },
+ {
+ { 72, 65, 67, 45 },
+ { { "0", { 29, 0 } }, { "1", { 77, 0 } }, { "2", { 98, 0 } },
+ { "3", { 58, 0 } } },
+ false,
+ { },
+ { },
+ },
+ /*
+ * The following is a contrived attack on the bin-packing algorithm that
+ * causes it to execute with n! complexity, where n is the number of
+ * resources. This case is very unrepresentative of real-world usage, and
+ * has been documented but disabled. The bin-packing problem is NP-hard, and
+ * we may not be able to fix this case at all.
+ */
+#if 0
+ {
+ { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 19 },
+ { { "0", { 1000, 0 } }, { "1", { 1001, 0 } }, { "2", { 1002, 0 } },
+ { "3", { 1003, 0 } }, { "4", { 1004, 0 } }, { "5", { 1005, 0 } },
+ { "6", { 1006, 0 } }, { "7", { 1007, 0 } }, { "8", { 1008, 0 } },
+ { "9", { 1009, 0 } } },
+ false,
+ { },
+ { },
+ },
+#endif
+ /*
+ * These cases are more representative of real-world usage (the resource
+ * sizes are all the same.)
+ */
+ {
+ { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 10 },
+ { { "0", { 1000, 0 } }, { "1", { 1000, 0 } }, { "2", { 1000, 0 } },
+ { "3", { 1000, 0 } }, { "4", { 1000, 0 } }, { "5", { 1000, 0 } },
+ { "6", { 1000, 0 } }, { "7", { 1000, 0 } }, { "8", { 1000, 0 } },
+ { "9", { 1000, 0 } } },
+ false,
+ { },
+ { },
+ },
+ {
+ { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 9 },
+ { { "0", { 1000, 0 } }, { "1", { 1000, 0 } }, { "2", { 1000, 0 } },
+ { "3", { 1000, 0 } }, { "4", { 1000, 0 } }, { "5", { 1000, 0 } },
+ { "6", { 1000, 0 } }, { "7", { 1000, 0 } }, { "8", { 1000, 0 } },
+ { "9", { 1000, 0 } } },
+ true,
+ {
+ { 0, 1000, "0" },
+ { 1, 999, "1" },
+ { 2, 998, "2" },
+ { 3, 997, "3" },
+ { 4, 996, "4" },
+ { 5, 995, "5" },
+ { 6, 994, "6" },
+ { 7, 993, "7" },
+ { 8, 992, "8" },
+ { 9, 991, "9" },
+ { 10, 9, "9" },
+ },
+ {
+ { 0, 1000, "0" },
+ { 1, 999, "1" },
+ { 2, 998, "2" },
+ { 3, 997, "3" },
+ { 4, 996, "4" },
+ { 5, 995, "5" },
+ { 6, 994, "6" },
+ { 7, 993, "7" },
+ { 8, 992, "8" },
+ { 9, 991, "9" },
+ { 10, 9, "9" },
+ },
+ },
+ /* clang-format on */
+};
+
+struct AllocationComparison
+{
+ cmCTestBinPackerAllocation First;
+ cmCTestBinPackerAllocation Second;
+ bool Equal;
+};
+
+static const std::vector<AllocationComparison> comparisons{
+ /* clang-format off */
+ { { 0, 1, "0" }, { 0, 1, "0" }, true },
+ { { 0, 1, "0" }, { 1, 1, "0" }, false },
+ { { 0, 1, "0" }, { 0, 2, "0" }, false },
+ { { 0, 1, "0" }, { 0, 1, "1" }, false },
+ /* clang-format on */
+};
+
+bool TestExpectedPackResult(const ExpectedPackResult& expected)
+{
+ std::vector<cmCTestBinPackerAllocation> roundRobinAllocations;
+ roundRobinAllocations.reserve(expected.SlotsNeeded.size());
+ std::size_t index = 0;
+ for (auto const& n : expected.SlotsNeeded) {
+ roundRobinAllocations.push_back({ index++, n, "" });
+ }
+
+ bool roundRobinResult = cmAllocateCTestHardwareRoundRobin(
+ expected.Hardware, roundRobinAllocations);
+ if (roundRobinResult != expected.ExpectedReturnValue) {
+ std::cout
+ << "cmAllocateCTestHardwareRoundRobin did not return expected value"
+ << std::endl;
+ return false;
+ }
+
+ if (roundRobinResult &&
+ roundRobinAllocations != expected.ExpectedRoundRobinAllocations) {
+ std::cout << "cmAllocateCTestHardwareRoundRobin did not return expected "
+ "allocations"
+ << std::endl;
+ return false;
+ }
+
+ std::vector<cmCTestBinPackerAllocation> blockAllocations;
+ blockAllocations.reserve(expected.SlotsNeeded.size());
+ index = 0;
+ for (auto const& n : expected.SlotsNeeded) {
+ blockAllocations.push_back({ index++, n, "" });
+ }
+
+ bool blockResult =
+ cmAllocateCTestHardwareBlock(expected.Hardware, blockAllocations);
+ if (blockResult != expected.ExpectedReturnValue) {
+ std::cout << "cmAllocateCTestHardwareBlock did not return expected value"
+ << std::endl;
+ return false;
+ }
+
+ if (blockResult && blockAllocations != expected.ExpectedBlockAllocations) {
+ std::cout << "cmAllocateCTestHardwareBlock did not return expected"
+ " allocations"
+ << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+int testCTestBinPacker(int /*unused*/, char* /*unused*/ [])
+{
+ int retval = 0;
+
+ for (auto const& comparison : comparisons) {
+ if ((comparison.First == comparison.Second) != comparison.Equal) {
+ std::cout << "Comparison did not match expected" << std::endl;
+ retval = 1;
+ }
+ if ((comparison.First != comparison.Second) == comparison.Equal) {
+ std::cout << "Comparison did not match expected" << std::endl;
+ retval = 1;
+ }
+ }
+
+ for (auto const& expected : expectedResults) {
+ if (!TestExpectedPackResult(expected)) {
+ retval = 1;
+ }
+ }
+
+ return retval;
+}
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c494b2973a1557c3373a2841ba39da6b8d4bdb5a
commit c494b2973a1557c3373a2841ba39da6b8d4bdb5a
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Mon Jul 8 17:09:34 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
CTest: Add cmCTestHardwareAllocator class
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 8e38590..57229db 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -918,6 +918,7 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
CTest/cmCTestGenericHandler.cxx
CTest/cmCTestHandlerCommand.cxx
+ CTest/cmCTestHardwareAllocator.cxx
CTest/cmCTestHardwareSpec.cxx
CTest/cmCTestLaunch.cxx
CTest/cmCTestMemCheckCommand.cxx
diff --git a/Source/CTest/cmCTestHardwareAllocator.cxx b/Source/CTest/cmCTestHardwareAllocator.cxx
new file mode 100644
index 0000000..2d1833d
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareAllocator.cxx
@@ -0,0 +1,86 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmCTestHardwareAllocator.h"
+
+#include <utility>
+#include <vector>
+
+#include "cmCTestHardwareSpec.h"
+
+void cmCTestHardwareAllocator::InitializeFromHardwareSpec(
+ const cmCTestHardwareSpec& spec)
+{
+ this->Resources.clear();
+
+ for (auto const& it : spec.LocalSocket.Resources) {
+ auto& res = this->Resources[it.first];
+ for (auto const& specRes : it.second) {
+ res[specRes.Id].Total = specRes.Capacity;
+ res[specRes.Id].Locked = 0;
+ }
+ }
+}
+
+const std::map<std::string,
+ std::map<std::string, cmCTestHardwareAllocator::Resource>>&
+cmCTestHardwareAllocator::GetResources() const
+{
+ return this->Resources;
+}
+
+bool cmCTestHardwareAllocator::AllocateResource(const std::string& name,
+ const std::string& id,
+ unsigned int slots)
+{
+ auto it = this->Resources.find(name);
+ if (it == this->Resources.end()) {
+ return false;
+ }
+
+ auto resIt = it->second.find(id);
+ if (resIt == it->second.end()) {
+ return false;
+ }
+
+ if (resIt->second.Total < resIt->second.Locked + slots) {
+ return false;
+ }
+
+ resIt->second.Locked += slots;
+ return true;
+}
+
+bool cmCTestHardwareAllocator::DeallocateResource(const std::string& name,
+ const std::string& id,
+ unsigned int slots)
+{
+ auto it = this->Resources.find(name);
+ if (it == this->Resources.end()) {
+ return false;
+ }
+
+ auto resIt = it->second.find(id);
+ if (resIt == it->second.end()) {
+ return false;
+ }
+
+ if (resIt->second.Locked < slots) {
+ return false;
+ }
+
+ resIt->second.Locked -= slots;
+ return true;
+}
+
+bool cmCTestHardwareAllocator::Resource::operator==(
+ const Resource& other) const
+{
+ return this->Total == other.Total && this->Locked == other.Locked;
+}
+
+bool cmCTestHardwareAllocator::Resource::operator!=(
+ const Resource& other) const
+{
+ return !(*this == other);
+}
diff --git a/Source/CTest/cmCTestHardwareAllocator.h b/Source/CTest/cmCTestHardwareAllocator.h
new file mode 100644
index 0000000..441f84d
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareAllocator.h
@@ -0,0 +1,39 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCTestHardwareAllocator_h
+#define cmCTestHardwareAllocator_h
+
+#include <map>
+#include <string>
+
+class cmCTestHardwareSpec;
+
+class cmCTestHardwareAllocator
+{
+public:
+ struct Resource
+ {
+ unsigned int Total;
+ unsigned int Locked;
+
+ unsigned int Free() const { return this->Total - this->Locked; }
+
+ bool operator==(const Resource& other) const;
+ bool operator!=(const Resource& other) const;
+ };
+
+ void InitializeFromHardwareSpec(const cmCTestHardwareSpec& spec);
+
+ const std::map<std::string, std::map<std::string, Resource>>& GetResources()
+ const;
+
+ bool AllocateResource(const std::string& name, const std::string& id,
+ unsigned int slots);
+ bool DeallocateResource(const std::string& name, const std::string& id,
+ unsigned int slots);
+
+private:
+ std::map<std::string, std::map<std::string, Resource>> Resources;
+};
+
+#endif
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 901f097..c24b64c 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -8,6 +8,7 @@ include_directories(
set(CMakeLib_TESTS
testArgumentParser.cxx
testCTestProcesses.cxx
+ testCTestHardwareAllocator.cxx
testCTestHardwareSpec.cxx
testGeneratedFileStream.cxx
testRST.cxx
diff --git a/Tests/CMakeLib/testCTestHardwareAllocator.cxx b/Tests/CMakeLib/testCTestHardwareAllocator.cxx
new file mode 100644
index 0000000..6f05d03
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareAllocator.cxx
@@ -0,0 +1,426 @@
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "cmCTestHardwareAllocator.h"
+#include "cmCTestHardwareSpec.h"
+
+static const cmCTestHardwareSpec spec{ { {
+ /* clang-format off */
+ { "gpus", { { "0", 4 }, { "1", 8 }, { "2", 0 }, { "3", 8 } } },
+ /* clang-format on */
+} } };
+
+bool testInitializeFromHardwareSpec()
+{
+ bool retval = true;
+
+ cmCTestHardwareAllocator allocator;
+ allocator.InitializeFromHardwareSpec(spec);
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 0 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.GetResources() != expected) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ return retval;
+}
+
+bool testAllocateResource()
+{
+ bool retval = true;
+
+ cmCTestHardwareAllocator allocator;
+ allocator.InitializeFromHardwareSpec(spec);
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected1{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 2 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (!allocator.AllocateResource("gpus", "0", 2)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"0\", 2) returned false, should be "
+ "true\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected1) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected2{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 4 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (!allocator.AllocateResource("gpus", "0", 2)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"0\", 2) returned false, should be "
+ "true\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected2) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected3{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 4 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.AllocateResource("gpus", "0", 1)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"0\", 1) returned true, should be "
+ "false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected3) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected4{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 4 } },
+ { "1", { 8, 7 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (!allocator.AllocateResource("gpus", "1", 7)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"1\", 7) returned false, should be "
+ "true\n";
+ retval = false;
+ }
+ if (allocator.AllocateResource("gpus", "1", 2)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"1\", 2) returned true, should be "
+ "false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected4) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected5{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 4 } },
+ { "1", { 8, 7 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.AllocateResource("gpus", "2", 1)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"2\", 1) returned true, should be "
+ "false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected5) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected6{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 4 } },
+ { "1", { 8, 7 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.AllocateResource("gpus", "4", 1)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"4\", 1) returned true, should be "
+ "false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected6) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected7{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 4 } },
+ { "1", { 8, 7 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.AllocateResource("threads", "0", 1)) {
+ std::cout
+ << "AllocateResource(\"threads\", \"0\", 1) returned true, should be"
+ " false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected7) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ return retval;
+}
+
+bool testDeallocateResource()
+{
+ bool retval = true;
+
+ cmCTestHardwareAllocator allocator;
+ allocator.InitializeFromHardwareSpec(spec);
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected1{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 1 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (!allocator.AllocateResource("gpus", "0", 2)) {
+ std::cout
+ << "AllocateResource(\"gpus\", \"0\", 2) returned false, should be "
+ "true\n";
+ retval = false;
+ }
+ if (!allocator.DeallocateResource("gpus", "0", 1)) {
+ std::cout
+ << "DeallocateResource(\"gpus\", \"0\", 1) returned false, should be"
+ " true\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected1) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected2{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 1 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.DeallocateResource("gpus", "0", 2)) {
+ std::cout
+ << "DeallocateResource(\"gpus\", \"0\", 2) returned true, should be"
+ " false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected2) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected3{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 0 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (!allocator.DeallocateResource("gpus", "0", 1)) {
+ std::cout
+ << "DeallocateResource(\"gpus\", \"0\", 1) returned false, should be"
+ " true\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected3) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected4{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 0 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.DeallocateResource("gpus", "0", 1)) {
+ std::cout
+ << "DeallocateResource(\"gpus\", \"0\", 1) returned true, should be"
+ " false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected4) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected5{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 0 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.DeallocateResource("gpus", "4", 1)) {
+ std::cout
+ << "DeallocateResource(\"gpus\", \"4\", 1) returned true, should be"
+ " false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected5) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ static const std::map<
+ std::string, std::map<std::string, cmCTestHardwareAllocator::Resource>>
+ expected6{
+ /* clang-format off */
+ { "gpus", {
+ { "0", { 4, 0 } },
+ { "1", { 8, 0 } },
+ { "2", { 0, 0 } },
+ { "3", { 8, 0 } },
+ } },
+ /* clang-format on */
+ };
+ if (allocator.DeallocateResource("threads", "0", 1)) {
+ std::cout
+ << "DeallocateResource(\"threads\", \"0\", 1) returned true, should be"
+ " false\n";
+ retval = false;
+ }
+ if (allocator.GetResources() != expected6) {
+ std::cout << "GetResources() did not return expected value\n";
+ retval = false;
+ }
+
+ return retval;
+}
+
+bool testResourceFree()
+{
+ bool retval = true;
+
+ const cmCTestHardwareAllocator::Resource r1{ 5, 0 };
+ if (r1.Free() != 5) {
+ std::cout << "cmCTestHardwareAllocator::Resource::Free() did not return "
+ "expected value for { 5, 0 }\n";
+ retval = false;
+ }
+
+ const cmCTestHardwareAllocator::Resource r2{ 3, 2 };
+ if (r2.Free() != 1) {
+ std::cout << "cmCTestHardwareAllocator::Resource::Free() did not return "
+ "expected value for { 3, 2 }\n";
+ retval = false;
+ }
+
+ const cmCTestHardwareAllocator::Resource r3{ 4, 4 };
+ if (r3.Free() != 0) {
+ std::cout << "cmCTestHardwareAllocator::Resource::Free() did not return "
+ "expected value for { 4, 4 }\n";
+ retval = false;
+ }
+
+ return retval;
+}
+
+int testCTestHardwareAllocator(int, char** const)
+{
+ int retval = 0;
+
+ if (!testInitializeFromHardwareSpec()) {
+ std::cout << "in testInitializeFromHardwareSpec()\n";
+ retval = -1;
+ }
+
+ if (!testAllocateResource()) {
+ std::cout << "in testAllocateResource()\n";
+ retval = -1;
+ }
+
+ if (!testDeallocateResource()) {
+ std::cout << "in testDeallocateResource()\n";
+ retval = -1;
+ }
+
+ if (!testResourceFree()) {
+ std::cout << "in testResourceFree()\n";
+ retval = -1;
+ }
+
+ return retval;
+}
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c8f48069430cb286ca593a967e0c3b8e5ea842c4
commit c8f48069430cb286ca593a967e0c3b8e5ea842c4
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Wed Jul 3 15:00:38 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
CTest: Add parser for hardware spec file
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 9750d0b..8e38590 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -918,6 +918,7 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
CTest/cmCTestGenericHandler.cxx
CTest/cmCTestHandlerCommand.cxx
+ CTest/cmCTestHardwareSpec.cxx
CTest/cmCTestLaunch.cxx
CTest/cmCTestMemCheckCommand.cxx
CTest/cmCTestMemCheckHandler.cxx
diff --git a/Source/CTest/cmCTestHardwareSpec.cxx b/Source/CTest/cmCTestHardwareSpec.cxx
new file mode 100644
index 0000000..137398a
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareSpec.cxx
@@ -0,0 +1,133 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCTestHardwareSpec.h"
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
+
+#include "cm_jsoncpp_reader.h"
+#include "cm_jsoncpp_value.h"
+
+static const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" };
+static const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" };
+
+bool cmCTestHardwareSpec::ReadFromJSONFile(const std::string& filename)
+{
+ cmsys::ifstream fin(filename.c_str());
+ if (!fin) {
+ return false;
+ }
+
+ Json::Value root;
+ Json::CharReaderBuilder builder;
+ if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
+ return false;
+ }
+
+ if (!root.isObject()) {
+ return false;
+ }
+
+ auto const& local = root["local"];
+ if (!local.isArray()) {
+ return false;
+ }
+ if (local.size() > 1) {
+ return false;
+ }
+
+ if (local.empty()) {
+ this->LocalSocket.Resources.clear();
+ return true;
+ }
+
+ auto const& localSocket = local[0];
+ if (!localSocket.isObject()) {
+ return false;
+ }
+ std::map<std::string, std::vector<cmCTestHardwareSpec::Resource>> resources;
+ cmsys::RegularExpressionMatch match;
+ for (auto const& key : localSocket.getMemberNames()) {
+ if (IdentifierRegex.find(key.c_str(), match)) {
+ auto const& value = localSocket[key];
+ auto& r = resources[key];
+ if (value.isArray()) {
+ for (auto const& item : value) {
+ if (item.isObject()) {
+ cmCTestHardwareSpec::Resource resource;
+
+ if (!item.isMember("id")) {
+ return false;
+ }
+ auto const& id = item["id"];
+ if (!id.isString()) {
+ return false;
+ }
+ resource.Id = id.asString();
+ if (!IdRegex.find(resource.Id.c_str(), match)) {
+ return false;
+ }
+
+ if (item.isMember("slots")) {
+ auto const& capacity = item["slots"];
+ if (!capacity.isConvertibleTo(Json::uintValue)) {
+ return false;
+ }
+ resource.Capacity = capacity.asUInt();
+ } else {
+ resource.Capacity = 1;
+ }
+
+ r.push_back(resource);
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ this->LocalSocket.Resources = std::move(resources);
+ return true;
+}
+
+bool cmCTestHardwareSpec::operator==(const cmCTestHardwareSpec& other) const
+{
+ return this->LocalSocket == other.LocalSocket;
+}
+
+bool cmCTestHardwareSpec::operator!=(const cmCTestHardwareSpec& other) const
+{
+ return !(*this == other);
+}
+
+bool cmCTestHardwareSpec::Socket::operator==(
+ const cmCTestHardwareSpec::Socket& other) const
+{
+ return this->Resources == other.Resources;
+}
+
+bool cmCTestHardwareSpec::Socket::operator!=(
+ const cmCTestHardwareSpec::Socket& other) const
+{
+ return !(*this == other);
+}
+
+bool cmCTestHardwareSpec::Resource::operator==(
+ const cmCTestHardwareSpec::Resource& other) const
+{
+ return this->Id == other.Id && this->Capacity == other.Capacity;
+}
+
+bool cmCTestHardwareSpec::Resource::operator!=(
+ const cmCTestHardwareSpec::Resource& other) const
+{
+ return !(*this == other);
+}
diff --git a/Source/CTest/cmCTestHardwareSpec.h b/Source/CTest/cmCTestHardwareSpec.h
new file mode 100644
index 0000000..a0b4cae
--- /dev/null
+++ b/Source/CTest/cmCTestHardwareSpec.h
@@ -0,0 +1,40 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCTestHardwareSpec_h
+#define cmCTestHardwareSpec_h
+
+#include <map>
+#include <string>
+#include <vector>
+
+class cmCTestHardwareSpec
+{
+public:
+ class Resource
+ {
+ public:
+ std::string Id;
+ unsigned int Capacity;
+
+ bool operator==(const Resource& other) const;
+ bool operator!=(const Resource& other) const;
+ };
+
+ class Socket
+ {
+ public:
+ std::map<std::string, std::vector<Resource>> Resources;
+
+ bool operator==(const Socket& other) const;
+ bool operator!=(const Socket& other) const;
+ };
+
+ Socket LocalSocket;
+
+ bool ReadFromJSONFile(const std::string& filename);
+
+ bool operator==(const cmCTestHardwareSpec& other) const;
+ bool operator!=(const cmCTestHardwareSpec& other) const;
+};
+
+#endif
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index abb258b..901f097 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -8,6 +8,7 @@ include_directories(
set(CMakeLib_TESTS
testArgumentParser.cxx
testCTestProcesses.cxx
+ testCTestHardwareSpec.cxx
testGeneratedFileStream.cxx
testRST.cxx
testRange.cxx
@@ -29,6 +30,7 @@ add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx)
set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
set(testUVProcessChain_ARGS $<TARGET_FILE:testUVProcessChainHelper>)
set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>)
+set(testCTestHardwareSpec_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
if(WIN32)
list(APPEND CMakeLib_TESTS
diff --git a/Tests/CMakeLib/testCTestHardwareSpec.cxx b/Tests/CMakeLib/testCTestHardwareSpec.cxx
new file mode 100644
index 0000000..3e3eccc
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec.cxx
@@ -0,0 +1,84 @@
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "cmCTestHardwareSpec.h"
+
+struct ExpectedSpec
+{
+ std::string Path;
+ bool ParseResult;
+ cmCTestHardwareSpec Expected;
+};
+
+static const std::vector<ExpectedSpec> expectedHardwareSpecs = {
+ /* clang-format off */
+ {"spec1.json", true, {{{
+ {"gpus", {
+ {"2", 4},
+ {"e", 1},
+ }},
+ {"threads", {
+ }},
+ }}}},
+ {"spec2.json", true, {{{
+ }}}},
+ {"spec3.json", false, {{{}}}},
+ {"spec4.json", false, {{{}}}},
+ {"spec5.json", false, {{{}}}},
+ {"spec6.json", false, {{{}}}},
+ {"spec7.json", false, {{{}}}},
+ {"spec8.json", false, {{{}}}},
+ {"spec9.json", false, {{{}}}},
+ {"spec10.json", false, {{{}}}},
+ {"spec11.json", false, {{{}}}},
+ {"spec12.json", false, {{{}}}},
+ {"spec13.json", false, {{{}}}},
+ {"spec14.json", true, {{{}}}},
+ {"spec15.json", true, {{{}}}},
+ {"spec16.json", true, {{{}}}},
+ {"spec17.json", false, {{{}}}},
+ {"spec18.json", false, {{{}}}},
+ {"noexist.json", false, {{{}}}},
+ /* clang-format on */
+};
+
+static bool testSpec(const std::string& path, bool expectedResult,
+ const cmCTestHardwareSpec& expected)
+{
+ cmCTestHardwareSpec actual;
+ bool result = actual.ReadFromJSONFile(path);
+ if (result != expectedResult) {
+ std::cout << "ReadFromJSONFile(\"" << path << "\") returned " << result
+ << ", should be " << expectedResult << std::endl;
+ return false;
+ }
+
+ if (result && actual != expected) {
+ std::cout << "ReadFromJSONFile(\"" << path
+ << "\") did not give expected spec" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+int testCTestHardwareSpec(int argc, char** const argv)
+{
+ if (argc < 2) {
+ std::cout << "Invalid arguments.\n";
+ return -1;
+ }
+
+ int retval = 0;
+ for (auto const& spec : expectedHardwareSpecs) {
+ std::string path = argv[1];
+ path += "/testCTestHardwareSpec_data/";
+ path += spec.Path;
+ if (!testSpec(path, spec.ParseResult, spec.Expected)) {
+ retval = -1;
+ }
+ }
+
+ return retval;
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json
new file mode 100644
index 0000000..ee3d0ce
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json
@@ -0,0 +1,23 @@
+{
+ "local": [
+ {
+ "gpus": [
+ {
+ "id": "2",
+ "slots": 4
+ },
+ {
+ "id": "e"
+ }
+ ],
+ ".reserved": [
+ {
+ "id": "a",
+ "slots": 3
+ }
+ ],
+ "threads": [
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json
new file mode 100644
index 0000000..22105d7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json
@@ -0,0 +1,11 @@
+{
+ "local": [
+ {
+ "gpus": [
+ {
+ "id": 4
+ }
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json
new file mode 100644
index 0000000..1e37ef5
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json
@@ -0,0 +1,12 @@
+{
+ "local": [
+ {
+ "gpus": [
+ {
+ "id": "4",
+ "slots": "giraffe"
+ }
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json
@@ -0,0 +1 @@
+[]
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json
new file mode 100644
index 0000000..6b7a9f4
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json
@@ -0,0 +1 @@
+not json
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json
new file mode 100644
index 0000000..ce708c7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json
@@ -0,0 +1,8 @@
+{
+ "local": [
+ {
+ "0": [
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json
new file mode 100644
index 0000000..78b6990
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json
@@ -0,0 +1,8 @@
+{
+ "local": [
+ {
+ "-": [
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json
new file mode 100644
index 0000000..95c7d26
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json
@@ -0,0 +1,8 @@
+{
+ "local": [
+ {
+ "A": [
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json
new file mode 100644
index 0000000..1b6ab4b
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json
@@ -0,0 +1,11 @@
+{
+ "local": [
+ {
+ "gpus": [
+ {
+ "id": "A"
+ }
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json
new file mode 100644
index 0000000..1a17df7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json
@@ -0,0 +1,11 @@
+{
+ "local": [
+ {
+ "gpus": [
+ {
+ "id": "-"
+ }
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json
new file mode 100644
index 0000000..6175b1a
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json
@@ -0,0 +1,4 @@
+{
+ "local": [
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json
new file mode 100644
index 0000000..82453ec
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json
@@ -0,0 +1,8 @@
+{
+ "local": [
+ {
+ },
+ {
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json
new file mode 100644
index 0000000..05e73d7
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json
@@ -0,0 +1,4 @@
+{
+ "local": {
+ }
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json
new file mode 100644
index 0000000..93c790d
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json
@@ -0,0 +1,5 @@
+{
+ "local": [
+ []
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json
new file mode 100644
index 0000000..28b6a4f
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json
@@ -0,0 +1,8 @@
+{
+ "local": [
+ {
+ "gpus": {
+ }
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json
new file mode 100644
index 0000000..79bd224
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json
@@ -0,0 +1,9 @@
+{
+ "local": [
+ {
+ "gpus": [
+ []
+ ]
+ }
+ ]
+}
diff --git a/Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json b/Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json
new file mode 100644
index 0000000..6bb1def
--- /dev/null
+++ b/Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json
@@ -0,0 +1,10 @@
+{
+ "local": [
+ {
+ "gpus": [
+ {
+ }
+ ]
+ }
+ ]
+}
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=bb4a1410592342a824b1dd755b7ca8897deac65c
commit bb4a1410592342a824b1dd755b7ca8897deac65c
Author: Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Thu Jun 27 16:38:19 2019 -0400
Commit: Brad King <brad.king at kitware.com>
CommitDate: Wed Oct 2 09:33:54 2019 -0400
CTest: Add lexer for PROCESSES property
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 4e3e842..9750d0b 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -922,6 +922,7 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmCTestMemCheckCommand.cxx
CTest/cmCTestMemCheckHandler.cxx
CTest/cmCTestMultiProcessHandler.cxx
+ CTest/cmCTestProcessesLexerHelper.cxx
CTest/cmCTestReadCustomFilesCommand.cxx
CTest/cmCTestRunScriptCommand.cxx
CTest/cmCTestRunTest.cxx
@@ -953,6 +954,10 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmCTestHG.h
CTest/cmCTestP4.cxx
CTest/cmCTestP4.h
+
+ LexerParser/cmCTestProcessesLexer.cxx
+ LexerParser/cmCTestProcessesLexer.h
+ LexerParser/cmCTestProcessesLexer.in.l
)
# Build CTestLib
diff --git a/Source/CTest/cmCTestProcessesLexerHelper.cxx b/Source/CTest/cmCTestProcessesLexerHelper.cxx
new file mode 100644
index 0000000..797164b
--- /dev/null
+++ b/Source/CTest/cmCTestProcessesLexerHelper.cxx
@@ -0,0 +1,55 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmCTestProcessesLexerHelper.h"
+
+#include "cmCTestProcessesLexer.h"
+#include "cmCTestTestHandler.h"
+
+cmCTestProcessesLexerHelper::cmCTestProcessesLexerHelper(
+ std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+ output)
+ : Output(output)
+{
+}
+
+bool cmCTestProcessesLexerHelper::ParseString(const std::string& value)
+{
+ yyscan_t lexer;
+ cmCTestProcesses_yylex_init_extra(this, &lexer);
+
+ auto state = cmCTestProcesses_yy_scan_string(value.c_str(), lexer);
+ int retval = cmCTestProcesses_yylex(lexer);
+ cmCTestProcesses_yy_delete_buffer(state, lexer);
+
+ cmCTestProcesses_yylex_destroy(lexer);
+ return retval == 0;
+}
+
+void cmCTestProcessesLexerHelper::SetProcessCount(unsigned int count)
+{
+ this->ProcessCount = count;
+}
+
+void cmCTestProcessesLexerHelper::SetResourceType(const std::string& type)
+{
+ this->ResourceType = type;
+}
+
+void cmCTestProcessesLexerHelper::SetNeededSlots(int count)
+{
+ this->NeededSlots = count;
+}
+
+void cmCTestProcessesLexerHelper::WriteRequirement()
+{
+ this->Process.push_back({ this->ResourceType, this->NeededSlots, 1 });
+}
+
+void cmCTestProcessesLexerHelper::WriteProcess()
+{
+ for (unsigned int i = 0; i < this->ProcessCount; ++i) {
+ this->Output.push_back(this->Process);
+ }
+ this->Process.clear();
+ this->ProcessCount = 1;
+}
diff --git a/Source/CTest/cmCTestProcessesLexerHelper.h b/Source/CTest/cmCTestProcessesLexerHelper.h
new file mode 100644
index 0000000..6c9289f
--- /dev/null
+++ b/Source/CTest/cmCTestProcessesLexerHelper.h
@@ -0,0 +1,44 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmCTestProcessesLexerHelper_h
+#define cmCTestProcessesLexerHelper_h
+
+#include <string>
+#include <vector>
+
+#include "cmCTestTestHandler.h"
+
+class cmCTestProcessesLexerHelper
+{
+public:
+ struct ParserType
+ {
+ };
+
+ cmCTestProcessesLexerHelper(
+ std::vector<
+ std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+ output);
+ ~cmCTestProcessesLexerHelper() = default;
+
+ bool ParseString(const std::string& value);
+
+ void SetProcessCount(unsigned int count);
+ void SetResourceType(const std::string& type);
+ void SetNeededSlots(int count);
+ void WriteRequirement();
+ void WriteProcess();
+
+private:
+ std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>&
+ Output;
+
+ unsigned int ProcessCount = 1;
+ std::string ResourceType;
+ int NeededSlots;
+ std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement> Process;
+};
+
+#define YY_EXTRA_TYPE cmCTestProcessesLexerHelper*
+
+#endif
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 67d16af..927e086 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -29,6 +29,7 @@
#include "cmAlgorithms.h"
#include "cmCTest.h"
#include "cmCTestMultiProcessHandler.h"
+#include "cmCTestProcessesLexerHelper.h"
#include "cmDuration.h"
#include "cmExecutionStatus.h"
#include "cmGeneratedFileStream.h"
@@ -1610,6 +1611,14 @@ std::string cmCTestTestHandler::FindExecutable(
return fullPath;
}
+bool cmCTestTestHandler::ParseProcessesProperty(
+ const std::string& val,
+ std::vector<std::vector<cmCTestTestResourceRequirement>>& processes)
+{
+ cmCTestProcessesLexerHelper lexer(processes);
+ return lexer.ParseString(val);
+}
+
void cmCTestTestHandler::GetListOfTests()
{
if (!this->IncludeLabelRegExp.empty()) {
@@ -2179,6 +2188,11 @@ bool cmCTestTestHandler::SetTestsProperties(
if (key == "PROCESSOR_AFFINITY") {
rt.WantAffinity = cmIsOn(val);
}
+ if (key == "PROCESSES") {
+ if (!ParseProcessesProperty(val, rt.Processes)) {
+ return false;
+ }
+ }
if (key == "SKIP_RETURN_CODE") {
rt.SkipReturnCode = atoi(val.c_str());
if (rt.SkipReturnCode < 0 || rt.SkipReturnCode > 255) {
@@ -2356,3 +2370,17 @@ bool cmCTestTestHandler::AddTest(const std::vector<std::string>& args)
this->TestList.push_back(test);
return true;
}
+
+bool cmCTestTestHandler::cmCTestTestResourceRequirement::operator==(
+ const cmCTestTestResourceRequirement& other) const
+{
+ return this->ResourceType == other.ResourceType &&
+ this->SlotsNeeded == other.SlotsNeeded &&
+ this->UnitsNeeded == other.UnitsNeeded;
+}
+
+bool cmCTestTestHandler::cmCTestTestResourceRequirement::operator!=(
+ const cmCTestTestResourceRequirement& other) const
+{
+ return !(*this == other);
+}
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 1807a5c..2602c30 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -102,6 +102,16 @@ public:
void Initialize() override;
+ struct cmCTestTestResourceRequirement
+ {
+ std::string ResourceType;
+ int SlotsNeeded;
+ int UnitsNeeded;
+
+ bool operator==(const cmCTestTestResourceRequirement& other) const;
+ bool operator!=(const cmCTestTestResourceRequirement& other) const;
+ };
+
// NOTE: This struct is Saved/Restored
// in cmCTestTestHandler, if you add to this class
// then you must add the new members to that code or
@@ -147,6 +157,7 @@ public:
std::set<std::string> FixturesCleanup;
std::set<std::string> FixturesRequired;
std::set<std::string> RequireSuccessDepends;
+ std::vector<std::vector<cmCTestTestResourceRequirement>> Processes;
// Private test generator properties used to track backtraces
cmListFileBacktrace Backtrace;
};
@@ -190,6 +201,10 @@ public:
std::vector<std::string>& extraPaths,
std::vector<std::string>& failed);
+ static bool ParseProcessesProperty(
+ const std::string& val,
+ std::vector<std::vector<cmCTestTestResourceRequirement>>& processes);
+
using ListOfTests = std::vector<cmCTestTestProperties>;
protected:
diff --git a/Source/LexerParser/.gitattributes b/Source/LexerParser/.gitattributes
index 47eedfb..e5a1d8b 100644
--- a/Source/LexerParser/.gitattributes
+++ b/Source/LexerParser/.gitattributes
@@ -2,6 +2,8 @@
/cmCommandArgumentLexer.h generated
/cmCommandArgumentParser.cxx generated
/cmCommandArgumentParserTokens.h generated
+/cmCTestProcessesLexer.cxx generated
+/cmCTestProcessesLexer.h generated
/cmDependsJavaLexer.cxx generated
/cmDependsJavaLexer.h generated
/cmDependsJavaParser.cxx generated
diff --git a/Source/LexerParser/cmCTestProcessesLexer.cxx b/Source/LexerParser/cmCTestProcessesLexer.cxx
new file mode 100644
index 0000000..2a5dab3
--- /dev/null
+++ b/Source/LexerParser/cmCTestProcessesLexer.cxx
@@ -0,0 +1,2224 @@
+#include "cmStandardLexer.h"
+
+#define FLEXINT_H 1
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yy_create_buffer
+#define cmCTestProcesses_yy_create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer cmCTestProcesses_yy_create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define cmCTestProcesses_yy_delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer cmCTestProcesses_yy_delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define cmCTestProcesses_yy_scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer cmCTestProcesses_yy_scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define cmCTestProcesses_yy_scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string cmCTestProcesses_yy_scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define cmCTestProcesses_yy_scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes cmCTestProcesses_yy_scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define cmCTestProcesses_yy_init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer cmCTestProcesses_yy_init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define cmCTestProcesses_yy_flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer cmCTestProcesses_yy_flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define cmCTestProcesses_yy_load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state cmCTestProcesses_yy_load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define cmCTestProcesses_yy_switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer cmCTestProcesses_yy_switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define cmCTestProcesses_yypush_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state cmCTestProcesses_yypush_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define cmCTestProcesses_yypop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state cmCTestProcesses_yypop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define cmCTestProcesses_yyensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack cmCTestProcesses_yyensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define cmCTestProcesses_yylex_ALREADY_DEFINED
+#else
+#define yylex cmCTestProcesses_yylex
+#endif
+
+#ifdef yyrestart
+#define cmCTestProcesses_yyrestart_ALREADY_DEFINED
+#else
+#define yyrestart cmCTestProcesses_yyrestart
+#endif
+
+#ifdef yylex_init
+#define cmCTestProcesses_yylex_init_ALREADY_DEFINED
+#else
+#define yylex_init cmCTestProcesses_yylex_init
+#endif
+
+#ifdef yylex_init_extra
+#define cmCTestProcesses_yylex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra cmCTestProcesses_yylex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define cmCTestProcesses_yylex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy cmCTestProcesses_yylex_destroy
+#endif
+
+#ifdef yyget_debug
+#define cmCTestProcesses_yyget_debug_ALREADY_DEFINED
+#else
+#define yyget_debug cmCTestProcesses_yyget_debug
+#endif
+
+#ifdef yyset_debug
+#define cmCTestProcesses_yyset_debug_ALREADY_DEFINED
+#else
+#define yyset_debug cmCTestProcesses_yyset_debug
+#endif
+
+#ifdef yyget_extra
+#define cmCTestProcesses_yyget_extra_ALREADY_DEFINED
+#else
+#define yyget_extra cmCTestProcesses_yyget_extra
+#endif
+
+#ifdef yyset_extra
+#define cmCTestProcesses_yyset_extra_ALREADY_DEFINED
+#else
+#define yyset_extra cmCTestProcesses_yyset_extra
+#endif
+
+#ifdef yyget_in
+#define cmCTestProcesses_yyget_in_ALREADY_DEFINED
+#else
+#define yyget_in cmCTestProcesses_yyget_in
+#endif
+
+#ifdef yyset_in
+#define cmCTestProcesses_yyset_in_ALREADY_DEFINED
+#else
+#define yyset_in cmCTestProcesses_yyset_in
+#endif
+
+#ifdef yyget_out
+#define cmCTestProcesses_yyget_out_ALREADY_DEFINED
+#else
+#define yyget_out cmCTestProcesses_yyget_out
+#endif
+
+#ifdef yyset_out
+#define cmCTestProcesses_yyset_out_ALREADY_DEFINED
+#else
+#define yyset_out cmCTestProcesses_yyset_out
+#endif
+
+#ifdef yyget_leng
+#define cmCTestProcesses_yyget_leng_ALREADY_DEFINED
+#else
+#define yyget_leng cmCTestProcesses_yyget_leng
+#endif
+
+#ifdef yyget_text
+#define cmCTestProcesses_yyget_text_ALREADY_DEFINED
+#else
+#define yyget_text cmCTestProcesses_yyget_text
+#endif
+
+#ifdef yyget_lineno
+#define cmCTestProcesses_yyget_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno cmCTestProcesses_yyget_lineno
+#endif
+
+#ifdef yyset_lineno
+#define cmCTestProcesses_yyset_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno cmCTestProcesses_yyset_lineno
+#endif
+
+#ifdef yyget_column
+#define cmCTestProcesses_yyget_column_ALREADY_DEFINED
+#else
+#define yyget_column cmCTestProcesses_yyget_column
+#endif
+
+#ifdef yyset_column
+#define cmCTestProcesses_yyset_column_ALREADY_DEFINED
+#else
+#define yyset_column cmCTestProcesses_yyset_column
+#endif
+
+#ifdef yywrap
+#define cmCTestProcesses_yywrap_ALREADY_DEFINED
+#else
+#define yywrap cmCTestProcesses_yywrap
+#endif
+
+#ifdef yyalloc
+#define cmCTestProcesses_yyalloc_ALREADY_DEFINED
+#else
+#define yyalloc cmCTestProcesses_yyalloc
+#endif
+
+#ifdef yyrealloc
+#define cmCTestProcesses_yyrealloc_ALREADY_DEFINED
+#else
+#define yyrealloc cmCTestProcesses_yyrealloc
+#endif
+
+#ifdef yyfree
+#define cmCTestProcesses_yyfree_ALREADY_DEFINED
+#else
+#define yyfree cmCTestProcesses_yyfree
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an
+ * integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin , yyscanner )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+ #define YY_LINENO_REWIND_TO(ptr)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+static void yyensure_buffer_stack ( yyscan_t yyscanner );
+static void yy_load_buffer_state ( yyscan_t yyscanner );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define cmCTestProcesses_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+typedef flex_uint8_t YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state ( yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner);
+static int yy_get_next_buffer ( yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+#define YY_NUM_RULES 8
+#define YY_END_OF_BUFFER 9
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static const flex_int16_t yy_accept[29] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 9, 7, 2, 5, 7, 4, 6, 3,
+ 2, 5, 0, 1, 4, 6, 3, 0
+ } ;
+
+static const YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 5, 6, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 7, 1, 7, 7, 7, 7,
+
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static const YY_CHAR yy_meta[8] =
+ { 0,
+ 1, 1, 1, 2, 2, 1, 2
+ } ;
+
+static const flex_int16_t yy_base[30] =
+ { 0,
+ 0, 0, 0, 0, 7, 0, 31, 12, 17, 0,
+ 0, 0, 34, 36, 29, 26, 26, 27, 23, 24,
+ 23, 20, 20, 36, 21, 16, 13, 36, 14
+ } ;
+
+static const flex_int16_t yy_def[30] =
+ { 0,
+ 28, 1, 1, 1, 28, 5, 1, 5, 5, 9,
+ 5, 5, 28, 28, 28, 28, 29, 28, 28, 28,
+ 28, 28, 29, 28, 28, 28, 28, 0, 28
+ } ;
+
+static const flex_int16_t yy_nxt[44] =
+ { 0,
+ 14, 14, 14, 15, 14, 16, 17, 14, 14, 18,
+ 14, 14, 19, 14, 14, 23, 27, 16, 17, 14,
+ 20, 26, 14, 25, 24, 22, 21, 27, 26, 25,
+ 24, 22, 21, 28, 14, 13, 28, 28, 28, 28,
+ 28, 28, 28
+ } ;
+
+static const flex_int16_t yy_chk[44] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5,
+ 5, 5, 5, 5, 8, 29, 27, 8, 8, 9,
+ 9, 26, 9, 25, 23, 22, 21, 20, 19, 18,
+ 17, 16, 15, 13, 7, 28, 28, 28, 28, 28,
+ 28, 28, 28
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+/*
+
+This file must be translated to C++ and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+ flex --nounistd -DFLEXINT_H --noline --header-file=cmCTestProcessesLexer.h -ocmCTestProcessesLexer.cxx cmCTestProcessesLexer.in.l
+
+Modify cmCTestProcessesLexer.cxx:
+ - remove trailing whitespace: sed -i 's/\s*$//' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+ - remove blank lines at end of file: sed -i '${/^$/d;}' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+ - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmCTestProcessesLexer.cxx
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+#ifndef __clang_analyzer__ /* Suppress clang scan-build warnings */
+
+#include "cmCTestProcessesLexerHelper.h"
+
+#include <string>
+
+#include <cstddef>
+
+/*--------------------------------------------------------------------------*/
+
+#define INITIAL 0
+#define PROCESSES_START 1
+#define PROCESSES_END 2
+#define RESOURCE_START 3
+#define RESOURCE_COUNT 4
+#define RESOURCE_END 5
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals ( yyscan_t yyscanner );
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
+
+ int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+
+ static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner);
+
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput ( yyscan_t yyscanner );
+#else
+static int input ( yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ yy_state_type yy_current_state;
+ char *yy_cp, *yy_bp;
+ int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+ }
+
+ yy_load_buffer_state( yyscanner );
+ }
+
+ {
+
+ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 29 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 36 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+{
+ BEGIN(RESOURCE_COUNT);
+ yyextra->SetResourceType(std::string(yytext, yyleng - 1));
+}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+{
+ BEGIN(PROCESSES_END);
+ std::size_t len = yyleng;
+ yyextra->SetProcessCount(std::stoll(yytext, &len, 10));
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+{
+ BEGIN(RESOURCE_END);
+ std::size_t len = yyleng;
+ yyextra->SetNeededSlots(std::stoll(yytext, &len, 10));
+ yyextra->WriteRequirement();
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+ BEGIN(RESOURCE_START);
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+ BEGIN(PROCESSES_START);
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+{
+ BEGIN(PROCESSES_START);
+ yyextra->WriteProcess();
+}
+ YY_BREAK
+case YY_STATE_EOF(RESOURCE_START):
+case YY_STATE_EOF(PROCESSES_END):
+case YY_STATE_EOF(RESOURCE_END):
+{
+ yyextra->WriteProcess();
+ return 0;
+}
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(PROCESSES_START):
+{
+ return 0;
+}
+ YY_BREAK
+case YY_STATE_EOF(RESOURCE_COUNT):
+{
+ return 1;
+}
+ YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+{
+ return 1;
+}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap( yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ char *source = yyg->yytext_ptr;
+ int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1);
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc( (void *) b->yy_ch_buf,
+ (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = NULL;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin , yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ /* "- 2" to take care of EOB's */
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ yy_state_type yy_current_state;
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 29 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ char *yy_cp = yyg->yy_c_buf_p;
+
+ YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 29 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ yy_is_jam = (yy_current_state == 28);
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+
+ static void yyunput (int c, char * yy_bp , yyscan_t yyscanner)
+{
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ int number_to_move = yyg->yy_n_chars + 2;
+ char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin , yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( yyscanner ) )
+ return 0;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner);
+ }
+
+ yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner);
+ yy_load_buffer_state( yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file , yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree( (void *) b->yy_ch_buf , yyscanner );
+
+ yyfree( (void *) b , yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_flush_buffer( b , yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return NULL;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b , yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner)
+{
+
+ return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = (yy_size_t) (_yybytes_len + 2);
+ buf = (char *) yyalloc( n , yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n , yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param _line_number line number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int _line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+ yylineno = _line_number;
+}
+
+/** Set the current column.
+ * @param _column_no column number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int _column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+ yycolumn = _column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * _in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = _in_str ;
+}
+
+void yyset_out (FILE * _out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = _out_str ;
+}
+
+int yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void yyset_debug (int _bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = _bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+int yylex_init(yyscan_t* ptr_yy_globals)
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals )
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = NULL;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = NULL;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = NULL;
+ yyout = NULL;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(yyg->yy_buffer_stack , yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ yyfree( yyg->yy_start_stack , yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s , yyscan_t yyscanner)
+{
+ int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ return malloc(size);
+}
+
+void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return realloc(ptr, size);
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+/*--------------------------------------------------------------------------*/
+
+#endif /* __clang_analyzer__ */
diff --git a/Source/LexerParser/cmCTestProcessesLexer.h b/Source/LexerParser/cmCTestProcessesLexer.h
new file mode 100644
index 0000000..b6437b4
--- /dev/null
+++ b/Source/LexerParser/cmCTestProcessesLexer.h
@@ -0,0 +1,692 @@
+#ifndef cmCTestProcesses_yyHEADER_H
+#define cmCTestProcesses_yyHEADER_H 1
+#define cmCTestProcesses_yyIN_HEADER 1
+
+#define FLEXINT_H 1
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+#ifdef yy_create_buffer
+#define cmCTestProcesses_yy_create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer cmCTestProcesses_yy_create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define cmCTestProcesses_yy_delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer cmCTestProcesses_yy_delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define cmCTestProcesses_yy_scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer cmCTestProcesses_yy_scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define cmCTestProcesses_yy_scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string cmCTestProcesses_yy_scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define cmCTestProcesses_yy_scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes cmCTestProcesses_yy_scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define cmCTestProcesses_yy_init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer cmCTestProcesses_yy_init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define cmCTestProcesses_yy_flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer cmCTestProcesses_yy_flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define cmCTestProcesses_yy_load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state cmCTestProcesses_yy_load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define cmCTestProcesses_yy_switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer cmCTestProcesses_yy_switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define cmCTestProcesses_yypush_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state cmCTestProcesses_yypush_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define cmCTestProcesses_yypop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state cmCTestProcesses_yypop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define cmCTestProcesses_yyensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack cmCTestProcesses_yyensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define cmCTestProcesses_yylex_ALREADY_DEFINED
+#else
+#define yylex cmCTestProcesses_yylex
+#endif
+
+#ifdef yyrestart
+#define cmCTestProcesses_yyrestart_ALREADY_DEFINED
+#else
+#define yyrestart cmCTestProcesses_yyrestart
+#endif
+
+#ifdef yylex_init
+#define cmCTestProcesses_yylex_init_ALREADY_DEFINED
+#else
+#define yylex_init cmCTestProcesses_yylex_init
+#endif
+
+#ifdef yylex_init_extra
+#define cmCTestProcesses_yylex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra cmCTestProcesses_yylex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define cmCTestProcesses_yylex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy cmCTestProcesses_yylex_destroy
+#endif
+
+#ifdef yyget_debug
+#define cmCTestProcesses_yyget_debug_ALREADY_DEFINED
+#else
+#define yyget_debug cmCTestProcesses_yyget_debug
+#endif
+
+#ifdef yyset_debug
+#define cmCTestProcesses_yyset_debug_ALREADY_DEFINED
+#else
+#define yyset_debug cmCTestProcesses_yyset_debug
+#endif
+
+#ifdef yyget_extra
+#define cmCTestProcesses_yyget_extra_ALREADY_DEFINED
+#else
+#define yyget_extra cmCTestProcesses_yyget_extra
+#endif
+
+#ifdef yyset_extra
+#define cmCTestProcesses_yyset_extra_ALREADY_DEFINED
+#else
+#define yyset_extra cmCTestProcesses_yyset_extra
+#endif
+
+#ifdef yyget_in
+#define cmCTestProcesses_yyget_in_ALREADY_DEFINED
+#else
+#define yyget_in cmCTestProcesses_yyget_in
+#endif
+
+#ifdef yyset_in
+#define cmCTestProcesses_yyset_in_ALREADY_DEFINED
+#else
+#define yyset_in cmCTestProcesses_yyset_in
+#endif
+
+#ifdef yyget_out
+#define cmCTestProcesses_yyget_out_ALREADY_DEFINED
+#else
+#define yyget_out cmCTestProcesses_yyget_out
+#endif
+
+#ifdef yyset_out
+#define cmCTestProcesses_yyset_out_ALREADY_DEFINED
+#else
+#define yyset_out cmCTestProcesses_yyset_out
+#endif
+
+#ifdef yyget_leng
+#define cmCTestProcesses_yyget_leng_ALREADY_DEFINED
+#else
+#define yyget_leng cmCTestProcesses_yyget_leng
+#endif
+
+#ifdef yyget_text
+#define cmCTestProcesses_yyget_text_ALREADY_DEFINED
+#else
+#define yyget_text cmCTestProcesses_yyget_text
+#endif
+
+#ifdef yyget_lineno
+#define cmCTestProcesses_yyget_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno cmCTestProcesses_yyget_lineno
+#endif
+
+#ifdef yyset_lineno
+#define cmCTestProcesses_yyset_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno cmCTestProcesses_yyset_lineno
+#endif
+
+#ifdef yyget_column
+#define cmCTestProcesses_yyget_column_ALREADY_DEFINED
+#else
+#define yyget_column cmCTestProcesses_yyget_column
+#endif
+
+#ifdef yyset_column
+#define cmCTestProcesses_yyset_column_ALREADY_DEFINED
+#else
+#define yyset_column cmCTestProcesses_yyset_column
+#endif
+
+#ifdef yywrap
+#define cmCTestProcesses_yywrap_ALREADY_DEFINED
+#else
+#define yywrap cmCTestProcesses_yywrap
+#endif
+
+#ifdef yyalloc
+#define cmCTestProcesses_yyalloc_ALREADY_DEFINED
+#else
+#define yyalloc cmCTestProcesses_yyalloc
+#endif
+
+#ifdef yyrealloc
+#define cmCTestProcesses_yyrealloc_ALREADY_DEFINED
+#else
+#define yyrealloc cmCTestProcesses_yyrealloc
+#endif
+
+#ifdef yyfree
+#define cmCTestProcesses_yyfree_ALREADY_DEFINED
+#else
+#define yyfree cmCTestProcesses_yyfree
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart ( FILE *input_file , yyscan_t yyscanner );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner );
+void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner );
+void yypop_buffer_state ( yyscan_t yyscanner );
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
+
+void *yyalloc ( yy_size_t , yyscan_t yyscanner );
+void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
+void yyfree ( void * , yyscan_t yyscanner );
+
+/* Begin user sect3 */
+
+#define cmCTestProcesses_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define PROCESSES_START 1
+#define PROCESSES_END 2
+#define RESOURCE_START 3
+#define RESOURCE_COUNT 4
+#define RESOURCE_END 5
+
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( yyscan_t yyscanner );
+
+int yyget_debug ( yyscan_t yyscanner );
+
+void yyset_debug ( int debug_flag , yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner );
+
+FILE *yyget_in ( yyscan_t yyscanner );
+
+void yyset_in ( FILE * _in_str , yyscan_t yyscanner );
+
+FILE *yyget_out ( yyscan_t yyscanner );
+
+void yyset_out ( FILE * _out_str , yyscan_t yyscanner );
+
+ int yyget_leng ( yyscan_t yyscanner );
+
+char *yyget_text ( yyscan_t yyscanner );
+
+int yyget_lineno ( yyscan_t yyscanner );
+
+void yyset_lineno ( int _line_number , yyscan_t yyscanner );
+
+int yyget_column ( yyscan_t yyscanner );
+
+void yyset_column ( int _column_no , yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( yyscan_t yyscanner );
+#else
+extern int yywrap ( yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * , yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#ifndef cmCTestProcesses_yy_create_buffer_ALREADY_DEFINED
+#undef yy_create_buffer
+#endif
+#ifndef cmCTestProcesses_yy_delete_buffer_ALREADY_DEFINED
+#undef yy_delete_buffer
+#endif
+#ifndef cmCTestProcesses_yy_scan_buffer_ALREADY_DEFINED
+#undef yy_scan_buffer
+#endif
+#ifndef cmCTestProcesses_yy_scan_string_ALREADY_DEFINED
+#undef yy_scan_string
+#endif
+#ifndef cmCTestProcesses_yy_scan_bytes_ALREADY_DEFINED
+#undef yy_scan_bytes
+#endif
+#ifndef cmCTestProcesses_yy_init_buffer_ALREADY_DEFINED
+#undef yy_init_buffer
+#endif
+#ifndef cmCTestProcesses_yy_flush_buffer_ALREADY_DEFINED
+#undef yy_flush_buffer
+#endif
+#ifndef cmCTestProcesses_yy_load_buffer_state_ALREADY_DEFINED
+#undef yy_load_buffer_state
+#endif
+#ifndef cmCTestProcesses_yy_switch_to_buffer_ALREADY_DEFINED
+#undef yy_switch_to_buffer
+#endif
+#ifndef cmCTestProcesses_yypush_buffer_state_ALREADY_DEFINED
+#undef yypush_buffer_state
+#endif
+#ifndef cmCTestProcesses_yypop_buffer_state_ALREADY_DEFINED
+#undef yypop_buffer_state
+#endif
+#ifndef cmCTestProcesses_yyensure_buffer_stack_ALREADY_DEFINED
+#undef yyensure_buffer_stack
+#endif
+#ifndef cmCTestProcesses_yylex_ALREADY_DEFINED
+#undef yylex
+#endif
+#ifndef cmCTestProcesses_yyrestart_ALREADY_DEFINED
+#undef yyrestart
+#endif
+#ifndef cmCTestProcesses_yylex_init_ALREADY_DEFINED
+#undef yylex_init
+#endif
+#ifndef cmCTestProcesses_yylex_init_extra_ALREADY_DEFINED
+#undef yylex_init_extra
+#endif
+#ifndef cmCTestProcesses_yylex_destroy_ALREADY_DEFINED
+#undef yylex_destroy
+#endif
+#ifndef cmCTestProcesses_yyget_debug_ALREADY_DEFINED
+#undef yyget_debug
+#endif
+#ifndef cmCTestProcesses_yyset_debug_ALREADY_DEFINED
+#undef yyset_debug
+#endif
+#ifndef cmCTestProcesses_yyget_extra_ALREADY_DEFINED
+#undef yyget_extra
+#endif
+#ifndef cmCTestProcesses_yyset_extra_ALREADY_DEFINED
+#undef yyset_extra
+#endif
+#ifndef cmCTestProcesses_yyget_in_ALREADY_DEFINED
+#undef yyget_in
+#endif
+#ifndef cmCTestProcesses_yyset_in_ALREADY_DEFINED
+#undef yyset_in
+#endif
+#ifndef cmCTestProcesses_yyget_out_ALREADY_DEFINED
+#undef yyget_out
+#endif
+#ifndef cmCTestProcesses_yyset_out_ALREADY_DEFINED
+#undef yyset_out
+#endif
+#ifndef cmCTestProcesses_yyget_leng_ALREADY_DEFINED
+#undef yyget_leng
+#endif
+#ifndef cmCTestProcesses_yyget_text_ALREADY_DEFINED
+#undef yyget_text
+#endif
+#ifndef cmCTestProcesses_yyget_lineno_ALREADY_DEFINED
+#undef yyget_lineno
+#endif
+#ifndef cmCTestProcesses_yyset_lineno_ALREADY_DEFINED
+#undef yyset_lineno
+#endif
+#ifndef cmCTestProcesses_yyget_column_ALREADY_DEFINED
+#undef yyget_column
+#endif
+#ifndef cmCTestProcesses_yyset_column_ALREADY_DEFINED
+#undef yyset_column
+#endif
+#ifndef cmCTestProcesses_yywrap_ALREADY_DEFINED
+#undef yywrap
+#endif
+#ifndef cmCTestProcesses_yyget_lval_ALREADY_DEFINED
+#undef yyget_lval
+#endif
+#ifndef cmCTestProcesses_yyset_lval_ALREADY_DEFINED
+#undef yyset_lval
+#endif
+#ifndef cmCTestProcesses_yyget_lloc_ALREADY_DEFINED
+#undef yyget_lloc
+#endif
+#ifndef cmCTestProcesses_yyset_lloc_ALREADY_DEFINED
+#undef yyset_lloc
+#endif
+#ifndef cmCTestProcesses_yyalloc_ALREADY_DEFINED
+#undef yyalloc
+#endif
+#ifndef cmCTestProcesses_yyrealloc_ALREADY_DEFINED
+#undef yyrealloc
+#endif
+#ifndef cmCTestProcesses_yyfree_ALREADY_DEFINED
+#undef yyfree
+#endif
+#ifndef cmCTestProcesses_yytext_ALREADY_DEFINED
+#undef yytext
+#endif
+#ifndef cmCTestProcesses_yyleng_ALREADY_DEFINED
+#undef yyleng
+#endif
+#ifndef cmCTestProcesses_yyin_ALREADY_DEFINED
+#undef yyin
+#endif
+#ifndef cmCTestProcesses_yyout_ALREADY_DEFINED
+#undef yyout
+#endif
+#ifndef cmCTestProcesses_yy_flex_debug_ALREADY_DEFINED
+#undef yy_flex_debug
+#endif
+#ifndef cmCTestProcesses_yylineno_ALREADY_DEFINED
+#undef yylineno
+#endif
+#ifndef cmCTestProcesses_yytables_fload_ALREADY_DEFINED
+#undef yytables_fload
+#endif
+#ifndef cmCTestProcesses_yytables_destroy_ALREADY_DEFINED
+#undef yytables_destroy
+#endif
+#ifndef cmCTestProcesses_yyTABLES_NAME_ALREADY_DEFINED
+#undef yyTABLES_NAME
+#endif
+
+#undef cmCTestProcesses_yyIN_HEADER
+#endif /* cmCTestProcesses_yyHEADER_H */
diff --git a/Source/LexerParser/cmCTestProcessesLexer.in.l b/Source/LexerParser/cmCTestProcessesLexer.in.l
new file mode 100644
index 0000000..ef494d5
--- /dev/null
+++ b/Source/LexerParser/cmCTestProcessesLexer.in.l
@@ -0,0 +1,102 @@
+%{
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+/*
+
+This file must be translated to C++ and modified to build everywhere.
+
+Run flex >= 2.6 like this:
+
+ flex --nounistd -DFLEXINT_H --noline --header-file=cmCTestProcessesLexer.h -ocmCTestProcessesLexer.cxx cmCTestProcessesLexer.in.l
+
+Modify cmCTestProcessesLexer.cxx:
+ - remove trailing whitespace: sed -i 's/\s*$//' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+ - remove blank lines at end of file: sed -i '${/^$/d;}' cmCTestProcessesLexer.h cmCTestProcessesLexer.cxx
+ - #include "cmStandardLexer.h" at the top: sed -i '1i#include "cmStandardLexer.h"' cmCTestProcessesLexer.cxx
+
+*/
+
+/* IWYU pragma: no_forward_declare yyguts_t */
+
+#ifndef __clang_analyzer__ /* Suppress clang scan-build warnings */
+
+#include "cmCTestProcessesLexerHelper.h"
+
+#include <string>
+
+#include <cstddef>
+
+/*--------------------------------------------------------------------------*/
+%}
+
+%option prefix="cmCTestProcesses_yy"
+
+%option reentrant
+%option noyywrap
+%option nodefault
+%pointer
+
+%s PROCESSES_START
+%s PROCESSES_END
+%s RESOURCE_START
+%s RESOURCE_COUNT
+%s RESOURCE_END
+
+NUMBER [0-9]+
+IDENTIFIER [a-z_][a-z0-9_]*
+
+%%
+
+<INITIAL,PROCESSES_START,RESOURCE_START>{IDENTIFIER}: {
+ BEGIN(RESOURCE_COUNT);
+ yyextra->SetResourceType(std::string(yytext, yyleng - 1));
+}
+
+<INITIAL,PROCESSES_START>{NUMBER} {
+ BEGIN(PROCESSES_END);
+ std::size_t len = yyleng;
+ yyextra->SetProcessCount(std::stoll(yytext, &len, 10));
+}
+
+<RESOURCE_COUNT>{NUMBER} {
+ BEGIN(RESOURCE_END);
+ std::size_t len = yyleng;
+ yyextra->SetNeededSlots(std::stoll(yytext, &len, 10));
+ yyextra->WriteRequirement();
+}
+
+<PROCESSES_END,RESOURCE_END>,+ {
+ BEGIN(RESOURCE_START);
+}
+
+<INITIAL,PROCESSES_START,RESOURCE_START>;+ {
+ BEGIN(PROCESSES_START);
+}
+
+<PROCESSES_END,RESOURCE_END>;+ {
+ BEGIN(PROCESSES_START);
+ yyextra->WriteProcess();
+}
+
+<RESOURCE_START,PROCESSES_END,RESOURCE_END><<EOF>> {
+ yyextra->WriteProcess();
+ return 0;
+}
+
+<INITIAL,PROCESSES_START><<EOF>> {
+ return 0;
+}
+
+<<EOF>> {
+ return 1;
+}
+
+.|\n {
+ return 1;
+}
+
+%%
+
+/*--------------------------------------------------------------------------*/
+
+#endif /* __clang_analyzer__ */
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 204810e..abb258b 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -2,10 +2,12 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMake_BINARY_DIR}/Source
${CMake_SOURCE_DIR}/Source
+ ${CMake_SOURCE_DIR}/Source/CTest
)
set(CMakeLib_TESTS
testArgumentParser.cxx
+ testCTestProcesses.cxx
testGeneratedFileStream.cxx
testRST.cxx
testRange.cxx
@@ -41,7 +43,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testXMLParser.h.in
create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS})
add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS})
-target_link_libraries(CMakeLibTests CMakeLib)
+target_link_libraries(CMakeLibTests CMakeLib CTestLib)
set_property(TARGET CMakeLibTests PROPERTY C_CLANG_TIDY "")
set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "")
diff --git a/Tests/CMakeLib/testCTestProcesses.cxx b/Tests/CMakeLib/testCTestProcesses.cxx
new file mode 100644
index 0000000..acf4f67
--- /dev/null
+++ b/Tests/CMakeLib/testCTestProcesses.cxx
@@ -0,0 +1,137 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "cmCTestTestHandler.h"
+
+struct ExpectedParseResult
+{
+ std::string String;
+ bool ExpectedReturnValue;
+ std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>
+ ExpectedValue;
+};
+
+static const std::vector<ExpectedParseResult> expectedResults{
+ /* clang-format off */
+ { "threads:2", true, {
+ { { "threads", 2, 1 } },
+ } },
+ { "3,threads:2", true, {
+ { { "threads", 2, 1 } },
+ { { "threads", 2, 1 } },
+ { { "threads", 2, 1 } },
+ } },
+ { "3,threads:2,gpus:4", true, {
+ { { "threads", 2, 1 }, { "gpus", 4, 1 } },
+ { { "threads", 2, 1 }, { "gpus", 4, 1 } },
+ { { "threads", 2, 1 }, { "gpus", 4, 1 } },
+ } },
+ { "2,threads:2;gpus:4", true, {
+ { { "threads", 2, 1 } },
+ { { "threads", 2, 1 } },
+ { { "gpus", 4, 1 } },
+ } },
+ { "threads:2;2,gpus:4", true, {
+ { { "threads", 2, 1 } },
+ { { "gpus", 4, 1 } },
+ { { "gpus", 4, 1 } },
+ } },
+ { "threads:2;gpus:4", true, {
+ { { "threads", 2, 1 } },
+ { { "gpus", 4, 1 } },
+ } },
+ { "1,threads:2;0,gpus:4", true, {
+ { { "threads", 2, 1 } },
+ } },
+ { "1,_:1", true, {
+ { { "_", 1, 1 } },
+ } },
+ { "1,a:1", true, {
+ { { "a", 1, 1 } },
+ } },
+ { "2", true, {
+ {},
+ {},
+ } },
+ { "1;2,threads:1", true, {
+ {},
+ { { "threads", 1, 1 } },
+ { { "threads", 1, 1 } },
+ } },
+ { "1,,threads:1", true, {
+ { { "threads", 1, 1 } },
+ } },
+ { ";1,threads:1", true, {
+ { { "threads", 1, 1 } },
+ } },
+ { "1,threads:1;", true, {
+ { { "threads", 1, 1 } },
+ } },
+ { "1,threads:1,", true, {
+ { { "threads", 1, 1 } },
+ } },
+ { "threads:1;;threads:2", true, {
+ { { "threads", 1, 1 } },
+ { { "threads", 2, 1 } },
+ } },
+ { "1,", true, {
+ {},
+ } },
+ { ";", true, {} },
+ { "", true, {} },
+ { ",", false, {} },
+ { "1,0:1", false, {} },
+ { "1,A:1", false, {} },
+ { "1,a-b:1", false, {} },
+ { "invalid", false, {} },
+ { ",1,invalid:1", false, {} },
+ { "1,1", false, {} },
+ { "-1,invalid:1", false, {} },
+ { "1,invalid:*", false, {} },
+ { "1,invalid:-1", false, {} },
+ { "1,invalid:-", false, {} },
+ { "1,invalid:ab2", false, {} },
+ { "1,invalid :2", false, {} },
+ { "1, invalid:2", false, {} },
+ { "1,invalid:ab", false, {} },
+ /* clang-format on */
+};
+
+bool TestExpectedParseResult(const ExpectedParseResult& expected)
+{
+ std::vector<std::vector<cmCTestTestHandler::cmCTestTestResourceRequirement>>
+ result;
+ bool retval;
+ if ((retval = cmCTestTestHandler::ParseProcessesProperty(
+ expected.String, result)) != expected.ExpectedReturnValue) {
+ std::cout << "ParseProcessesProperty(\"" << expected.String
+ << "\") returned " << retval << ", should be "
+ << expected.ExpectedReturnValue << std::endl;
+ return false;
+ }
+
+ if (result != expected.ExpectedValue) {
+ std::cout << "ParseProcessesProperty(\"" << expected.String
+ << "\") did not yield expected set of processes" << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+int testCTestProcesses(int /*unused*/, char* /*unused*/ [])
+{
+ int retval = 0;
+
+ for (auto const& expected : expectedResults) {
+ if (!TestExpectedParseResult(expected)) {
+ retval = 1;
+ }
+ }
+
+ return retval;
+}
diff --git a/Utilities/Scripts/regenerate-lexers.bash b/Utilities/Scripts/regenerate-lexers.bash
index b09f25b..2bd58fe 100755
--- a/Utilities/Scripts/regenerate-lexers.bash
+++ b/Utilities/Scripts/regenerate-lexers.bash
@@ -11,6 +11,7 @@ pushd "${BASH_SOURCE%/*}/../../Source/LexerParser" > /dev/null
for lexer in \
CommandArgument \
+ CTestProcesses \
DependsJava \
Expr \
Fortran
-----------------------------------------------------------------------
Summary of changes:
Help/command/ctest_test.rst | 6 +
Help/manual/cmake-properties.7.rst | 1 +
Help/manual/ctest.1.rst | 229 ++
Help/prop_test/PROCESSES.rst | 54 +
Help/prop_test/RESOURCE_LOCK.rst | 8 +
Help/release/dev/ctest-hardware-allocation.rst | 6 +
Source/CMakeLists.txt | 8 +
Source/CTest/cmCTestBinPacker.cxx | 201 ++
Source/CTest/cmCTestBinPacker.h | 31 +
Source/CTest/cmCTestHardwareAllocator.cxx | 86 +
Source/CTest/cmCTestHardwareAllocator.h | 39 +
Source/CTest/cmCTestHardwareSpec.cxx | 133 ++
Source/CTest/cmCTestHardwareSpec.h | 40 +
Source/CTest/cmCTestMultiProcessHandler.cxx | 166 +-
Source/CTest/cmCTestMultiProcessHandler.h | 33 +-
Source/CTest/cmCTestProcessesLexerHelper.cxx | 55 +
Source/CTest/cmCTestProcessesLexerHelper.h | 44 +
Source/CTest/cmCTestRunTest.cxx | 43 +
Source/CTest/cmCTestRunTest.h | 19 +-
Source/CTest/cmCTestTestCommand.cxx | 4 +
Source/CTest/cmCTestTestCommand.h | 1 +
Source/CTest/cmCTestTestHandler.cxx | 43 +
Source/CTest/cmCTestTestHandler.h | 19 +
Source/LexerParser/.gitattributes | 2 +
Source/LexerParser/cmCTestProcessesLexer.cxx | 2224 ++++++++++++++++++++
Source/LexerParser/cmCTestProcessesLexer.h | 692 ++++++
Source/LexerParser/cmCTestProcessesLexer.in.l | 102 +
Source/cmCTest.cxx | 9 +
Source/ctest.cxx | 1 +
Tests/CMakeLib/CMakeLists.txt | 8 +-
Tests/CMakeLib/testCTestBinPacker.cxx | 300 +++
Tests/CMakeLib/testCTestHardwareAllocator.cxx | 426 ++++
Tests/CMakeLib/testCTestHardwareSpec.cxx | 84 +
.../CMakeLib/testCTestHardwareSpec_data/spec1.json | 23 +
.../testCTestHardwareSpec_data/spec10.json | 11 +
.../testCTestHardwareSpec_data/spec11.json | 12 +
.../testCTestHardwareSpec_data/spec12.json | 1 +
.../testCTestHardwareSpec_data/spec13.json | 1 +
.../testCTestHardwareSpec_data/spec14.json | 8 +
.../testCTestHardwareSpec_data/spec15.json | 8 +
.../testCTestHardwareSpec_data/spec16.json | 8 +
.../testCTestHardwareSpec_data/spec17.json | 11 +
.../testCTestHardwareSpec_data/spec18.json | 11 +
.../CMakeLib/testCTestHardwareSpec_data/spec2.json | 4 +
.../CMakeLib/testCTestHardwareSpec_data/spec3.json | 8 +
.../CMakeLib/testCTestHardwareSpec_data/spec4.json | 4 +
.../CMakeLib/testCTestHardwareSpec_data/spec5.json | 2 +
.../CMakeLib/testCTestHardwareSpec_data/spec6.json | 5 +
.../CMakeLib/testCTestHardwareSpec_data/spec7.json | 8 +
.../CMakeLib/testCTestHardwareSpec_data/spec8.json | 9 +
.../CMakeLib/testCTestHardwareSpec_data/spec9.json | 10 +
Tests/CMakeLib/testCTestProcesses.cxx | 137 ++
Tests/RunCMake/CMakeLists.txt | 38 +
Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake | 1 +
.../CTestCommandLine/show-only_json-v1_check.py | 65 +-
.../CTestHardwareAllocation/CMakeLists.txt.in | 9 +
.../CTestHardwareAllocation/HardwareCommon.cmake | 23 +
.../CTestHardwareAllocation/RunCMakeTest.cmake | 167 ++
.../checkfree1-ctest-s-hw-check.cmake | 1 +
.../CTestHardwareAllocation/checkfree1.cmake | 7 +
.../checkfree2-ctest-s-hw-check.cmake | 1 +
.../CTestHardwareAllocation/checkfree2.cmake | 8 +
.../cthwalloc-verify-baddealloc-result.txt} | 0
.../cthwalloc-verify-baddealloc.log | 2 +
.../cthwalloc-verify-badtest1-result.txt} | 0
.../cthwalloc-verify-badtest1.log | 1 +
.../cthwalloc-verify-badtest2-result.txt} | 0
.../cthwalloc-verify-badtest2.log | 2 +
.../cthwalloc-verify-badtest3-result.txt} | 0
.../cthwalloc-verify-badtest3.log | 3 +
.../cthwalloc-verify-badtest4-result.txt} | 0
.../cthwalloc-verify-badtest4.log | 3 +
.../cthwalloc-verify-badtest5-result.txt} | 0
.../cthwalloc-verify-badtest5.log | 1 +
.../cthwalloc-verify-good1.log | 14 +
.../cthwalloc-verify-good2.log} | 0
.../cthwalloc-verify-leak-result.txt} | 0
.../cthwalloc-verify-leak.log | 1 +
.../cthwalloc-verify-nobegin-result.txt} | 0
.../cthwalloc-verify-nobegin.log} | 0
.../cthwalloc-verify-noend-result.txt} | 0
.../cthwalloc-verify-noend.log | 1 +
.../cthwalloc-verify-noid-result.txt} | 0
.../cthwalloc-verify-noid.log | 2 +
.../cthwalloc-verify-nolog-result.txt} | 0
.../cthwalloc-verify-nores-result.txt} | 0
.../cthwalloc-verify-nores.log | 2 +
.../cthwalloc-verify-notenough-result.txt} | 0
.../cthwalloc-verify-notenough.log | 2 +
.../cthwalloc-write-noproc-count-result.txt} | 0
.../cthwalloc-write-proc-badcount-result.txt} | 0
.../cthwalloc-write-proc-badres-result.txt} | 0
.../cthwalloc-write-proc-badwidgets1-result.txt} | 0
.../cthwalloc-write-proc-badwidgets2-result.txt} | 0
.../cthwalloc-write-proc-badwidgets3-result.txt} | 0
.../cthwalloc-write-proc-badwidgets4-result.txt} | 0
.../cthwalloc-write-proc-badwidgets5-result.txt} | 0
.../cthwalloc-write-proc-badwidgets6-result.txt} | 0
.../cthwalloc-write-proc-badwidgets7-result.txt} | 0
.../cthwalloc-write-proc-good1-check.cmake | 20 +
.../cthwalloc-write-proc-good2-check.cmake | 6 +
.../cthwalloc-write-proc-nocount-result.txt} | 0
.../cthwalloc-write-proc-nores-result.txt} | 0
.../cthwalloc-write-proc-nowidgets-result.txt} | 0
.../RunCMake/CTestHardwareAllocation/cthwalloc.cxx | 396 ++++
.../ensure_parallel-ctest-s-hw-check.cmake | 16 +
.../CTestHardwareAllocation/ensure_parallel.cmake | 11 +
Tests/RunCMake/CTestHardwareAllocation/hwspec.json | 55 +
.../lotsoftests-ctest-s-hw-check.cmake | 1 +
.../CTestHardwareAllocation/lotsoftests.cmake | 16 +
.../notenough1-ctest-s-hw-check.cmake | 3 +
.../notenough1-ctest-s-hw-result.txt} | 0
.../notenough1-ctest-s-hw-stderr.txt | 4 +
.../CTestHardwareAllocation/notenough1.cmake | 5 +
.../notenough2-ctest-s-hw-check.cmake | 3 +
.../notenough2-ctest-s-hw-result.txt} | 0
.../notenough2-ctest-s-hw-stderr.txt | 4 +
.../CTestHardwareAllocation/notenough2.cmake | 5 +
.../process_count-ctest-s-hw-check.cmake | 1 +
.../CTestHardwareAllocation/process_count.cmake | 5 +
.../RunCMake/CTestHardwareAllocation/test.cmake.in | 23 +
Utilities/Scripts/regenerate-lexers.bash | 1 +
122 files changed, 6315 insertions(+), 12 deletions(-)
create mode 100644 Help/prop_test/PROCESSES.rst
create mode 100644 Help/release/dev/ctest-hardware-allocation.rst
create mode 100644 Source/CTest/cmCTestBinPacker.cxx
create mode 100644 Source/CTest/cmCTestBinPacker.h
create mode 100644 Source/CTest/cmCTestHardwareAllocator.cxx
create mode 100644 Source/CTest/cmCTestHardwareAllocator.h
create mode 100644 Source/CTest/cmCTestHardwareSpec.cxx
create mode 100644 Source/CTest/cmCTestHardwareSpec.h
create mode 100644 Source/CTest/cmCTestProcessesLexerHelper.cxx
create mode 100644 Source/CTest/cmCTestProcessesLexerHelper.h
create mode 100644 Source/LexerParser/cmCTestProcessesLexer.cxx
create mode 100644 Source/LexerParser/cmCTestProcessesLexer.h
create mode 100644 Source/LexerParser/cmCTestProcessesLexer.in.l
create mode 100644 Tests/CMakeLib/testCTestBinPacker.cxx
create mode 100644 Tests/CMakeLib/testCTestHardwareAllocator.cxx
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec.cxx
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec1.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec10.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec11.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec12.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec13.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec14.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec15.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec16.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec17.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec18.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec2.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec3.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec4.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec5.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec6.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec7.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec8.json
create mode 100644 Tests/CMakeLib/testCTestHardwareSpec_data/spec9.json
create mode 100644 Tests/CMakeLib/testCTestProcesses.cxx
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/CMakeLists.txt.in
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/HardwareCommon.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/RunCMakeTest.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/checkfree1-ctest-s-hw-check.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/checkfree1.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/checkfree2-ctest-s-hw-check.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/checkfree2.cmake
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-baddealloc-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-baddealloc.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-badtest1-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest1.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-badtest2-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest2.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-badtest3-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest3.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-badtest4-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest4.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-badtest5-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-badtest5.log
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-good1.log
copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/CTestHardwareAllocation/cthwalloc-verify-good2.log} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-leak-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-leak.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-nobegin-result.txt} (100%)
copy Tests/{Wrapping/vtkIncluded.cxx => RunCMake/CTestHardwareAllocation/cthwalloc-verify-nobegin.log} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-noend-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noend.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-noid-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-noid.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-nolog-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-nores-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-nores.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-verify-notenough-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-verify-notenough.log
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-noproc-count-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badcount-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badres-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets1-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets2-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets3-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets4-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets5-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets6-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-badwidgets7-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good1-check.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc-write-proc-good2-check.cmake
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-nocount-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-nores-result.txt} (100%)
copy Tests/RunCMake/{while/MissingArgument-result.txt => CTestHardwareAllocation/cthwalloc-write-proc-nowidgets-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/cthwalloc.cxx
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/ensure_parallel-ctest-s-hw-check.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/ensure_parallel.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/hwspec.json
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/lotsoftests-ctest-s-hw-check.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/lotsoftests.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-check.cmake
copy Tests/RunCMake/{ctest_submit/RepeatRETURN_VALUE-result.txt => CTestHardwareAllocation/notenough1-ctest-s-hw-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/notenough1-ctest-s-hw-stderr.txt
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/notenough1.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-check.cmake
copy Tests/RunCMake/{ctest_submit/RepeatRETURN_VALUE-result.txt => CTestHardwareAllocation/notenough2-ctest-s-hw-result.txt} (100%)
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/notenough2-ctest-s-hw-stderr.txt
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/notenough2.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/process_count-ctest-s-hw-check.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/process_count.cmake
create mode 100644 Tests/RunCMake/CTestHardwareAllocation/test.cmake.in
hooks/post-receive
--
CMake
More information about the Cmake-commits
mailing list