mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Makefiles: Group parallel build output via GNU Make --output-sync
A parallel `cmake --build` with a Makefiles generator interleaved the output of concurrent recipes. When CMake is the one passing the parallel flag and a build-time probe finds GNU Make 4.0 or newer, append `-Otarget` so each recipe's output stays grouped. A native `-- -j` leaves the job count unset and opts out. Honor `USES_TERMINAL` under grouping by prefixing such recipes with a `$(CMAKE_USES_TERMINAL_PREFIX)` variable, empty by default and set to `+` next to `-Otarget`, so interactive commands stay unbuffered. Fixes: #27510
This commit is contained in:
@@ -869,6 +869,16 @@ following options:
|
||||
Some native build tools always build in parallel. The use of ``<jobs>``
|
||||
value of ``1`` can be used to limit to a single job.
|
||||
|
||||
.. versionadded:: 4.5
|
||||
When CMake is the one passing the parallel flag to the build tool (through
|
||||
this option or the :envvar:`CMAKE_BUILD_PARALLEL_LEVEL` environment
|
||||
variable) and that tool is GNU Make 4.0 or newer, the output of each
|
||||
recipe is grouped so that concurrent jobs do not interleave. Request
|
||||
parallelism natively, e.g. ``cmake --build . -- -j``, to opt out and
|
||||
stream output as it is produced. Since
|
||||
:envvar:`CMAKE_BUILD_PARALLEL_LEVEL` likewise enables grouping, unset it
|
||||
to opt out when it is set.
|
||||
|
||||
.. option:: -t <tgt>..., --target <tgt>...
|
||||
|
||||
Build ``<tgt>`` instead of the default target. Multiple targets may be
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
cmake-build-output-sync
|
||||
-----------------------
|
||||
|
||||
* The :option:`cmake --build` command now groups the output of each recipe in
|
||||
a parallel build so that concurrent jobs do not interleave, when CMake is
|
||||
the one passing the parallel flag to the build tool (via :option:`--parallel
|
||||
<cmake--build --parallel>` or :envvar:`CMAKE_BUILD_PARALLEL_LEVEL`) and that
|
||||
tool is GNU Make 4.0 or newer. Request parallelism natively, e.g.
|
||||
``cmake --build . -- -j``, to opt out and stream output as it is produced.
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <cmext/algorithm>
|
||||
#include <cmext/memory>
|
||||
|
||||
#include "cmsys/RegularExpression.hxx"
|
||||
|
||||
#include "cmDuration.h"
|
||||
#include "cmGeneratedFileStream.h"
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
@@ -589,6 +592,21 @@ cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
|
||||
} else {
|
||||
makeCommand.Add(cmStrCat("-j", jobs));
|
||||
}
|
||||
|
||||
// Group each recipe's output so parallel jobs do not interleave. Only
|
||||
// done when CMake is the one passing the parallel flag to make; a native
|
||||
// `-- -j` leaves jobs unset and thus opts out. Grouping requires GNU
|
||||
// Make 4.0+ (its --output-sync); IsGNUMakeJobServerAware() excludes
|
||||
// non-GNU makes such as JOM before the version probe runs. Emitted
|
||||
// before the user's make options so a native `-- -O<mode>` overrides it.
|
||||
if (this->IsGNUMakeJobServerAware() &&
|
||||
this->MakeSupportsOutputSync(makeProgram)) {
|
||||
makeCommand.Add("-Otarget");
|
||||
// Set the (empty by default) USES_TERMINAL recipe prefix to "+" so
|
||||
// GNU Make leaves those recipes unbuffered, keeping interactive
|
||||
// commands able to reach the terminal under -Otarget.
|
||||
makeCommand.Add("CMAKE_USES_TERMINAL_PREFIX=+");
|
||||
}
|
||||
}
|
||||
|
||||
makeCommand.Add(makeOptions.begin(), makeOptions.end());
|
||||
@@ -604,6 +622,35 @@ cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
|
||||
return { std::move(makeCommand) };
|
||||
}
|
||||
|
||||
bool cmGlobalUnixMakefileGenerator3::MakeSupportsOutputSync(
|
||||
std::string const& makeProgram)
|
||||
{
|
||||
if (this->OutputSyncSupportState == OutputSyncSupport::Unknown) {
|
||||
// Any probe failure (spawn error, timeout, non-GNU or old make) leaves
|
||||
// grouping off; it is a convenience and must never fail the build.
|
||||
this->OutputSyncSupportState = OutputSyncSupport::No;
|
||||
|
||||
std::string version;
|
||||
std::string error;
|
||||
int retVal = 1;
|
||||
std::vector<std::string> command{ this->SelectMakeProgram(makeProgram),
|
||||
"--version" };
|
||||
if (cmSystemTools::RunSingleCommand(command, &version, &error, &retVal,
|
||||
nullptr, cmSystemTools::OUTPUT_NONE,
|
||||
cmDuration(30)) &&
|
||||
retVal == 0) {
|
||||
// GNU Make's version line is not localized, so no LC_ALL=C is needed.
|
||||
cmsys::RegularExpression versionRegex("GNU Make ([0-9]+(\\.[0-9]+)*)");
|
||||
if (versionRegex.find(version) &&
|
||||
!cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
|
||||
versionRegex.match(1), "4.0")) {
|
||||
this->OutputSyncSupportState = OutputSyncSupport::Yes;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this->OutputSyncSupportState == OutputSyncSupport::Yes;
|
||||
}
|
||||
|
||||
void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules(
|
||||
std::ostream& ruleFileStream, std::set<std::string>& emitted)
|
||||
{
|
||||
|
||||
@@ -309,6 +309,18 @@ protected:
|
||||
private:
|
||||
char const* GetBuildIgnoreErrorsFlag() const override { return "-i"; }
|
||||
|
||||
// Probe the make tool (once, memoized) for whether it is GNU Make 4.0 or
|
||||
// newer and thus supports grouped output via --output-sync.
|
||||
bool MakeSupportsOutputSync(std::string const& makeProgram);
|
||||
|
||||
enum class OutputSyncSupport
|
||||
{
|
||||
Unknown,
|
||||
Yes,
|
||||
No
|
||||
};
|
||||
OutputSyncSupport OutputSyncSupportState = OutputSyncSupport::Unknown;
|
||||
|
||||
std::map<cmStateSnapshot, std::set<cmGeneratorTarget const*>,
|
||||
cmStateSnapshot::StrictWeakOrder>
|
||||
DirectoryTargetsMap;
|
||||
|
||||
@@ -674,6 +674,13 @@ void cmLocalUnixMakefileGenerator3::WriteMakeVariables(
|
||||
"NULL=nul\n"
|
||||
"!ENDIF\n";
|
||||
}
|
||||
if (gg->IsGNUMakeJobServerAware()) {
|
||||
// Toggle for USES_TERMINAL recipes: empty here, set to "+" by
|
||||
// "cmake --build" under --output-sync (see AppendCustomCommand).
|
||||
makefileStream << "# Prefix for USES_TERMINAL recipes under output sync.\n"
|
||||
"CMAKE_USES_TERMINAL_PREFIX =\n"
|
||||
"\n";
|
||||
}
|
||||
if (this->IsWindowsShell()) {
|
||||
makefileStream << "SHELL = cmd.exe\n"
|
||||
"\n";
|
||||
@@ -1135,6 +1142,16 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand(
|
||||
if (ccg.GetCC().GetJobserverAware() && gg->IsGNUMakeJobServerAware()) {
|
||||
std::transform(commands1.begin(), commands1.end(), commands1.begin(),
|
||||
[](std::string const& cmd) { return cmStrCat('+', cmd); });
|
||||
} else if (ccg.GetCC().GetUsesTerminal() && gg->IsGNUMakeJobServerAware()) {
|
||||
// Prefix USES_TERMINAL recipes with $(CMAKE_USES_TERMINAL_PREFIX): empty
|
||||
// by default, but set to "+" by "cmake --build" under --output-sync so
|
||||
// GNU Make leaves the recipe unbuffered and an interactive command keeps
|
||||
// the terminal. Jobserver-aware commands already carry "+" from the
|
||||
// branch above and are excluded here to avoid a doubled prefix.
|
||||
std::transform(commands1.begin(), commands1.end(), commands1.begin(),
|
||||
[](std::string const& cmd) {
|
||||
return cmStrCat("$(CMAKE_USES_TERMINAL_PREFIX)", cmd);
|
||||
});
|
||||
}
|
||||
|
||||
// push back the custom commands
|
||||
|
||||
@@ -201,7 +201,8 @@ if(NOT CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
|
||||
endif()
|
||||
add_executable(detect_jobserver detect_jobserver.c)
|
||||
if(CMAKE_GENERATOR MATCHES "Make")
|
||||
add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU} -DDETECT_JOBSERVER=$<TARGET_FILE:detect_jobserver>)
|
||||
add_executable(fake_make Make/fake_make.c)
|
||||
add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU} -DDETECT_JOBSERVER=$<TARGET_FILE:detect_jobserver> -DFAKE_MAKE=$<TARGET_FILE:fake_make>)
|
||||
endif()
|
||||
unset(ninja_test_with_qt_version)
|
||||
unset(ninja_qt_args)
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Shared check for the OutputSync argv/probe cases. The calling test sets
|
||||
# expectation variables (all optional) before each build command:
|
||||
# expect_otarget 0/1 -- "-Otarget" must be absent/present
|
||||
# expect_otarget_count N -- exact number of "-Otarget" occurrences
|
||||
# expect_uses_terminal_flag 0/1 -- "CMAKE_USES_TERMINAL_PREFIX=+" absent/present
|
||||
# expect_probe 0/1 -- the make tool must not/must be probed
|
||||
# expect_probe_count N -- exact number of "--version" probes
|
||||
# expect_order_onone 0/1 -- "-Otarget" must appear before "-Onone"
|
||||
|
||||
set(record "${RunCMake_TEST_BINARY_DIR}/fake_make_record.txt")
|
||||
set(marker "${RunCMake_TEST_BINARY_DIR}/fake_make_probe.txt")
|
||||
|
||||
if(EXISTS "${record}")
|
||||
file(READ "${record}" record_content)
|
||||
else()
|
||||
set(record_content "")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${marker}")
|
||||
file(READ "${marker}" marker_content)
|
||||
else()
|
||||
set(marker_content "")
|
||||
endif()
|
||||
|
||||
string(REGEX MATCHALL "-Otarget" _otarget_matches "${record_content}")
|
||||
list(LENGTH _otarget_matches _otarget_count)
|
||||
|
||||
string(REGEX MATCHALL "probe" _probe_matches "${marker_content}")
|
||||
list(LENGTH _probe_matches _probe_count)
|
||||
|
||||
if(DEFINED expect_otarget)
|
||||
if(expect_otarget AND _otarget_count EQUAL 0)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Expected '-Otarget' in the build command but recorded:\n${record_content}\n")
|
||||
elseif(NOT expect_otarget AND _otarget_count GREATER 0)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Did not expect '-Otarget' in the build command but recorded:\n${record_content}\n")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED expect_otarget_count AND NOT _otarget_count EQUAL expect_otarget_count)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Expected ${expect_otarget_count} '-Otarget' occurrence(s) but found ${_otarget_count}:\n${record_content}\n")
|
||||
endif()
|
||||
|
||||
if(DEFINED expect_uses_terminal_flag)
|
||||
string(FIND "${record_content}" "CMAKE_USES_TERMINAL_PREFIX=+" _ut_pos)
|
||||
if(expect_uses_terminal_flag AND _ut_pos EQUAL -1)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Expected 'CMAKE_USES_TERMINAL_PREFIX=+' in the build command but recorded:\n${record_content}\n")
|
||||
elseif(NOT expect_uses_terminal_flag AND NOT _ut_pos EQUAL -1)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Did not expect 'CMAKE_USES_TERMINAL_PREFIX=+' in the build command but recorded:\n${record_content}\n")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED expect_probe)
|
||||
if(expect_probe AND _probe_count EQUAL 0)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Expected the make tool to be probed with '--version' but it was not.\n")
|
||||
elseif(NOT expect_probe AND _probe_count GREATER 0)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Did not expect a '--version' probe but it ran ${_probe_count} time(s).\n")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED expect_probe_count AND NOT _probe_count EQUAL expect_probe_count)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Expected ${expect_probe_count} probe(s) but found ${_probe_count}.\n")
|
||||
endif()
|
||||
|
||||
if(DEFINED expect_order_onone AND expect_order_onone)
|
||||
if(NOT record_content MATCHES "-Otarget[^\n]*-Onone")
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Expected '-Otarget' to appear before '-Onone' but recorded:\n${record_content}\n")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
add_custom_target(drive ALL COMMAND $(CMAKE_COMMAND) -E true)
|
||||
@@ -0,0 +1,37 @@
|
||||
set(BUILD_DIR "${RunCMake_BINARY_DIR}/OutputSyncUsesTerminal-build")
|
||||
|
||||
function(check_has target regex)
|
||||
file(STRINGS "${BUILD_DIR}/${target}" lines REGEX "${regex}")
|
||||
list(LENGTH lines len)
|
||||
if(len EQUAL 0)
|
||||
set(RunCMake_TEST_FAILED
|
||||
"${RunCMake_TEST_FAILED}Expected to find '${regex}' in ${target}\n"
|
||||
PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(check_missing target regex)
|
||||
file(STRINGS "${BUILD_DIR}/${target}" lines REGEX "${regex}")
|
||||
list(LENGTH lines len)
|
||||
if(NOT len EQUAL 0)
|
||||
set(RunCMake_TEST_FAILED
|
||||
"${RunCMake_TEST_FAILED}Did not expect to find '${regex}' in ${target}: ${lines}\n"
|
||||
PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# The USES_TERMINAL recipe is prefixed with the build-time toggle variable.
|
||||
check_has("CMakeFiles/term.dir/build.make"
|
||||
[[\$\(CMAKE_USES_TERMINAL_PREFIX\)\$\(CMAKE_COMMAND\) -E true]])
|
||||
|
||||
# A plain recipe references no prefix.
|
||||
check_missing("CMakeFiles/plain.dir/build.make" [[\$\(CMAKE_USES_TERMINAL_PREFIX\)]])
|
||||
|
||||
# A jobserver-aware recipe keeps the literal '+' and is not given the toggle.
|
||||
check_has("CMakeFiles/jsa.dir/build.make" [[\+\$\(CMAKE_COMMAND\) -E true]])
|
||||
check_missing("CMakeFiles/jsa.dir/build.make" [[\$\(CMAKE_USES_TERMINAL_PREFIX\)]])
|
||||
|
||||
# A recipe that is both jobserver-aware and USES_TERMINAL uses only '+'
|
||||
# (no doubled prefix).
|
||||
check_has("CMakeFiles/both.dir/build.make" [[\+\$\(CMAKE_COMMAND\) -E true]])
|
||||
check_missing("CMakeFiles/both.dir/build.make" [[\$\(CMAKE_USES_TERMINAL_PREFIX\)]])
|
||||
@@ -0,0 +1,20 @@
|
||||
# Recipe that wants interactive terminal access: prefixed with the toggle.
|
||||
add_custom_target(term ALL
|
||||
COMMAND $(CMAKE_COMMAND) -E true
|
||||
USES_TERMINAL)
|
||||
|
||||
# Ordinary recipe: no prefix at all.
|
||||
add_custom_target(plain ALL
|
||||
COMMAND $(CMAKE_COMMAND) -E true)
|
||||
|
||||
# Jobserver-aware recipe: keeps the literal '+' prefix.
|
||||
add_custom_target(jsa ALL
|
||||
COMMAND $(CMAKE_COMMAND) -E true
|
||||
JOB_SERVER_AWARE ON)
|
||||
|
||||
# Both jobserver-aware and interactive: the '+' branch wins, so the toggle
|
||||
# prefix must not be added (no doubled prefix).
|
||||
add_custom_target(both ALL
|
||||
COMMAND $(CMAKE_COMMAND) -E true
|
||||
USES_TERMINAL
|
||||
JOB_SERVER_AWARE ON)
|
||||
@@ -135,3 +135,130 @@ if(MAKE_IS_GNU)
|
||||
# commands with the '+' operator.
|
||||
run_cmake(GNUMakeJobServerAware)
|
||||
endif()
|
||||
|
||||
# Output synchronization (-Otarget) is emitted only for the GNU Make family of
|
||||
# generators. Drive these cases with a fake "make" so the behavior can be
|
||||
# asserted independently of the host's real make tool.
|
||||
if(FAKE_MAKE AND RunCMake_GENERATOR MATCHES "Unix Makefiles|MinGW Makefiles|MSYS Makefiles")
|
||||
function(run_OutputSync)
|
||||
# Use the fake make for the configure step so the build steps inherit it
|
||||
# from the cache.
|
||||
set(RunCMake_MAKE_PROGRAM "${FAKE_MAKE}")
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/OutputSync-build)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
run_cmake(OutputSync)
|
||||
|
||||
set(RunCMake-check-file OutputSync-check.cmake)
|
||||
set(record "${RunCMake_TEST_BINARY_DIR}/fake_make_record.txt")
|
||||
set(marker "${RunCMake_TEST_BINARY_DIR}/fake_make_probe.txt")
|
||||
set(ENV{FAKE_MAKE_RECORD} "${record}")
|
||||
set(ENV{FAKE_MAKE_PROBE_MARKER} "${marker}")
|
||||
|
||||
# CMake-driven parallel build with GNU Make >= 4.0: group the output.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "GNU Make 4.4.1")
|
||||
set(expect_otarget 1)
|
||||
set(expect_uses_terminal_flag 1)
|
||||
set(expect_probe 1)
|
||||
run_cmake_command(OutputSync-parallel ${CMAKE_COMMAND} --build . --parallel 2)
|
||||
|
||||
# Serial build: no grouping and no probe.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(expect_otarget 0)
|
||||
set(expect_uses_terminal_flag 0)
|
||||
set(expect_probe 0)
|
||||
run_cmake_command(OutputSync-serial ${CMAKE_COMMAND} --build .)
|
||||
|
||||
# Native "-- -j" opt-out: CMake adds neither -j nor -Otarget, no probe.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(expect_otarget 0)
|
||||
set(expect_uses_terminal_flag 0)
|
||||
set(expect_probe 0)
|
||||
run_cmake_command(OutputSync-native-j ${CMAKE_COMMAND} --build . -- -j)
|
||||
|
||||
# A user's native "-O" overrides CMake's: -Otarget appears before -Onone.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(expect_otarget 1)
|
||||
set(expect_uses_terminal_flag 1)
|
||||
set(expect_probe 1)
|
||||
set(expect_order_onone 1)
|
||||
run_cmake_command(OutputSync-override ${CMAKE_COMMAND} --build . --parallel 2 -- -Onone)
|
||||
unset(expect_order_onone)
|
||||
|
||||
# GNU Make < 4.0 does not support --output-sync.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "GNU Make 3.81")
|
||||
set(expect_otarget 0)
|
||||
set(expect_uses_terminal_flag 0)
|
||||
set(expect_probe 1)
|
||||
run_cmake_command(OutputSync-old ${CMAKE_COMMAND} --build . --parallel 2)
|
||||
|
||||
# 4.0 is the first release to support --output-sync; verify the lower
|
||||
# boundary and the rest of the 4.0-4.2 series are classified as supported.
|
||||
foreach(v IN ITEMS 4.0 4.1 4.2)
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "GNU Make ${v}")
|
||||
set(expect_otarget 1)
|
||||
set(expect_uses_terminal_flag 1)
|
||||
set(expect_probe 1)
|
||||
run_cmake_command(OutputSync-v${v} ${CMAKE_COMMAND} --build . --parallel 2)
|
||||
endforeach()
|
||||
|
||||
# A non-GNU make is not grouped.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "bmake version 20200710")
|
||||
set(expect_otarget 0)
|
||||
set(expect_uses_terminal_flag 0)
|
||||
set(expect_probe 1)
|
||||
run_cmake_command(OutputSync-nongnu ${CMAKE_COMMAND} --build . --parallel 2)
|
||||
|
||||
# Unparsable "--version" output: probe fails gracefully, build still runs.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "garbage output")
|
||||
set(expect_otarget 0)
|
||||
set(expect_uses_terminal_flag 0)
|
||||
set(expect_probe 1)
|
||||
run_cmake_command(OutputSync-garbage ${CMAKE_COMMAND} --build . --parallel 2)
|
||||
|
||||
# Probe exits non-zero: treated as unsupported, build still runs.
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "GNU Make 4.4.1")
|
||||
set(ENV{FAKE_MAKE_VERSION_RESULT} "2")
|
||||
set(expect_otarget 0)
|
||||
set(expect_uses_terminal_flag 0)
|
||||
set(expect_probe 1)
|
||||
run_cmake_command(OutputSync-probe-fail ${CMAKE_COMMAND} --build . --parallel 2)
|
||||
unset(ENV{FAKE_MAKE_VERSION_RESULT})
|
||||
|
||||
# --clean-first runs two build commands but probes only once (memoized).
|
||||
file(REMOVE "${record}" "${marker}")
|
||||
set(ENV{FAKE_MAKE_VERSION} "GNU Make 4.4.1")
|
||||
set(expect_otarget 1)
|
||||
set(expect_otarget_count 2)
|
||||
set(expect_uses_terminal_flag 1)
|
||||
set(expect_probe 1)
|
||||
set(expect_probe_count 1)
|
||||
run_cmake_command(OutputSync-clean-first ${CMAKE_COMMAND} --build . --parallel 2 --clean-first)
|
||||
unset(expect_otarget_count)
|
||||
unset(expect_probe_count)
|
||||
|
||||
unset(ENV{FAKE_MAKE_VERSION})
|
||||
unset(ENV{FAKE_MAKE_RECORD})
|
||||
unset(ENV{FAKE_MAKE_PROBE_MARKER})
|
||||
endfunction()
|
||||
run_OutputSync()
|
||||
|
||||
# USES_TERMINAL recipe prefixing is a generation-time change; inspect the
|
||||
# generated build.make directly.
|
||||
function(run_OutputSyncUsesTerminal)
|
||||
set(RunCMake_MAKE_PROGRAM "${FAKE_MAKE}")
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/OutputSyncUsesTerminal-build)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
run_cmake(OutputSyncUsesTerminal)
|
||||
endfunction()
|
||||
run_OutputSyncUsesTerminal()
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
|
||||
/* Stand-in for "make" used by the RunCMake.Make output-sync tests so they can
|
||||
assert what "cmake --build" passes to make without a real make tool.
|
||||
|
||||
On "--version" (the output-sync probe): touch $FAKE_MAKE_PROBE_MARKER, print
|
||||
$FAKE_MAKE_VERSION (default a GNU Make 4.x banner), and exit with
|
||||
$FAKE_MAKE_VERSION_RESULT (default 0).
|
||||
Otherwise (the build): append the argument list to $FAKE_MAKE_RECORD and
|
||||
exit 0. */
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int i;
|
||||
int isVersion = 0;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--version") == 0) {
|
||||
isVersion = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isVersion) {
|
||||
char const* marker = getenv("FAKE_MAKE_PROBE_MARKER");
|
||||
char const* banner = getenv("FAKE_MAKE_VERSION");
|
||||
char const* result = getenv("FAKE_MAKE_VERSION_RESULT");
|
||||
if (marker) {
|
||||
FILE* f = fopen(marker, "a");
|
||||
if (f) {
|
||||
fprintf(f, "probe\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
if (!banner) {
|
||||
banner = "GNU Make 4.4.1";
|
||||
}
|
||||
if (banner[0] != '\0') {
|
||||
printf("%s\n", banner);
|
||||
}
|
||||
return result ? atoi(result) : 0;
|
||||
}
|
||||
|
||||
{
|
||||
char const* record = getenv("FAKE_MAKE_RECORD");
|
||||
if (record) {
|
||||
FILE* f = fopen(record, "a");
|
||||
if (f) {
|
||||
for (i = 1; i < argc; ++i) {
|
||||
fprintf(f, "%s%s", i > 1 ? " " : "", argv[i]);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user