define_property(): Add INITIALIZE_FROM_VARIABLE argument

Fixes: #20698
This commit is contained in:
Kyle Edwards
2022-01-13 15:59:48 -05:00
parent 1ca83ae2bb
commit fce24e4f10
27 changed files with 198 additions and 16 deletions
+7 -1
View File
@@ -9,7 +9,8 @@ Define and document custom properties.
TEST | VARIABLE | CACHED_VARIABLE> TEST | VARIABLE | CACHED_VARIABLE>
PROPERTY <name> [INHERITED] PROPERTY <name> [INHERITED]
[BRIEF_DOCS <brief-doc> [docs...]] [BRIEF_DOCS <brief-doc> [docs...]]
[FULL_DOCS <full-doc> [docs...]]) [FULL_DOCS <full-doc> [docs...]]
[INITIALIZE_FROM_VARIABLE <variable>])
Defines one property in a scope for use with the :command:`set_property` and Defines one property in a scope for use with the :command:`set_property` and
:command:`get_property` commands. This is primarily useful to associate :command:`get_property` commands. This is primarily useful to associate
@@ -57,3 +58,8 @@ The ``BRIEF_DOCS`` and ``FULL_DOCS`` options are followed by strings to be
associated with the property as its brief and full documentation. associated with the property as its brief and full documentation.
Corresponding options to the :command:`get_property` command will retrieve Corresponding options to the :command:`get_property` command will retrieve
the documentation. the documentation.
The ``INITIALIZE_FROM_VARIABLE`` option is followed by the name of a variable
from which to initialize the property. The variable name must end with the
property name, must have a prefix before the property name, and must not begin
with ``CMAKE_`` or ``_CMAKE_``.
@@ -0,0 +1,5 @@
target-properties-from-variables
--------------------------------
* The :command:`define_property` command gained a new
``INITIALIZE_FROM_VARIABLE`` argument.
+43 -1
View File
@@ -2,6 +2,9 @@
file Copyright.txt or https://cmake.org/licensing for details. */ file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmDefinePropertyCommand.h" #include "cmDefinePropertyCommand.h"
#include <algorithm>
#include <iterator>
#include <cmext/string_view> #include <cmext/string_view>
#include "cmArgumentParser.h" #include "cmArgumentParser.h"
@@ -50,12 +53,14 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
std::string PropertyName; std::string PropertyName;
std::vector<std::string> BriefDocs; std::vector<std::string> BriefDocs;
std::vector<std::string> FullDocs; std::vector<std::string> FullDocs;
std::string initializeFromVariable;
cmArgumentParser<void> parser; cmArgumentParser<void> parser;
parser.Bind("PROPERTY"_s, PropertyName); parser.Bind("PROPERTY"_s, PropertyName);
parser.Bind("BRIEF_DOCS"_s, BriefDocs); parser.Bind("BRIEF_DOCS"_s, BriefDocs);
parser.Bind("FULL_DOCS"_s, FullDocs); parser.Bind("FULL_DOCS"_s, FullDocs);
parser.Bind("INHERITED"_s, inherited); parser.Bind("INHERITED"_s, inherited);
parser.Bind("INITIALIZE_FROM_VARIABLE"_s, initializeFromVariable);
std::vector<std::string> invalidArgs; std::vector<std::string> invalidArgs;
parser.Parse(cmMakeRange(args).advance(1), &invalidArgs); parser.Parse(cmMakeRange(args).advance(1), &invalidArgs);
@@ -71,10 +76,47 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
return false; return false;
} }
if (!initializeFromVariable.empty()) {
// Make sure property scope is TARGET.
if (scope != cmProperty::TARGET) {
status.SetError(
"Scope must be TARGET if INITIALIZE_FROM_VARIABLE is specified");
return false;
}
// Make sure the variable has the property name as a suffix.
if (!cmHasSuffix(initializeFromVariable, PropertyName)) {
status.SetError(cmStrCat("Variable name \"", initializeFromVariable,
"\" does not end with property name \"",
PropertyName, "\""));
return false;
}
if (initializeFromVariable == PropertyName) {
status.SetError(cmStrCat(
"Variable name must have a non-empty prefix before property name \"",
PropertyName, "\""));
return false;
}
}
// Make sure the variable is not reserved.
static constexpr const char* reservedPrefixes[] = {
"CMAKE_",
"_CMAKE_",
};
if (std::any_of(std::begin(reservedPrefixes), std::end(reservedPrefixes),
[&initializeFromVariable](const char* prefix) {
return cmHasPrefix(initializeFromVariable, prefix);
})) {
status.SetError(
cmStrCat("variable name \"", initializeFromVariable, "\" is reserved"));
return false;
}
// Actually define the property. // Actually define the property.
status.GetMakefile().GetState()->DefineProperty( status.GetMakefile().GetState()->DefineProperty(
PropertyName, scope, cmJoin(BriefDocs, ""), cmJoin(FullDocs, ""), PropertyName, scope, cmJoin(BriefDocs, ""), cmJoin(FullDocs, ""),
inherited); inherited, initializeFromVariable);
return true; return true;
} }
+10 -7
View File
@@ -6,31 +6,34 @@
cmPropertyDefinition::cmPropertyDefinition(std::string shortDescription, cmPropertyDefinition::cmPropertyDefinition(std::string shortDescription,
std::string fullDescription, std::string fullDescription,
bool chained) bool chained,
std::string initializeFromVariable)
: ShortDescription(std::move(shortDescription)) : ShortDescription(std::move(shortDescription))
, FullDescription(std::move(fullDescription)) , FullDescription(std::move(fullDescription))
, Chained(chained) , Chained(chained)
, InitializeFromVariable(std::move(initializeFromVariable))
{ {
} }
void cmPropertyDefinitionMap::DefineProperty( void cmPropertyDefinitionMap::DefineProperty(
const std::string& name, cmProperty::ScopeType scope, const std::string& name, cmProperty::ScopeType scope,
const std::string& ShortDescription, const std::string& FullDescription, const std::string& ShortDescription, const std::string& FullDescription,
bool chain) bool chain, const std::string& initializeFromVariable)
{ {
auto it = this->Map_.find(key_type(name, scope)); auto it = this->Map_.find(KeyType(name, scope));
if (it == this->Map_.end()) { if (it == this->Map_.end()) {
// try_emplace() since C++17 // try_emplace() since C++17
this->Map_.emplace( this->Map_.emplace(std::piecewise_construct,
std::piecewise_construct, std::forward_as_tuple(name, scope), std::forward_as_tuple(name, scope),
std::forward_as_tuple(ShortDescription, FullDescription, chain)); std::forward_as_tuple(ShortDescription, FullDescription,
chain, initializeFromVariable));
} }
} }
cmPropertyDefinition const* cmPropertyDefinitionMap::GetPropertyDefinition( cmPropertyDefinition const* cmPropertyDefinitionMap::GetPropertyDefinition(
const std::string& name, cmProperty::ScopeType scope) const const std::string& name, cmProperty::ScopeType scope) const
{ {
auto it = this->Map_.find(key_type(name, scope)); auto it = this->Map_.find(KeyType(name, scope));
if (it != this->Map_.end()) { if (it != this->Map_.end()) {
return &it->second; return &it->second;
} }
+18 -4
View File
@@ -22,7 +22,8 @@ class cmPropertyDefinition
public: public:
/// Constructor /// Constructor
cmPropertyDefinition(std::string shortDescription, cmPropertyDefinition(std::string shortDescription,
std::string fullDescription, bool chained); std::string fullDescription, bool chained,
std::string initializeFromVariable);
/// Is the property chained? /// Is the property chained?
bool IsChained() const { return this->Chained; } bool IsChained() const { return this->Chained; }
@@ -39,10 +40,17 @@ public:
return this->FullDescription; return this->FullDescription;
} }
/// Get the variable the property is initialized from
const std::string& GetInitializeFromVariable() const
{
return this->InitializeFromVariable;
}
private: private:
std::string ShortDescription; std::string ShortDescription;
std::string FullDescription; std::string FullDescription;
bool Chained; bool Chained;
std::string InitializeFromVariable;
}; };
/** \class cmPropertyDefinitionMap /** \class cmPropertyDefinitionMap
@@ -54,13 +62,19 @@ public:
// define the property // define the property
void DefineProperty(const std::string& name, cmProperty::ScopeType scope, void DefineProperty(const std::string& name, cmProperty::ScopeType scope,
const std::string& ShortDescription, const std::string& ShortDescription,
const std::string& FullDescription, bool chain); const std::string& FullDescription, bool chain,
const std::string& initializeFromVariable);
// get the property definition if present, otherwise nullptr // get the property definition if present, otherwise nullptr
cmPropertyDefinition const* GetPropertyDefinition( cmPropertyDefinition const* GetPropertyDefinition(
const std::string& name, cmProperty::ScopeType scope) const; const std::string& name, cmProperty::ScopeType scope) const;
using KeyType = std::pair<std::string, cmProperty::ScopeType>;
const std::map<KeyType, cmPropertyDefinition>& GetMap() const
{
return this->Map_;
}
private: private:
using key_type = std::pair<std::string, cmProperty::ScopeType>; std::map<KeyType, cmPropertyDefinition> Map_;
std::map<key_type, cmPropertyDefinition> Map_;
}; };
+4 -2
View File
@@ -327,10 +327,12 @@ cmStateSnapshot cmState::Reset()
void cmState::DefineProperty(const std::string& name, void cmState::DefineProperty(const std::string& name,
cmProperty::ScopeType scope, cmProperty::ScopeType scope,
const std::string& ShortDescription, const std::string& ShortDescription,
const std::string& FullDescription, bool chained) const std::string& FullDescription, bool chained,
const std::string& initializeFromVariable)
{ {
this->PropertyDefinitions.DefineProperty(name, scope, ShortDescription, this->PropertyDefinitions.DefineProperty(name, scope, ShortDescription,
FullDescription, chained); FullDescription, chained,
initializeFromVariable);
} }
cmPropertyDefinition const* cmState::GetPropertyDefinition( cmPropertyDefinition const* cmState::GetPropertyDefinition(
+7 -1
View File
@@ -133,12 +133,18 @@ public:
// Define a property // Define a property
void DefineProperty(const std::string& name, cmProperty::ScopeType scope, void DefineProperty(const std::string& name, cmProperty::ScopeType scope,
const std::string& ShortDescription, const std::string& ShortDescription,
const std::string& FullDescription, bool chain = false); const std::string& FullDescription, bool chain = false,
const std::string& initializeFromVariable = "");
// get property definition // get property definition
cmPropertyDefinition const* GetPropertyDefinition( cmPropertyDefinition const* GetPropertyDefinition(
const std::string& name, cmProperty::ScopeType scope) const; const std::string& name, cmProperty::ScopeType scope) const;
const cmPropertyDefinitionMap& GetPropertyDefinitions() const
{
return this->PropertyDefinitions;
}
bool IsPropertyChained(const std::string& name, bool IsPropertyChained(const std::string& name,
cmProperty::ScopeType scope) const; cmProperty::ScopeType scope) const;
+11
View File
@@ -28,6 +28,7 @@
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmMessageType.h" #include "cmMessageType.h"
#include "cmProperty.h" #include "cmProperty.h"
#include "cmPropertyDefinition.h"
#include "cmPropertyMap.h" #include "cmPropertyMap.h"
#include "cmRange.h" #include "cmRange.h"
#include "cmSourceFile.h" #include "cmSourceFile.h"
@@ -557,6 +558,16 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
} }
} }
} }
for (auto const& prop : mf->GetState()->GetPropertyDefinitions().GetMap()) {
if (prop.first.second == cmProperty::TARGET &&
!prop.second.GetInitializeFromVariable().empty()) {
if (auto value =
mf->GetDefinition(prop.second.GetInitializeFromVariable())) {
this->SetProperty(prop.first.first, value);
}
}
}
} }
cmTarget::cmTarget(cmTarget&&) noexcept = default; cmTarget::cmTarget(cmTarget&&) noexcept = default;
@@ -1,3 +1,9 @@
include(RunCMake) include(RunCMake)
run_cmake(define_property) run_cmake(define_property)
run_cmake(define_property-INITIALIZE_FROM_VARIABLE)
run_cmake(define_property-INITIALIZE_FROM_VARIABLE-invalid_1)
run_cmake(define_property-INITIALIZE_FROM_VARIABLE-invalid_2)
run_cmake(define_property-INITIALIZE_FROM_VARIABLE-non_target)
run_cmake(define_property-INITIALIZE_FROM_VARIABLE-wrong_suffix)
run_cmake(define_property-INITIALIZE_FROM_VARIABLE-no_prefix)
@@ -0,0 +1,4 @@
^CMake Error at define_property-INITIALIZE_FROM_VARIABLE-invalid_1\.cmake:[0-9]+ \(define_property\):
define_property variable name "CMAKE_PROP1" is reserved
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE CMAKE_PROP1
)
@@ -0,0 +1,4 @@
^CMake Error at define_property-INITIALIZE_FROM_VARIABLE-invalid_2\.cmake:[0-9]+ \(define_property\):
define_property variable name "_CMAKE_PROP1" is reserved
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE _CMAKE_PROP1
)
@@ -0,0 +1,5 @@
^CMake Error at define_property-INITIALIZE_FROM_VARIABLE-no_prefix\.cmake:[0-9]+ \(define_property\):
define_property Variable name must have a non-empty prefix before property
name "PROP1"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE PROP1
)
@@ -0,0 +1,5 @@
^CMake Error at define_property-INITIALIZE_FROM_VARIABLE-non_target\.cmake:[0-9]+ \(define_property\):
define_property Scope must be TARGET if INITIALIZE_FROM_VARIABLE is
specified
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,3 @@
define_property(GLOBAL PROPERTY PROP1
INITIALIZE_FROM_VARIABLE Test_PROP1
)
@@ -0,0 +1,11 @@
define_property(TARGET PROPERTY PROP2
INITIALIZE_FROM_VARIABLE Test_PROP2
)
define_property(TARGET PROPERTY PROP3
INITIALIZE_FROM_VARIABLE Test_PROP3
)
add_executable(sub_exe ../main.c)
assert_prop_eq(sub_exe PROP1 "Hello")
assert_prop_eq(sub_exe PROP2 "world")
assert_prop_eq(sub_exe PROP3 "!")
@@ -0,0 +1,5 @@
^CMake Error at define_property-INITIALIZE_FROM_VARIABLE-wrong_suffix\.cmake:[0-9]+ \(define_property\):
define_property Variable name "Test_PROP2" does not end with property name
"PROP1"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE Test_PROP2
)
@@ -0,0 +1,29 @@
enable_language(C)
function(assert_prop_eq tgt name value)
get_property(actual_value TARGET ${tgt} PROPERTY ${name})
if(NOT actual_value STREQUAL value)
message(SEND_ERROR "Expected value of ${name}:\n ${value}\nActual value:\n ${actual_value}")
endif()
endfunction()
function(assert_prop_undef tgt name)
get_property(actual_value TARGET ${tgt} PROPERTY ${name})
if(DEFINED actual_value)
message(SEND_ERROR "Expected ${name} to be undefined, actual value:\n ${actual_value}")
endif()
endfunction()
set(Test_PROP1 "Hello")
set(Test_PROP2 "world")
set(Test_PROP3 "!")
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE Test_PROP1
)
add_subdirectory(define_property-INITIALIZE_FROM_VARIABLE-subdirectory)
add_executable(top_exe main.c)
assert_prop_eq(top_exe PROP1 "Hello")
assert_prop_eq(top_exe PROP2 "world")
assert_prop_eq(top_exe PROP3 "!")
+4
View File
@@ -0,0 +1,4 @@
int main(void)
{
return 0;
}