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

View File

@@ -9,7 +9,8 @@ Define and document custom properties.
TEST | VARIABLE | CACHED_VARIABLE>
PROPERTY <name> [INHERITED]
[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
: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.
Corresponding options to the :command:`get_property` command will retrieve
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_``.

View File

@@ -0,0 +1,5 @@
target-properties-from-variables
--------------------------------
* The :command:`define_property` command gained a new
``INITIALIZE_FROM_VARIABLE`` argument.

View File

@@ -2,6 +2,9 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmDefinePropertyCommand.h"
#include <algorithm>
#include <iterator>
#include <cmext/string_view>
#include "cmArgumentParser.h"
@@ -50,12 +53,14 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
std::string PropertyName;
std::vector<std::string> BriefDocs;
std::vector<std::string> FullDocs;
std::string initializeFromVariable;
cmArgumentParser<void> parser;
parser.Bind("PROPERTY"_s, PropertyName);
parser.Bind("BRIEF_DOCS"_s, BriefDocs);
parser.Bind("FULL_DOCS"_s, FullDocs);
parser.Bind("INHERITED"_s, inherited);
parser.Bind("INITIALIZE_FROM_VARIABLE"_s, initializeFromVariable);
std::vector<std::string> invalidArgs;
parser.Parse(cmMakeRange(args).advance(1), &invalidArgs);
@@ -71,10 +76,47 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
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.
status.GetMakefile().GetState()->DefineProperty(
PropertyName, scope, cmJoin(BriefDocs, ""), cmJoin(FullDocs, ""),
inherited);
inherited, initializeFromVariable);
return true;
}

View File

@@ -6,31 +6,34 @@
cmPropertyDefinition::cmPropertyDefinition(std::string shortDescription,
std::string fullDescription,
bool chained)
bool chained,
std::string initializeFromVariable)
: ShortDescription(std::move(shortDescription))
, FullDescription(std::move(fullDescription))
, Chained(chained)
, InitializeFromVariable(std::move(initializeFromVariable))
{
}
void cmPropertyDefinitionMap::DefineProperty(
const std::string& name, cmProperty::ScopeType scope,
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()) {
// try_emplace() since C++17
this->Map_.emplace(
std::piecewise_construct, std::forward_as_tuple(name, scope),
std::forward_as_tuple(ShortDescription, FullDescription, chain));
this->Map_.emplace(std::piecewise_construct,
std::forward_as_tuple(name, scope),
std::forward_as_tuple(ShortDescription, FullDescription,
chain, initializeFromVariable));
}
}
cmPropertyDefinition const* cmPropertyDefinitionMap::GetPropertyDefinition(
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()) {
return &it->second;
}

View File

@@ -22,7 +22,8 @@ class cmPropertyDefinition
public:
/// Constructor
cmPropertyDefinition(std::string shortDescription,
std::string fullDescription, bool chained);
std::string fullDescription, bool chained,
std::string initializeFromVariable);
/// Is the property chained?
bool IsChained() const { return this->Chained; }
@@ -39,10 +40,17 @@ public:
return this->FullDescription;
}
/// Get the variable the property is initialized from
const std::string& GetInitializeFromVariable() const
{
return this->InitializeFromVariable;
}
private:
std::string ShortDescription;
std::string FullDescription;
bool Chained;
std::string InitializeFromVariable;
};
/** \class cmPropertyDefinitionMap
@@ -54,13 +62,19 @@ public:
// define the property
void DefineProperty(const std::string& name, cmProperty::ScopeType scope,
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
cmPropertyDefinition const* GetPropertyDefinition(
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:
using key_type = std::pair<std::string, cmProperty::ScopeType>;
std::map<key_type, cmPropertyDefinition> Map_;
std::map<KeyType, cmPropertyDefinition> Map_;
};

View File

@@ -327,10 +327,12 @@ cmStateSnapshot cmState::Reset()
void cmState::DefineProperty(const std::string& name,
cmProperty::ScopeType scope,
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,
FullDescription, chained);
FullDescription, chained,
initializeFromVariable);
}
cmPropertyDefinition const* cmState::GetPropertyDefinition(

View File

@@ -133,12 +133,18 @@ public:
// Define a property
void DefineProperty(const std::string& name, cmProperty::ScopeType scope,
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
cmPropertyDefinition const* GetPropertyDefinition(
const std::string& name, cmProperty::ScopeType scope) const;
const cmPropertyDefinitionMap& GetPropertyDefinitions() const
{
return this->PropertyDefinitions;
}
bool IsPropertyChained(const std::string& name,
cmProperty::ScopeType scope) const;

View File

@@ -28,6 +28,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmProperty.h"
#include "cmPropertyDefinition.h"
#include "cmPropertyMap.h"
#include "cmRange.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;

View File

@@ -1,3 +1,9 @@
include(RunCMake)
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)

View File

@@ -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\)$

View File

@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE CMAKE_PROP1
)

View File

@@ -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\)$

View File

@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE _CMAKE_PROP1
)

View File

@@ -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\)$

View File

@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE PROP1
)

View File

@@ -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\)$

View File

@@ -0,0 +1,3 @@
define_property(GLOBAL PROPERTY PROP1
INITIALIZE_FROM_VARIABLE Test_PROP1
)

View File

@@ -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 "!")

View File

@@ -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\)$

View File

@@ -0,0 +1,3 @@
define_property(TARGET PROPERTY PROP1
INITIALIZE_FROM_VARIABLE Test_PROP2
)

View File

@@ -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 "!")

View File

@@ -0,0 +1,4 @@
int main(void)
{
return 0;
}