mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08:00
Merge topic 'custom-command-output-genex'
c257c25419
add_custom_{command,target}: Add genex support to OUTPUT and BYPRODUCTSf36af9228b
cmLocalGenerator: Evaluate generator expressions in custom command outputsc887cefd9a
cmLocalGenerator: Simplify custom command output cmSourceFile creation947ba01bf9
cmLocalGenerator: Factor out helper to expand custom command output paths1902d28ebc
cmLocalGenerator: Refactor UpdateOutputToSourceMap to avoid boolean trape4034eabe9
cmLocalGenerator: Re-order logic in CreateGeneratedSource706c48301d
cmCustomCommandGenerator: Treat relative outputs w.r.t. build dir5d23c5446e
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
This commit is contained in:
@@ -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.
|
||||
|
@@ -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,
|
||||
|
6
Help/release/dev/custom-command-output-genex.rst
Normal file
6
Help/release/dev/custom-command-output-genex.rst
Normal file
@@ -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.
|
@@ -181,8 +181,6 @@ set(SRCS
|
||||
cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
|
||||
cmCacheManager.cxx
|
||||
cmCacheManager.h
|
||||
cmCheckCustomOutputs.h
|
||||
cmCheckCustomOutputs.cxx
|
||||
cmCLocaleEnvironmentScope.h
|
||||
cmCLocaleEnvironmentScope.cxx
|
||||
cmCMakePath.h
|
||||
|
@@ -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,
|
||||
|
@@ -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(
|
||||
|
@@ -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;
|
||||
}
|
@@ -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);
|
@@ -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);
|
||||
}
|
||||
}
|
||||
cm::append(output, result);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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
|
||||
|
@@ -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;
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
|
13
Tests/ConfigSources/custom1.cpp.in
Normal file
13
Tests/ConfigSources/custom1.cpp.in
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifdef CUSTOM_CFG_DEBUG
|
||||
int custom1_debug()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_CFG_OTHER
|
||||
int custom1_other()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
13
Tests/ConfigSources/custom2.cpp.in
Normal file
13
Tests/ConfigSources/custom2.cpp.in
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifdef CUSTOM_CFG_DEBUG
|
||||
int custom2_debug()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_CFG_OTHER
|
||||
int custom2_other()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
13
Tests/ConfigSources/custom3.cpp.in
Normal file
13
Tests/ConfigSources/custom3.cpp.in
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifdef CUSTOM_CFG_DEBUG
|
||||
int custom3_debug()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_CFG_OTHER
|
||||
int custom3_other()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
13
Tests/ConfigSources/custom4.cpp.in
Normal file
13
Tests/ConfigSources/custom4.cpp.in
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifdef CUSTOM_CFG_DEBUG
|
||||
int custom4_debug()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_CFG_OTHER
|
||||
int custom4_other()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
13
Tests/ConfigSources/custom5.cpp.in
Normal file
13
Tests/ConfigSources/custom5.cpp.in
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifdef CUSTOM_CFG_DEBUG
|
||||
int custom5_debug()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CUSTOM_CFG_OTHER
|
||||
int custom5_other()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@@ -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();
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
37
Tests/RunCMake/VS10Project/CustomCommandGenex-check.cmake
Normal file
37
Tests/RunCMake/VS10Project/CustomCommandGenex-check.cmake
Normal file
@@ -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()
|
21
Tests/RunCMake/VS10Project/CustomCommandGenex.cmake
Normal file
21
Tests/RunCMake/VS10Project/CustomCommandGenex.cmake
Normal file
@@ -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")
|
@@ -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)
|
||||
|
@@ -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\)
|
||||
|
||||
)+
|
||||
|
@@ -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>")
|
||||
|
@@ -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\)
|
||||
|
||||
)+
|
||||
|
@@ -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)
|
||||
|
@@ -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\)
|
||||
|
||||
)+
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user