diff options
author | Brad King <brad.king@kitware.com> | 2020-12-14 12:22:34 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2020-12-14 12:22:45 (GMT) |
commit | fedfe763ee062456d8ee352dc9ebfc835973893f (patch) | |
tree | 8b7cdc5e17675cf69a19d21d789b796e022414b9 | |
parent | 979af92e9efb136518669ba69b1e299b8f0d4e13 (diff) | |
parent | c257c25419c68e755b0f8289d8d563437bf9e0c2 (diff) | |
download | CMake-fedfe763ee062456d8ee352dc9ebfc835973893f.zip CMake-fedfe763ee062456d8ee352dc9ebfc835973893f.tar.gz CMake-fedfe763ee062456d8ee352dc9ebfc835973893f.tar.bz2 |
Merge topic 'custom-command-output-genex'
c257c25419 add_custom_{command,target}: Add genex support to OUTPUT and BYPRODUCTS
f36af9228b cmLocalGenerator: Evaluate generator expressions in custom command outputs
c887cefd9a cmLocalGenerator: Simplify custom command output cmSourceFile creation
947ba01bf9 cmLocalGenerator: Factor out helper to expand custom command output paths
1902d28ebc cmLocalGenerator: Refactor UpdateOutputToSourceMap to avoid boolean trap
e4034eabe9 cmLocalGenerator: Re-order logic in CreateGeneratedSource
706c48301d cmCustomCommandGenerator: Treat relative outputs w.r.t. build dir
5d23c5446e cmCustomCommandGenerator: Refactor OUTPUT and DEPENDS path evaluation
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Kyle Edwards <kyle.edwards@kitware.com>
Acked-by: Pavel Solodovnikov <hellyeahdominate@gmail.com>
Acked-by: Ben Boeckel <ben.boeckel@kitware.com>
Merge-request: !5402
30 files changed, 614 insertions, 179 deletions
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 45e4e3e..4464ad6 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -46,6 +46,12 @@ The options are: Append the ``COMMAND`` and ``DEPENDS`` option values to the custom command for the first output specified. There must have already been a previous call to this command with the same output. + + If the previous call specified the output via a generator expression, + the output specified by the current call must match in at least one + configuration after evaluating generator expressions. In this case, + the appended commands and dependencies apply to all configurations. + The ``COMMENT``, ``MAIN_DEPENDENCY``, and ``WORKING_DIRECTORY`` options are currently ignored when APPEND is given, but may be used in the future. @@ -73,6 +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)>`. + ``COMMAND`` Specify the command-line(s) to execute at build time. If more than one ``COMMAND`` is specified they will be executed in order, @@ -220,6 +229,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)>`. + ``USES_TERMINAL`` .. versionadded:: 3.2 @@ -259,6 +271,44 @@ The options are: ``DEPFILE`` should also be relative to :variable:`CMAKE_CURRENT_BINARY_DIR` (see policy :policy:`CMP0116`.) +Examples: Generating Files +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Custom commands may be used to generate source files. +For example, the code: + +.. code-block:: cmake + + add_custom_command( + OUTPUT out.c + COMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt + -o out.c + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txt + VERBATIM) + add_library(myLib out.c) + +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: + +.. 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") + +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 ^^^^^^^^^^^^ @@ -308,3 +358,39 @@ of the following is specified: configuration and no "empty-string-command" will be added. This allows to add individual build events for every configuration. + +Examples: Build Events +^^^^^^^^^^^^^^^^^^^^^^ + +A ``POST_BUILD`` event may be used to post-process a binary after linking. +For example, the code: + +.. code-block:: cmake + + add_executable(myExe myExe.c) + add_custom_command( + TARGET myExe POST_BUILD + COMMAND someHasher -i "$<TARGET_FILE:myExe>" + -o "$<TARGET_FILE:myExe>.hash" + VERBATIM) + +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: + +.. 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") + +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. diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst index 7c29dda..85e1e16 100644 --- a/Help/command/add_custom_target.rst +++ b/Help/command/add_custom_target.rst @@ -54,6 +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)>`. + ``COMMAND`` Specify the command-line(s) to execute at build time. If more than one ``COMMAND`` is specified they will be executed in order, diff --git a/Help/release/dev/custom-command-output-genex.rst b/Help/release/dev/custom-command-output-genex.rst new file mode 100644 index 0000000..215349f --- /dev/null +++ b/Help/release/dev/custom-command-output-genex.rst @@ -0,0 +1,6 @@ +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. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index c3b7e50..c5b67c0 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -181,8 +181,6 @@ set(SRCS cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h cmCacheManager.cxx cmCacheManager.h - cmCheckCustomOutputs.h - cmCheckCustomOutputs.cxx cmCLocaleEnvironmentScope.h cmCLocaleEnvironmentScope.cxx cmCMakePath.h diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index c1f98fa..ccd7255 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -5,11 +5,11 @@ #include <sstream> #include <unordered_set> -#include "cmCheckCustomOutputs.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmCustomCommandTypes.h" #include "cmExecutionStatus.h" +#include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -188,7 +188,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, case doing_output: case doing_outputs: case doing_byproducts: - if (!cmSystemTools::FileIsFullPath(copy)) { + if (!cmSystemTools::FileIsFullPath(copy) && + cmGeneratorExpression::Find(copy) != 0) { // This is an output to be generated, so it should be // under the build tree. filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/'); @@ -296,13 +297,6 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args, return false; } - // Make sure the output names and locations are safe. - if (!cmCheckCustomOutputs(output, "OUTPUT", status) || - !cmCheckCustomOutputs(outputs, "OUTPUTS", status) || - !cmCheckCustomOutputs(byproducts, "BYPRODUCTS", status)) { - return false; - } - // Check for an append request. if (append) { mf.AppendCustomCommandToOutput(output[0], depends, implicit_depends, diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx index aa98d89..104065f 100644 --- a/Source/cmAddCustomTargetCommand.cxx +++ b/Source/cmAddCustomTargetCommand.cxx @@ -4,7 +4,6 @@ #include <utility> -#include "cmCheckCustomOutputs.h" #include "cmCustomCommandLines.h" #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" @@ -120,12 +119,16 @@ bool cmAddCustomTargetCommand(std::vector<std::string> const& args, break; case doing_byproducts: { std::string filename; - if (!cmSystemTools::FileIsFullPath(copy)) { + if (!cmSystemTools::FileIsFullPath(copy) && + cmGeneratorExpression::Find(copy) != 0) { filename = cmStrCat(mf.GetCurrentBinaryDirectory(), '/'); } filename += copy; cmSystemTools::ConvertToUnixSlashes(filename); - byproducts.push_back(cmSystemTools::CollapseFullPath(filename)); + if (cmSystemTools::FileIsFullPath(filename)) { + filename = cmSystemTools::CollapseFullPath(filename); + } + byproducts.push_back(filename); } break; case doing_depends: { std::string dep = copy; @@ -206,11 +209,6 @@ bool cmAddCustomTargetCommand(std::vector<std::string> const& args, return false; } - // Make sure the byproduct names and locations are safe. - if (!cmCheckCustomOutputs(byproducts, "BYPRODUCTS", status)) { - return false; - } - // Add the utility target to the makefile. bool escapeOldStyle = !verbatim; cmTarget* target = mf.AddUtilityCommand( diff --git a/Source/cmCheckCustomOutputs.cxx b/Source/cmCheckCustomOutputs.cxx deleted file mode 100644 index 7645c88..0000000 --- a/Source/cmCheckCustomOutputs.cxx +++ /dev/null @@ -1,36 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmCheckCustomOutputs.h" - -#include "cmExecutionStatus.h" -#include "cmMakefile.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" - -bool cmCheckCustomOutputs(const std::vector<std::string>& outputs, - cm::string_view keyword, cmExecutionStatus& status) -{ - cmMakefile& mf = status.GetMakefile(); - - for (std::string const& o : outputs) { - // Make sure the file will not be generated into the source - // directory during an out of source build. - if (!mf.CanIWriteThisFile(o)) { - status.SetError( - cmStrCat("attempted to have a file\n ", o, - "\nin a source directory as an output of custom command.")); - cmSystemTools::SetFatalErrorOccured(); - return false; - } - - // Make sure the output file name has no invalid characters. - std::string::size_type pos = o.find_first_of("#<>"); - if (pos != std::string::npos) { - status.SetError(cmStrCat("called with ", keyword, " containing a \"", - o[pos], "\". This character is not allowed.")); - return false; - } - } - - return true; -} diff --git a/Source/cmCheckCustomOutputs.h b/Source/cmCheckCustomOutputs.h deleted file mode 100644 index 2752ed4..0000000 --- a/Source/cmCheckCustomOutputs.h +++ /dev/null @@ -1,15 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> -#include <vector> - -#include <cm/string_view> - -class cmExecutionStatus; - -bool cmCheckCustomOutputs(const std::vector<std::string>& outputs, - cm::string_view keyword, cmExecutionStatus& status); diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx index 08a0574..64cd88e 100644 --- a/Source/cmCustomCommandGenerator.cxx +++ b/Source/cmCustomCommandGenerator.cxx @@ -24,22 +24,38 @@ #include "cmTransformDepfile.h" namespace { -void AppendPaths(const std::vector<std::string>& inputs, - cmGeneratorExpression const& ge, cmLocalGenerator* lg, - std::string const& config, std::vector<std::string>& output) +std::vector<std::string> EvaluateDepends(std::vector<std::string> const& paths, + cmGeneratorExpression const& ge, + cmLocalGenerator* lg, + std::string const& config) { - for (std::string const& in : inputs) { - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(in); - std::vector<std::string> result = - cmExpandedList(cge->Evaluate(lg, config)); - for (std::string& it : result) { - cmSystemTools::ConvertToUnixSlashes(it); - if (cmSystemTools::FileIsFullPath(it)) { - it = cmSystemTools::CollapseFullPath(it); - } + 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); + cm::append(depends, cmExpandedList(ep)); + } + for (std::string& p : depends) { + if (cmSystemTools::FileIsFullPath(p)) { + p = cmSystemTools::CollapseFullPath(p); + } else { + cmSystemTools::ConvertToUnixSlashes(p); } - cm::append(output, result); } + return depends; +} + +std::vector<std::string> EvaluateOutputs(std::vector<std::string> const& paths, + cmGeneratorExpression const& ge, + cmLocalGenerator* lg, + std::string const& config) +{ + std::vector<std::string> outputs; + for (std::string const& p : paths) { + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(p); + cm::append(outputs, lg->ExpandCustomCommandOutputPaths(*cge, config)); + } + return outputs; } } @@ -121,9 +137,10 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(cmCustomCommand const& cc, this->CommandLines.push_back(std::move(argv)); } - AppendPaths(cc.GetByproducts(), ge, this->LG, this->Config, - this->Byproducts); - AppendPaths(cc.GetDepends(), ge, this->LG, this->Config, this->Depends); + this->Outputs = EvaluateOutputs(cc.GetOutputs(), ge, this->LG, this->Config); + this->Byproducts = + EvaluateOutputs(cc.GetByproducts(), ge, this->LG, this->Config); + this->Depends = EvaluateDepends(cc.GetDepends(), ge, this->LG, this->Config); const std::string& workingdirectory = this->CC->GetWorkingDirectory(); if (!workingdirectory.empty()) { @@ -326,7 +343,7 @@ std::string cmCustomCommandGenerator::GetWorkingDirectory() const std::vector<std::string> const& cmCustomCommandGenerator::GetOutputs() const { - return this->CC->GetOutputs(); + return this->Outputs; } std::vector<std::string> const& cmCustomCommandGenerator::GetByproducts() const diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index cb0d7df..dac3596 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -24,6 +24,7 @@ class cmCustomCommandGenerator bool MakeVars; cmCustomCommandLines CommandLines; std::vector<std::vector<std::string>> EmulatorsWithArguments; + std::vector<std::string> Outputs; std::vector<std::string> Byproducts; std::vector<std::string> Depends; std::string WorkingDirectory; diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index c9dfdea..9f9d725 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -17,9 +17,11 @@ #include <cm/memory> #include <cm/string_view> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmsys/RegularExpression.hxx" +#include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" @@ -3812,38 +3814,95 @@ void cmLocalGenerator::GenerateFrameworkInfoPList( } namespace { +cm::string_view CustomOutputRoleKeyword(cmLocalGenerator::OutputRole role) +{ + return (role == cmLocalGenerator::OutputRole::Primary ? "OUTPUT"_s + : "BYPRODUCTS"_s); +} + void CreateGeneratedSource(cmLocalGenerator& lg, const std::string& output, + cmLocalGenerator::OutputRole role, cmCommandOrigin origin, const cmListFileBacktrace& lfbt) { - if (cmGeneratorExpression::Find(output) == std::string::npos) { - // Outputs without generator expressions from the project are already - // created and marked as generated. Do not mark them again, because - // other commands might have overwritten the property. - if (origin == cmCommandOrigin::Generator) { - lg.GetMakefile()->GetOrCreateGeneratedSource(output); - } - } else { + if (cmGeneratorExpression::Find(output) != std::string::npos) { lg.GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, "Generator expressions in custom command outputs are not implemented!", lfbt); + return; + } + + // Make sure the file will not be generated into the source + // directory during an out of source build. + if (!lg.GetMakefile()->CanIWriteThisFile(output)) { + lg.GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(CustomOutputRoleKeyword(role), " path\n ", output, + "\nin a source directory as an output of custom command."), + lfbt); + return; + } + + // Make sure the output file name has no invalid characters. + std::string::size_type pos = output.find_first_of("#<>"); + if (pos != std::string::npos) { + lg.GetCMakeInstance()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(CustomOutputRoleKeyword(role), " containing a \"", output[pos], + "\" is not allowed."), + lfbt); + return; + } + + // Outputs without generator expressions from the project are already + // created and marked as generated. Do not mark them again, because + // other commands might have overwritten the property. + if (origin == cmCommandOrigin::Generator) { + lg.GetMakefile()->GetOrCreateGeneratedSource(output); } } -void CreateGeneratedSources(cmLocalGenerator& lg, - const std::vector<std::string>& outputs, - cmCommandOrigin origin, - const cmListFileBacktrace& lfbt) +std::string ComputeCustomCommandRuleFileName(cmLocalGenerator& lg, + cmListFileBacktrace const& bt, + std::string const& output) { - for (std::string const& o : outputs) { - CreateGeneratedSource(lg, o, origin, lfbt); + // If the output path has no generator expressions, use it directly. + if (cmGeneratorExpression::Find(output) == std::string::npos) { + return output; } + + // The output path contains a generator expression, but we must choose + // a single source file path to which to attach the custom command. + // Use some heuristics to provie a nice-looking name when possible. + + // If the only genex is $<CONFIG>, replace that gracefully. + { + std::string simple = output; + cmSystemTools::ReplaceString(simple, "$<CONFIG>", "(CONFIG)"); + if (cmGeneratorExpression::Find(simple) == std::string::npos) { + return simple; + } + } + + // If the genex evaluates to the same value in all configurations, use that. + { + std::vector<std::string> allConfigOutputs = + lg.ExpandCustomCommandOutputGenex(output, bt); + if (allConfigOutputs.size() == 1) { + return allConfigOutputs.front(); + } + } + + // Fall back to a deterministic unique name. + cmCryptoHash h(cmCryptoHash::AlgoSHA256); + return cmStrCat(lg.GetCurrentBinaryDirectory(), "/CMakeFiles/", + h.HashString(output).substr(0, 16)); } cmSourceFile* AddCustomCommand( cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, - const std::vector<std::string>& outputs, + cmCommandOrigin origin, const std::vector<std::string>& outputs, const std::vector<std::string>& byproducts, const std::vector<std::string>& depends, const std::string& main_dependency, const cmImplicitDependsList& implicit_depends, @@ -3880,7 +3939,8 @@ cmSourceFile* AddCustomCommand( cmGlobalGenerator* gg = lg.GetGlobalGenerator(); // Construct a rule file associated with the first output produced. - std::string outName = gg->GenerateRuleFile(outputs[0]); + std::string outName = gg->GenerateRuleFile( + ComputeCustomCommandRuleFileName(lg, lfbt, outputs[0])); // Check if the rule file already exists. file = mf->GetSource(outName, cmSourceFileLocationKind::Known); @@ -3923,7 +3983,10 @@ cmSourceFile* AddCustomCommand( cc->SetJobPool(job_pool); file->SetCustomCommand(std::move(cc)); - lg.AddSourceOutputs(file, outputs, byproducts); + lg.AddSourceOutputs(file, outputs, cmLocalGenerator::OutputRole::Primary, + lfbt, origin); + lg.AddSourceOutputs(file, byproducts, + cmLocalGenerator::OutputRole::Byproduct, lfbt, origin); } return file; } @@ -3967,9 +4030,6 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg, const std::string& job_pool, bool command_expand_lists, bool stdPipesUTF8) { - // Always create the byproduct sources and mark them generated. - CreateGeneratedSources(lg, byproducts, origin, lfbt); - // Add the command to the appropriate build step for the target. std::vector<std::string> no_output; cmCustomCommand cc(no_output, byproducts, depends, commandLines, lfbt, @@ -3992,7 +4052,7 @@ void AddCustomCommandToTarget(cmLocalGenerator& lg, break; } - lg.AddTargetByproducts(target, byproducts); + lg.AddTargetByproducts(target, byproducts, lfbt, origin); } cmSourceFile* AddCustomCommandToOutput( @@ -4006,14 +4066,11 @@ cmSourceFile* AddCustomCommandToOutput( bool uses_terminal, bool command_expand_lists, const std::string& depfile, const std::string& job_pool, bool stdPipesUTF8) { - // Always create the output sources and mark them generated. - CreateGeneratedSources(lg, outputs, origin, lfbt); - CreateGeneratedSources(lg, byproducts, origin, lfbt); - - return AddCustomCommand( - lg, lfbt, outputs, byproducts, depends, main_dependency, implicit_depends, - commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal, - command_expand_lists, depfile, job_pool, stdPipesUTF8); + return AddCustomCommand(lg, lfbt, origin, outputs, byproducts, depends, + main_dependency, implicit_depends, commandLines, + comment, workingDir, replace, escapeOldStyle, + uses_terminal, command_expand_lists, depfile, + job_pool, stdPipesUTF8); } void AppendCustomCommandToOutput(cmLocalGenerator& lg, @@ -4024,7 +4081,22 @@ void AppendCustomCommandToOutput(cmLocalGenerator& lg, const cmCustomCommandLines& commandLines) { // Lookup an existing command. - if (cmSourceFile* sf = lg.GetSourceFileWithOutput(output)) { + cmSourceFile* sf = nullptr; + if (cmGeneratorExpression::Find(output) == std::string::npos) { + sf = lg.GetSourceFileWithOutput(output); + } else { + // This output path has a generator expression. Evaluate it to + // find the output for any configurations. + for (std::string const& out : + lg.ExpandCustomCommandOutputGenex(output, lfbt)) { + sf = lg.GetSourceFileWithOutput(out); + if (sf) { + break; + } + } + } + + if (sf) { if (cmCustomCommand* cc = sf->GetCustomCommand()) { cc->AppendCommands(commandLines); cc->AppendDepends(depends); @@ -4051,10 +4123,6 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, bool uses_terminal, bool command_expand_lists, const std::string& job_pool, bool stdPipesUTF8) { - // Always create the byproduct sources and mark them generated. - CreateGeneratedSource(lg, force.Name, origin, lfbt); - CreateGeneratedSources(lg, byproducts, origin, lfbt); - // Use an empty comment to avoid generation of default comment. if (!comment) { comment = ""; @@ -4063,12 +4131,12 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, std::string no_main_dependency; cmImplicitDependsList no_implicit_depends; cmSourceFile* rule = AddCustomCommand( - lg, lfbt, { force.Name }, byproducts, depends, no_main_dependency, + lg, lfbt, origin, { force.Name }, byproducts, depends, no_main_dependency, no_implicit_depends, commandLines, comment, workingDir, /*replace=*/false, escapeOldStyle, uses_terminal, command_expand_lists, /*depfile=*/"", job_pool, stdPipesUTF8); if (rule) { - lg.AddTargetByproducts(target, byproducts); + lg.AddTargetByproducts(target, byproducts, lfbt, origin); } if (!force.NameCMP0049.empty()) { @@ -4166,34 +4234,87 @@ cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput( return nullptr; } +std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputPaths( + cmCompiledGeneratorExpression const& cge, std::string const& config) +{ + std::vector<std::string> paths = cmExpandedList(cge.Evaluate(this, config)); + for (std::string& p : paths) { + p = cmSystemTools::CollapseFullPath(p, this->GetCurrentBinaryDirectory()); + } + return paths; +} + +std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputGenex( + std::string const& o, cmListFileBacktrace const& bt) +{ + std::vector<std::string> allConfigOutputs; + cmGeneratorExpression ge(bt); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(o); + std::vector<std::string> configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& config : configs) { + std::vector<std::string> configOutputs = + this->ExpandCustomCommandOutputPaths(*cge, config); + allConfigOutputs.reserve(allConfigOutputs.size() + configOutputs.size()); + std::move(configOutputs.begin(), configOutputs.end(), + std::back_inserter(allConfigOutputs)); + } + auto endUnique = + cmRemoveDuplicates(allConfigOutputs.begin(), allConfigOutputs.end()); + allConfigOutputs.erase(endUnique, allConfigOutputs.end()); + return allConfigOutputs; +} + void cmLocalGenerator::AddTargetByproducts( - cmTarget* target, const std::vector<std::string>& byproducts) + cmTarget* target, const std::vector<std::string>& byproducts, + cmListFileBacktrace const& bt, cmCommandOrigin origin) { for (std::string const& o : byproducts) { - this->UpdateOutputToSourceMap(o, target); + if (cmGeneratorExpression::Find(o) == std::string::npos) { + this->UpdateOutputToSourceMap(o, target, bt, origin); + continue; + } + + // This byproduct path has a generator expression. Evaluate it to + // register the byproducts for all configurations. + for (std::string const& b : this->ExpandCustomCommandOutputGenex(o, bt)) { + this->UpdateOutputToSourceMap(b, target, bt, cmCommandOrigin::Generator); + } } } void cmLocalGenerator::AddSourceOutputs( cmSourceFile* source, const std::vector<std::string>& outputs, - const std::vector<std::string>& byproducts) + OutputRole role, cmListFileBacktrace const& bt, cmCommandOrigin origin) { for (std::string const& o : outputs) { - this->UpdateOutputToSourceMap(o, source, false); - } - for (std::string const& o : byproducts) { - this->UpdateOutputToSourceMap(o, source, true); + if (cmGeneratorExpression::Find(o) == std::string::npos) { + this->UpdateOutputToSourceMap(o, source, role, bt, origin); + continue; + } + + // This output path has a generator expression. Evaluate it to + // register the outputs for all configurations. + for (std::string const& out : + this->ExpandCustomCommandOutputGenex(o, bt)) { + this->UpdateOutputToSourceMap(out, source, role, bt, + cmCommandOrigin::Generator); + } } } void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct, - cmTarget* target) + cmTarget* target, + cmListFileBacktrace const& bt, + cmCommandOrigin origin) { SourceEntry entry; entry.Sources.Target = target; auto pr = this->OutputToSource.emplace(byproduct, entry); - if (!pr.second) { + if (pr.second) { + CreateGeneratedSource(*this, byproduct, OutputRole::Byproduct, origin, bt); + } else { SourceEntry& current = pr.first->second; // Has the target already been set? if (!current.Sources.Target) { @@ -4210,18 +4331,22 @@ void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct, void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source, - bool byproduct) + OutputRole role, + cmListFileBacktrace const& bt, + cmCommandOrigin origin) { SourceEntry entry; entry.Sources.Source = source; - entry.Sources.SourceIsByproduct = byproduct; + entry.Sources.SourceIsByproduct = role == OutputRole::Byproduct; auto pr = this->OutputToSource.emplace(output, entry); - if (!pr.second) { + if (pr.second) { + CreateGeneratedSource(*this, output, role, origin, bt); + } else { SourceEntry& current = pr.first->second; // Outputs take precedence over byproducts if (!current.Sources.Source || - (current.Sources.SourceIsByproduct && !byproduct)) { + (current.Sources.SourceIsByproduct && role == OutputRole::Primary)) { current.Sources.Source = source; current.Sources.SourceIsByproduct = false; } else { diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 09e820a..581badb 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -22,6 +22,7 @@ #include "cmProperty.h" #include "cmStateSnapshot.h" +class cmCompiledGeneratorExpression; class cmComputeLinkInformation; class cmCustomCommandGenerator; class cmCustomCommandLines; @@ -362,18 +363,32 @@ public: bool command_expand_lists = false, const std::string& job_pool = "", bool stdPipesUTF8 = false); + std::vector<std::string> ExpandCustomCommandOutputPaths( + cmCompiledGeneratorExpression const& cge, std::string const& config); + std::vector<std::string> ExpandCustomCommandOutputGenex( + std::string const& o, cmListFileBacktrace const& bt); + /** * Add target byproducts. */ void AddTargetByproducts(cmTarget* target, - const std::vector<std::string>& byproducts); + const std::vector<std::string>& byproducts, + cmListFileBacktrace const& bt, + cmCommandOrigin origin); + + enum class OutputRole + { + Primary, + Byproduct, + }; /** * Add source file outputs. */ void AddSourceOutputs(cmSourceFile* source, - const std::vector<std::string>& outputs, - const std::vector<std::string>& byproducts); + std::vector<std::string> const& outputs, + OutputRole role, cmListFileBacktrace const& bt, + cmCommandOrigin origin); /** * Return the target if the provided source name is a byproduct of a utility @@ -607,9 +622,12 @@ private: using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>; OutputToSourceMap OutputToSource; - void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target); + void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target, + cmListFileBacktrace const& bt, + cmCommandOrigin origin); void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source, - bool byproduct); + OutputRole role, cmListFileBacktrace const& bt, + cmCommandOrigin origin); void AddSharedFlags(std::string& flags, const std::string& lang, bool shared); diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt index 1db00cc..5513af8 100644 --- a/Tests/ConfigSources/CMakeLists.txt +++ b/Tests/ConfigSources/CMakeLists.txt @@ -16,6 +16,72 @@ void config_$<CONFIG>() {} ]] ) +# Custom command outputs named with the configuration(s). +add_custom_command( + OUTPUT "custom1_$<CONFIG>.cpp" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom1.cpp.in" "custom1_$<CONFIG>.cpp" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom1.cpp.in + VERBATIM + ) +# Output path starts in a generator expression. +add_custom_command( + OUTPUT "$<1:custom2_$<CONFIG>.cpp>" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom2.cpp.in" "custom2_$<CONFIG>.cpp" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom2.cpp.in + VERBATIM + ) +# Source file generated as a custom command's byproduct. +add_custom_command( + OUTPUT custom3.txt + BYPRODUCTS "$<1:custom3_$<CONFIG>.cpp>" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom3.cpp.in" "custom3_$<CONFIG>.cpp" + COMMAND ${CMAKE_COMMAND} -E touch custom3.txt + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom3.cpp.in + VERBATIM + ) +# Source file generated as a custom target's byproduct. +add_custom_target(custom4 + BYPRODUCTS "custom4_$<CONFIG>.cpp" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/custom4.cpp.in" "custom4_$<CONFIG>.cpp" + VERBATIM + ) +# Source file generated by appended custom command. +add_custom_command( + OUTPUT "custom5_$<CONFIG>.cpp" + COMMAND ${CMAKE_COMMAND} -E echo custom5_$<CONFIG>.cpp + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom5.cpp.in + VERBATIM + ) +add_custom_command(APPEND + OUTPUT "custom5_$<CONFIG>.cpp" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom5.cpp.in" "custom5_$<CONFIG>.cpp.in" + VERBATIM + ) +# Appending through any configuration's output affects all configurations. +if(CMAKE_CONFIGURATION_TYPES MATCHES ";([^;]+)$") + set(last_config "${CMAKE_MATCH_1}") +else() + set(last_config ${CMAKE_BUILD_TYPE}) +endif() +add_custom_command(APPEND + OUTPUT "custom5_${last_config}.cpp" + COMMAND ${CMAKE_COMMAND} -E copy "custom5_$<CONFIG>.cpp.in" "custom5_$<CONFIG>.cpp" + VERBATIM + ) +foreach(n RANGE 1 5) + set_property(SOURCE custom${n}_Debug.cpp PROPERTY COMPILE_DEFINITIONS CUSTOM_CFG_DEBUG) + foreach(other Release RelWithDebInfo MinSizeRel) + set_property(SOURCE custom${n}_${other}.cpp PROPERTY COMPILE_DEFINITIONS CUSTOM_CFG_OTHER) + endforeach() +endforeach() +add_library(Custom STATIC + custom1_$<CONFIG>.cpp + custom2_$<CONFIG>.cpp + custom3_$<CONFIG>.cpp custom3.txt + custom4_$<CONFIG>.cpp + custom5_$<CONFIG>.cpp + ) + # Per-config sources via INTERFACE_SOURCES. add_library(iface INTERFACE) target_sources(iface INTERFACE @@ -34,7 +100,7 @@ add_executable(ConfigSources $<$<CONFIG:NotAConfig>:does_not_exist.cpp> ${CMAKE_CURRENT_BINARY_DIR}/config_$<CONFIG>.cpp ) -target_link_libraries(ConfigSources iface) +target_link_libraries(ConfigSources Custom iface) # Per-config sources via LINK_LIBRARIES. add_library(iface_debug INTERFACE) @@ -53,6 +119,7 @@ target_compile_definitions(ConfigSourcesLink PRIVATE "$<$<NOT:$<CONFIG:Debug>>:CFG_OTHER>" ) target_link_libraries(ConfigSourcesLink PRIVATE + Custom "$<$<CONFIG:Debug>:iface_debug>" "$<$<NOT:$<CONFIG:Debug>>:iface_other>" "$<$<CONFIG:NotAConfig>:iface_does_not_exist>" @@ -70,7 +137,7 @@ target_compile_definitions(ConfigSourcesLinkIface PRIVATE "$<$<CONFIG:Debug>:CFG_DEBUG>" "$<$<NOT:$<CONFIG:Debug>>:CFG_OTHER>" ) -target_link_libraries(ConfigSourcesLinkIface ConfigSourcesIface) +target_link_libraries(ConfigSourcesLinkIface Custom ConfigSourcesIface) # A target with sources in only one configuration that is not the # first in CMAKE_CONFIGURATION_TYPES. diff --git a/Tests/ConfigSources/custom1.cpp.in b/Tests/ConfigSources/custom1.cpp.in new file mode 100644 index 0000000..e5f21c7 --- /dev/null +++ b/Tests/ConfigSources/custom1.cpp.in @@ -0,0 +1,13 @@ +#ifdef CUSTOM_CFG_DEBUG +int custom1_debug() +{ + return 0; +} +#endif + +#ifdef CUSTOM_CFG_OTHER +int custom1_other() +{ + return 0; +} +#endif diff --git a/Tests/ConfigSources/custom2.cpp.in b/Tests/ConfigSources/custom2.cpp.in new file mode 100644 index 0000000..438c1fd --- /dev/null +++ b/Tests/ConfigSources/custom2.cpp.in @@ -0,0 +1,13 @@ +#ifdef CUSTOM_CFG_DEBUG +int custom2_debug() +{ + return 0; +} +#endif + +#ifdef CUSTOM_CFG_OTHER +int custom2_other() +{ + return 0; +} +#endif diff --git a/Tests/ConfigSources/custom3.cpp.in b/Tests/ConfigSources/custom3.cpp.in new file mode 100644 index 0000000..4545b69 --- /dev/null +++ b/Tests/ConfigSources/custom3.cpp.in @@ -0,0 +1,13 @@ +#ifdef CUSTOM_CFG_DEBUG +int custom3_debug() +{ + return 0; +} +#endif + +#ifdef CUSTOM_CFG_OTHER +int custom3_other() +{ + return 0; +} +#endif diff --git a/Tests/ConfigSources/custom4.cpp.in b/Tests/ConfigSources/custom4.cpp.in new file mode 100644 index 0000000..8a8b2a8 --- /dev/null +++ b/Tests/ConfigSources/custom4.cpp.in @@ -0,0 +1,13 @@ +#ifdef CUSTOM_CFG_DEBUG +int custom4_debug() +{ + return 0; +} +#endif + +#ifdef CUSTOM_CFG_OTHER +int custom4_other() +{ + return 0; +} +#endif diff --git a/Tests/ConfigSources/custom5.cpp.in b/Tests/ConfigSources/custom5.cpp.in new file mode 100644 index 0000000..51f40ae --- /dev/null +++ b/Tests/ConfigSources/custom5.cpp.in @@ -0,0 +1,13 @@ +#ifdef CUSTOM_CFG_DEBUG +int custom5_debug() +{ + return 0; +} +#endif + +#ifdef CUSTOM_CFG_OTHER +int custom5_other() +{ + return 0; +} +#endif diff --git a/Tests/ConfigSources/main_debug.cpp b/Tests/ConfigSources/main_debug.cpp index 9b1e68a..ef776f8 100644 --- a/Tests/ConfigSources/main_debug.cpp +++ b/Tests/ConfigSources/main_debug.cpp @@ -7,7 +7,14 @@ #include "iface.h" +extern int custom1_debug(); +extern int custom2_debug(); +extern int custom3_debug(); +extern int custom4_debug(); +extern int custom5_debug(); + int main(int argc, char** argv) { - return iface_src() + iface_debug(); + return iface_src() + iface_debug() + custom1_debug() + custom2_debug() + + custom3_debug() + custom4_debug() + custom5_debug(); } diff --git a/Tests/ConfigSources/main_other.cpp b/Tests/ConfigSources/main_other.cpp index 3184a19..74f2156 100644 --- a/Tests/ConfigSources/main_other.cpp +++ b/Tests/ConfigSources/main_other.cpp @@ -7,7 +7,14 @@ #include "iface.h" +extern int custom1_other(); +extern int custom2_other(); +extern int custom3_other(); +extern int custom4_other(); +extern int custom5_other(); + int main(int argc, char** argv) { - return iface_src() + iface_other(); + return iface_src() + iface_other() + custom1_other() + custom2_other() + + custom3_other() + custom4_other() + custom5_other(); } diff --git a/Tests/RunCMake/VS10Project/CustomCommandGenex-check.cmake b/Tests/RunCMake/VS10Project/CustomCommandGenex-check.cmake new file mode 100644 index 0000000..a7047bc --- /dev/null +++ b/Tests/RunCMake/VS10Project/CustomCommandGenex-check.cmake @@ -0,0 +1,37 @@ +set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj") +if(NOT EXISTS "${vcProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.") + return() +endif() + +set(found_CustomBuild_out 0) +set(found_CustomBuild_out_CONFIG 0) +set(found_CustomBuild_out_CONFIG_CONFIG 0) +set(found_CustomBuild_out_HASH 0) +file(STRINGS "${vcProjectFile}" lines) +foreach(line IN LISTS lines) + if(line MATCHES [[<CustomBuild Include=".*\\out\.txt\.rule">]]) + set(found_CustomBuild_out 1) + endif() + if(line MATCHES [[<CustomBuild Include=".*\\out-\(CONFIG\)\.txt\.rule">]]) + set(found_CustomBuild_out_CONFIG 1) + endif() + if(line MATCHES [[<CustomBuild Include=".*\\out-\(CONFIG\)-\(CONFIG\)\.txt\.rule">]]) + set(found_CustomBuild_out_CONFIG_CONFIG 1) + endif() + if(line MATCHES [[<CustomBuild Include=".*\\[0-9A-Fa-f]+\.rule">]]) + set(found_CustomBuild_out_HASH 1) + endif() +endforeach() +if(NOT found_CustomBuild_out) + string(APPEND RunCMake_TEST_FAILED "CustomBuild for out.txt.rule not found in\n ${vcProjectFile}\n") +endif() +if(NOT found_CustomBuild_out_CONFIG) + string(APPEND RunCMake_TEST_FAILED "CustomBuild for out-(CONFIG).txt.rule not found in\n ${vcProjectFile}\n") +endif() +if(NOT found_CustomBuild_out_CONFIG_CONFIG) + string(APPEND RunCMake_TEST_FAILED "CustomBuild for out-(CONFIG)-(CONFIG).txt.rule not found in\n ${vcProjectFile}\n") +endif() +if(NOT found_CustomBuild_out_HASH) + string(APPEND RunCMake_TEST_FAILED "CustomBuild for <hash>.rule not found in\n ${vcProjectFile}\n") +endif() diff --git a/Tests/RunCMake/VS10Project/CustomCommandGenex.cmake b/Tests/RunCMake/VS10Project/CustomCommandGenex.cmake new file mode 100644 index 0000000..5b69dc2 --- /dev/null +++ b/Tests/RunCMake/VS10Project/CustomCommandGenex.cmake @@ -0,0 +1,21 @@ +add_custom_command( + OUTPUT "$<1:out.txt>" + COMMAND ${CMAKE_COMMAND} -E touch "out.txt" + VERBATIM + ) +add_custom_command( + OUTPUT "out-$<CONFIG>.txt" + COMMAND ${CMAKE_COMMAND} -E touch "out-$<CONFIG>.txt" + VERBATIM + ) +add_custom_command( + OUTPUT "out-$<CONFIG>-$<CONFIG>.txt" + COMMAND ${CMAKE_COMMAND} -E touch "out-$<CONFIG>-$<CONFIG>.txt" + VERBATIM + ) +add_custom_command( + OUTPUT "out-$<CONFIG>-$<CONFIG:Debug>.txt" + COMMAND ${CMAKE_COMMAND} -E touch "out-$<CONFIG>-$<CONFIG:Debug>.txt" + VERBATIM + ) +add_custom_target(foo DEPENDS "out.txt" "out-$<CONFIG>.txt" "out-$<CONFIG>-$<CONFIG>.txt" "out-$<CONFIG>-$<CONFIG:Debug>.txt") diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake index 133dacc..d5ed136 100644 --- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake +++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake @@ -7,6 +7,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREA run_cmake(LanguageStandard) endif() +run_cmake(CustomCommandGenex) run_cmake(VsCsharpSourceGroup) run_cmake(VsCSharpCompilerOpts) run_cmake(ExplicitCMakeLists) diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt index 086e397..6d51575 100644 --- a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt +++ b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt @@ -1,36 +1,47 @@ CMake Error at BadByproduct.cmake:2 \(add_custom_command\): - add_custom_command called with BYPRODUCTS containing a "#". This character - is not allowed. + BYPRODUCTS containing a "#" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) CMake Error at BadByproduct.cmake:3 \(add_custom_command\): - add_custom_command called with BYPRODUCTS containing a "<". This character - is not allowed. + BYPRODUCTS containing a "<" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) CMake Error at BadByproduct.cmake:4 \(add_custom_command\): - add_custom_command called with BYPRODUCTS containing a ">". This character - is not allowed. + BYPRODUCTS containing a ">" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) - +( CMake Error at BadByproduct.cmake:5 \(add_custom_command\): - add_custom_command called with BYPRODUCTS containing a "<". This character - is not allowed. + BYPRODUCTS containing a "#" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) - +)+ CMake Error at BadByproduct.cmake:6 \(add_custom_command\): - add_custom_command attempted to have a file + BYPRODUCTS path .*RunCMake/add_custom_command/f in a source directory as an output of custom command. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + +( +CMake Error at BadByproduct.cmake:7 \(add_custom_command\): + Error evaluating generator expression: + + \$<TARGET_PROPERTY:prop> + + \$<TARGET_PROPERTY:prop> may only be used with binary targets. It may not + be used with add_custom_command or add_custom_target. Specify the target + to read a property from using the \$<TARGET_PROPERTY:tgt,prop> signature + instead. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + +)+ diff --git a/Tests/RunCMake/add_custom_command/BadByproduct.cmake b/Tests/RunCMake/add_custom_command/BadByproduct.cmake index 91bca52..7c786a4 100644 --- a/Tests/RunCMake/add_custom_command/BadByproduct.cmake +++ b/Tests/RunCMake/add_custom_command/BadByproduct.cmake @@ -4,3 +4,4 @@ add_custom_command(OUTPUT b BYPRODUCTS "a<") add_custom_command(OUTPUT c BYPRODUCTS "a>") add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/#") add_custom_command(OUTPUT e BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/f) +add_custom_command(OUTPUT f BYPRODUCTS "$<TARGET_PROPERTY:prop>") diff --git a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt index 731e58d..506bec9 100644 --- a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt +++ b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt @@ -1,36 +1,47 @@ CMake Error at BadOutput.cmake:2 \(add_custom_command\): - add_custom_command called with OUTPUT containing a "#". This character is - not allowed. + OUTPUT containing a "#" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) CMake Error at BadOutput.cmake:3 \(add_custom_command\): - add_custom_command called with OUTPUT containing a "<". This character is - not allowed. + OUTPUT containing a "<" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) CMake Error at BadOutput.cmake:4 \(add_custom_command\): - add_custom_command called with OUTPUT containing a ">". This character is - not allowed. + OUTPUT containing a ">" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) - +( CMake Error at BadOutput.cmake:5 \(add_custom_command\): - add_custom_command called with OUTPUT containing a "<". This character is - not allowed. + OUTPUT containing a "#" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) - +)+ CMake Error at BadOutput.cmake:6 \(add_custom_command\): - add_custom_command attempted to have a file + OUTPUT path .*RunCMake/add_custom_command/e in a source directory as an output of custom command. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + +( +CMake Error at BadOutput.cmake:7 \(add_custom_command\): + Error evaluating generator expression: + + \$<TARGET_PROPERTY:prop> + + \$<TARGET_PROPERTY:prop> may only be used with binary targets. It may not + be used with add_custom_command or add_custom_target. Specify the target + to read a property from using the \$<TARGET_PROPERTY:tgt,prop> signature + instead. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + +)+ diff --git a/Tests/RunCMake/add_custom_command/BadOutput.cmake b/Tests/RunCMake/add_custom_command/BadOutput.cmake index 6875fe9..77acb7f 100644 --- a/Tests/RunCMake/add_custom_command/BadOutput.cmake +++ b/Tests/RunCMake/add_custom_command/BadOutput.cmake @@ -4,3 +4,4 @@ add_custom_command(OUTPUT "a<" COMMAND b) add_custom_command(OUTPUT "a>" COMMAND c) add_custom_command(OUTPUT "$<CONFIG>/#" COMMAND d) add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/e COMMAND f) +add_custom_command(OUTPUT "$<TARGET_PROPERTY:prop>" COMMAND g) diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt index 0f58550..4f0f005 100644 --- a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt +++ b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt @@ -1,36 +1,47 @@ CMake Error at BadByproduct.cmake:2 \(add_custom_target\): - add_custom_target called with BYPRODUCTS containing a "#". This character - is not allowed. + BYPRODUCTS containing a "#" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) CMake Error at BadByproduct.cmake:3 \(add_custom_target\): - add_custom_target called with BYPRODUCTS containing a "<". This character - is not allowed. + BYPRODUCTS containing a "<" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) CMake Error at BadByproduct.cmake:4 \(add_custom_target\): - add_custom_target called with BYPRODUCTS containing a ">". This character - is not allowed. + BYPRODUCTS containing a ">" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) - +( CMake Error at BadByproduct.cmake:5 \(add_custom_target\): - add_custom_target called with BYPRODUCTS containing a "<". This character - is not allowed. + BYPRODUCTS containing a "#" is not allowed. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) - +)+ CMake Error at BadByproduct.cmake:6 \(add_custom_target\): - add_custom_target attempted to have a file + BYPRODUCTS path .*RunCMake/add_custom_target/j in a source directory as an output of custom command. Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) + +( +CMake Error at BadByproduct.cmake:7 \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_PROPERTY:prop> + + \$<TARGET_PROPERTY:prop> may only be used with binary targets. It may not + be used with add_custom_command or add_custom_target. Specify the target + to read a property from using the \$<TARGET_PROPERTY:tgt,prop> signature + instead. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) + +)+ diff --git a/Tests/RunCMake/add_custom_target/BadByproduct.cmake b/Tests/RunCMake/add_custom_target/BadByproduct.cmake index 963d641..e97f9fd 100644 --- a/Tests/RunCMake/add_custom_target/BadByproduct.cmake +++ b/Tests/RunCMake/add_custom_target/BadByproduct.cmake @@ -4,3 +4,4 @@ add_custom_target(c BYPRODUCTS "a<" COMMAND d) add_custom_target(e BYPRODUCTS "a>" COMMAND f) add_custom_target(g BYPRODUCTS "$<CONFIG>/#" COMMAND h) add_custom_target(i BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/j COMMAND k) +add_custom_target(l BYPRODUCTS "$<TARGET_PROPERTY:prop>" COMMAND m) @@ -293,7 +293,6 @@ CMAKE_CXX_SOURCES="\ cmCMakePolicyCommand \ cmCPackPropertiesGenerator \ cmCacheManager \ - cmCheckCustomOutputs \ cmCommand \ cmCommandArgumentParserHelper \ cmCommands \ |