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:
Abdelmaged Khalifa 2023-02-12 01:00:40 +02:00 committed by Brad King
parent 48c69eeafe
commit 082ccd7530
8 changed files with 104 additions and 27 deletions

View File

@ -25,7 +25,8 @@ The first signature is for adding a custom command to produce an output:
[DEPFILE depfile] [DEPFILE depfile]
[JOB_POOL job_pool] [JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL] [VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]) [COMMAND_EXPAND_LISTS]
[DEPENDS_EXPLICIT_ONLY])
This defines a command to generate specified ``OUTPUT`` file(s). This defines a command to generate specified ``OUTPUT`` file(s).
A target created in the same directory (``CMakeLists.txt`` file) A target created in the same directory (``CMakeLists.txt`` file)
@ -357,6 +358,21 @@ The options are:
:ref:`Makefile Generators`, :ref:`Visual Studio Generators`, :ref:`Makefile Generators`, :ref:`Visual Studio Generators`,
and the :generator:`Xcode` generator. 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 Examples: Generating Files
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^

View 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.

View File

@ -49,6 +49,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
bool append = false; bool append = false;
bool uses_terminal = false; bool uses_terminal = false;
bool command_expand_lists = false; bool command_expand_lists = false;
bool depends_explicit_only = false;
std::string implicit_depends_lang; std::string implicit_depends_lang;
cmImplicitDependsList implicit_depends; cmImplicitDependsList implicit_depends;
@ -104,6 +105,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
MAKE_STATIC_KEYWORD(USES_TERMINAL); MAKE_STATIC_KEYWORD(USES_TERMINAL);
MAKE_STATIC_KEYWORD(VERBATIM); MAKE_STATIC_KEYWORD(VERBATIM);
MAKE_STATIC_KEYWORD(WORKING_DIRECTORY); MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY);
#undef MAKE_STATIC_KEYWORD #undef MAKE_STATIC_KEYWORD
static std::unordered_set<std::string> const keywords{ static std::unordered_set<std::string> const keywords{
keyAPPEND, keyAPPEND,
@ -126,7 +128,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
keyTARGET, keyTARGET,
keyUSES_TERMINAL, keyUSES_TERMINAL,
keyVERBATIM, keyVERBATIM,
keyWORKING_DIRECTORY keyWORKING_DIRECTORY,
keyDEPENDS_EXPLICIT_ONLY
}; };
for (std::string const& copy : args) { for (std::string const& copy : args) {
@ -155,6 +158,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
uses_terminal = true; uses_terminal = true;
} else if (copy == keyCOMMAND_EXPAND_LISTS) { } else if (copy == keyCOMMAND_EXPAND_LISTS) {
command_expand_lists = true; command_expand_lists = true;
} else if (copy == keyDEPENDS_EXPLICIT_ONLY) {
depends_explicit_only = true;
} else if (copy == keyTARGET) { } else if (copy == keyTARGET) {
doing = doing_target; doing = doing_target;
} else if (copy == keyARGS) { } else if (copy == keyARGS) {
@ -329,6 +334,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
cc->SetDepfile(depfile); cc->SetDepfile(depfile);
cc->SetJobPool(job_pool); cc->SetJobPool(job_pool);
cc->SetCommandExpandLists(command_expand_lists); cc->SetCommandExpandLists(command_expand_lists);
cc->SetDependsExplicitOnly(depends_explicit_only);
if (source.empty() && output.empty()) { if (source.empty() && output.empty()) {
// Source is empty, use the target. // Source is empty, use the target.
mf.AddCustomCommandToTarget(target, cctype, std::move(cc)); mf.AddCustomCommandToTarget(target, cctype, std::move(cc));

View File

@ -164,6 +164,16 @@ void cmCustomCommand::SetCommandExpandLists(bool b)
this->CommandExpandLists = 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 const std::string& cmCustomCommand::GetDepfile() const
{ {
return this->Depfile; return this->Depfile;

View File

@ -102,6 +102,11 @@ public:
bool GetCommandExpandLists() const; bool GetCommandExpandLists() const;
void SetCommandExpandLists(bool b); 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) */ /** Set/Get the depfile (used by the Ninja generator) */
const std::string& GetDepfile() const; const std::string& GetDepfile() const;
void SetDepfile(const std::string& depfile); void SetDepfile(const std::string& depfile);
@ -141,6 +146,7 @@ private:
bool CommandExpandLists = false; bool CommandExpandLists = false;
bool StdPipesUTF8 = false; bool StdPipesUTF8 = false;
bool HasMainDependency_ = false; bool HasMainDependency_ = false;
bool DependsExplicitOnly = false;
// Policies are NEW for synthesized custom commands, and set by cmMakefile for // Policies are NEW for synthesized custom commands, and set by cmMakefile for
// user-created custom commands. // user-created custom commands.

View File

@ -586,6 +586,7 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
cmNinjaDeps orderOnlyDeps; cmNinjaDeps orderOnlyDeps;
if (!cc->GetDependsExplicitOnly()) {
// A custom command may appear on multiple targets. However, some build // A custom command may appear on multiple targets. However, some build
// systems exist where the target dependencies on some of the targets are // systems exist where the target dependencies on some of the targets are
// overspecified, leading to a dependency cycle. If we assume all target // overspecified, leading to a dependency cycle. If we assume all target
@ -613,6 +614,7 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
std::back_inserter(depsIntersection)); std::back_inserter(depsIntersection));
orderOnlyDeps = depsIntersection; orderOnlyDeps = depsIntersection;
} }
}
const std::vector<std::string>& outputs = ccg.GetOutputs(); const std::vector<std::string>& outputs = ccg.GetOutputs();
const std::vector<std::string>& byproducts = ccg.GetByproducts(); const std::vector<std::string>& byproducts = ccg.GetByproducts();

View 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)

View File

@ -190,6 +190,18 @@ function (run_LooseObjectDepends)
endfunction () endfunction ()
run_LooseObjectDepends() 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) function (run_AssumedSources)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build)
run_cmake(AssumedSources) run_cmake(AssumedSources)