CMake/Source/cmSetSourceFilesPropertiesCommand.cxx
Alexandru Croitor 3d4b70ea64 set_source_files_properties: Allow specification of directory scope
Both set_source_files_properties() and set_property(SOURCE) now accept
two new optional arguments: DIRECTORY and TARGET_DIRECTORY.

The DIRECTORY option takes a list of relative or absolute paths
pointing to processed source directories (add_subdirectory was
already called on them).

These paths specify directory scopes where the source file properties
will be set. Previously the scope was always the currently processed
source directory.

Similarly TARGET_DIRECTORY takes a list of targets, whose source
directories will be used as the list of scopes where to set the
source file properties.

get_property() and get_source_file_property() also get the same
new arguments, except only one value can be specified instead
of a list.

Fixes: #20128
2020-05-14 16:31:22 +02:00

165 lines
5.8 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmSetSourceFilesPropertiesCommand.h"
#include <algorithm>
#include <iterator>
#include <cm/string_view>
#include <cmext/algorithm>
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmSetPropertyCommand.h"
#include "cmSourceFile.h"
#include "cmStringAlgorithms.h"
static bool RunCommandForScope(
cmMakefile* mf, std::vector<std::string>::const_iterator file_begin,
std::vector<std::string>::const_iterator file_end,
std::vector<std::string>::const_iterator prop_begin,
std::vector<std::string>::const_iterator prop_end, std::string& errors);
bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
if (args.size() < 2) {
status.SetError("called with incorrect number of arguments");
return false;
}
// break the arguments into source file names and properties
// old style allows for specifier before PROPERTIES keyword
static const cm::string_view prop_names[] = {
"ABSTRACT", "GENERATED", "WRAP_EXCLUDE", "COMPILE_FLAGS",
"OBJECT_DEPENDS", "PROPERTIES", "DIRECTORY", "TARGET_DIRECTORY"
};
auto isNotAPropertyKeyword =
[](const std::vector<std::string>::const_iterator& arg_it) {
return std::all_of(
std::begin(prop_names), std::end(prop_names),
[&arg_it](cm::string_view prop_name) { return *arg_it != prop_name; });
};
auto options_begin = std::find_first_of(
args.begin(), args.end(), std::begin(prop_names), std::end(prop_names));
auto options_it = options_begin;
// Handle directory options.
std::vector<std::string> source_file_directories;
std::vector<std::string> source_file_target_directories;
bool source_file_directory_option_enabled = false;
bool source_file_target_option_enabled = false;
std::vector<cmMakefile*> source_file_directory_makefiles;
if (options_it != args.end() && *options_it == "DIRECTORY") {
source_file_directory_option_enabled = true;
++options_it;
while (options_it != args.end() && isNotAPropertyKeyword(options_it)) {
source_file_directories.push_back(*options_it);
++options_it;
}
} else if (options_it != args.end() && *options_it == "TARGET_DIRECTORY") {
source_file_target_option_enabled = true;
++options_it;
while (options_it != args.end() && isNotAPropertyKeyword(options_it)) {
source_file_target_directories.push_back(*options_it);
++options_it;
}
}
const auto props_begin = options_it;
bool file_scopes_handled =
SetPropertyCommand::HandleAndValidateSourceFileDirectortoryScopes(
status, source_file_directory_option_enabled,
source_file_target_option_enabled, source_file_directories,
source_file_target_directories, source_file_directory_makefiles);
if (!file_scopes_handled) {
return false;
}
std::vector<std::string> files;
bool source_file_paths_should_be_absolute =
source_file_directory_option_enabled || source_file_target_option_enabled;
SetPropertyCommand::MakeSourceFilePathsAbsoluteIfNeeded(
status, files, args.begin(), options_begin,
source_file_paths_should_be_absolute);
// Now call the worker function for each directory scope represented by a
// cmMakefile instance.
std::string errors;
for (const auto mf : source_file_directory_makefiles) {
bool ret = RunCommandForScope(mf, files.begin(), files.end(), props_begin,
args.end(), errors);
if (!ret) {
status.SetError(errors);
return ret;
}
}
return true;
}
static bool RunCommandForScope(
cmMakefile* mf, std::vector<std::string>::const_iterator file_begin,
std::vector<std::string>::const_iterator file_end,
std::vector<std::string>::const_iterator prop_begin,
std::vector<std::string>::const_iterator prop_end, std::string& errors)
{
std::vector<std::string> propertyPairs;
// build the property pairs
for (auto j = prop_begin; j != prop_end; ++j) {
// consume old style options
if (*j == "ABSTRACT" || *j == "GENERATED" || *j == "WRAP_EXCLUDE") {
propertyPairs.emplace_back(*j);
propertyPairs.emplace_back("1");
} else if (*j == "COMPILE_FLAGS") {
propertyPairs.emplace_back("COMPILE_FLAGS");
++j;
if (j == prop_end) {
errors = "called with incorrect number of arguments "
"COMPILE_FLAGS with no flags";
return false;
}
propertyPairs.push_back(*j);
} else if (*j == "OBJECT_DEPENDS") {
propertyPairs.emplace_back("OBJECT_DEPENDS");
++j;
if (j == prop_end) {
errors = "called with incorrect number of arguments "
"OBJECT_DEPENDS with no dependencies";
return false;
}
propertyPairs.push_back(*j);
} else if (*j == "PROPERTIES") {
// PROPERTIES is followed by new style prop value pairs
cmStringRange newStyleProps{ j + 1, prop_end };
if (newStyleProps.size() % 2 != 0) {
errors = "called with incorrect number of arguments.";
return false;
}
// set newStyleProps as is.
cm::append(propertyPairs, newStyleProps);
// break out of the loop.
break;
} else {
errors = "called with illegal arguments, maybe missing a "
"PROPERTIES specifier?";
return false;
}
}
// loop over all the files
for (const std::string& sfname : cmStringRange{ file_begin, file_end }) {
// get the source file
if (cmSourceFile* sf = mf->GetOrCreateSource(sfname)) {
// loop through the props and set them
for (auto k = propertyPairs.begin(); k != propertyPairs.end(); k += 2) {
sf->SetProperty(*k, (k + 1)->c_str());
}
}
}
return true;
}