mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08:00
Merge topic 'genex-target-intermediate-dir'
f195c9ef2a
genex: Add TARGET_INTERMEDIATE_DIR expression5c5634db52
cmGeneratorExpressionNode: Factor out HasKnownObjectFileLocation check Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !11217
This commit is contained in:
@@ -2836,6 +2836,13 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
|
|||||||
This generator expression can e.g. be used to create a batch file using
|
This generator expression can e.g. be used to create a batch file using
|
||||||
:command:`file(GENERATE)` which sets the PATH environment variable accordingly.
|
:command:`file(GENERATE)` which sets the PATH environment variable accordingly.
|
||||||
|
|
||||||
|
.. genex:: $<TARGET_INTERMEDIATE_DIR:tgt>
|
||||||
|
|
||||||
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
|
The full path to the directory where intermediate target files, such as
|
||||||
|
object and dependency files, are stored.
|
||||||
|
|
||||||
Export And Install Expressions
|
Export And Install Expressions
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
6
Help/release/dev/genex-target-intermediate-dir.rst
Normal file
6
Help/release/dev/genex-target-intermediate-dir.rst
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
genex-target-intermediate-dir
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
* The :genex:`TARGET_INTERMEDIATE_DIR` generator expression was
|
||||||
|
added to refer to a target's intermediate files directory in
|
||||||
|
the build tree.
|
@@ -52,6 +52,31 @@
|
|||||||
#include "cmValue.h"
|
#include "cmValue.h"
|
||||||
#include "cmake.h"
|
#include "cmake.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool HasKnownObjectFileLocation(cm::GenEx::Evaluation* eval,
|
||||||
|
GeneratorExpressionContent const* content,
|
||||||
|
std::string const& genex,
|
||||||
|
cmGeneratorTarget const* target)
|
||||||
|
{
|
||||||
|
std::string reason;
|
||||||
|
if (!eval->EvaluateForBuildsystem &&
|
||||||
|
!target->Target->HasKnownObjectFileLocation(&reason)) {
|
||||||
|
std::ostringstream e;
|
||||||
|
e << "The evaluation of the " << genex
|
||||||
|
<< " generator expression "
|
||||||
|
"is only suitable for consumption by CMake (limited"
|
||||||
|
<< reason
|
||||||
|
<< "). "
|
||||||
|
"It is not suitable for writing out elsewhere.";
|
||||||
|
reportError(eval, content->GetOriginalExpression(), e.str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
|
std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
|
||||||
std::string const& prop, cm::GenEx::Evaluation* eval,
|
std::string const& prop, cm::GenEx::Evaluation* eval,
|
||||||
cmGeneratorTarget const* headTarget,
|
cmGeneratorTarget const* headTarget,
|
||||||
@@ -3227,6 +3252,71 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
|
|||||||
}
|
}
|
||||||
} targetPropertyNode;
|
} targetPropertyNode;
|
||||||
|
|
||||||
|
static const struct targetIntermediateDirNode
|
||||||
|
: public cmGeneratorExpressionNode
|
||||||
|
{
|
||||||
|
targetIntermediateDirNode() {} // NOLINT(modernize-use-equals-default)
|
||||||
|
|
||||||
|
static char const* GetErrorText(std::string const& targetName)
|
||||||
|
{
|
||||||
|
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
|
||||||
|
if (targetName.empty()) {
|
||||||
|
return "$<TARGET_INTERMEDIATE_DIR:tgt> expression requires a non-empty "
|
||||||
|
"target name.";
|
||||||
|
}
|
||||||
|
if (!cmGeneratorExpression::IsValidTargetName(targetName)) {
|
||||||
|
return "Target name not supported.";
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Evaluate(
|
||||||
|
std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval,
|
||||||
|
GeneratorExpressionContent const* content,
|
||||||
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
||||||
|
{
|
||||||
|
cmGeneratorTarget const* target = nullptr;
|
||||||
|
std::string targetName;
|
||||||
|
|
||||||
|
if (parameters.size() == 1) {
|
||||||
|
targetName = parameters[0];
|
||||||
|
|
||||||
|
if (char const* e = GetErrorText(targetName)) {
|
||||||
|
reportError(eval, content->GetOriginalExpression(), e);
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
cmLocalGenerator const* lg = eval->CurrentTarget
|
||||||
|
? eval->CurrentTarget->GetLocalGenerator()
|
||||||
|
: eval->Context.LG;
|
||||||
|
target = lg->FindGeneratorTargetToUse(targetName);
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
std::ostringstream e;
|
||||||
|
e << "Target \"" << targetName << "\" not found.";
|
||||||
|
reportError(eval, content->GetOriginalExpression(), e.str());
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
eval->AllTargets.insert(target);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
reportError(
|
||||||
|
eval, content->GetOriginalExpression(),
|
||||||
|
"$<TARGET_INTERMEDIATE_DIR:...> expression requires one parameter");
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(target);
|
||||||
|
|
||||||
|
if (!HasKnownObjectFileLocation(eval, content, "TARGET_INTERMEDIATE_DIR",
|
||||||
|
target)) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmSystemTools::CollapseFullPath(
|
||||||
|
target->GetObjectDirectory(eval->Context.Config));
|
||||||
|
}
|
||||||
|
} targetIntermediateDirNode;
|
||||||
|
|
||||||
static const struct TargetNameNode : public cmGeneratorExpressionNode
|
static const struct TargetNameNode : public cmGeneratorExpressionNode
|
||||||
{
|
{
|
||||||
TargetNameNode() {} // NOLINT(modernize-use-equals-default)
|
TargetNameNode() {} // NOLINT(modernize-use-equals-default)
|
||||||
@@ -3282,19 +3372,8 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
|
|||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator();
|
cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator();
|
||||||
{
|
if (!HasKnownObjectFileLocation(eval, content, "TARGET_OBJECTS", gt)) {
|
||||||
std::string reason;
|
return std::string();
|
||||||
if (!eval->EvaluateForBuildsystem &&
|
|
||||||
!gt->Target->HasKnownObjectFileLocation(&reason)) {
|
|
||||||
std::ostringstream e;
|
|
||||||
e << "The evaluation of the TARGET_OBJECTS generator expression "
|
|
||||||
"is only suitable for consumption by CMake (limited"
|
|
||||||
<< reason
|
|
||||||
<< "). "
|
|
||||||
"It is not suitable for writing out elsewhere.";
|
|
||||||
reportError(eval, content->GetOriginalExpression(), e.str());
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmList objects;
|
cmList objects;
|
||||||
@@ -4809,6 +4888,7 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
|
|||||||
{ "SEMICOLON", &semicolonNode },
|
{ "SEMICOLON", &semicolonNode },
|
||||||
{ "QUOTE", "eNode },
|
{ "QUOTE", "eNode },
|
||||||
{ "TARGET_PROPERTY", &targetPropertyNode },
|
{ "TARGET_PROPERTY", &targetPropertyNode },
|
||||||
|
{ "TARGET_INTERMEDIATE_DIR", &targetIntermediateDirNode },
|
||||||
{ "TARGET_NAME", &targetNameNode },
|
{ "TARGET_NAME", &targetNameNode },
|
||||||
{ "TARGET_OBJECTS", &targetObjectsNode },
|
{ "TARGET_OBJECTS", &targetObjectsNode },
|
||||||
{ "TARGET_POLICY", &targetPolicyNode },
|
{ "TARGET_POLICY", &targetPolicyNode },
|
||||||
|
@@ -526,8 +526,13 @@ add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
|
|||||||
add_RunCMake_test(GenEx-PATH)
|
add_RunCMake_test(GenEx-PATH)
|
||||||
add_RunCMake_test(GenEx-PATH_EQUAL)
|
add_RunCMake_test(GenEx-PATH_EQUAL)
|
||||||
add_RunCMake_test(GenEx-LIST)
|
add_RunCMake_test(GenEx-LIST)
|
||||||
add_RunCMake_test(GeneratorExpression -DCMake_TEST_OBJC=${CMake_TEST_OBJC} -DCMake_TEST_Fortran=${CMake_TEST_Fortran}
|
add_RunCMake_test(GeneratorExpression
|
||||||
-DCMake_TEST_CUDA=${CMake_TEST_CUDA} -DCMake_TEST_HIP=${CMake_TEST_HIP})
|
-DCMake_TEST_OBJC=${CMake_TEST_OBJC}
|
||||||
|
-DCMake_TEST_Fortran=${CMake_TEST_Fortran}
|
||||||
|
-DCMake_TEST_CUDA=${CMake_TEST_CUDA}
|
||||||
|
-DCMake_TEST_HIP=${CMake_TEST_HIP}
|
||||||
|
-DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION}
|
||||||
|
)
|
||||||
add_RunCMake_test(GeneratorExpressionShortCircuit)
|
add_RunCMake_test(GeneratorExpressionShortCircuit)
|
||||||
add_RunCMake_test(GeneratorInstance)
|
add_RunCMake_test(GeneratorInstance)
|
||||||
add_RunCMake_test(GeneratorPlatform)
|
add_RunCMake_test(GeneratorPlatform)
|
||||||
|
@@ -56,7 +56,10 @@ function(run_cmake_build test)
|
|||||||
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
|
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
run_cmake(${test})
|
block()
|
||||||
|
unset(RunCMake-check-file)
|
||||||
|
run_cmake(${test})
|
||||||
|
endblock()
|
||||||
|
|
||||||
set(RunCMake_TEST_NO_CLEAN TRUE)
|
set(RunCMake_TEST_NO_CLEAN TRUE)
|
||||||
run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
|
run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
|
||||||
@@ -138,3 +141,19 @@ run_cmake(CMP0085-WARN)
|
|||||||
set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0085:STRING=NEW)
|
set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0085:STRING=NEW)
|
||||||
run_cmake(CMP0085-NEW)
|
run_cmake(CMP0085-NEW)
|
||||||
unset(RunCMake_TEST_OPTIONS)
|
unset(RunCMake_TEST_OPTIONS)
|
||||||
|
|
||||||
|
if(RunCMake_GENERATOR STREQUAL "Xcode" AND "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
|
||||||
|
run_cmake(TARGET_INTERMEDIATE_DIR-Xcode)
|
||||||
|
else()
|
||||||
|
block()
|
||||||
|
set(RunCMake-check-file TARGET_INTERMEDIATE_DIR-build-check.cmake)
|
||||||
|
foreach(strategy IN ITEMS SHORT FULL)
|
||||||
|
set(CMAKE_INTERMEDIATE_DIR_STRATEGY ${strategy})
|
||||||
|
set(RunCMake_TEST_OPTIONS -DCMAKE_INTERMEDIATE_DIR_STRATEGY=${strategy})
|
||||||
|
run_cmake_build(TARGET_INTERMEDIATE_DIR-${strategy})
|
||||||
|
endforeach()
|
||||||
|
endblock()
|
||||||
|
endif()
|
||||||
|
run_cmake(TARGET_INTERMEDIATE_DIR-bad-arg)
|
||||||
|
run_cmake(TARGET_INTERMEDIATE_DIR-bad-target)
|
||||||
|
run_cmake(TARGET_INTERMEDIATE_DIR-not-a-target)
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
include(TARGET_INTERMEDIATE_DIR.cmake)
|
@@ -0,0 +1 @@
|
|||||||
|
include(TARGET_INTERMEDIATE_DIR.cmake)
|
@@ -0,0 +1 @@
|
|||||||
|
1
|
@@ -0,0 +1,8 @@
|
|||||||
|
CMake Error at TARGET_INTERMEDIATE_DIR\.cmake:[0-9]+ \(file\):
|
||||||
|
Error evaluating generator expression:
|
||||||
|
|
||||||
|
\$\<TARGET_INTERMEDIATE_DIR:foo\>
|
||||||
|
|
||||||
|
The evaluation of the TARGET_INTERMEDIATE_DIR generator expression is only
|
||||||
|
suitable for consumption by CMake \(limited under Xcode with multiple
|
||||||
|
architectures\)\. It is not suitable for writing out elsewhere\.
|
@@ -0,0 +1 @@
|
|||||||
|
include(TARGET_INTERMEDIATE_DIR.cmake)
|
@@ -0,0 +1 @@
|
|||||||
|
1
|
@@ -0,0 +1,8 @@
|
|||||||
|
CMake Error at TARGET_INTERMEDIATE_DIR-bad-arg.cmake:1 \(file\):
|
||||||
|
Error evaluating generator expression:
|
||||||
|
|
||||||
|
\$<TARGET_INTERMEDIATE_DIR>
|
||||||
|
|
||||||
|
\$<TARGET_INTERMEDIATE_DIR> expression requires exactly one parameter.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:[0-9]+ \(include\)
|
@@ -0,0 +1,2 @@
|
|||||||
|
file(GENERATE OUTPUT TARGET_INTERMEDIATE_DIR-no-arg-generated.txt
|
||||||
|
CONTENT "$<TARGET_INTERMEDIATE_DIR>")
|
@@ -0,0 +1 @@
|
|||||||
|
1
|
@@ -0,0 +1,8 @@
|
|||||||
|
CMake Error at TARGET_INTERMEDIATE_DIR-bad-target.cmake:1 \(file\):
|
||||||
|
Error evaluating generator expression:
|
||||||
|
|
||||||
|
\$<TARGET_INTERMEDIATE_DIR:!>
|
||||||
|
|
||||||
|
Target name not supported.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:[0-9]+ \(include\)
|
@@ -0,0 +1,2 @@
|
|||||||
|
file(GENERATE OUTPUT TARGET_INTERMEDIATE_DIR-no-arg-generated.txt
|
||||||
|
CONTENT "$<TARGET_INTERMEDIATE_DIR:!>")
|
@@ -0,0 +1,46 @@
|
|||||||
|
file(READ "${RunCMake_TEST_BINARY_DIR}/TARGET_INTERMEDIATE_DIR-generated.txt" dir)
|
||||||
|
|
||||||
|
# Default Case
|
||||||
|
set(expected "foo\\.dir")
|
||||||
|
|
||||||
|
# Short Object Names
|
||||||
|
if (CMAKE_INTERMEDIATE_DIR_STRATEGY STREQUAL "SHORT")
|
||||||
|
if (RunCMake_GENERATOR MATCHES "Ninja|Make|Visual Studio")
|
||||||
|
set(expected "[\\\\/][._]o[\\\\/][0-9a-f]+")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
if (RunCMake_GENERATOR MATCHES "Xcode")
|
||||||
|
set(expected "foo.build")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Append Config subdirectory
|
||||||
|
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
string(APPEND expected "[\\\\/]Release")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Xcode has additional paths
|
||||||
|
if (NOT RunCMake_GENERATOR MATCHES "Xcode")
|
||||||
|
string(APPEND expected "$")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT dir MATCHES "${expected}")
|
||||||
|
set(RunCMake_TEST_FAILED "actual content:\n [[${dir}]]\nbut expected to match:\n [[${expected}]]")
|
||||||
|
elseif(NOT IS_DIRECTORY "${dir}")
|
||||||
|
set(RunCMake_TEST_FAILED "target intermediate directory does not exist: [[${dir}]]")
|
||||||
|
else()
|
||||||
|
file(GLOB object_files "${dir}/*${CMAKE_C_OUTPUT_EXTENSION}")
|
||||||
|
if (NOT object_files)
|
||||||
|
set(RunCMake_TEST_FAILED "no object files found in intermediate directory: [[${dir}]]")
|
||||||
|
endif()
|
||||||
|
if (CMAKE_INTERMEDIATE_DIR_STRATEGY STREQUAL "FULL")
|
||||||
|
set(object_name "${dir}/simple.c${CMAKE_C_OUTPUT_EXTENSION}")
|
||||||
|
if (RunCMake_GENERATOR MATCHES "Xcode|Visual Studio")
|
||||||
|
set(object_name "${dir}/simple${CMAKE_C_OUTPUT_EXTENSION}")
|
||||||
|
endif()
|
||||||
|
if (NOT EXISTS "${object_name}")
|
||||||
|
set(RunCMake_TEST_FAILED "simple.c object file not found in intermediate directory: [[${dir}]]")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
@@ -0,0 +1 @@
|
|||||||
|
1
|
@@ -0,0 +1,8 @@
|
|||||||
|
CMake Error at TARGET_INTERMEDIATE_DIR-not-a-target.cmake:1 \(file\):
|
||||||
|
Error evaluating generator expression:
|
||||||
|
|
||||||
|
\$<TARGET_INTERMEDIATE_DIR:bar>
|
||||||
|
|
||||||
|
Target \"bar\" not found.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
CMakeLists.txt:[0-9]+ \(include\)
|
@@ -0,0 +1,2 @@
|
|||||||
|
file(GENERATE OUTPUT TARGET_INTERMEDIATE_DIR-not-a-target-generated.txt
|
||||||
|
CONTENT "$<TARGET_INTERMEDIATE_DIR:bar>")
|
@@ -0,0 +1,4 @@
|
|||||||
|
enable_language(C)
|
||||||
|
add_executable(foo simple.c)
|
||||||
|
file(GENERATE OUTPUT TARGET_INTERMEDIATE_DIR-generated.txt
|
||||||
|
CONTENT "$<TARGET_INTERMEDIATE_DIR:foo>")
|
4
Tests/RunCMake/GeneratorExpression/simple.c
Normal file
4
Tests/RunCMake/GeneratorExpression/simple.c
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user