add_custom_command: Add DEPENDS_EXPLICIT_ONLY option for Ninja
Add option `DEPENDS_EXPLICIT_ONLY` to `add_custom_command` to indicate that implicit dependencies coming from users of the output are not needed, and only consider dependencies explicitly specified in the custom command. Fixes: #17097
This commit is contained in:
parent
48c69eeafe
commit
082ccd7530
@ -25,7 +25,8 @@ The first signature is for adding a custom command to produce an output:
|
||||
[DEPFILE depfile]
|
||||
[JOB_POOL job_pool]
|
||||
[VERBATIM] [APPEND] [USES_TERMINAL]
|
||||
[COMMAND_EXPAND_LISTS])
|
||||
[COMMAND_EXPAND_LISTS]
|
||||
[DEPENDS_EXPLICIT_ONLY])
|
||||
|
||||
This defines a command to generate specified ``OUTPUT`` file(s).
|
||||
A target created in the same directory (``CMakeLists.txt`` file)
|
||||
@ -357,6 +358,21 @@ The options are:
|
||||
:ref:`Makefile Generators`, :ref:`Visual Studio Generators`,
|
||||
and the :generator:`Xcode` generator.
|
||||
|
||||
``DEPENDS_EXPLICIT_ONLY``
|
||||
|
||||
.. versionadded:: 3.27
|
||||
|
||||
Indicate that the command's ``DEPENDS`` argument represents all files
|
||||
required by the command and implicit dependencies are not required.
|
||||
|
||||
Without this option, if any target uses the output of the custom command,
|
||||
CMake will consider that target's dependencies as implicit dependencies for
|
||||
the custom command in case this custom command requires files implicitly
|
||||
created by those targets.
|
||||
|
||||
Only the :ref:`Ninja Generators` actually use this information to remove
|
||||
unnecessary implicit dependencies.
|
||||
|
||||
Examples: Generating Files
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
7
Help/release/dev/ninja-custom-command-depends.rst
Normal file
7
Help/release/dev/ninja-custom-command-depends.rst
Normal file
@ -0,0 +1,7 @@
|
||||
ninja-custom-command-depends
|
||||
----------------------------
|
||||
|
||||
* The :command:`add_custom_command` command gained a new
|
||||
``DEPENDS_EXPLICIT_ONLY`` option to tell the :ref:`Ninja Generators`
|
||||
not to add any dependencies implied by the target to which it is
|
||||
attached.
|
@ -49,6 +49,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
|
||||
bool append = false;
|
||||
bool uses_terminal = false;
|
||||
bool command_expand_lists = false;
|
||||
bool depends_explicit_only = false;
|
||||
std::string implicit_depends_lang;
|
||||
cmImplicitDependsList implicit_depends;
|
||||
|
||||
@ -104,6 +105,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
|
||||
MAKE_STATIC_KEYWORD(USES_TERMINAL);
|
||||
MAKE_STATIC_KEYWORD(VERBATIM);
|
||||
MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
|
||||
MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY);
|
||||
#undef MAKE_STATIC_KEYWORD
|
||||
static std::unordered_set<std::string> const keywords{
|
||||
keyAPPEND,
|
||||
@ -126,7 +128,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
|
||||
keyTARGET,
|
||||
keyUSES_TERMINAL,
|
||||
keyVERBATIM,
|
||||
keyWORKING_DIRECTORY
|
||||
keyWORKING_DIRECTORY,
|
||||
keyDEPENDS_EXPLICIT_ONLY
|
||||
};
|
||||
|
||||
for (std::string const& copy : args) {
|
||||
@ -155,6 +158,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
|
||||
uses_terminal = true;
|
||||
} else if (copy == keyCOMMAND_EXPAND_LISTS) {
|
||||
command_expand_lists = true;
|
||||
} else if (copy == keyDEPENDS_EXPLICIT_ONLY) {
|
||||
depends_explicit_only = true;
|
||||
} else if (copy == keyTARGET) {
|
||||
doing = doing_target;
|
||||
} else if (copy == keyARGS) {
|
||||
@ -329,6 +334,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
|
||||
cc->SetDepfile(depfile);
|
||||
cc->SetJobPool(job_pool);
|
||||
cc->SetCommandExpandLists(command_expand_lists);
|
||||
cc->SetDependsExplicitOnly(depends_explicit_only);
|
||||
if (source.empty() && output.empty()) {
|
||||
// Source is empty, use the target.
|
||||
mf.AddCustomCommandToTarget(target, cctype, std::move(cc));
|
||||
|
@ -164,6 +164,16 @@ void cmCustomCommand::SetCommandExpandLists(bool b)
|
||||
this->CommandExpandLists = b;
|
||||
}
|
||||
|
||||
bool cmCustomCommand::GetDependsExplicitOnly() const
|
||||
{
|
||||
return this->DependsExplicitOnly;
|
||||
}
|
||||
|
||||
void cmCustomCommand::SetDependsExplicitOnly(bool b)
|
||||
{
|
||||
this->DependsExplicitOnly = b;
|
||||
}
|
||||
|
||||
const std::string& cmCustomCommand::GetDepfile() const
|
||||
{
|
||||
return this->Depfile;
|
||||
|
@ -102,6 +102,11 @@ public:
|
||||
bool GetCommandExpandLists() const;
|
||||
void SetCommandExpandLists(bool b);
|
||||
|
||||
/** Set/Get whether to use additional dependencies coming from
|
||||
users of OUTPUT of the custom command. */
|
||||
bool GetDependsExplicitOnly() const;
|
||||
void SetDependsExplicitOnly(bool b);
|
||||
|
||||
/** Set/Get the depfile (used by the Ninja generator) */
|
||||
const std::string& GetDepfile() const;
|
||||
void SetDepfile(const std::string& depfile);
|
||||
@ -141,6 +146,7 @@ private:
|
||||
bool CommandExpandLists = false;
|
||||
bool StdPipesUTF8 = false;
|
||||
bool HasMainDependency_ = false;
|
||||
bool DependsExplicitOnly = false;
|
||||
|
||||
// Policies are NEW for synthesized custom commands, and set by cmMakefile for
|
||||
// user-created custom commands.
|
||||
|
@ -586,32 +586,34 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
|
||||
|
||||
cmNinjaDeps orderOnlyDeps;
|
||||
|
||||
// A custom command may appear on multiple targets. However, some build
|
||||
// systems exist where the target dependencies on some of the targets are
|
||||
// overspecified, leading to a dependency cycle. If we assume all target
|
||||
// dependencies are a superset of the true target dependencies for this
|
||||
// custom command, we can take the set intersection of all target
|
||||
// dependencies to obtain a correct dependency list.
|
||||
//
|
||||
// FIXME: This won't work in certain obscure scenarios involving indirect
|
||||
// dependencies.
|
||||
auto j = targets.begin();
|
||||
assert(j != targets.end());
|
||||
this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
|
||||
*j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
|
||||
std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
|
||||
++j;
|
||||
|
||||
for (; j != targets.end(); ++j) {
|
||||
std::vector<std::string> jDeps;
|
||||
std::vector<std::string> depsIntersection;
|
||||
if (!cc->GetDependsExplicitOnly()) {
|
||||
// A custom command may appear on multiple targets. However, some build
|
||||
// systems exist where the target dependencies on some of the targets are
|
||||
// overspecified, leading to a dependency cycle. If we assume all target
|
||||
// dependencies are a superset of the true target dependencies for this
|
||||
// custom command, we can take the set intersection of all target
|
||||
// dependencies to obtain a correct dependency list.
|
||||
//
|
||||
// FIXME: This won't work in certain obscure scenarios involving indirect
|
||||
// dependencies.
|
||||
auto j = targets.begin();
|
||||
assert(j != targets.end());
|
||||
this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
|
||||
*j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
|
||||
std::sort(jDeps.begin(), jDeps.end());
|
||||
std::set_intersection(orderOnlyDeps.begin(), orderOnlyDeps.end(),
|
||||
jDeps.begin(), jDeps.end(),
|
||||
std::back_inserter(depsIntersection));
|
||||
orderOnlyDeps = depsIntersection;
|
||||
*j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
|
||||
std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
|
||||
++j;
|
||||
|
||||
for (; j != targets.end(); ++j) {
|
||||
std::vector<std::string> jDeps;
|
||||
std::vector<std::string> depsIntersection;
|
||||
this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
|
||||
*j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
|
||||
std::sort(jDeps.begin(), jDeps.end());
|
||||
std::set_intersection(orderOnlyDeps.begin(), orderOnlyDeps.end(),
|
||||
jDeps.begin(), jDeps.end(),
|
||||
std::back_inserter(depsIntersection));
|
||||
orderOnlyDeps = depsIntersection;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string>& outputs = ccg.GetOutputs();
|
||||
|
18
Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake
Normal file
18
Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake
Normal file
@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
project(CustomCommandExplicitDepends C)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command.h"
|
||||
COMMAND "${CMAKE_COMMAND}" -E touch
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/command.h"
|
||||
COMMENT "Creating command.h"
|
||||
DEPENDS_EXPLICIT_ONLY
|
||||
)
|
||||
|
||||
add_library(dep STATIC dep.c)
|
||||
|
||||
add_library(top STATIC
|
||||
top.c
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/command.h"
|
||||
)
|
||||
target_link_libraries(top PRIVATE dep)
|
@ -190,6 +190,18 @@ function (run_LooseObjectDepends)
|
||||
endfunction ()
|
||||
run_LooseObjectDepends()
|
||||
|
||||
function (run_CustomCommandExplictDepends)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CustomCommandExplicitDepends-build)
|
||||
run_cmake(CustomCommandExplicitDepends)
|
||||
run_ninja("${RunCMake_TEST_BINARY_DIR}" "command.h")
|
||||
if (EXISTS "${RunCMake_TEST_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}dep${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
message(FATAL_ERROR
|
||||
"The `dep` library was created when requesting an custom command to be "
|
||||
"generated; this should no longer be necessary when passing DEPENDS_EXPLICIT_ONLY keyword.")
|
||||
endif ()
|
||||
endfunction ()
|
||||
run_CustomCommandExplictDepends()
|
||||
|
||||
function (run_AssumedSources)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build)
|
||||
run_cmake(AssumedSources)
|
||||
|
Loading…
Reference in New Issue
Block a user