mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
cmake_language: Add PRINT_PROPERTIES operation
Print CMake entities' properties in human-readable form for debugging. Issue: #27513
This commit is contained in:
@@ -20,6 +20,16 @@ Synopsis
|
||||
cmake_language(`PRINT_TARGETS`_ <filter>...)
|
||||
cmake_language(`PRINT_VARIABLES`_
|
||||
[{ ALL [<filter>...] | NAMED <vars>... }])
|
||||
cmake_language(PRINT_PROPERTIES
|
||||
`TARGETS <PRINT_PROPERTIES-TARGETS_>`__ <targets>...
|
||||
<options>...
|
||||
[{ ALL [<filter>] | NAMED <properties>... }])
|
||||
cmake_language(PRINT_PROPERTIES
|
||||
{ `SOURCES <PRINT_PROPERTIES-SOURCES_>`__ <sources>... |
|
||||
`DIRECTORIES <PRINT_PROPERTIES-DIRECTORIES_>`__ <dirs>... |
|
||||
`TESTS <PRINT_PROPERTIES-TESTS_>`__ <tests>... |
|
||||
`CACHE_ENTRIES <PRINT_PROPERTIES-CACHE_ENTRIES_>`__ <entries>... }
|
||||
NAMED <properties>...)
|
||||
|
||||
Introduction
|
||||
^^^^^^^^^^^^
|
||||
@@ -683,3 +693,169 @@ Gives::
|
||||
Variables in scope at '/path/to/CMakeLists.txt' matching name '^MY_' (case sensitive):
|
||||
MY_FLAG = "on"
|
||||
CACHE{MY_PATH}:FILEPATH = "/some/path"
|
||||
|
||||
Printing Properties
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 4.5
|
||||
|
||||
.. signature::
|
||||
cmake_language(PRINT_PROPERTIES
|
||||
TARGETS <targets>... <options>...
|
||||
[{ ALL [<filter>] | NAMED <properties>... }])
|
||||
:target: PRINT_PROPERTIES-TARGETS
|
||||
|
||||
Prints the values of properties for the specified targets.
|
||||
The set of properties to print may be specified by one of:
|
||||
|
||||
``ALL [<filter>]`` (default)
|
||||
Enumerates every property set on each named target, printing one entry
|
||||
per property.
|
||||
|
||||
The optional ``<filter>`` may be one of:
|
||||
|
||||
``PROPERTY_NAME_REGEX <name-regex>``
|
||||
Print properties whose name matches the given regular expression.
|
||||
``PROPERTY_VALUE_REGEX <value-regex>``
|
||||
Print properties whose value matches the given regular expression.
|
||||
|
||||
If no property survives the regex filters for a given target, a
|
||||
``CMake Warning`` is emitted in place of that target's block.
|
||||
|
||||
``NAMED <properties>...``
|
||||
Prints exactly the named properties on each entity, in the order given.
|
||||
A property that is not set prints as ``<NOTFOUND>``.
|
||||
|
||||
The ``<options>...`` are:
|
||||
|
||||
``DEFERRED``
|
||||
Defers the print to generation time rather than running during
|
||||
configure. Useful when the properties of interest are populated by
|
||||
later configure-time commands. On its own it does not change which
|
||||
targets are visited - only the named targets are printed.
|
||||
|
||||
``FOLLOW_DEPENDENCIES``
|
||||
Implies ``DEFERRED``. When set, the walk visits every target reachable
|
||||
from each named target through any linkage - ``PUBLIC``, ``INTERFACE``,
|
||||
``PRIVATE``, and :genex:`$<LINK_ONLY:...>` - and prints the requested
|
||||
properties for each.
|
||||
|
||||
.. note::
|
||||
``FOLLOW_DEPENDENCIES`` does not turn on generator-expression
|
||||
evaluation for the property *values* this command prints.
|
||||
The only place generator expression evaluation happens is internal:
|
||||
on each visited target's link list (the effective ``LINK_LIBRARIES`` /
|
||||
``INTERFACE_LINK_LIBRARIES``), evaluated solely to identify which
|
||||
dependencies are reachable. Every printed property value,
|
||||
including the link-list properties themselves, is emitted exactly
|
||||
as stored, with generator expressions intact and unsubstituted.
|
||||
This matches the behavior of :command:`get_target_property`.
|
||||
|
||||
.. signature::
|
||||
cmake_language(PRINT_PROPERTIES SOURCES <sources>... NAMED <properties>...)
|
||||
cmake_language(PRINT_PROPERTIES DIRECTORIES <dirs>... NAMED <properties>...)
|
||||
cmake_language(PRINT_PROPERTIES TESTS <tests>... NAMED <properties>...)
|
||||
cmake_language(PRINT_PROPERTIES CACHE_ENTRIES <entries>... NAMED <properties>...)
|
||||
:target:
|
||||
PRINT_PROPERTIES-SOURCES
|
||||
PRINT_PROPERTIES-DIRECTORIES
|
||||
PRINT_PROPERTIES-TESTS
|
||||
PRINT_PROPERTIES-CACHE_ENTRIES
|
||||
|
||||
Prints the values of properties for the specified source files, directories,
|
||||
tests, or cache entries. Exactly one scope keyword must be specified.
|
||||
The set of properties to print must be specified by:
|
||||
|
||||
``NAMED <properties>...``
|
||||
Prints exactly the named properties on each entity, in the order given.
|
||||
A property that is not set prints as ``<NOTFOUND>``.
|
||||
|
||||
Printing Properties Examples
|
||||
""""""""""""""""""""""""""""
|
||||
|
||||
Printing the ``LOCATION`` and ``INTERFACE_INCLUDE_DIRECTORIES`` properties for
|
||||
both targets ``foo`` and ``bar``:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS foo bar
|
||||
NAMED LOCATION INTERFACE_INCLUDE_DIRECTORIES
|
||||
)
|
||||
|
||||
Gives::
|
||||
|
||||
-- Printing properties...
|
||||
Properties for TARGET foo:
|
||||
foo.LOCATION = "/usr/lib/libfoo.so"
|
||||
foo.INTERFACE_INCLUDE_DIRECTORIES = "/usr/include;/usr/include/foo"
|
||||
Properties for TARGET bar:
|
||||
bar.LOCATION = "/usr/lib/libbar.so"
|
||||
bar.INTERFACE_INCLUDE_DIRECTORIES = "/usr/include;/usr/include/bar"
|
||||
|
||||
Printing all properties on a target, filtered to a couple of names:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(my_lib src.cpp)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS my_lib
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX "AUTOMOC"
|
||||
PROPERTY_VALUE_REGEX "(ON|OFF)"
|
||||
)
|
||||
|
||||
Gives::
|
||||
|
||||
-- Printing properties...
|
||||
All properties for TARGET my_lib matching name 'AUTOMOC' and value '(ON|OFF)':
|
||||
my_lib.AUTOMOC_COMPILER_PREDEFINES = "ON"
|
||||
my_lib.AUTOMOC_PATH_PREFIX = "OFF"
|
||||
|
||||
Deferring the print of one target's properties to generation time, without
|
||||
visiting any dependencies:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_executable(myexe main.c)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS myexe
|
||||
DEFERRED
|
||||
NAMED SOURCES
|
||||
)
|
||||
|
||||
Gives::
|
||||
|
||||
-- Printing properties...
|
||||
Properties for TARGET myexe:
|
||||
myexe.SOURCES = "main.c"
|
||||
|
||||
Printing a property on a target and every reachable dependency:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(leaflib STATIC src.c)
|
||||
set_target_properties(leaflib PROPERTIES MY_PROP "leaf")
|
||||
|
||||
add_library(mylib STATIC src.c)
|
||||
target_link_libraries(mylib PRIVATE leaflib)
|
||||
set_target_properties(mylib PROPERTIES MY_PROP "mylib")
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
FOLLOW_DEPENDENCIES
|
||||
NAMED MY_PROP
|
||||
)
|
||||
|
||||
Gives::
|
||||
|
||||
-- Printing properties...
|
||||
Properties for TARGET mylib (and all reachable):
|
||||
mylib.MY_PROP = "mylib"
|
||||
leaflib.MY_PROP = "leaf"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
print_all_properties
|
||||
--------------------
|
||||
|
||||
* The :command:`cmake_language(PRINT_PROPERTIES)` command was added
|
||||
to print CMake entities' properties in human-readable form for
|
||||
debugging.
|
||||
@@ -5,8 +5,10 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -23,10 +25,15 @@
|
||||
#include "cmDependencyProvider.h"
|
||||
#include "cmExecutionStatus.h"
|
||||
#include "cmExperimental.h"
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmGetPropertyCommand.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmLinkItem.h"
|
||||
#include "cmListFileCache.h"
|
||||
#include "cmLocalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmMessageType.h" // IWYU pragma: keep
|
||||
#include "cmPropertyMap.h"
|
||||
#include "cmRange.h"
|
||||
#include "cmState.h"
|
||||
#include "cmStateSnapshot.h"
|
||||
@@ -34,6 +41,8 @@
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmTarget.h"
|
||||
#include "cmTargetPropertyComputer.h"
|
||||
#include "cmTargetPropertyHelper.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmake.h"
|
||||
|
||||
@@ -785,8 +794,649 @@ bool cmCMakeLanguageCommandPRINT_VARIABLES(
|
||||
parsedArgs.CmakePrintVariables);
|
||||
return true;
|
||||
}
|
||||
// Walks every target reachable from `root` through any linkage - PUBLIC,
|
||||
// INTERFACE, PRIVATE, and `$<LINK_ONLY:>`-wrapped deps. At each level we
|
||||
// union the link implementation and link interface, each queried with both
|
||||
// UseTo::Compile and UseTo::Link, so deps that only appear under LINK_ONLY
|
||||
// (which evaluates to empty under UseTo::Compile) still come through.
|
||||
std::vector<cmGeneratorTarget const*> CollectDependentTargets(
|
||||
cmGeneratorTarget const* root, std::string const& config)
|
||||
{
|
||||
std::vector<cmGeneratorTarget const*> deps;
|
||||
std::set<cmGeneratorTarget const*> visited;
|
||||
visited.insert(root);
|
||||
std::vector<cmGeneratorTarget const*> queue;
|
||||
queue.push_back(root);
|
||||
while (!queue.empty()) {
|
||||
cmGeneratorTarget const* cur = queue.back();
|
||||
queue.pop_back();
|
||||
auto visit = [&](std::vector<cmLinkItem> const& libs) {
|
||||
for (cmLinkItem const& item : libs) {
|
||||
cmGeneratorTarget const* dep = item.Target;
|
||||
if (!dep || !visited.insert(dep).second) {
|
||||
continue;
|
||||
}
|
||||
deps.push_back(dep);
|
||||
queue.push_back(dep);
|
||||
}
|
||||
};
|
||||
for (auto useTo : { cmGeneratorTarget::UseTo::Compile,
|
||||
cmGeneratorTarget::UseTo::Link }) {
|
||||
if (cmLinkImplementationLibraries const* impl =
|
||||
cur->GetLinkImplementationLibraries(config, useTo)) {
|
||||
visit(impl->Libraries);
|
||||
}
|
||||
if (cmLinkInterfaceLibraries const* iface =
|
||||
cur->GetLinkInterfaceLibraries(config, root, useTo)) {
|
||||
visit(iface->Libraries);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
// Append one property line to `out`:
|
||||
// " <entityName>.<propertyName> = \"<value>\""
|
||||
// A null `value` writes "<NOTFOUND>" instead of "= \"...\"".
|
||||
void WritePropertyLine(std::string& out, std::string const& entityName,
|
||||
std::string const& propertyName, cmValue value)
|
||||
{
|
||||
out += cmStrCat(" ", entityName, ".", propertyName);
|
||||
if (value) {
|
||||
out += cmStrCat(" = \"", *value, "\"");
|
||||
} else {
|
||||
out += " = <NOTFOUND>";
|
||||
}
|
||||
out += "\n";
|
||||
}
|
||||
|
||||
enum class BlockKind
|
||||
{
|
||||
Explicit,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class HeaderSuffix
|
||||
{
|
||||
None,
|
||||
Reachable,
|
||||
};
|
||||
|
||||
void EmitBlockHeader(
|
||||
std::string& out, std::string const& entityName,
|
||||
std::string const& entityType, BlockKind kind, HeaderSuffix suffix,
|
||||
cm::optional<std::string> const& nameRegexStr = cm::nullopt,
|
||||
cm::optional<std::string> const& valueRegexStr = cm::nullopt)
|
||||
{
|
||||
out +=
|
||||
cmStrCat(" ", (kind == BlockKind::All ? "All properties" : "Properties"),
|
||||
" for ", entityType, " ", entityName);
|
||||
if (suffix == HeaderSuffix::Reachable) {
|
||||
out += " (and all reachable)";
|
||||
}
|
||||
if (kind == BlockKind::All && (nameRegexStr || valueRegexStr)) {
|
||||
out += " matching";
|
||||
if (nameRegexStr) {
|
||||
out += cmStrCat(" name '", *nameRegexStr, "'");
|
||||
}
|
||||
if (nameRegexStr && valueRegexStr) {
|
||||
out += " and";
|
||||
}
|
||||
if (valueRegexStr) {
|
||||
out += cmStrCat(" value '", *valueRegexStr, "'");
|
||||
}
|
||||
}
|
||||
out += ":\n";
|
||||
}
|
||||
|
||||
// Build the "no properties matched" warning text used when the regex filter
|
||||
// wipes everything out for a target's ALL block. Returned without any
|
||||
// "Warning:" prefix - callers feed it to IssueMessage, which adds the
|
||||
// standard "CMake Warning at <file>:<line>" framing.
|
||||
std::string EmptyMatchWarningMessage(
|
||||
std::string const& entityName, std::string const& entityType,
|
||||
cm::optional<std::string> const& nameRegexStr,
|
||||
cm::optional<std::string> const& valueRegexStr)
|
||||
{
|
||||
std::string msg =
|
||||
cmStrCat("No properties for ", entityType, " ", entityName, " matching");
|
||||
if (nameRegexStr) {
|
||||
msg += cmStrCat(" name '", *nameRegexStr, "'");
|
||||
}
|
||||
if (nameRegexStr && valueRegexStr) {
|
||||
msg += " and";
|
||||
}
|
||||
if (valueRegexStr) {
|
||||
msg += cmStrCat(" value '", *valueRegexStr, "'");
|
||||
}
|
||||
msg += " in cmake_language(PRINT_PROPERTIES ...).";
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Entity kinds supported by cmake_language(PRINT_PROPERTIES).
|
||||
enum class EntityKind
|
||||
{
|
||||
Target,
|
||||
Source,
|
||||
Test,
|
||||
Directory,
|
||||
Cache
|
||||
};
|
||||
|
||||
// Display name used in PRINT_PROPERTIES output ("TARGET", "SOURCE", etc.).
|
||||
char const* EntityTypeName(EntityKind kind)
|
||||
{
|
||||
switch (kind) {
|
||||
case EntityKind::Target:
|
||||
return "TARGET";
|
||||
case EntityKind::Source:
|
||||
return "SOURCE";
|
||||
case EntityKind::Test:
|
||||
return "TEST";
|
||||
case EntityKind::Directory:
|
||||
return "DIRECTORY";
|
||||
case EntityKind::Cache:
|
||||
return "CACHE";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Dispatch a property lookup to the appropriate per-entity helper.
|
||||
bool GetPropertyHelper(cmExecutionStatus& status, EntityKind kind,
|
||||
std::string const& name,
|
||||
std::string const& propertyName, cmValue& out)
|
||||
{
|
||||
switch (kind) {
|
||||
case EntityKind::Target:
|
||||
return GetPropertyCommand::LookupTargetProperty(status, name,
|
||||
propertyName, out);
|
||||
case EntityKind::Source:
|
||||
return GetPropertyCommand::LookupSourceProperty(status, name,
|
||||
propertyName, out);
|
||||
case EntityKind::Test:
|
||||
return GetPropertyCommand::LookupTestProperty(status, name, propertyName,
|
||||
out);
|
||||
case EntityKind::Directory:
|
||||
return GetPropertyCommand::LookupDirectoryProperty(status, name,
|
||||
propertyName, out);
|
||||
case EntityKind::Cache:
|
||||
return GetPropertyCommand::LookupCacheProperty(status, name,
|
||||
propertyName, out);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Emit one ALL-mode block: prints all properties matching the specified
|
||||
// regexes for a head target (`targets[0]`) and optionally any reachable
|
||||
// dependencies (`targets[1]` and on).
|
||||
bool EmitAllPropertiesBlock(std::string& out, std::string const& headerName,
|
||||
std::vector<cmTarget*> const& targets,
|
||||
HeaderSuffix suffix,
|
||||
cm::optional<cmsys::RegularExpression>& nameRegex,
|
||||
cm::optional<cmsys::RegularExpression>& valueRegex,
|
||||
cm::optional<std::string> const& nameRegexStr,
|
||||
cm::optional<std::string> const& valueRegexStr)
|
||||
{
|
||||
std::string groupBuf;
|
||||
bool groupEmitted = false;
|
||||
for (cmTarget* t : targets) {
|
||||
std::string const& rowName =
|
||||
(t == targets.front()) ? headerName : t->GetName();
|
||||
for (auto const& kv : t->GetExtendedProperties().GetList()) {
|
||||
if (nameRegex && !nameRegex->find(kv.first)) {
|
||||
continue;
|
||||
}
|
||||
if (valueRegex && !valueRegex->find(kv.second)) {
|
||||
continue;
|
||||
}
|
||||
WritePropertyLine(groupBuf, rowName, kv.first, cmValue(kv.second));
|
||||
groupEmitted = true;
|
||||
}
|
||||
}
|
||||
if (!groupEmitted) {
|
||||
return false;
|
||||
}
|
||||
EmitBlockHeader(out, headerName, "TARGET", BlockKind::All, suffix,
|
||||
nameRegexStr, valueRegexStr);
|
||||
out += groupBuf;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Outcome of emitting a NAMED-mode block.
|
||||
enum class NamedBlockResult
|
||||
{
|
||||
Failed, // a property lookup hard-failed
|
||||
Emitted, // at least one property line was written
|
||||
Empty, // every requested property was skipped; header suppressed
|
||||
};
|
||||
|
||||
// Emit one block of user-specified properties for a head target (`targets[0]`)
|
||||
// and optionally any reachable dependencies (`targets[1]` and on).
|
||||
NamedBlockResult EmitNamedPropertiesBlock(
|
||||
std::string& out, std::string const& headerName,
|
||||
std::vector<cmTarget*> const& targets,
|
||||
std::vector<std::string> const& namedProperties, HeaderSuffix suffix,
|
||||
cmMakefile& mf)
|
||||
{
|
||||
bool headerWritten = false;
|
||||
for (cmTarget* t : targets) {
|
||||
std::string const& rowName =
|
||||
(t == targets.front()) ? headerName : t->GetName();
|
||||
for (std::string const& propName : namedProperties) {
|
||||
// A computed location property may not be read from a non-imported
|
||||
// target; skip it with a warning rather than triggering the getter's
|
||||
// fatal error.
|
||||
if (!t->IsImported() &&
|
||||
cmTargetPropertyComputer::IsComputedLocationProperty(t->GetType(),
|
||||
propName)) {
|
||||
mf.IssueMessage(
|
||||
MessageType::WARNING,
|
||||
cmStrCat("The ", propName,
|
||||
" property may not be read from non-imported target \"",
|
||||
rowName, "\"; skipping."));
|
||||
continue;
|
||||
}
|
||||
cmValue v;
|
||||
if (cmGetTargetProperty(rowName, t, propName, mf, v) !=
|
||||
cmGetTargetPropertyResult::Success) {
|
||||
return NamedBlockResult::Failed;
|
||||
}
|
||||
if (!headerWritten) {
|
||||
EmitBlockHeader(out, headerName, "TARGET", BlockKind::Explicit,
|
||||
suffix);
|
||||
headerWritten = true;
|
||||
}
|
||||
WritePropertyLine(out, rowName, propName, v);
|
||||
}
|
||||
}
|
||||
return headerWritten ? NamedBlockResult::Emitted : NamedBlockResult::Empty;
|
||||
}
|
||||
|
||||
bool PrintPropertiesConfigureTime(
|
||||
cmExecutionStatus& status, std::vector<std::string> const& namedProperties,
|
||||
bool all, std::vector<std::string> const& entityNames, EntityKind kind,
|
||||
cm::optional<std::string> const& propertyNameRegexStr,
|
||||
cm::optional<std::string> const& propertyValueRegexStr,
|
||||
cm::optional<cmsys::RegularExpression>& propertyNameRegex,
|
||||
cm::optional<cmsys::RegularExpression>& propertyValueRegex,
|
||||
std::string const& messagePrefix)
|
||||
{
|
||||
cmMakefile& makefile = status.GetMakefile();
|
||||
bool const hasRegex = propertyNameRegexStr || propertyValueRegexStr;
|
||||
std::string out = messagePrefix;
|
||||
bool anyEmitted = false;
|
||||
for (auto const& entityName : entityNames) {
|
||||
if (kind == EntityKind::Target) {
|
||||
cmTarget* target = makefile.FindTargetToUse(entityName);
|
||||
if (!target) {
|
||||
out += cmStrCat("\n No such TARGET \"", entityName, "\" !\n\n");
|
||||
anyEmitted = true;
|
||||
continue;
|
||||
}
|
||||
if (all) {
|
||||
bool const emitted = EmitAllPropertiesBlock(
|
||||
out, entityName, { target }, HeaderSuffix::None, propertyNameRegex,
|
||||
propertyValueRegex, propertyNameRegexStr, propertyValueRegexStr);
|
||||
if (emitted) {
|
||||
anyEmitted = true;
|
||||
} else if (hasRegex) {
|
||||
makefile.IssueMessage(MessageType::WARNING,
|
||||
EmptyMatchWarningMessage(
|
||||
entityName, "TARGET", propertyNameRegexStr,
|
||||
propertyValueRegexStr));
|
||||
}
|
||||
} else {
|
||||
NamedBlockResult const result = EmitNamedPropertiesBlock(
|
||||
out, entityName, { target }, namedProperties, HeaderSuffix::None,
|
||||
makefile);
|
||||
if (result == NamedBlockResult::Failed) {
|
||||
status.SetError(cmStrCat(
|
||||
"failed to retrieve properties for TARGET \"", entityName, "\""));
|
||||
return false;
|
||||
}
|
||||
if (result == NamedBlockResult::Emitted) {
|
||||
anyEmitted = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
EmitBlockHeader(out, entityName, EntityTypeName(kind),
|
||||
BlockKind::Explicit, HeaderSuffix::None);
|
||||
for (auto const& propertyName : namedProperties) {
|
||||
cmValue v;
|
||||
if (!GetPropertyHelper(status, kind, entityName, propertyName, v)) {
|
||||
return false;
|
||||
}
|
||||
WritePropertyLine(out, entityName, propertyName, v);
|
||||
}
|
||||
anyEmitted = true;
|
||||
}
|
||||
}
|
||||
if (anyEmitted) {
|
||||
makefile.DisplayStatus(out, -1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrintTargetPropertiesDeferred(
|
||||
std::vector<std::string> targetNames,
|
||||
std::vector<std::string> namedProperties, bool all,
|
||||
cm::optional<std::string> propertyNameRegexStr,
|
||||
cm::optional<std::string> propertyValueRegexStr,
|
||||
cm::optional<cmsys::RegularExpression> propertyNameRegex,
|
||||
cm::optional<cmsys::RegularExpression> propertyValueRegex,
|
||||
bool followDependencies, std::string messagePrefix,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
cmListFileBacktrace const bt = status.GetMakefile().GetBacktrace();
|
||||
status.GetMakefile().AddGeneratorAction(
|
||||
[targetNames, namedProperties, all, propertyNameRegexStr,
|
||||
propertyValueRegexStr, propertyNameRegex, propertyValueRegex,
|
||||
followDependencies, messagePrefix, bt](
|
||||
cmLocalGenerator& lg, cmListFileBacktrace const& /*lambdaBt*/) mutable {
|
||||
std::vector<std::string> const configs =
|
||||
lg.GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
|
||||
std::string const config =
|
||||
configs.empty() ? std::string() : configs.front();
|
||||
bool const hasRegex = propertyNameRegexStr || propertyValueRegexStr;
|
||||
cmake* cmakeInst = lg.GetMakefile()->GetCMakeInstance();
|
||||
|
||||
std::string out = messagePrefix;
|
||||
bool anyEmitted = false;
|
||||
for (std::string const& name : targetNames) {
|
||||
cmGeneratorTarget* root = lg.FindGeneratorTargetToUse(name);
|
||||
if (!root) {
|
||||
out += cmStrCat("\n No such TARGET \"", name, "\" !\n\n");
|
||||
anyEmitted = true;
|
||||
continue;
|
||||
}
|
||||
std::vector<cmTarget*> targets;
|
||||
targets.push_back(root->Target);
|
||||
HeaderSuffix const suffix =
|
||||
followDependencies ? HeaderSuffix::Reachable : HeaderSuffix::None;
|
||||
if (followDependencies) {
|
||||
for (cmGeneratorTarget const* dep :
|
||||
CollectDependentTargets(root, config)) {
|
||||
targets.push_back(dep->Target);
|
||||
}
|
||||
}
|
||||
if (all) {
|
||||
bool const emitted = EmitAllPropertiesBlock(
|
||||
out, name, targets, suffix, propertyNameRegex, propertyValueRegex,
|
||||
propertyNameRegexStr, propertyValueRegexStr);
|
||||
if (emitted) {
|
||||
anyEmitted = true;
|
||||
} else if (hasRegex) {
|
||||
cmakeInst->IssueMessage(
|
||||
MessageType::WARNING,
|
||||
EmptyMatchWarningMessage(name, "TARGET", propertyNameRegexStr,
|
||||
propertyValueRegexStr),
|
||||
bt);
|
||||
}
|
||||
} else {
|
||||
NamedBlockResult const result = EmitNamedPropertiesBlock(
|
||||
out, name, targets, namedProperties, suffix, *lg.GetMakefile());
|
||||
if (result == NamedBlockResult::Failed) {
|
||||
cmakeInst->IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
cmStrCat("failed to retrieve properties for TARGET \"", name,
|
||||
"\""),
|
||||
bt);
|
||||
return;
|
||||
}
|
||||
if (result == NamedBlockResult::Emitted) {
|
||||
anyEmitted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (anyEmitted) {
|
||||
lg.GetMakefile()->DisplayStatus(out, -1);
|
||||
}
|
||||
},
|
||||
cmMakefile::GeneratorActionWhen::AfterGeneratorTargets);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmCMakeLanguageCommandPRINT_PROPERTIES(
|
||||
std::vector<std::string> const& args, cmExecutionStatus& status)
|
||||
{
|
||||
struct PrintPropertiesArg : public ArgumentParser::ParseResult
|
||||
{
|
||||
bool All = false;
|
||||
ArgumentParser::NonEmpty<std::vector<std::string>> Named;
|
||||
cm::optional<std::string> PropertyNameRegex;
|
||||
cm::optional<std::string> PropertyValueRegex;
|
||||
// Internal: set by the deprecated cmake_print_properties() module wrapper
|
||||
// to select the historical leading-blank-line output instead of the
|
||||
// banner. Not part of the public interface.
|
||||
bool CmakePrintProperties = false;
|
||||
};
|
||||
|
||||
auto ArgsParser =
|
||||
cmArgumentParser<PrintPropertiesArg>()
|
||||
.Bind("ALL"_s, &PrintPropertiesArg::All)
|
||||
.Bind("NAMED"_s, &PrintPropertiesArg::Named)
|
||||
.Bind("PROPERTY_NAME_REGEX"_s, &PrintPropertiesArg::PropertyNameRegex)
|
||||
.Bind("PROPERTY_VALUE_REGEX"_s, &PrintPropertiesArg::PropertyValueRegex)
|
||||
.Bind("__CMAKE_PRINT_PROPERTIES"_s,
|
||||
&PrintPropertiesArg::CmakePrintProperties);
|
||||
|
||||
std::vector<std::string> unparsed;
|
||||
auto parsedArgs = ArgsParser.Parse(args, &unparsed);
|
||||
|
||||
if (unparsed.empty()) {
|
||||
return FatalError(status,
|
||||
cmStrCat("mode keyword missing in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call, ",
|
||||
"there must be exactly one of TARGETS SOURCES "
|
||||
"TESTS DIRECTORIES CACHE_ENTRIES"));
|
||||
}
|
||||
|
||||
if (parsedArgs.MaybeReportError(status.GetMakefile())) {
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second parse args to get the mode
|
||||
struct PrintPropertiesModesArgs : public ArgumentParser::ParseResult
|
||||
{
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> Targets;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> Sources;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> Tests;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
|
||||
ArgumentParser::MaybeEmpty<std::vector<std::string>> CacheEntries;
|
||||
bool Deferred = false;
|
||||
bool FollowDependencies = false;
|
||||
};
|
||||
auto const ArgsParserMode =
|
||||
cmArgumentParser<PrintPropertiesModesArgs>()
|
||||
.Bind("TARGETS"_s, &PrintPropertiesModesArgs::Targets)
|
||||
.Bind("SOURCES"_s, &PrintPropertiesModesArgs::Sources)
|
||||
.Bind("TESTS"_s, &PrintPropertiesModesArgs::Tests)
|
||||
.Bind("DIRECTORIES"_s, &PrintPropertiesModesArgs::Directories)
|
||||
.Bind("CACHE_ENTRIES"_s, &PrintPropertiesModesArgs::CacheEntries)
|
||||
.Bind("DEFERRED"_s, &PrintPropertiesModesArgs::Deferred)
|
||||
.Bind("FOLLOW_DEPENDENCIES"_s,
|
||||
&PrintPropertiesModesArgs::FollowDependencies);
|
||||
|
||||
std::vector<std::string> modeArgs = unparsed;
|
||||
unparsed.clear();
|
||||
auto parsedArgsMode = ArgsParserMode.Parse(modeArgs, &unparsed);
|
||||
|
||||
if (!unparsed.empty()) {
|
||||
return FatalError(
|
||||
status, cmStrCat("Unknown keywords: \"", cmJoin(unparsed, " "), "\""));
|
||||
}
|
||||
|
||||
if (parsedArgsMode.MaybeReportError(status.GetMakefile())) {
|
||||
cmSystemTools::SetFatalErrorOccurred();
|
||||
return true;
|
||||
}
|
||||
std::vector<std::string> modes;
|
||||
std::vector<std::string> items;
|
||||
EntityKind kind = EntityKind::Target;
|
||||
if (!parsedArgsMode.Targets.empty()) {
|
||||
modes.push_back("TARGETS");
|
||||
items = parsedArgsMode.Targets;
|
||||
kind = EntityKind::Target;
|
||||
}
|
||||
if (!parsedArgsMode.Sources.empty()) {
|
||||
modes.push_back("SOURCES");
|
||||
items = parsedArgsMode.Sources;
|
||||
kind = EntityKind::Source;
|
||||
}
|
||||
if (!parsedArgsMode.Tests.empty()) {
|
||||
modes.push_back("TESTS");
|
||||
items = parsedArgsMode.Tests;
|
||||
kind = EntityKind::Test;
|
||||
}
|
||||
if (!parsedArgsMode.Directories.empty()) {
|
||||
modes.push_back("DIRECTORIES");
|
||||
items = parsedArgsMode.Directories;
|
||||
kind = EntityKind::Directory;
|
||||
}
|
||||
if (!parsedArgsMode.CacheEntries.empty()) {
|
||||
modes.push_back("CACHE_ENTRIES");
|
||||
items = parsedArgsMode.CacheEntries;
|
||||
kind = EntityKind::Cache;
|
||||
}
|
||||
|
||||
if (modes.empty()) {
|
||||
return FatalError(status,
|
||||
cmStrCat("mode keyword missing in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call, ",
|
||||
"there must be exactly one of TARGETS SOURCES "
|
||||
"TESTS DIRECTORIES CACHE_ENTRIES"));
|
||||
}
|
||||
if (modes.size() > 1) {
|
||||
return FatalError(status,
|
||||
cmStrCat("multiple mode keywords used in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call, ",
|
||||
"there must be exactly one of TARGETS SOURCES "
|
||||
"TESTS DIRECTORIES CACHE_ENTRIES."));
|
||||
}
|
||||
std::string const mode = modes[0];
|
||||
bool const isTargets = (mode == "TARGETS");
|
||||
bool const hasNamed = !parsedArgs.Named.empty();
|
||||
bool const hasRegex =
|
||||
parsedArgs.PropertyNameRegex || parsedArgs.PropertyValueRegex;
|
||||
|
||||
if (!isTargets) {
|
||||
if (parsedArgs.All) {
|
||||
return FatalError(status,
|
||||
cmStrCat("ALL keyword in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"is only valid with the TARGETS scope."));
|
||||
}
|
||||
if (hasRegex) {
|
||||
return FatalError(
|
||||
status,
|
||||
cmStrCat("PROPERTY_NAME_REGEX and PROPERTY_VALUE_REGEX in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"are only valid with the TARGETS scope and ALL."));
|
||||
}
|
||||
if (parsedArgsMode.Deferred) {
|
||||
return FatalError(status,
|
||||
cmStrCat("DEFERRED keyword in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"is only valid with the TARGETS scope."));
|
||||
}
|
||||
if (parsedArgsMode.FollowDependencies) {
|
||||
return FatalError(status,
|
||||
cmStrCat("FOLLOW_DEPENDENCIES keyword in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"is only valid with the TARGETS scope."));
|
||||
}
|
||||
if (!hasNamed) {
|
||||
return FatalError(status,
|
||||
cmStrCat("NAMED keyword missing in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"with ", mode, " scope."));
|
||||
}
|
||||
} else {
|
||||
// ALL and NAMED are mutually exclusive on TARGETS.
|
||||
if (parsedArgs.All && hasNamed) {
|
||||
return FatalError(status,
|
||||
cmStrCat("ALL and NAMED keywords in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"are mutually exclusive."));
|
||||
}
|
||||
// Regex filters require ALL - explicit or implicit. Combining regex
|
||||
// with NAMED is an error.
|
||||
if (hasNamed && hasRegex) {
|
||||
return FatalError(
|
||||
status,
|
||||
cmStrCat("PROPERTY_NAME_REGEX and PROPERTY_VALUE_REGEX in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"are only valid with ALL, not NAMED."));
|
||||
}
|
||||
}
|
||||
|
||||
// FOLLOW_DEPENDENCIES implies DEFERRED.
|
||||
if (parsedArgsMode.FollowDependencies) {
|
||||
parsedArgsMode.Deferred = true;
|
||||
}
|
||||
|
||||
// Default to ALL when neither ALL nor NAMED is given (TARGETS only - the
|
||||
// non-TARGETS path already rejected this combination above).
|
||||
bool const all = parsedArgs.All || !hasNamed;
|
||||
|
||||
// __CMAKE_PRINT_PROPERTIES marks the deprecated cmake_print_properties()
|
||||
// wrapper, which is always NAMED; reject it in ALL enumeration mode.
|
||||
if (all && parsedArgs.CmakePrintProperties) {
|
||||
return FatalError(status,
|
||||
cmStrCat("__CMAKE_PRINT_PROPERTIES in ",
|
||||
"cmake_language(PRINT_PROPERTIES) call ",
|
||||
"is only valid with NAMED."));
|
||||
}
|
||||
|
||||
// Compile regexes once up front so syntax errors are reported here rather
|
||||
// than from inside helpers (in particular, from the deferred lambda at
|
||||
// generate time).
|
||||
cm::optional<cmsys::RegularExpression> propertyNameRegex;
|
||||
cm::optional<cmsys::RegularExpression> propertyValueRegex;
|
||||
if (parsedArgs.PropertyNameRegex) {
|
||||
cmsys::RegularExpression re;
|
||||
if (!re.compile(*parsedArgs.PropertyNameRegex)) {
|
||||
return FatalError(status,
|
||||
cmStrCat("PROPERTY_NAME_REGEX regular expression \"",
|
||||
*parsedArgs.PropertyNameRegex,
|
||||
"\" cannot compile."));
|
||||
}
|
||||
propertyNameRegex = std::move(re);
|
||||
}
|
||||
if (parsedArgs.PropertyValueRegex) {
|
||||
cmsys::RegularExpression re;
|
||||
if (!re.compile(*parsedArgs.PropertyValueRegex)) {
|
||||
return FatalError(status,
|
||||
cmStrCat("PROPERTY_VALUE_REGEX regular expression \"",
|
||||
*parsedArgs.PropertyValueRegex,
|
||||
"\" cannot compile."));
|
||||
}
|
||||
propertyValueRegex = std::move(re);
|
||||
}
|
||||
|
||||
// The message opens with a leading banner line. The internal
|
||||
// __CMAKE_PRINT_PROPERTIES marker (not part of the public interface) selects
|
||||
// the legacy format instead; cmake_print_properties() sets it to reproduce
|
||||
// its historical leading blank line.
|
||||
std::string const messagePrefix = parsedArgs.CmakePrintProperties
|
||||
? std::string("\n")
|
||||
: std::string("Printing properties...\n");
|
||||
|
||||
if (parsedArgsMode.Deferred) {
|
||||
return PrintTargetPropertiesDeferred(
|
||||
items, parsedArgs.Named, all, parsedArgs.PropertyNameRegex,
|
||||
parsedArgs.PropertyValueRegex, std::move(propertyNameRegex),
|
||||
std::move(propertyValueRegex), parsedArgsMode.FollowDependencies,
|
||||
messagePrefix, status);
|
||||
}
|
||||
|
||||
return PrintPropertiesConfigureTime(
|
||||
status, parsedArgs.Named, all, items, kind, parsedArgs.PropertyNameRegex,
|
||||
parsedArgs.PropertyValueRegex, propertyNameRegex, propertyValueRegex,
|
||||
messagePrefix);
|
||||
}
|
||||
}
|
||||
bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
@@ -1007,5 +1657,13 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
|
||||
FatalError(status, "TRACE OFF request without a corresponding TRACE ON");
|
||||
}
|
||||
|
||||
if (expArgs[expArg] == "PRINT_PROPERTIES") {
|
||||
++expArg;
|
||||
finishArgs();
|
||||
std::vector<std::string> const printPropertyArgs(expArgs.begin() + expArg,
|
||||
expArgs.end());
|
||||
return cmCMakeLanguageCommandPRINT_PROPERTIES(printPropertyArgs, status);
|
||||
}
|
||||
|
||||
return FatalError(status, "called with unknown meta-operation");
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "cmValue.h"
|
||||
|
||||
class cmMakefile;
|
||||
class cmValue;
|
||||
|
||||
enum class cmGetDirectoryPropertyResult
|
||||
{
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
#include "cmGetPropertyCommand.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <cm/string_view>
|
||||
#include <cmext/string_view>
|
||||
|
||||
#include "cmDirectoryPropertyHelper.h"
|
||||
#include "cmExecutionStatus.h"
|
||||
#include "cmFileSet.h"
|
||||
@@ -510,6 +505,17 @@ bool LookupSourceProperty(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LookupSourceProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName, cmValue& out)
|
||||
{
|
||||
std::vector<std::string> noDirectories;
|
||||
std::vector<std::string> noTargetDirectories;
|
||||
return LookupSourceProperty(status, name, propertyName,
|
||||
/*sourceFileDirectoryOptionEnabled=*/false,
|
||||
/*sourceFileTargetOptionEnabled=*/false,
|
||||
noDirectories, noTargetDirectories, out);
|
||||
}
|
||||
|
||||
bool LookupTestProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName,
|
||||
bool testDirectoryOptionEnabled,
|
||||
@@ -532,6 +538,15 @@ bool LookupTestProperty(cmExecutionStatus& status, std::string const& name,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LookupTestProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName, cmValue& out)
|
||||
{
|
||||
std::string noDirectory;
|
||||
return LookupTestProperty(status, name, propertyName,
|
||||
/*testDirectoryOptionEnabled=*/false, noDirectory,
|
||||
out);
|
||||
}
|
||||
|
||||
bool LookupCacheProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName, cmValue& out)
|
||||
{
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cmValue.h"
|
||||
|
||||
class cmExecutionStatus;
|
||||
class cmValue;
|
||||
|
||||
namespace GetPropertyCommand {
|
||||
|
||||
@@ -33,11 +32,19 @@ bool LookupSourceProperty(
|
||||
std::vector<std::string>& sourceFileDirectories,
|
||||
std::vector<std::string>& sourceFileTargetDirectories, cmValue& out);
|
||||
|
||||
// Convenience overload: no DIRECTORY / TARGET_DIRECTORY scoping.
|
||||
bool LookupSourceProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName, cmValue& out);
|
||||
|
||||
bool LookupTestProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName,
|
||||
bool testDirectoryOptionEnabled,
|
||||
std::string& testDirectory, cmValue& out);
|
||||
|
||||
// Convenience overload: no DIRECTORY scoping.
|
||||
bool LookupTestProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName, cmValue& out);
|
||||
|
||||
bool LookupCacheProperty(cmExecutionStatus& status, std::string const& name,
|
||||
std::string const& propertyName, cmValue& out);
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cmValue.h"
|
||||
|
||||
class cmExecutionStatus;
|
||||
class cmValue;
|
||||
|
||||
enum class cmGetSourceFilePropertyResult
|
||||
{
|
||||
|
||||
+82
-7
@@ -163,6 +163,11 @@ struct FileSetType
|
||||
|
||||
void AddFileSet(std::string const& name, cm::FileSetMetadata::Visibility vis,
|
||||
cmListFileBacktrace bt);
|
||||
|
||||
// We recompute this every time since some of the property
|
||||
// names depend on names of the file sets
|
||||
cmPropertyMap GetProperties(cmTarget const* tgt,
|
||||
cmTargetInternals const* impl) const;
|
||||
};
|
||||
|
||||
struct UsageRequirementProperty
|
||||
@@ -874,6 +879,43 @@ void FileSetType::AddFileSet(std::string const& name,
|
||||
}
|
||||
}
|
||||
|
||||
cmPropertyMap FileSetType::GetProperties(cmTarget const* tgt,
|
||||
cmTargetInternals const* impl) const
|
||||
{
|
||||
std::set<std::string> propNames{ std::string(this->DefaultDirectoryProperty),
|
||||
std::string(this->DefaultPathProperty),
|
||||
std::string(this->SelfEntries.PropertyName),
|
||||
std::string(
|
||||
this->InterfaceEntries.PropertyName) };
|
||||
|
||||
for (auto const& entry : this->SelfEntries.Entries) {
|
||||
std::string directoryPropertyName =
|
||||
cmStrCat(this->DirectoryPrefix, entry.Value);
|
||||
std::string pathPropertyName = cmStrCat(this->PathPrefix, entry.Value);
|
||||
propNames.emplace(directoryPropertyName);
|
||||
propNames.emplace(pathPropertyName);
|
||||
}
|
||||
|
||||
for (auto const& entry : this->InterfaceEntries.Entries) {
|
||||
std::string directoryPropertyName =
|
||||
cmStrCat(this->DirectoryPrefix, entry.Value);
|
||||
std::string pathPropertyName = cmStrCat(this->PathPrefix, entry.Value);
|
||||
propNames.emplace(directoryPropertyName);
|
||||
propNames.emplace(pathPropertyName);
|
||||
}
|
||||
|
||||
cmPropertyMap propertyMap;
|
||||
|
||||
for (std::string const& prop : propNames) {
|
||||
auto value = this->ReadProperties(tgt, impl, prop);
|
||||
if (value.first) {
|
||||
propertyMap.SetProperty(prop, value.second);
|
||||
}
|
||||
}
|
||||
|
||||
return propertyMap;
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
bool UsageRequirementProperty::Write(
|
||||
cmTargetInternals const* impl, cm::optional<cmListFileBacktrace> const& bt,
|
||||
@@ -2669,13 +2711,7 @@ void cmTarget::CheckProperty(std::string const& prop,
|
||||
}
|
||||
}
|
||||
|
||||
cmValue cmTarget::GetComputedProperty(std::string const& prop,
|
||||
cmMakefile& mf) const
|
||||
{
|
||||
return cmTargetPropertyComputer::GetProperty(this, prop, mf);
|
||||
}
|
||||
|
||||
cmValue cmTarget::GetProperty(std::string const& prop) const
|
||||
std::unordered_set<std::string> const& cmTarget::GetSpecialPropertyNames()
|
||||
{
|
||||
static std::unordered_set<std::string> const specialProps{
|
||||
propC_STANDARD,
|
||||
@@ -2710,6 +2746,18 @@ cmValue cmTarget::GetProperty(std::string const& prop) const
|
||||
propIMPORTED_CXX_MODULES_COMPILE_OPTIONS,
|
||||
propIMPORTED_CXX_MODULES_LINK_LIBRARIES,
|
||||
};
|
||||
return specialProps;
|
||||
}
|
||||
|
||||
cmValue cmTarget::GetComputedProperty(std::string const& prop,
|
||||
cmMakefile& mf) const
|
||||
{
|
||||
return cmTargetPropertyComputer::GetProperty(this, prop, mf);
|
||||
}
|
||||
|
||||
cmValue cmTarget::GetProperty(std::string const& prop) const
|
||||
{
|
||||
auto const& specialProps = cmTarget::GetSpecialPropertyNames();
|
||||
if (specialProps.count(prop)) {
|
||||
if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
|
||||
prop == propCUDA_STANDARD || prop == propHIP_STANDARD ||
|
||||
@@ -2867,6 +2915,33 @@ cmPropertyMap const& cmTarget::GetDirectProperties() const
|
||||
return this->impl->Properties;
|
||||
}
|
||||
|
||||
cmPropertyMap cmTarget::GetExtendedProperties() const
|
||||
{
|
||||
// Get properties in the base property map
|
||||
cmPropertyMap pm = this->impl->Properties;
|
||||
|
||||
// Get special properties
|
||||
auto const& specialProps = cmTarget::GetSpecialPropertyNames();
|
||||
for (auto const& propName : specialProps) {
|
||||
cmValue propValue = this->GetProperty(propName);
|
||||
if (propValue) {
|
||||
pm.SetProperty(propName, propValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Get fileset properties
|
||||
for (auto const& fileSetType : this->impl->FileSetTypes) {
|
||||
cmPropertyMap fileSetProperties =
|
||||
fileSetType.second.GetProperties(this, this->impl.get());
|
||||
auto fileSetPropertiesList = fileSetProperties.GetList();
|
||||
for (auto const& propPair : fileSetPropertiesList) {
|
||||
pm.SetProperty(propPair.first, propPair.second);
|
||||
}
|
||||
}
|
||||
|
||||
return pm;
|
||||
}
|
||||
|
||||
bool cmTarget::IsDLLPlatform() const
|
||||
{
|
||||
return this->impl->IsDLLPlatform;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -232,9 +233,15 @@ public:
|
||||
std::string const& GetSafeProperty(std::string const& prop) const;
|
||||
bool GetPropertyAsBool(std::string const& prop) const;
|
||||
void CheckProperty(std::string const& prop, cmMakefile* context) const;
|
||||
static std::unordered_set<std::string> const& GetSpecialPropertyNames();
|
||||
cmValue GetComputedProperty(std::string const& prop, cmMakefile& mf) const;
|
||||
//! Get properties set directly on this target (no special/computed/chained)
|
||||
cmPropertyMap const& GetDirectProperties() const;
|
||||
/**
|
||||
* Get the properties in the property map plus
|
||||
* special properties, fileset properties, etc.
|
||||
*/
|
||||
cmPropertyMap GetExtendedProperties() const;
|
||||
|
||||
//! Return whether or not the target is for a DLL platform.
|
||||
bool IsDLLPlatform() const;
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
#include "cmTargetPropertyHelper.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmTarget.h"
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "cmValue.h"
|
||||
|
||||
class cmMakefile;
|
||||
class cmTarget;
|
||||
class cmValue;
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "cmValue.h"
|
||||
|
||||
class cmExecutionStatus;
|
||||
class cmValue;
|
||||
|
||||
enum class cmGetTestPropertyResult
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
.*--.*
|
||||
+Properties for TARGET mylib:
|
||||
+mylib\.TYPE = "STATIC_LIBRARY"
|
||||
+mylib\.NAME = "mylib"
|
||||
|
||||
--.*
|
||||
+Printing all properties for TARGET mylib matching name '\^MY_PROP_' and value '\^alpha\$':
|
||||
+mylib\.MY_PROP_X = "alpha"
|
||||
|
||||
-- Configuring done .*
|
||||
--.*
|
||||
+Printing all properties for TARGET mylib \(and all reachable\) matching name '\^MY_PROP_' and value '\^alpha\$':
|
||||
+mylib\.MY_PROP_X = "alpha"
|
||||
+mydep\.MY_PROP_X = "alpha"
|
||||
|
||||
--.*
|
||||
+Properties for TARGET mylib:
|
||||
+mylib\.TYPE = "STATIC_LIBRARY"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,49 @@
|
||||
include(CMakePrintHelpers)
|
||||
enable_language(C)
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/stub.c" "void stub(void) {}\n")
|
||||
|
||||
add_library(mydep STATIC "${CMAKE_CURRENT_BINARY_DIR}/stub.c")
|
||||
set_target_properties(mydep PROPERTIES
|
||||
MY_PROP_X "alpha"
|
||||
MY_PROP_Y "beta"
|
||||
OTHER_PROP "alpha"
|
||||
)
|
||||
|
||||
add_library(mylib STATIC "${CMAKE_CURRENT_BINARY_DIR}/stub.c")
|
||||
target_link_libraries(mylib PRIVATE mydep)
|
||||
set_target_properties(mylib PROPERTIES
|
||||
MY_PROP_X "alpha"
|
||||
MY_PROP_Y "beta"
|
||||
OTHER_PROP "alpha"
|
||||
)
|
||||
|
||||
# (1) configure-time, specific properties — exercises forwarding of TARGETS
|
||||
# + PROPERTIES <names>.
|
||||
cmake_print_properties(TARGETS mylib PROPERTIES TYPE NAME)
|
||||
|
||||
# (2) configure-time, ALL + both regex keywords — exercises forwarding of
|
||||
# PROPERTY_NAME_REGEX and PROPERTY_VALUE_REGEX together.
|
||||
cmake_print_properties(
|
||||
TARGETS mylib
|
||||
PROPERTIES ALL
|
||||
PROPERTY_NAME_REGEX "^MY_PROP_"
|
||||
PROPERTY_VALUE_REGEX "^alpha$"
|
||||
)
|
||||
|
||||
# (3) DEFERRED + FOLLOW_DEPENDENCIES + ALL + both regexes — exercises
|
||||
# forwarding of every new keyword at once, and verifies the walker
|
||||
# actually reaches the PRIVATE dep at generate time (mydep must appear
|
||||
# in the output, not just mylib).
|
||||
cmake_print_properties(
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
PROPERTIES ALL
|
||||
PROPERTY_NAME_REGEX "^MY_PROP_"
|
||||
PROPERTY_VALUE_REGEX "^alpha$"
|
||||
)
|
||||
|
||||
# (4) DEFERRED alone + specific name — exercises DEFERRED forwarding by
|
||||
# itself (no dependency walk; mydep must NOT appear).
|
||||
cmake_print_properties(TARGETS mylib DEFERRED PROPERTIES TYPE)
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET nothing:
|
||||
+nothing\.LINKER_LANGUAGE = <NOTFOUND>
|
||||
+nothing\.TYPE = \"STATIC_LIBRARY\"
|
||||
+Properties for TARGET something:
|
||||
+something\.LINKER_LANGUAGE = <NOTFOUND>
|
||||
+something\.TYPE = \"EXECUTABLE\"
|
||||
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for SOURCE nothing\.c:
|
||||
+nothing\.c\.COMPILE_DEFINITIONS = <NOTFOUND>
|
||||
+nothing\.c\.LANGUAGE = \"C\"
|
||||
+Properties for SOURCE something\.c:
|
||||
+something\.c\.COMPILE_DEFINITIONS = \"SOMETHING=1\"
|
||||
+something\.c\.LANGUAGE = \"C\"
|
||||
@@ -0,0 +1,26 @@
|
||||
enable_language(C)
|
||||
|
||||
set_property(SOURCE nothing.c PROPERTY LANGUAGE C)
|
||||
set_property(SOURCE something.c PROPERTY
|
||||
COMPILE_DEFINITIONS SOMETHING=1)
|
||||
|
||||
add_library(nothing STATIC nothing.c nothing.h)
|
||||
|
||||
add_executable(something something.c something.h)
|
||||
target_link_libraries(something PUBLIC nothing)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS nothing something
|
||||
NAMED
|
||||
LINKER_LANGUAGE
|
||||
TYPE
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES nothing.c something.c
|
||||
NAMED
|
||||
COMPILE_DEFINITIONS
|
||||
LANGUAGE
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib:
|
||||
.* +mylib.HEADER_DIRS = \"[^\"]*/Tests/RunCMake/cmake_language\"
|
||||
.* +mylib.HEADER_DIRS_HEADERS = \"[^\"]*/Tests/RunCMake/cmake_language\"
|
||||
.* +mylib.HEADER_SET = \"[^\"]*/Tests/RunCMake/cmake_language/nothing.h\"
|
||||
.* +mylib.HEADER_SETS = \"HEADERS\"
|
||||
.* +mylib.HEADER_SET_HEADERS = \"[^\"]*/Tests/RunCMake/cmake_language/nothing.h\"
|
||||
.* +mylib.IMPORTED = \"FALSE\"
|
||||
.* +mylib.INCLUDE_DIRECTORIES = \"\$\<BUILD_INTERFACE:[^\"]*/Tests/RunCMake/cmake_language\>\"
|
||||
.* +mylib.INTERFACE_HEADER_SETS = \"HEADERS\"
|
||||
.* +mylib.INTERFACE_INCLUDE_DIRECTORIES = \"\$\<BUILD_INTERFACE:[^\"]*/Tests/RunCMake/cmake_language\>\"
|
||||
.* +mylib.NAME = \"mylib\"
|
||||
.* +mylib.SOURCES = \"nothing.c\"
|
||||
.* +mylib.TYPE = \"STATIC_LIBRARY\"
|
||||
@@ -0,0 +1,22 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
target_sources(mylib
|
||||
PUBLIC
|
||||
FILE_SET HEADERS
|
||||
FILES nothing.h
|
||||
)
|
||||
|
||||
add_executable(something something.c something.h)
|
||||
|
||||
target_link_libraries(something PUBLIC nothing)
|
||||
|
||||
# Printing all for library. To reduce maintenance burden, we only pin a few
|
||||
# properties in the expected output instead of listing every property reported
|
||||
# by ALL.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesAllAndNamed\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language ALL and NAMED keywords in cmake_language\(PRINT_PROPERTIES\)
|
||||
call are mutually exclusive\.
|
||||
@@ -0,0 +1,10 @@
|
||||
enable_language(C)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
# ALL and NAMED are mutually exclusive.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
NAMED MY_PROP
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib \(and all reachable\) matching name '\^MY_' and value 'alpha':
|
||||
+mylib\.MY_PROP = "alpha-mylib"
|
||||
+leaflib\.MY_PROP = "alpha-leaf"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,29 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(leaflib STATIC nothing.c)
|
||||
set_target_properties(leaflib PROPERTIES
|
||||
MY_PROP "alpha-leaf"
|
||||
MY_OTHER "beta-leaf"
|
||||
OTHER_PROP "alpha-other"
|
||||
)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
target_link_libraries(mylib PUBLIC leaflib)
|
||||
set_target_properties(mylib PROPERTIES
|
||||
MY_PROP "alpha-mylib"
|
||||
MY_OTHER "beta-mylib"
|
||||
OTHER_PROP "alpha-other"
|
||||
)
|
||||
|
||||
# Only properties whose name starts with MY_ AND whose value contains
|
||||
# "alpha" should print: MY_PROP on both targets.
|
||||
# MY_OTHER fails the value regex; OTHER_PROP fails the name regex.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX "^MY_"
|
||||
PROPERTY_VALUE_REGEX "alpha"
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib \(and all reachable\) matching name '\^MY_PROP':
|
||||
+mylib\.MY_PROP = "mylib"
|
||||
+mylib\.MY_PROP_2 = "mylib2"
|
||||
+intermediate\.MY_PROP = "intermediate"
|
||||
+intermediate\.MY_PROP_2 = "intermediate2"
|
||||
+leaflib\.MY_PROP = "leaf"
|
||||
+leaflib\.MY_PROP_2 = "leaf2"
|
||||
+linkonlylib\.MY_PROP = "linkonly"
|
||||
+linkonlylib\.MY_PROP_2 = "linkonly2"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,42 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(leaflib STATIC nothing.c)
|
||||
set_target_properties(leaflib PROPERTIES
|
||||
MY_PROP "leaf"
|
||||
MY_PROP_2 "leaf2"
|
||||
)
|
||||
|
||||
add_library(linkonlylib STATIC nothing.c)
|
||||
set_target_properties(linkonlylib PROPERTIES
|
||||
MY_PROP "linkonly"
|
||||
MY_PROP_2 "linkonly2"
|
||||
)
|
||||
|
||||
add_library(intermediate STATIC nothing.c)
|
||||
target_link_libraries(intermediate
|
||||
PRIVATE leaflib
|
||||
INTERFACE $<LINK_ONLY:linkonlylib>
|
||||
)
|
||||
set_target_properties(intermediate PROPERTIES
|
||||
MY_PROP "intermediate"
|
||||
MY_PROP_2 "intermediate2"
|
||||
)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
target_link_libraries(mylib PUBLIC intermediate)
|
||||
set_target_properties(mylib PROPERTIES
|
||||
MY_PROP "mylib"
|
||||
MY_PROP_2 "mylib2"
|
||||
)
|
||||
|
||||
# ALL + FOLLOW_DEPENDENCIES with a tight name regex pins the unified
|
||||
# printer's ALL branch: each reachable target's matching properties show
|
||||
# up in walk order, covering PRIVATE and $<LINK_ONLY:> traversal.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX "^MY_PROP"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesAllRequiresTargets\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language ALL keyword in cmake_language\(PRINT_PROPERTIES\) call is only
|
||||
valid with the TARGETS scope\.
|
||||
@@ -0,0 +1,6 @@
|
||||
# ALL is only valid with the TARGETS scope.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES nothing.c
|
||||
ALL
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Printing properties\.\.\.
|
||||
Properties for CACHE MY_CACHE_A:
|
||||
MY_CACHE_A\.TYPE = "STRING"
|
||||
MY_CACHE_A\.HELPSTRING = "help A"
|
||||
MY_CACHE_A\.NOT_SET = <NOTFOUND>
|
||||
Properties for CACHE MY_CACHE_B:
|
||||
MY_CACHE_B\.TYPE = "PATH"
|
||||
MY_CACHE_B\.HELPSTRING = "help B"
|
||||
MY_CACHE_B\.NOT_SET = <NOTFOUND>
|
||||
@@ -0,0 +1,11 @@
|
||||
set(MY_CACHE_A "a_val" CACHE STRING "help A")
|
||||
set(MY_CACHE_B "b_val" CACHE PATH "help B")
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
CACHE_ENTRIES MY_CACHE_A MY_CACHE_B
|
||||
NAMED
|
||||
TYPE
|
||||
HELPSTRING
|
||||
NOT_SET
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET mylib \(and all reachable\):
|
||||
+mylib\.MY_PROP = "mylib"
|
||||
+debug_dep\.MY_PROP = "debug"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,25 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(debug_dep STATIC nothing.c)
|
||||
set_target_properties(debug_dep PROPERTIES MY_PROP "debug")
|
||||
|
||||
add_library(release_dep STATIC nothing.c)
|
||||
set_target_properties(release_dep PROPERTIES MY_PROP "release")
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
target_link_libraries(mylib PRIVATE
|
||||
$<$<CONFIG:Debug>:debug_dep>
|
||||
$<$<CONFIG:Release>:release_dep>
|
||||
)
|
||||
set_target_properties(mylib PROPERTIES MY_PROP "mylib")
|
||||
|
||||
# Driven with -DCMAKE_BUILD_TYPE=Debug - only debug_dep should be reached
|
||||
# by the walker, since the $<CONFIG:Release> arm evaluates to empty under
|
||||
# Debug.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
NAMED MY_PROP
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET mylib:
|
||||
+mylib\.MY_PROP = "mylib_value"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,11 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
set_target_properties(mylib PROPERTIES MY_PROP "mylib_value")
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
NAMED MY_PROP
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesDeferredRequiresTargets\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language DEFERRED keyword in cmake_language\(PRINT_PROPERTIES\) call is
|
||||
only valid with the TARGETS scope\.
|
||||
@@ -0,0 +1,8 @@
|
||||
# DEFERRED is only meaningful with TARGETS - pairing it with a non-TARGETS
|
||||
# scope must produce a fatal error.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES nothing.c
|
||||
DEFERRED
|
||||
NAMED LANGUAGE
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Printing properties\.\.\.
|
||||
Properties for DIRECTORY \.:
|
||||
\.\.MY_PROP = "top_val"
|
||||
\.\.LABELS = "top_label"
|
||||
\.\.NOT_SET = <NOTFOUND>
|
||||
Properties for DIRECTORY PrintPropertiesDirectories-sub:
|
||||
PrintPropertiesDirectories-sub\.MY_PROP = "sub_val"
|
||||
PrintPropertiesDirectories-sub\.LABELS = "sub_label"
|
||||
PrintPropertiesDirectories-sub\.NOT_SET = <NOTFOUND>
|
||||
@@ -0,0 +1 @@
|
||||
set_directory_properties(PROPERTIES MY_PROP "sub_val" LABELS "sub_label")
|
||||
@@ -0,0 +1,11 @@
|
||||
set_directory_properties(PROPERTIES MY_PROP "top_val" LABELS "top_label")
|
||||
add_subdirectory(PrintPropertiesDirectories-sub)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
DIRECTORIES . PrintPropertiesDirectories-sub
|
||||
NAMED
|
||||
MY_PROP
|
||||
LABELS
|
||||
NOT_SET
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Configuring done .*
|
||||
CMake Warning at PrintPropertiesEmptyMatch\.cmake:[0-9]+ \(cmake_language\):
|
||||
No properties for TARGET mylib matching name 'ZZZ_NO_SUCH_PROPERTY' in
|
||||
cmake_language\(PRINT_PROPERTIES \.\.\.\)\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
CMake Warning at PrintPropertiesEmptyMatch\.cmake:[0-9]+ \(cmake_language\):
|
||||
No properties for TARGET mylib matching value 'ZZZ_NO_SUCH_VALUE' in
|
||||
cmake_language\(PRINT_PROPERTIES \.\.\.\)\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,24 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(libdep INTERFACE)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
target_link_libraries(mylib PRIVATE libdep)
|
||||
|
||||
# One call per regex so each warning text is short and won't wrap.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX "ZZZ_NO_SUCH_PROPERTY"
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
ALL
|
||||
PROPERTY_VALUE_REGEX "ZZZ_NO_SUCH_VALUE"
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib matching name 'INTERFACE':
|
||||
+mylib.INTERFACE_CXX_MODULE_SETS = \"\"
|
||||
+mylib.INTERFACE_HEADER_SETS = \"HEADERS\"
|
||||
+mylib.INTERFACE_INCLUDE_DIRECTORIES = \"\$\<BUILD_INTERFACE:[^\"]*/Tests/RunCMake/cmake_language\>\"
|
||||
+mylib.INTERFACE_SOURCE_SETS = \"\"
|
||||
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib matching value 'HEADERS':
|
||||
+mylib.HEADER_SETS = \"HEADERS\"
|
||||
+mylib.INTERFACE_HEADER_SETS = \"HEADERS\"
|
||||
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib matching name 'INTERFACE' and value 'HEADERS':
|
||||
+mylib.INTERFACE_HEADER_SETS = \"HEADERS\"
|
||||
@@ -0,0 +1,38 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
target_sources(mylib
|
||||
PUBLIC
|
||||
FILE_SET HEADERS
|
||||
FILES nothing.h
|
||||
)
|
||||
|
||||
# Add a property
|
||||
set_target_properties(mylib
|
||||
PROPERTIES
|
||||
some_property some_value
|
||||
another_property another_value
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX INTERFACE
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
PROPERTY_VALUE_REGEX HEADERS
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX INTERFACE
|
||||
PROPERTY_VALUE_REGEX HEADERS
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET mylib \(and all reachable\):
|
||||
+mylib\.MY_PROP = "mylib"
|
||||
+mylib\.MY_PROP_2 = "mylib2"
|
||||
+intermediate\.MY_PROP = "intermediate"
|
||||
+intermediate\.MY_PROP_2 = "intermediate2"
|
||||
+leaflib\.MY_PROP = "leaf"
|
||||
+leaflib\.MY_PROP_2 = "leaf2"
|
||||
+linkonlylib\.MY_PROP = "linkonly"
|
||||
+linkonlylib\.MY_PROP_2 = "linkonly2"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,38 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(leaflib STATIC nothing.c)
|
||||
set_target_properties(leaflib PROPERTIES
|
||||
MY_PROP "leaf"
|
||||
MY_PROP_2 "leaf2"
|
||||
)
|
||||
|
||||
add_library(linkonlylib STATIC nothing.c)
|
||||
set_target_properties(linkonlylib PROPERTIES
|
||||
MY_PROP "linkonly"
|
||||
MY_PROP_2 "linkonly2"
|
||||
)
|
||||
|
||||
add_library(intermediate STATIC nothing.c)
|
||||
target_link_libraries(intermediate
|
||||
PRIVATE leaflib
|
||||
INTERFACE $<LINK_ONLY:linkonlylib>
|
||||
)
|
||||
set_target_properties(intermediate PROPERTIES
|
||||
MY_PROP "intermediate"
|
||||
MY_PROP_2 "intermediate2"
|
||||
)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
target_link_libraries(mylib PUBLIC intermediate)
|
||||
set_target_properties(mylib PROPERTIES
|
||||
MY_PROP "mylib"
|
||||
MY_PROP_2 "mylib2"
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
DEFERRED
|
||||
FOLLOW_DEPENDENCIES
|
||||
NAMED MY_PROP MY_PROP_2
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET mylib \(and all reachable\):
|
||||
+mylib\.MY_PROP = "mylib"
|
||||
+leaflib\.MY_PROP = "leaf"
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,15 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(leaflib STATIC nothing.c)
|
||||
set_target_properties(leaflib PROPERTIES MY_PROP "leaf")
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
target_link_libraries(mylib PUBLIC leaflib)
|
||||
set_target_properties(mylib PROPERTIES MY_PROP "mylib")
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
FOLLOW_DEPENDENCIES
|
||||
NAMED MY_PROP
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesFollowRequiresTargets\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language FOLLOW_DEPENDENCIES keyword in
|
||||
cmake_language\(PRINT_PROPERTIES\) call is only valid with the TARGETS scope\.
|
||||
@@ -0,0 +1,7 @@
|
||||
# FOLLOW_DEPENDENCIES is only valid with the TARGETS scope.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES nothing.c
|
||||
FOLLOW_DEPENDENCIES
|
||||
NAMED LANGUAGE
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Printing properties\.\.\.
|
||||
+All properties for TARGET mylib matching name '\^MY_MARKER\$':
|
||||
+mylib\.MY_MARKER = "marker_value"
|
||||
@@ -0,0 +1,12 @@
|
||||
enable_language(C)
|
||||
|
||||
add_library(mylib STATIC nothing.c)
|
||||
set_target_properties(mylib PROPERTIES MY_MARKER "marker_value")
|
||||
|
||||
# No ALL or NAMED keyword - ALL should be implied. Narrow with a regex so
|
||||
# the output is deterministic across cmake builds.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
PROPERTY_NAME_REGEX "^MY_MARKER$"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesLegacyFlagRequiresNamed\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language __CMAKE_PRINT_PROPERTIES in cmake_language\(PRINT_PROPERTIES\)
|
||||
call is only valid with NAMED\.
|
||||
@@ -0,0 +1,3 @@
|
||||
# __CMAKE_PRINT_PROPERTIES marks the legacy cmake_print_properties() call, which
|
||||
# is always NAMED; here no NAMED is given (implicit ALL), which rejects it.
|
||||
cmake_language(PRINT_PROPERTIES TARGETS some_target __CMAKE_PRINT_PROPERTIES)
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET myimp:
|
||||
+myimp\.LOCATION = "/imported/libmyimp\.so"
|
||||
@@ -0,0 +1,5 @@
|
||||
add_library(myimp SHARED IMPORTED)
|
||||
set_target_properties(myimp PROPERTIES IMPORTED_LOCATION "/imported/libmyimp.so")
|
||||
|
||||
# Reading LOCATION from an imported target succeeds.
|
||||
cmake_language(PRINT_PROPERTIES TARGETS myimp NAMED LOCATION)
|
||||
@@ -0,0 +1,19 @@
|
||||
CMake Warning at PrintPropertiesLocationNonImported\.cmake:[0-9]+ \(cmake_language\):
|
||||
The LOCATION property may not be read from non-imported target "mylib";
|
||||
skipping\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
CMake Warning at PrintPropertiesLocationNonImported\.cmake:[0-9]+ \(cmake_language\):
|
||||
The LOCATION_Debug property may not be read from non-imported target
|
||||
"mylib"; skipping\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
CMake Warning at PrintPropertiesLocationNonImported\.cmake:[0-9]+ \(cmake_language\):
|
||||
The Debug_LOCATION property may not be read from non-imported target
|
||||
"mylib"; skipping\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET mylib:
|
||||
+mylib\.TYPE = "STATIC_LIBRARY"
|
||||
+mylib\.NAME = "mylib"
|
||||
@@ -0,0 +1,8 @@
|
||||
enable_language(C)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
# Reading a computed location property from a non-imported target
|
||||
# is skipped with a warning instead of read. Any other requested properties
|
||||
# should still be printed.
|
||||
cmake_language(PRINT_PROPERTIES TARGETS mylib
|
||||
NAMED TYPE LOCATION LOCATION_Debug Debug_LOCATION NAME)
|
||||
@@ -0,0 +1,19 @@
|
||||
CMake Warning at PrintPropertiesLocationOnlySkipped\.cmake:[0-9]+ \(cmake_language\):
|
||||
The LOCATION property may not be read from non-imported target "mylib";
|
||||
skipping\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
CMake Warning at PrintPropertiesLocationOnlySkipped\.cmake:[0-9]+ \(cmake_language\):
|
||||
The LOCATION_Debug property may not be read from non-imported target
|
||||
"mylib"; skipping\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
|
||||
|
||||
CMake Warning at PrintPropertiesLocationOnlySkipped\.cmake:[0-9]+ \(cmake_language\):
|
||||
The Debug_LOCATION property may not be read from non-imported target
|
||||
"mylib"; skipping\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
||||
@@ -0,0 +1,7 @@
|
||||
enable_language(C)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
# If we only read LOCATION based properties on a non-imported target, print just
|
||||
# the warning and suppress the header.
|
||||
cmake_language(PRINT_PROPERTIES TARGETS mylib
|
||||
NAMED LOCATION LOCATION_Debug Debug_LOCATION)
|
||||
@@ -0,0 +1,12 @@
|
||||
-- Printing properties\.\.\.
|
||||
|
||||
+No such TARGET "does_not_exist" !
|
||||
|
||||
|
||||
-- Configuring done .*
|
||||
-- Printing properties\.\.\.
|
||||
|
||||
+No such TARGET "does_not_exist" !
|
||||
|
||||
|
||||
-- Generating done .*
|
||||
@@ -0,0 +1,14 @@
|
||||
# Configure-time: prints a status-line note and continues.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS does_not_exist
|
||||
NAMED MY_PROP
|
||||
)
|
||||
|
||||
# Deferred: prints a status-line note at generate time and continues.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS does_not_exist
|
||||
DEFERRED
|
||||
NAMED MY_PROP
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesNameRegexError\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language PROPERTY_NAME_REGEX regular expression "\[unbalanced" cannot
|
||||
compile\.
|
||||
@@ -0,0 +1,10 @@
|
||||
enable_language(C)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
# Unbalanced bracket - regex compile must fail with a fatal error.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
PROPERTY_NAME_REGEX "[unbalanced"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesNamedRequired\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language NAMED keyword missing in cmake_language\(PRINT_PROPERTIES\)
|
||||
call with SOURCES scope\.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Non-TARGETS scopes require NAMED.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES nothing.c
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,4 @@
|
||||
^CMake Error at PrintPropertiesRegexRequiresTargets\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language PROPERTY_NAME_REGEX and PROPERTY_VALUE_REGEX in
|
||||
cmake_language\(PRINT_PROPERTIES\) call are only valid with the TARGETS scope
|
||||
and ALL\.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Regex filters are only valid with the TARGETS scope and ALL.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES nothing.c
|
||||
NAMED LANGUAGE
|
||||
PROPERTY_NAME_REGEX "LANG"
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesRegexWithNamed\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language PROPERTY_NAME_REGEX and PROPERTY_VALUE_REGEX in
|
||||
cmake_language\(PRINT_PROPERTIES\) call are only valid with ALL, not NAMED\.
|
||||
@@ -0,0 +1,10 @@
|
||||
enable_language(C)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
# Regex filters require ALL; combining with NAMED is an error.
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
NAMED MY_PROP
|
||||
PROPERTY_NAME_REGEX "MY_"
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for TARGET rot13:
|
||||
+rot13.SOURCES = \"rot13.c;rot13.h\"
|
||||
+rot13.POSITION_INDEPENDENT_CODE = \"True\"
|
||||
|
||||
-- Printing properties\.\.\.
|
||||
+Properties for SOURCE rot13.c:
|
||||
+rot13.c.LOCATION = \"[^\"]*/cmake_language/rot13.c\"
|
||||
+rot13.c.LANGUAGE = \"C\"
|
||||
@@ -0,0 +1,21 @@
|
||||
enable_language(C)
|
||||
|
||||
set_property(SOURCE rot13.c PROPERTY LANGUAGE C)
|
||||
|
||||
add_library(rot13 SHARED rot13.c rot13.h)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS rot13
|
||||
NAMED
|
||||
SOURCES
|
||||
POSITION_INDEPENDENT_CODE
|
||||
)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
SOURCES rot13.c
|
||||
NAMED
|
||||
LOCATION
|
||||
LANGUAGE
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Printing properties\.\.\.
|
||||
Properties for TEST test_a:
|
||||
test_a\.MY_PROP = "a_val"
|
||||
test_a\.TIMEOUT = "30"
|
||||
test_a\.NOT_SET = <NOTFOUND>
|
||||
Properties for TEST test_b:
|
||||
test_b\.MY_PROP = "b_val"
|
||||
test_b\.TIMEOUT = "60"
|
||||
test_b\.NOT_SET = <NOTFOUND>
|
||||
@@ -0,0 +1,14 @@
|
||||
enable_testing()
|
||||
add_test(NAME test_a COMMAND "${CMAKE_COMMAND}" -E true)
|
||||
add_test(NAME test_b COMMAND "${CMAKE_COMMAND}" -E true)
|
||||
set_tests_properties(test_a PROPERTIES MY_PROP "a_val" TIMEOUT 30)
|
||||
set_tests_properties(test_b PROPERTIES MY_PROP "b_val" TIMEOUT 60)
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TESTS test_a test_b
|
||||
NAMED
|
||||
MY_PROP
|
||||
TIMEOUT
|
||||
NOT_SET
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,3 @@
|
||||
^CMake Error at PrintPropertiesValueRegexError\.cmake:[0-9]+ \(cmake_language\):
|
||||
cmake_language PROPERTY_VALUE_REGEX regular expression "\[unbalanced" cannot
|
||||
compile\.
|
||||
@@ -0,0 +1,10 @@
|
||||
enable_language(C)
|
||||
add_library(mylib STATIC nothing.c)
|
||||
|
||||
|
||||
cmake_language(
|
||||
PRINT_PROPERTIES
|
||||
TARGETS mylib
|
||||
ALL
|
||||
PROPERTY_VALUE_REGEX "[unbalanced"
|
||||
)
|
||||
@@ -167,6 +167,47 @@ run_cmake(Experimental/ExportPackageDependencies-wrong)
|
||||
run_cmake(Experimental/ExportPackageDependencies-quiet)
|
||||
run_cmake(Experimental/Unknown)
|
||||
|
||||
run_cmake(PrintProperties)
|
||||
run_cmake(PrintPropertiesSources)
|
||||
run_cmake(PrintPropertiesDirectories)
|
||||
run_cmake(PrintPropertiesTests)
|
||||
run_cmake(PrintPropertiesCacheEntries)
|
||||
run_cmake(PrintPropertiesLegacyFlagRequiresNamed)
|
||||
run_cmake(PrintPropertiesAll)
|
||||
run_cmake(PrintPropertiesImplicitAll)
|
||||
run_cmake(PrintPropertiesFiltering)
|
||||
run_cmake(PrintPropertiesDeferred)
|
||||
run_cmake(PrintPropertiesFollowDependencies)
|
||||
# Merge stdout+stderr so the expected output can assert that warnings /
|
||||
# generator-time messages fall between "Configuring done" and
|
||||
# "Generating done" - i.e. from the generator-action lambda.
|
||||
block()
|
||||
set(RunCMake_TEST_OUTPUT_MERGE 1)
|
||||
run_cmake(PrintPropertiesAllFollowDependencies)
|
||||
run_cmake(PrintPropertiesAllFollowBothRegex)
|
||||
run_cmake(PrintPropertiesFollowImpliesDeferred)
|
||||
run_cmake(PrintPropertiesEmptyMatch)
|
||||
run_cmake(PrintPropertiesMissingTarget)
|
||||
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
|
||||
run_cmake(PrintPropertiesConfigSpecific)
|
||||
endblock()
|
||||
run_cmake(PrintPropertiesAllAndNamed)
|
||||
run_cmake(PrintPropertiesRegexWithNamed)
|
||||
run_cmake(PrintPropertiesNamedRequired)
|
||||
run_cmake(PrintPropertiesAllRequiresTargets)
|
||||
run_cmake(PrintPropertiesRegexRequiresTargets)
|
||||
run_cmake(PrintPropertiesDeferredRequiresTargets)
|
||||
run_cmake(PrintPropertiesFollowRequiresTargets)
|
||||
run_cmake(PrintPropertiesNameRegexError)
|
||||
run_cmake(PrintPropertiesValueRegexError)
|
||||
run_cmake(PrintPropertiesLocationNonImported)
|
||||
run_cmake(PrintPropertiesLocationImported)
|
||||
block()
|
||||
# All requested properties are skipped, so no block header is emitted.
|
||||
set(RunCMake_TEST_NOT_EXPECT_stdout "Properties for TARGET")
|
||||
run_cmake(PrintPropertiesLocationOnlySkipped)
|
||||
endblock()
|
||||
|
||||
run_cmake(trace)
|
||||
run_cmake(trace_seq)
|
||||
run_cmake(trace_expand)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "nothing.h"
|
||||
|
||||
void nothing(void)
|
||||
{
|
||||
(void*)0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef NOTHING_H
|
||||
#define NOTHING_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void nothing();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "rot13.h"
|
||||
|
||||
void rot13(char* in)
|
||||
{
|
||||
char* end = in + strlen(in);
|
||||
for (char* c = in; c < end; c++) {
|
||||
if (*c >= 'a' && *c <= 'z') {
|
||||
*c += (*c < 'n') ? 13 : -13;
|
||||
continue;
|
||||
}
|
||||
if (*c >= 'A' && *c <= 'Z') {
|
||||
*c += (*c < 'N') ? 13 : -13;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef ROT13_H
|
||||
#define ROT13_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void rot13(char* in);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "something.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
nothing();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef SOMETHING_H
|
||||
#define SOMETHING_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nothing.h"
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user