Commit 34469a4f authored by Brad King's avatar Brad King Committed by Kitware Robot
Browse files

Merge topic 'custom-command-output-genex-nmc'

1526ae3a Tests: Add cases for Ninja Multi-Config cross-config custom commands
dcf9f4d2 Ninja Multi-Config: Add support for cross-config custom commands
15467f12 cmLocalGenerator: Adopt custom target 'force' output name generation
7b64b0cd cmLocalGenerator: Refactor custom command generator construction
d29da8ed cmMakefile: Simplify custom target 'force' output name generation
2b1cc175

 Help: Clarify version adding add_custom_{command,target} OUTPUT genex support
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !5612
parents d8654c2a 1526ae3a
Pipeline #205253 failed with stages
in 79 minutes and 58 seconds
......@@ -79,8 +79,9 @@ The options are:
The :ref:`Makefile Generators` will remove ``BYPRODUCTS`` and other
:prop_sf:`GENERATED` files during ``make clean``.
Since CMake 3.20, arguments to ``BYPRODUCTS`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
.. versionadded:: 3.20
Arguments to ``BYPRODUCTS`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
``COMMAND``
Specify the command-line(s) to execute at build time.
......@@ -229,8 +230,9 @@ The options are:
as a file on disk it should be marked with the :prop_sf:`SYMBOLIC`
source file property.
Since CMake 3.20, arguments to ``OUTPUT`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
.. versionadded:: 3.20
Arguments to ``OUTPUT`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
``USES_TERMINAL``
.. versionadded:: 3.2
......@@ -291,23 +293,24 @@ adds a custom command to run ``someTool`` to generate ``out.c`` and then
compile the generated source as part of a library. The generation rule
will re-run whenever ``in.txt`` changes.
Since CMake 3.20, one may use generator expressions to specify
per-configuration outputs. For example, the code:
.. versionadded:: 3.20
One may use generator expressions to specify per-configuration outputs.
For example, the code:
.. code-block:: cmake
.. code-block:: cmake
add_custom_command(
OUTPUT "out-$<CONFIG>.c"
COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
-o "out-$<CONFIG>.c"
-c "$<CONFIG>"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
VERBATIM)
add_library(myLib "out-$<CONFIG>.c")
add_custom_command(
OUTPUT "out-$<CONFIG>.c"
COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
-o "out-$<CONFIG>.c"
-c "$<CONFIG>"
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt
VERBATIM)
add_library(myLib "out-$<CONFIG>.c")
adds a custom command to run ``someTool`` to generate ``out-<config>.c``,
where ``<config>`` is the build configuration, and then compile the generated
source as part of a library.
adds a custom command to run ``someTool`` to generate ``out-<config>.c``,
where ``<config>`` is the build configuration, and then compile the generated
source as part of a library.
Build Events
^^^^^^^^^^^^
......@@ -377,20 +380,30 @@ For example, the code:
will run ``someHasher`` to produce a ``.hash`` file next to the executable
after linking.
Since CMake 3.20, one may use generator expressions to specify
per-configuration byproducts. For example, the code:
.. versionadded:: 3.20
One may use generator expressions to specify per-configuration byproducts.
For example, the code:
.. code-block:: cmake
.. code-block:: cmake
add_library(myPlugin MODULE myPlugin.c)
add_custom_command(
TARGET myPlugin POST_BUILD
COMMAND someHasher -i "$<TARGET_FILE:myPlugin>"
--as-code "myPlugin-hash-$<CONFIG>.c"
BYPRODUCTS "myPlugin-hash-$<CONFIG>.c"
VERBATIM)
add_executable(myExe myExe.c "myPlugin-hash-$<CONFIG>.c")
add_library(myPlugin MODULE myPlugin.c)
add_custom_command(
TARGET myPlugin POST_BUILD
COMMAND someHasher -i "$<TARGET_FILE:myPlugin>"
--as-code "myPlugin-hash-$<CONFIG>.c"
BYPRODUCTS "myPlugin-hash-$<CONFIG>.c"
VERBATIM)
add_executable(myExe myExe.c "myPlugin-hash-$<CONFIG>.c")
will run ``someHasher`` after linking ``myPlugin``, e.g. to produce a ``.c``
file containing code to check the hash of ``myPlugin`` that the ``myExe``
executable can use to verify it before loading.
Ninja Multi-Config
^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.20
will run ``someHasher`` after linking ``myPlugin``, e.g. to produce a ``.c``
file containing code to check the hash of ``myPlugin`` that the ``myExe``
executable can use to verify it before loading.
``add_custom_command`` supports the :generator:`Ninja Multi-Config`
generator's cross-config capabilities. See the generator documentation
for more information.
......@@ -54,8 +54,9 @@ The options are:
The :ref:`Makefile Generators` will remove ``BYPRODUCTS`` and other
:prop_sf:`GENERATED` files during ``make clean``.
Since CMake 3.20, arguments to ``BYPRODUCTS`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
.. versionadded:: 3.20
Arguments to ``BYPRODUCTS`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
``COMMAND``
Specify the command-line(s) to execute at build time.
......@@ -167,3 +168,12 @@ The options are:
.. versionadded:: 3.13
Arguments to ``WORKING_DIRECTORY`` may use
:manual:`generator expressions <cmake-generator-expressions(7)>`.
Ninja Multi-Config
^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.20
``add_custom_target`` supports the :generator:`Ninja Multi-Config`
generator's cross-config capabilities. See the generator documentation
for more information.
......@@ -86,3 +86,47 @@ used to generate ``generated.c``, which would be used to build the ``Debug``
configuration of ``generated``. This is useful for running a release-optimized
version of a generator utility while still building the debug version of the
targets built with the generated code.
Custom Commands
^^^^^^^^^^^^^^^
.. versionadded:: 3.20
The ``Ninja Multi-Config`` generator adds extra capabilities to
:command:`add_custom_command` and :command:`add_custom_target` through its
cross-config mode. The ``COMMAND``, ``DEPENDS``, and ``WORKING_DIRECTORY``
arguments can be evaluated in the context of either the "command config" (the
"native" configuration of the ``build-<Config>.ninja`` file in use) or the
"output config" (the configuration used to evaluate the ``OUTPUT`` and
``BYPRODUCTS``).
If either ``OUTPUT`` or ``BYPRODUCTS`` names a path that is common to
more than one configuration (e.g. it does not use any generator expressions),
all arguments are evaluated in the command config by default.
If all ``OUTPUT`` and ``BYPRODUCTS`` paths are unique to each configuration
(e.g. by using the ``$<CONFIG>`` generator expression), the first argument of
``COMMAND`` is still evaluated in the command config by default, while all
subsequent arguments, as well as the arguments to ``DEPENDS`` and
``WORKING_DIRECTORY``, are evaluated in the output config. These defaults can
be overridden with the ``$<OUTPUT_CONFIG:...>`` and ``$<COMMAND_CONFIG:...>``
generator-expressions. Note that if a target is specified by its name in
``DEPENDS``, or as the first argument of ``COMMAND``, it is always evaluated
in the command config, even if it is wrapped in ``$<OUTPUT_CONFIG:...>``
(because its plain name is not a generator expression).
As an example, consider the following:
.. code-block:: cmake
add_custom_command(
OUTPUT "$<CONFIG>.txt"
COMMAND generator "$<CONFIG>.txt" "$<OUTPUT_CONFIG:$<CONFIG>>" "$<COMMAND_CONFIG:$<CONFIG>>"
DEPENDS tgt1 "$<TARGET_FILE:tgt2>" "$<OUTPUT_CONFIG:$<TARGET_FILE:tgt3>>" "$<COMMAND_CONFIG:$<TARGET_FILE:tgt4>>"
)
Assume that ``generator``, ``tgt1``, ``tgt2``, ``tgt3``, and ``tgt4`` are all
executable targets, and assume that ``$<CONFIG>.txt`` is built in the ``Debug``
output config using the ``Release`` command config. The ``Release`` build of
the ``generator`` target is called with ``Debug.txt Debug Release`` as
arguments. The command depends on the ``Release`` builds of ``tgt1`` and
``tgt4``, and the ``Debug`` builds of ``tgt2`` and ``tgt3``.
......@@ -816,6 +816,24 @@ Output-Related Expressions
``;`` on Windows). Be sure to enclose the argument containing this genex
in double quotes in CMake source code so that ``;`` does not split arguments.
``$<OUTPUT_CONFIG:...>``
.. versionadded:: 3.20
Only valid in :command:`add_custom_command` and :command:`add_custom_target`
as the outer-most generator expression in an argument.
With the :generator:`Ninja Multi-Config` generator, generator expressions
in ``...`` are evaluated using the custom command's "output config".
With other generators, the content of ``...`` is evaluated normally.
``$<COMMAND_CONFIG:...>``
.. versionadded:: 3.20
Only valid in :command:`add_custom_command` and :command:`add_custom_target`
as the outer-most generator expression in an argument.
With the :generator:`Ninja Multi-Config` generator, generator expressions
in ``...`` are evaluated using the custom command's "command config".
With other generators, the content of ``...`` is evaluated normally.
Debugging
=========
......
......@@ -4,3 +4,8 @@ custom-command-output-genex
* :command:`add_custom_command` and :command:`add_custom_target` now
support :manual:`generator expressions <cmake-generator-expressions(7)>`
in their ``OUTPUT`` and ``BYPRODUCTS`` options.
Their ``COMMAND``, ``WORKING_DIRECTORY``, and ``DEPENDS`` options gained
support for new generator expressions ``$<COMMAND_CONFIG:...>`` and
``$<OUTPUT_CONFIG:...>`` that control cross-config handling when using
the :generator:`Ninja Multi-Config` generator.
......@@ -7,7 +7,9 @@
#include <utility>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmCryptoHash.h"
#include "cmCustomCommand.h"
......@@ -24,15 +26,95 @@
#include "cmTransformDepfile.h"
namespace {
std::string EvaluateSplitConfigGenex(
cm::string_view input, cmGeneratorExpression const& ge, cmLocalGenerator* lg,
bool useOutputConfig, std::string const& outputConfig,
std::string const& commandConfig,
std::set<BT<std::pair<std::string, bool>>>* utils = nullptr)
{
std::string result;
while (!input.empty()) {
// Copy non-genex content directly to the result.
std::string::size_type pos = input.find("$<");
result += input.substr(0, pos);
if (pos == std::string::npos) {
break;
}
input = input.substr(pos);
// Find the balanced end of this regex.
size_t nestingLevel = 1;
for (pos = 2; pos < input.size(); ++pos) {
cm::string_view cur = input.substr(pos);
if (cmHasLiteralPrefix(cur, "$<")) {
++nestingLevel;
++pos;
continue;
}
if (cmHasLiteralPrefix(cur, ">")) {
--nestingLevel;
if (nestingLevel == 0) {
++pos;
break;
}
}
}
// Split this genex from following input.
cm::string_view genex = input.substr(0, pos);
input = input.substr(pos);
// Convert an outer COMMAND_CONFIG or OUTPUT_CONFIG to the matching config.
std::string const* config =
useOutputConfig ? &outputConfig : &commandConfig;
if (nestingLevel == 0) {
static cm::string_view const COMMAND_CONFIG = "$<COMMAND_CONFIG:"_s;
static cm::string_view const OUTPUT_CONFIG = "$<OUTPUT_CONFIG:"_s;
if (cmHasPrefix(genex, COMMAND_CONFIG)) {
genex.remove_prefix(COMMAND_CONFIG.size());
genex.remove_suffix(1);
useOutputConfig = false;
config = &commandConfig;
} else if (cmHasPrefix(genex, OUTPUT_CONFIG)) {
genex.remove_prefix(OUTPUT_CONFIG.size());
genex.remove_suffix(1);
useOutputConfig = true;
config = &outputConfig;
}
}
// Evaluate this genex in the selected configuration.
std::unique_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(std::string(genex));
result += cge->Evaluate(lg, *config);
// Record targets referenced by the genex.
if (utils) {
// FIXME: What is the proper condition for a cross-dependency?
bool const cross = !useOutputConfig;
for (cmGeneratorTarget* gt : cge->GetTargets()) {
utils->emplace(BT<std::pair<std::string, bool>>(
{ gt->GetName(), cross }, cge->GetBacktrace()));
}
}
}
return result;
}
std::vector<std::string> EvaluateDepends(std::vector<std::string> const& paths,
cmGeneratorExpression const& ge,
cmLocalGenerator* lg,
std::string const& config)
std::string const& outputConfig,
std::string const& commandConfig)
{
std::vector<std::string> depends;
for (std::string const& p : paths) {
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p);
std::string const& ep = cge->Evaluate(lg, config);
std::string const& ep =
EvaluateSplitConfigGenex(p, ge, lg, /*useOutputConfig=*/true,
/*outputConfig=*/outputConfig,
/*commandConfig=*/commandConfig);
cm::append(depends, cmExpandedList(ep));
}
for (std::string& p : depends) {
......@@ -59,12 +141,12 @@ std::vector<std::string> EvaluateOutputs(std::vector<std::string> const& paths,
}
}
cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
std::string config,
cmLocalGenerator* lg,
bool transformDepfile)
cmCustomCommandGenerator::cmCustomCommandGenerator(
cmCustomCommand const& cc, std::string config, cmLocalGenerator* lg,
bool transformDepfile, cm::optional<std::string> crossConfig)
: CC(&cc)
, Config(std::move(config))
, OutputConfig(crossConfig ? *crossConfig : config)
, CommandConfig(std::move(config))
, LG(lg)
, OldStyle(cc.GetEscapeOldStyle())
, MakeVars(cc.GetEscapeAllowMakeVars())
......@@ -75,18 +157,20 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
const cmCustomCommandLines& cmdlines = this->CC->GetCommandLines();
for (cmCustomCommandLine const& cmdline : cmdlines) {
cmCustomCommandLine argv;
// For the command itself, we default to the COMMAND_CONFIG.
bool useOutputConfig = false;
for (std::string const& clarg : cmdline) {
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(clarg);
std::string parsed_arg = cge->Evaluate(this->LG, this->Config);
for (cmGeneratorTarget* gt : cge->GetTargets()) {
this->Utilities.emplace(BT<std::pair<std::string, bool>>(
{ gt->GetName(), true }, cge->GetBacktrace()));
}
std::string parsed_arg = EvaluateSplitConfigGenex(
clarg, ge, this->LG, useOutputConfig, this->OutputConfig,
this->CommandConfig, &this->Utilities);
if (this->CC->GetCommandExpandLists()) {
cm::append(argv, cmExpandedList(parsed_arg));
} else {
argv.push_back(std::move(parsed_arg));
}
// For remaining arguments, we default to the OUTPUT_CONFIG.
useOutputConfig = true;
}
if (!argv.empty()) {
......@@ -94,8 +178,10 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
// collect the target to add a target-level dependency on it.
cmGeneratorTarget* gt = this->LG->FindGeneratorTargetToUse(argv.front());
if (gt && gt->GetType() == cmStateEnums::EXECUTABLE) {
// FIXME: What is the proper condition for a cross-dependency?
bool const cross = true;
this->Utilities.emplace(BT<std::pair<std::string, bool>>(
{ gt->GetName(), true }, cc.GetBacktrace()));
{ gt->GetName(), cross }, cc.GetBacktrace()));
}
} else {
// Later code assumes at least one entry exists, but expanding
......@@ -137,16 +223,18 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc,
this->CommandLines.push_back(std::move(argv));
}
this->Outputs = EvaluateOutputs(cc.GetOutputs(), ge, this->LG, this->Config);
this->Outputs =
EvaluateOutputs(cc.GetOutputs(), ge, this->LG, this->OutputConfig);
this->Byproducts =
EvaluateOutputs(cc.GetByproducts(), ge, this->LG, this->Config);
this->Depends = EvaluateDepends(cc.GetDepends(), ge, this->LG, this->Config);
EvaluateOutputs(cc.GetByproducts(), ge, this->LG, this->OutputConfig);
this->Depends = EvaluateDepends(cc.GetDepends(), ge, this->LG,
this->OutputConfig, this->CommandConfig);
const std::string& workingdirectory = this->CC->GetWorkingDirectory();
if (!workingdirectory.empty()) {
std::unique_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(workingdirectory);
this->WorkingDirectory = cge->Evaluate(this->LG, this->Config);
this->WorkingDirectory =
EvaluateSplitConfigGenex(workingdirectory, ge, this->LG, true,
this->OutputConfig, this->CommandConfig);
// Convert working directory to a full path.
if (!this->WorkingDirectory.empty()) {
std::string const& build_dir = this->LG->GetCurrentBinaryDirectory();
......@@ -203,7 +291,7 @@ const char* cmCustomCommandGenerator::GetArgv0Location(unsigned int c) const
(target->IsImported() ||
target->GetProperty("CROSSCOMPILING_EMULATOR") ||
!this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING"))) {
return target->GetLocation(this->Config).c_str();
return target->GetLocation(this->CommandConfig).c_str();
}
return nullptr;
}
......
......@@ -9,6 +9,8 @@
#include <utility>
#include <vector>
#include <cm/optional>
#include "cmCustomCommandLines.h"
#include "cmListFileCache.h"
......@@ -18,7 +20,8 @@ class cmLocalGenerator;
class cmCustomCommandGenerator
{
cmCustomCommand const* CC;
std::string Config;
std::string OutputConfig;
std::string CommandConfig;
cmLocalGenerator* LG;
bool OldStyle;
bool MakeVars;
......@@ -36,7 +39,8 @@ class cmCustomCommandGenerator
public:
cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config,
cmLocalGenerator* lg, bool transformDepfile = true);
cmLocalGenerator* lg, bool transformDepfile = true,
cm::optional<std::string> crossConfig = {});
cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete;
cmCustomCommandGenerator(cmCustomCommandGenerator&&) = default;
cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) =
......@@ -55,4 +59,7 @@ public:
bool HasOnlyEmptyCommandLines() const;
std::string GetFullDepfile() const;
std::string GetInternalDepfile() const;
const std::string& GetOutputConfig() const { return this->OutputConfig; }
const std::string& GetCommandConfig() const { return this->CommandConfig; }
};
......@@ -27,10 +27,3 @@ enum class cmObjectLibraryCommands
Reject,
Accept
};
/** Utility target output source file name. */
struct cmUtilityOutput
{
std::string Name;
std::string NameCMP0049;
};
......@@ -3064,7 +3064,7 @@ bool cmTargetTraceDependencies::IsUtility(std::string const& dep)
} else {
// The original name of the dependency was not a full path. It
// must name a target, so add the target-level dependency.
this->GeneratorTarget->Target->AddUtility(util, false);
this->GeneratorTarget->Target->AddUtility(util, true);
return true;
}
}
......@@ -3079,15 +3079,16 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc)
std::set<std::string> depends;
for (std::string const& config :
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) {
cmCustomCommandGenerator ccg(cc, config, this->LocalGenerator);
for (cmCustomCommandGenerator const& ccg :
this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) {
// Collect target-level dependencies referenced in command lines.
for (auto const& util : ccg.GetUtilities()) {
this->GeneratorTarget->Target->AddUtility(util);
}
// Collect target-level dependencies referenced in command lines.
for (auto const& util : ccg.GetUtilities()) {
this->GeneratorTarget->Target->AddUtility(util);
// Collect file-level dependencies referenced in DEPENDS.
depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end());
}
// Collect file-level dependencies referenced in DEPENDS.
depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end());
}
// Queue file-level dependencies.
......
......@@ -56,6 +56,52 @@ std::string const cmGlobalNinjaGenerator::SHELL_NOOP = "cd .";
std::string const cmGlobalNinjaGenerator::SHELL_NOOP = ":";
#endif
bool operator==(
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
{
return lhs.Target == rhs.Target && lhs.Config == rhs.Config &&
lhs.GenexOutput == rhs.GenexOutput;
}
bool operator!=(
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
{
return !(lhs == rhs);
}
bool operator<(
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
{
return lhs.Target < rhs.Target ||
(lhs.Target == rhs.Target &&
(lhs.Config < rhs.Config ||
(lhs.Config == rhs.Config && lhs.GenexOutput < rhs.GenexOutput)));
}
bool operator>(
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
{
return rhs < lhs;
}
bool operator<=(
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
{
return !(lhs > rhs);
}
bool operator>=(
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& lhs,
const cmGlobalNinjaGenerator::ByConfig::TargetDependsClosureKey& rhs)
{
return rhs <= lhs;
}
void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
{
for (int i = 0; i < count; ++i) {
......@@ -1194,23 +1240,30 @@ void cmGlobalNinjaGenerator::AppendTargetDepends(
void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
cmGeneratorTarget const* target, cmNinjaDeps& outputs,
const std::string& config)
const std::string& config, const std::string& fileConfig, bool genexOutput)
{
cmNinjaOuts outs;
this->AppendTargetDependsClosure(target, outs, config, true);
this->AppendTargetDependsClosure(target, outs, config, fileConfig,
genexOutput, true);
cm::append(outputs, outs);
}
void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
cmGeneratorTarget const* target, cmNinjaOuts& outputs,
const std::string& config, bool omit_self)
const std::string& config, const std::string& fileConfig, bool genexOutput,
bool omit_self)
{
// try to locate the target in the cache
auto find = this->Configs[config].TargetDependsClosures.lower_bound(target);
ByConfig::TargetDependsClosureKey key{
target,
config,
genexOutput,
};
auto find = this->Configs[fileConfig].TargetDependsClosures.lower_bound(key);
if (find == this->Configs[config].TargetDependsClosures.end() ||
find->first != target) {
if (find == this->Configs[fileConfig].TargetDependsClosures.end() ||
find->first != key) {
// We now calculate the closure outputs by inspecting the dependent
// targets recursively.
// For that we have to distinguish between a local result set that is only
......@@ -1220,18 +1273,27 @@ void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
cmNinjaOuts this_outs; // this will be the new cache entry
for (auto const& dep_target : this->GetTargetDirectDepends(target)) {
if (!dep_target->IsInBuildSystem() ||
(target->GetType() != cmStateEnums::UTILITY &&
dep_target->GetType() != cmStateEnums::UTILITY &&
this->EnableCrossConfigBuild() && !dep_target.IsCross())) {
if (!dep_target->IsInBuildSystem()) {
continue;
}
if (!this->IsSingleConfigUtility(target) &&