diff options
20 files changed, 739 insertions, 49 deletions
diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst index c0f9b46..80d97fd 100644 --- a/Help/command/get_property.rst +++ b/Help/command/get_property.rst @@ -10,6 +10,7 @@ Get a property. DIRECTORY [<dir>] | TARGET <target> | SOURCE <source> | + [<TARGET_DIRECTORY ... | DIRECTORY ...>] | INSTALL <file> | TEST <test> | CACHE <entry> | @@ -49,6 +50,15 @@ It must be one of the following: ``VARIABLE`` Scope is unique and does not accept a name. +In the ``SOURCE`` case, the queried source file scope can be changed by +specifying one of the additional options: ``DIRECTORY`` or ``TARGET_DIRECTORY``. + +``DIRECTORY`` takes a path to a processed directory, and the source file property +will be read from that directory scope. + +``TARGET_DIRECTORY`` takes the name of an existing target. The source file +property will be read from this target's directory scope. + The required ``PROPERTY`` option is immediately followed by the name of the property to get. If the property is not set an empty value is returned, although some properties support inheriting from a parent scope diff --git a/Help/command/get_source_file_property.rst b/Help/command/get_source_file_property.rst index decec19..893a1b6 100644 --- a/Help/command/get_source_file_property.rst +++ b/Help/command/get_source_file_property.rst @@ -5,7 +5,7 @@ Get a property for a source file. .. code-block:: cmake - get_source_file_property(VAR file property) + get_source_file_property(VAR file [<TARGET_DIRECTORY ... | DIRECTORY ...>] property) Gets a property from a source file. The value of the property is stored in the variable ``VAR``. If the source property is not found, the @@ -15,6 +15,15 @@ or not (see :command:`define_property`). Non-inherited properties will set parent scope as described for the :command:`define_property` command and if still unable to find the property, ``VAR`` will be set to an empty string. +The queried source file scope can be changed by specifying one of the +additional options: ``DIRECTORY`` or ``TARGET_DIRECTORY``. + +``DIRECTORY`` takes a path to a processed directory, and the source file property +will be read from that directory scope. + +``TARGET_DIRECTORY`` takes the name of an existing target. The source file +property will be read from this target's directory scope. + Use :command:`set_source_files_properties` to set property values. Source file properties usually control how the file is built. One property that is always there is :prop_sf:`LOCATION`. diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst index 1728c98..de28be3 100644 --- a/Help/command/set_property.rst +++ b/Help/command/set_property.rst @@ -8,7 +8,8 @@ Set a named property in a given scope. set_property(<GLOBAL | DIRECTORY [<dir>] | TARGET [<target1> ...] | - SOURCE [<src1> ...] | + SOURCE [<src1> ...] + [<TARGET_DIRECTORY ... | DIRECTORY ...>] | INSTALL [<file1> ...] | TEST [<test1> ...] | CACHE [<entry1> ...] > @@ -34,8 +35,16 @@ It must be one of the following: ``SOURCE`` Scope may name zero or more source files. Note that source - file properties are visible only to targets added in the same + file properties are by default visible only to targets added in the same directory (``CMakeLists.txt``). + The file properties can be made visible in a different directory by specifying + one of the additional options: ``TARGET_DIRECTORY`` or ``DIRECTORY``. + + ``DIRECTORY`` takes a list of processed directories paths, and sets the file + properties in those directory scopes. + + ``TARGET_DIRECTORY`` takes a list of existing targets. The file + properties will be set in these targets' directory scopes. See also the :command:`set_source_files_properties` command. ``INSTALL`` diff --git a/Help/command/set_source_files_properties.rst b/Help/command/set_source_files_properties.rst index ab95d70..8adfb9e 100644 --- a/Help/command/set_source_files_properties.rst +++ b/Help/command/set_source_files_properties.rst @@ -6,12 +6,25 @@ Source files can have properties that affect how they are built. .. code-block:: cmake set_source_files_properties([file1 [file2 [...]]] + [<TARGET_DIRECTORY ... | DIRECTORY ...>] PROPERTIES prop1 value1 [prop2 value2 [...]]) Sets properties associated with source files using a key/value paired list. +Note that source file properties are by default visible only to +targets added in the same directory (``CMakeLists.txt``). + +The file properties can be made visible in a different directory by specifying +one of the additional options: ``TARGET_DIRECTORY`` or ``DIRECTORY``. + +``DIRECTORY`` takes a list of processed directories paths, and sets the file +properties in those directory scopes. + +``TARGET_DIRECTORY`` takes a list of existing targets. The file +properties will be set in these targets' directory scopes. + See also the :command:`set_property(SOURCE)` command. See :ref:`Source File Properties` for the list of properties known diff --git a/Help/release/dev/sf-property-scopes.rst b/Help/release/dev/sf-property-scopes.rst new file mode 100644 index 0000000..0b61625 --- /dev/null +++ b/Help/release/dev/sf-property-scopes.rst @@ -0,0 +1,15 @@ +sf-property-scopes +------------------ + +* The :command:`set_property` with the ``SOURCE`` scope gained the + ``DIRECTORY`` and ``TARGET_DIRECTORY`` options to set properties + in the provided directory scopes. +* The :command:`set_source_files_properties` gained the ``DIRECTORY`` + and ``TARGET_DIRECTORY`` options to set properties in the provided + directory scopes. +* The :command:`get_property` with ``SOURCE`` scope gained the + ``DIRECTORY`` and ``TARGET_DIRECTORY`` options to get a property + from the provided directory scope. +* The :command:`get_source_file_property` gained the ``DIRECTORY`` + and ``TARGET_DIRECTORY`` options to get a property from the + provided directory scope. diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx index c3ac672..851f426 100644 --- a/Source/cmGetPropertyCommand.cxx +++ b/Source/cmGetPropertyCommand.cxx @@ -11,6 +11,7 @@ #include "cmPolicies.h" #include "cmProperty.h" #include "cmPropertyDefinition.h" +#include "cmSetPropertyCommand.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStringAlgorithms.h" @@ -48,7 +49,9 @@ bool HandleTargetMode(cmExecutionStatus& status, const std::string& name, const std::string& propertyName); bool HandleSourceMode(cmExecutionStatus& status, const std::string& name, OutType infoType, const std::string& variable, - const std::string& propertyName); + const std::string& propertyName, + cmMakefile& directory_makefile, + bool source_file_paths_should_be_absolute); bool HandleTestMode(cmExecutionStatus& status, const std::string& name, OutType infoType, const std::string& variable, const std::string& propertyName); @@ -78,6 +81,11 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args, std::string name; std::string propertyName; + 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; + // Get the scope from which to get the property. cmProperty::ScopeType scope; if (args[1] == "GLOBAL") { @@ -111,7 +119,9 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args, DoingNone, DoingName, DoingProperty, - DoingType + DoingType, + DoingSourceDirectory, + DoingSourceTargetDirectory }; Doing doing = DoingName; for (unsigned int i = 2; i < args.size(); ++i) { @@ -132,6 +142,20 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args, } else if (doing == DoingName) { doing = DoingNone; name = args[i]; + } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE && + args[i] == "DIRECTORY") { + doing = DoingSourceDirectory; + source_file_directory_option_enabled = true; + } else if (doing == DoingNone && scope == cmProperty::SOURCE_FILE && + args[i] == "TARGET_DIRECTORY") { + doing = DoingSourceTargetDirectory; + source_file_target_option_enabled = true; + } else if (doing == DoingSourceDirectory) { + source_file_directories.push_back(args[i]); + doing = DoingNone; + } else if (doing == DoingSourceTargetDirectory) { + source_file_target_directories.push_back(args[i]); + doing = DoingNone; } else if (doing == DoingProperty) { doing = DoingNone; propertyName = args[i]; @@ -147,6 +171,16 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args, return false; } + std::vector<cmMakefile*> source_file_directory_makefiles; + 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; + } + // Compute requested output. if (infoType == OutBriefDoc) { // Lookup brief documentation. @@ -180,6 +214,11 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args, } } else { // Dispatch property getting. + cmMakefile& directory_scope_mf = *(source_file_directory_makefiles[0]); + bool source_file_paths_should_be_absolute = + source_file_directory_option_enabled || + source_file_target_option_enabled; + switch (scope) { case cmProperty::GLOBAL: return HandleGlobalMode(status, name, infoType, variable, @@ -191,8 +230,9 @@ bool cmGetPropertyCommand(std::vector<std::string> const& args, return HandleTargetMode(status, name, infoType, variable, propertyName); case cmProperty::SOURCE_FILE: - return HandleSourceMode(status, name, infoType, variable, - propertyName); + return HandleSourceMode(status, name, infoType, variable, propertyName, + directory_scope_mf, + source_file_paths_should_be_absolute); case cmProperty::TEST: return HandleTestMode(status, name, infoType, variable, propertyName); case cmProperty::VARIABLE: @@ -331,7 +371,9 @@ bool HandleTargetMode(cmExecutionStatus& status, const std::string& name, bool HandleSourceMode(cmExecutionStatus& status, const std::string& name, OutType infoType, const std::string& variable, - const std::string& propertyName) + const std::string& propertyName, + cmMakefile& directory_makefile, + const bool source_file_paths_should_be_absolute) { if (name.empty()) { status.SetError("not given name for SOURCE scope."); @@ -339,12 +381,17 @@ bool HandleSourceMode(cmExecutionStatus& status, const std::string& name, } // Get the source file. - if (cmSourceFile* sf = status.GetMakefile().GetOrCreateSource(name)) { + const std::string source_file_absolute_path = + SetPropertyCommand::MakeSourceFilePathAbsoluteIfNeeded( + status, name, source_file_paths_should_be_absolute); + if (cmSourceFile* sf = + directory_makefile.GetOrCreateSource(source_file_absolute_path)) { return StoreResult(infoType, status.GetMakefile(), variable, sf->GetPropertyForUser(propertyName)); } status.SetError( - cmStrCat("given SOURCE name that could not be found or created: ", name)); + cmStrCat("given SOURCE name that could not be found or created: ", + source_file_absolute_path)); return false; } diff --git a/Source/cmGetSourceFilePropertyCommand.cxx b/Source/cmGetSourceFilePropertyCommand.cxx index eefdc6c..5395bc8 100644 --- a/Source/cmGetSourceFilePropertyCommand.cxx +++ b/Source/cmGetSourceFilePropertyCommand.cxx @@ -4,35 +4,71 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmSetPropertyCommand.h" #include "cmSourceFile.h" bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - if (args.size() != 3) { + std::vector<std::string>::size_type args_size = args.size(); + if (args_size != 3 && args_size != 5) { status.SetError("called with incorrect number of arguments"); return false; } + + 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; + + int property_arg_index = 2; + if (args[2] == "DIRECTORY" && args_size == 5) { + property_arg_index = 4; + source_file_directory_option_enabled = true; + source_file_directories.push_back(args[3]); + } else if (args[2] == "TARGET_DIRECTORY" && args_size == 5) { + property_arg_index = 4; + source_file_target_option_enabled = true; + source_file_target_directories.push_back(args[3]); + } + + std::vector<cmMakefile*> source_file_directory_makefiles; + 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::string const& var = args[0]; - std::string const& file = args[1]; - cmMakefile& mf = status.GetMakefile(); + bool source_file_paths_should_be_absolute = + source_file_directory_option_enabled || source_file_target_option_enabled; + std::string const file = + SetPropertyCommand::MakeSourceFilePathAbsoluteIfNeeded( + status, args[1], source_file_paths_should_be_absolute); + cmMakefile& mf = *source_file_directory_makefiles[0]; cmSourceFile* sf = mf.GetSource(file); // for the location we must create a source file first - if (!sf && args[2] == "LOCATION") { + if (!sf && args[property_arg_index] == "LOCATION") { sf = mf.CreateSource(file); } + if (sf) { const char* prop = nullptr; - if (!args[2].empty()) { - prop = sf->GetPropertyForUser(args[2]); + if (!args[property_arg_index].empty()) { + prop = sf->GetPropertyForUser(args[property_arg_index]); } if (prop) { - mf.AddDefinition(var, prop); + // Set the value on the original Makefile scope, not the scope of the + // requested directory. + status.GetMakefile().AddDefinition(var, prop); return true; } } - mf.AddDefinition(var, "NOTFOUND"); + status.GetMakefile().AddDefinition(var, "NOTFOUND"); return true; } diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 4dbbbd7..07cb7c9 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -43,7 +43,9 @@ bool HandleSourceMode(cmExecutionStatus& status, const std::set<std::string>& names, const std::string& propertyName, const std::string& propertyValue, bool appendAsString, - bool appendMode, bool remove); + bool appendMode, bool remove, + const std::vector<cmMakefile*>& directory_makefiles, + bool source_file_paths_should_be_absolute); bool HandleSource(cmSourceFile* sf, const std::string& propertyName, const std::string& propertyValue, bool appendAsString, bool appendMode, bool remove); @@ -74,6 +76,131 @@ bool HandleInstall(cmInstalledFile* file, cmMakefile& makefile, bool appendMode, bool remove); } +namespace SetPropertyCommand { +bool HandleSourceFileDirectoryScopes( + cmExecutionStatus& status, std::vector<std::string>& source_file_directories, + std::vector<std::string>& source_file_target_directories, + std::vector<cmMakefile*>& directory_makefiles) +{ + cmMakefile* current_dir_mf = &status.GetMakefile(); + if (!source_file_directories.empty()) { + for (const std::string& dir_path : source_file_directories) { + const std::string absolute_dir_path = cmSystemTools::CollapseFullPath( + dir_path, current_dir_mf->GetCurrentSourceDirectory()); + cmMakefile* dir_mf = + status.GetMakefile().GetGlobalGenerator()->FindMakefile( + absolute_dir_path); + if (!dir_mf) { + status.SetError(cmStrCat("given non-existent DIRECTORY ", dir_path)); + return false; + } + directory_makefiles.push_back(dir_mf); + } + } else if (!source_file_target_directories.empty()) { + for (const std::string& target_name : source_file_target_directories) { + cmTarget* target = current_dir_mf->FindTargetToUse(target_name); + if (!target) { + status.SetError(cmStrCat( + "given non-existent target for DIRECTORY_TARGET ", target_name)); + return false; + } + cmProp target_source_dir = target->GetProperty("SOURCE_DIR"); + cmMakefile* target_dir_mf = + status.GetMakefile().GetGlobalGenerator()->FindMakefile( + *target_source_dir); + directory_makefiles.push_back(target_dir_mf); + } + } else { + directory_makefiles.push_back(current_dir_mf); + } + return true; +} + +bool HandleSourceFileDirectoryScopeValidation( + cmExecutionStatus& status, bool source_file_directory_option_enabled, + bool source_file_target_option_enabled, + std::vector<std::string>& source_file_directories, + std::vector<std::string>& source_file_target_directories) +{ + // Validate source file directory scopes. + if (source_file_directory_option_enabled && + source_file_directories.empty()) { + std::string errors = "called with incorrect number of arguments " + "no value provided to the DIRECTORY option"; + status.SetError(errors); + return false; + } + if (source_file_target_option_enabled && + source_file_target_directories.empty()) { + std::string errors = "called with incorrect number of arguments " + "no value provided to the TARGET_DIRECTORY option"; + status.SetError(errors); + return false; + } + return true; +} + +bool HandleAndValidateSourceFileDirectortoryScopes( + cmExecutionStatus& status, bool source_file_directory_option_enabled, + bool source_file_target_option_enabled, + std::vector<std::string>& source_file_directories, + std::vector<std::string>& source_file_target_directories, + std::vector<cmMakefile*>& source_file_directory_makefiles) +{ + bool scope_options_valid = + SetPropertyCommand::HandleSourceFileDirectoryScopeValidation( + status, source_file_directory_option_enabled, + source_file_target_option_enabled, source_file_directories, + source_file_target_directories); + if (!scope_options_valid) { + return false; + } + + scope_options_valid = SetPropertyCommand::HandleSourceFileDirectoryScopes( + status, source_file_directories, source_file_target_directories, + source_file_directory_makefiles); + return scope_options_valid; +} + +std::string MakeSourceFilePathAbsoluteIfNeeded( + cmExecutionStatus& status, const std::string& source_file_path, + const bool needed) +{ + if (!needed) { + return source_file_path; + } + const std::string absolute_file_path = cmSystemTools::CollapseFullPath( + source_file_path, status.GetMakefile().GetCurrentSourceDirectory()); + return absolute_file_path; +} + +void MakeSourceFilePathsAbsoluteIfNeeded( + cmExecutionStatus& status, + std::vector<std::string>& source_files_absolute_paths, + std::vector<std::string>::const_iterator files_it_begin, + std::vector<std::string>::const_iterator files_it_end, const bool needed) +{ + + // Make the file paths absolute, so that relative source file paths are + // picked up relative to the command calling site, regardless of the + // directory scope. + std::vector<std::string>::difference_type num_files = + files_it_end - files_it_begin; + source_files_absolute_paths.reserve(num_files); + + if (!needed) { + source_files_absolute_paths.assign(files_it_begin, files_it_end); + return; + } + + for (; files_it_begin != files_it_end; ++files_it_begin) { + const std::string absolute_file_path = + MakeSourceFilePathAbsoluteIfNeeded(status, *files_it_begin, true); + source_files_absolute_paths.push_back(absolute_file_path); + } +} +} + bool cmSetPropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -114,13 +241,20 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args, std::string propertyName; std::string propertyValue; + 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; + // Parse the rest of the arguments up to the values. enum Doing { DoingNone, DoingNames, DoingProperty, - DoingValues + DoingValues, + DoingSourceDirectory, + DoingSourceTargetDirectory }; Doing doing = DoingNames; const char* sep = ""; @@ -137,8 +271,20 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args, appendMode = true; remove = false; appendAsString = true; + } else if (doing == DoingNames && scope == cmProperty::SOURCE_FILE && + arg == "DIRECTORY") { + doing = DoingSourceDirectory; + source_file_directory_option_enabled = true; + } else if (doing == DoingNames && scope == cmProperty::SOURCE_FILE && + arg == "TARGET_DIRECTORY") { + doing = DoingSourceTargetDirectory; + source_file_target_option_enabled = true; } else if (doing == DoingNames) { names.insert(arg); + } else if (doing == DoingSourceDirectory) { + source_file_directories.push_back(arg); + } else if (doing == DoingSourceTargetDirectory) { + source_file_target_directories.push_back(arg); } else if (doing == DoingProperty) { propertyName = arg; doing = DoingValues; @@ -159,6 +305,18 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args, return false; } + std::vector<cmMakefile*> source_file_directory_makefiles; + 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; + } + bool source_file_paths_should_be_absolute = + source_file_directory_option_enabled || source_file_target_option_enabled; + // Dispatch property setting. switch (scope) { case cmProperty::GLOBAL: @@ -172,7 +330,9 @@ bool cmSetPropertyCommand(std::vector<std::string> const& args, appendAsString, appendMode, remove); case cmProperty::SOURCE_FILE: return HandleSourceMode(status, names, propertyName, propertyValue, - appendAsString, appendMode, remove); + appendAsString, appendMode, remove, + source_file_directory_makefiles, + source_file_paths_should_be_absolute); case cmProperty::TEST: return HandleTestMode(status, names, propertyName, propertyValue, appendAsString, appendMode, remove); @@ -315,21 +475,32 @@ bool HandleSourceMode(cmExecutionStatus& status, const std::set<std::string>& names, const std::string& propertyName, const std::string& propertyValue, bool appendAsString, - bool appendMode, bool remove) + bool appendMode, bool remove, + const std::vector<cmMakefile*>& directory_makefiles, + const bool source_file_paths_should_be_absolute) { - for (std::string const& name : names) { - // Get the source file. - if (cmSourceFile* sf = status.GetMakefile().GetOrCreateSource(name)) { - if (!HandleSource(sf, propertyName, propertyValue, appendAsString, - appendMode, remove)) { + std::vector<std::string> files_absolute; + std::vector<std::string> unique_files(names.begin(), names.end()); + SetPropertyCommand::MakeSourceFilePathsAbsoluteIfNeeded( + status, files_absolute, unique_files.begin(), unique_files.end(), + source_file_paths_should_be_absolute); + + for (const auto mf : directory_makefiles) { + for (std::string const& name : files_absolute) { + // Get the source file. + if (cmSourceFile* sf = mf->GetOrCreateSource(name)) { + if (!HandleSource(sf, propertyName, propertyValue, appendAsString, + appendMode, remove)) { + return false; + } + } else { + status.SetError(cmStrCat( + "given SOURCE name that could not be found or created: ", name)); return false; } - } else { - status.SetError(cmStrCat( - "given SOURCE name that could not be found or created: ", name)); - return false; } } + return true; } diff --git a/Source/cmSetPropertyCommand.h b/Source/cmSetPropertyCommand.h index ec36f84..af566a3 100644 --- a/Source/cmSetPropertyCommand.h +++ b/Source/cmSetPropertyCommand.h @@ -8,9 +8,38 @@ #include <string> #include <vector> +class cmMakefile; class cmExecutionStatus; bool cmSetPropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); +namespace SetPropertyCommand { +bool HandleSourceFileDirectoryScopes( + cmExecutionStatus& status, std::vector<std::string>& source_file_directories, + std::vector<std::string>& source_file_target_directories, + std::vector<cmMakefile*>& directory_makefiles); + +bool HandleSourceFileDirectoryScopeValidation( + cmExecutionStatus& status, bool source_file_directory_option_enabled, + bool source_file_target_option_enabled, + std::vector<std::string>& source_file_directories, + std::vector<std::string>& source_file_target_directories); + +bool HandleAndValidateSourceFileDirectortoryScopes( + cmExecutionStatus& status, bool source_directories_option_encountered, + bool source_target_directories_option_encountered, + std::vector<std::string>& source_directories, + std::vector<std::string>& source_target_directories, + std::vector<cmMakefile*>& source_file_directory_makefiles); + +std::string MakeSourceFilePathAbsoluteIfNeeded( + cmExecutionStatus& status, const std::string& source_file_path, bool needed); +void MakeSourceFilePathsAbsoluteIfNeeded( + cmExecutionStatus& status, + std::vector<std::string>& source_files_absolute_paths, + std::vector<std::string>::const_iterator files_it_begin, + std::vector<std::string>::const_iterator files_it_end, bool needed); +} + #endif diff --git a/Source/cmSetSourceFilesPropertiesCommand.cxx b/Source/cmSetSourceFilesPropertiesCommand.cxx index 7a53a1d..3135a06 100644 --- a/Source/cmSetSourceFilesPropertiesCommand.cxx +++ b/Source/cmSetSourceFilesPropertiesCommand.cxx @@ -10,9 +10,16 @@ #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) { @@ -23,16 +30,86 @@ bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args, // break the arguments into source file names and properties // old style allows for specifier before PROPERTIES keyword - static const cm::string_view propNames[] = { - "ABSTRACT", "GENERATED", "WRAP_EXCLUDE", - "COMPILE_FLAGS", "OBJECT_DEPENDS", "PROPERTIES" + static const cm::string_view prop_names[] = { + "ABSTRACT", "GENERATED", "WRAP_EXCLUDE", "COMPILE_FLAGS", + "OBJECT_DEPENDS", "PROPERTIES", "DIRECTORY", "TARGET_DIRECTORY" }; - auto propsBegin = std::find_first_of( - args.begin(), args.end(), std::begin(propNames), std::end(propNames)); + 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 = propsBegin; j != args.end(); ++j) { + 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); @@ -40,26 +117,26 @@ bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args, } else if (*j == "COMPILE_FLAGS") { propertyPairs.emplace_back("COMPILE_FLAGS"); ++j; - if (j == args.end()) { - status.SetError("called with incorrect number of arguments " - "COMPILE_FLAGS with no flags"); + 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 == args.end()) { - status.SetError("called with incorrect number of arguments " - "OBJECT_DEPENDS with no dependencies"); + 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, args.end() }; + cmStringRange newStyleProps{ j + 1, prop_end }; if (newStyleProps.size() % 2 != 0) { - status.SetError("called with incorrect number of arguments."); + errors = "called with incorrect number of arguments."; return false; } // set newStyleProps as is. @@ -67,16 +144,16 @@ bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args, // break out of the loop. break; } else { - status.SetError("called with illegal arguments, maybe missing a " - "PROPERTIES specifier?"); + errors = "called with illegal arguments, maybe missing a " + "PROPERTIES specifier?"; return false; } } // loop over all the files - for (const std::string& sfname : cmStringRange{ args.begin(), propsBegin }) { + for (const std::string& sfname : cmStringRange{ file_begin, file_end }) { // get the source file - if (cmSourceFile* sf = status.GetMakefile().GetOrCreateSource(sfname)) { + 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()); diff --git a/Tests/Properties/CMakeLists.txt b/Tests/Properties/CMakeLists.txt index a263061..f93f553 100644 --- a/Tests/Properties/CMakeLists.txt +++ b/Tests/Properties/CMakeLists.txt @@ -144,4 +144,143 @@ set_property(CACHE SOME_ENTRY PROPERTY ADVANCED "${expect_ADVANCED}") set_property(CACHE SOME_ENTRY PROPERTY STRINGS "${expect_STRINGS}") check_cache_props() +function(generate_file_for_set_property_test i target_name) + set(src_path "${CMAKE_CURRENT_BINARY_DIR}/src${i}.cpp") + file(GENERATE OUTPUT "${src_path}" CONTENT + "#ifndef def${i}\n\ + #error Expected def${i}\n\ + #endif\n\ + #ifdef _WIN32\n\ + __declspec(dllexport)\n\ + #endif\n\ + void dummy_symbol${i}() {}\n") + target_sources(${target_name} PRIVATE "${src_path}") +endfunction() + +add_library(maindirtest SHARED) add_subdirectory(SubDir2) + +set(src_prefix "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/") + +# Set property + target directory +set_property(SOURCE "${src_prefix}/src1.cpp" + TARGET_DIRECTORY set_prop_lib_1 + PROPERTY COMPILE_DEFINITIONS def1) + +# Append property + target directory +set_property(SOURCE "${src_prefix}/src2.cpp" + TARGET_DIRECTORY set_prop_lib_1 + APPEND PROPERTY COMPILE_DEFINITIONS def2) + +# Set property + relative directory path +set_property(SOURCE "${src_prefix}/src3.cpp" + DIRECTORY SubDir2 + PROPERTY COMPILE_DEFINITIONS def3) + +# Set property + absolute directory path +set_property(SOURCE "${src_prefix}/src4.cpp" + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SubDir2" + PROPERTY COMPILE_DEFINITIONS def4) + +# Append property + relative directory path +set_property(SOURCE "${src_prefix}/src5.cpp" + DIRECTORY SubDir2 + APPEND PROPERTY COMPILE_DEFINITIONS def5) + +# Append property + absolute directory path +set_property(SOURCE "${src_prefix}/src6.cpp" + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SubDir2" + APPEND PROPERTY COMPILE_DEFINITIONS def6) + + +# Target directory +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src10.cpp" + TARGET_DIRECTORY set_prop_lib_1 + PROPERTIES COMPILE_DEFINITIONS def10) + +# Relative directory path +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src11.cpp" + DIRECTORY SubDir2 + PROPERTIES COMPILE_DEFINITIONS def11) + +# Absolute directory path +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src12.cpp" + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SubDir2" + PROPERTIES COMPILE_DEFINITIONS def12) + + +# Multiple files + absolute directory path +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src20.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src21.cpp" + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SubDir2" + PROPERTIES COMPILE_DEFINITIONS "def20;def21") + +# Multiple files + multiple target directories +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src22.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src23.cpp" + TARGET_DIRECTORY set_prop_lib_2 set_prop_lib_3 + PROPERTIES COMPILE_DEFINITIONS "def22;def23") + + +# Multiple files in multiple relative directories +generate_file_for_set_property_test(30 maindirtest) +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/src30.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src31.cpp" + DIRECTORY . SubDir2 + PROPERTIES COMPILE_DEFINITIONS "def30;def31") + +# Check that specifying files without any properties doesn't crash. +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/src30.cpp" + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src31.cpp") + +function(check_get_property_value expected) + if(NOT actual STREQUAL expected) + message(SEND_ERROR "Error: get_property returned unexpected value\n" + "actual: ${actual}\n" + "expected: ${expected}") + endif() +endfunction() + +# Get property + target directory +get_property(actual + SOURCE "${src_prefix}/src1.cpp" + TARGET_DIRECTORY set_prop_lib_1 + PROPERTY COMPILE_DEFINITIONS) +check_get_property_value("def1") + +# Get property + relative directory path +get_property(actual + SOURCE "${src_prefix}/src3.cpp" + DIRECTORY SubDir2 + PROPERTY COMPILE_DEFINITIONS) +check_get_property_value("def3") + +# Get property + absolute directory path +get_property(actual + SOURCE "${src_prefix}/src4.cpp" + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SubDir2" + PROPERTY COMPILE_DEFINITIONS) +check_get_property_value("def4") + + +# Get property + target directory +unset(actual) +get_source_file_property(actual + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src10.cpp" + TARGET_DIRECTORY set_prop_lib_1 + COMPILE_DEFINITIONS) +check_get_property_value("def10") + +# Get property + relative directory path +get_source_file_property(actual + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src11.cpp" + DIRECTORY SubDir2 + COMPILE_DEFINITIONS) +check_get_property_value("def11") + +# Get property + absolute directory path +get_source_file_property(actual + "${CMAKE_CURRENT_BINARY_DIR}/SubDir2/src12.cpp" + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SubDir2" + COMPILE_DEFINITIONS) +check_get_property_value("def12") diff --git a/Tests/Properties/SubDir2/CMakeLists.txt b/Tests/Properties/SubDir2/CMakeLists.txt index 377dc83..9b2c79e 100644 --- a/Tests/Properties/SubDir2/CMakeLists.txt +++ b/Tests/Properties/SubDir2/CMakeLists.txt @@ -3,3 +3,28 @@ set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/../subdirtest.cxx" PROPERTIES COMPILE_DEFINITIONS SUBDIR_TEST) add_executable(subdirtest "${CMAKE_CURRENT_SOURCE_DIR}/../subdirtest.cxx") + +# For set_property +add_library(set_prop_lib_1 SHARED) +foreach(i RANGE 1 6) + generate_file_for_set_property_test(${i} set_prop_lib_1) +endforeach() + +# For set_source_files_properties +foreach(i RANGE 10 12) + generate_file_for_set_property_test(${i} set_prop_lib_1) +endforeach() + +# For set_source_files_properties + multiple files + absolute directory path +add_library(set_prop_lib_2 SHARED) +foreach(i RANGE 20 21) + generate_file_for_set_property_test(${i} set_prop_lib_1) +endforeach() + +# For set_source_files_properties + multiple files + multiple target directories +add_library(set_prop_lib_3 SHARED) +generate_file_for_set_property_test(22 set_prop_lib_2) +generate_file_for_set_property_test(23 set_prop_lib_3) + +# For set_source_files_properties + multiple files in multiple directories +generate_file_for_set_property_test(31 set_prop_lib_3) diff --git a/Tests/RunCMake/get_property/RunCMakeTest.cmake b/Tests/RunCMake/get_property/RunCMakeTest.cmake index 6e36473..c4ee53d 100644 --- a/Tests/RunCMake/get_property/RunCMakeTest.cmake +++ b/Tests/RunCMake/get_property/RunCMakeTest.cmake @@ -5,6 +5,7 @@ run_cmake(directory_properties) run_cmake(global_properties) run_cmake(install_properties) run_cmake(source_properties) +run_cmake(source_properties_failures) run_cmake(target_properties) run_cmake(test_properties) run_cmake(DebugConfigurations) diff --git a/Tests/RunCMake/get_property/source_properties_failures-result.txt b/Tests/RunCMake/get_property/source_properties_failures-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/get_property/source_properties_failures-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/get_property/source_properties_failures-stderr.txt b/Tests/RunCMake/get_property/source_properties_failures-stderr.txt new file mode 100644 index 0000000..a500e2e --- /dev/null +++ b/Tests/RunCMake/get_property/source_properties_failures-stderr.txt @@ -0,0 +1,66 @@ +^CMake Error at source_properties_failures.cmake:1 \(set_source_files_properties\): + set_source_files_properties called with incorrect number of arguments no + value provided to the DIRECTORY option +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:2 \(set_source_files_properties\): + set_source_files_properties given non-existent DIRECTORY non_existing_dir +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:3 \(set_source_files_properties\): + set_source_files_properties called with incorrect number of arguments no + value provided to the TARGET_DIRECTORY option +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:4 \(set_source_files_properties\): + set_source_files_properties given non-existent target for DIRECTORY_TARGET + non_existing_target +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:6 \(get_property\): + get_property called with incorrect number of arguments no value provided to + the DIRECTORY option +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:7 \(get_property\): + get_property given non-existent DIRECTORY non_existing_dir +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:8 \(get_property\): + get_property called with incorrect number of arguments no value provided to + the TARGET_DIRECTORY option +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:9 \(get_property\): + get_property given non-existent target for DIRECTORY_TARGET + non_existing_dir +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:11 \(get_source_file_property\): + get_source_file_property given non-existent DIRECTORY PROPERTY +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:12 \(get_source_file_property\): + get_source_file_property called with incorrect number of arguments +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:13 \(get_source_file_property\): + get_source_file_property given non-existent target for DIRECTORY_TARGET + PROPERTY +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at source_properties_failures.cmake:14 \(get_source_file_property\): + get_source_file_property called with incorrect number of arguments +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/get_property/source_properties_failures.cmake b/Tests/RunCMake/get_property/source_properties_failures.cmake new file mode 100644 index 0000000..f4b87f9 --- /dev/null +++ b/Tests/RunCMake/get_property/source_properties_failures.cmake @@ -0,0 +1,14 @@ +set_source_files_properties(a.txt DIRECTORY PROPERTIES COMPILE_DEFINITIONS "def") +set_source_files_properties(a.txt DIRECTORY non_existing_dir PROPERTIES COMPILE_DEFINITIONS "def") +set_source_files_properties(a.txt TARGET_DIRECTORY PROPERTIES COMPILE_DEFINITIONS "def") +set_source_files_properties(a.txt TARGET_DIRECTORY non_existing_target PROPERTIES COMPILE_DEFINITIONS "def") + +get_property(in_var SOURCE a.txt DIRECTORY PROPERTY COMPILE_DEFINITIONS) +get_property(in_var SOURCE a.txt DIRECTORY non_existing_dir PROPERTY COMPILE_DEFINITIONS) +get_property(in_var SOURCE a.txt TARGET_DIRECTORY PROPERTY COMPILE_DEFINITIONS) +get_property(in_var SOURCE a.txt TARGET_DIRECTORY non_existing_dir PROPERTY COMPILE_DEFINITIONS) + +get_source_file_property(in_var a.txt DIRECTORY PROPERTY COMPILE_DEFINITIONS) +get_source_file_property(in_var a.txt DIRECTORY non_existing_dir PROPERTY COMPILE_DEFINITIONS) +get_source_file_property(in_var a.txt TARGET_DIRECTORY PROPERTY COMPILE_DEFINITIONS) +get_source_file_property(in_var a.txt TARGET_DIRECTORY non_existing_dir PROPERTY COMPILE_DEFINITIONS) diff --git a/Tests/RunCMake/set_property/RunCMakeTest.cmake b/Tests/RunCMake/set_property/RunCMakeTest.cmake index 8d4614c..692c6b9 100644 --- a/Tests/RunCMake/set_property/RunCMakeTest.cmake +++ b/Tests/RunCMake/set_property/RunCMakeTest.cmake @@ -9,6 +9,7 @@ run_cmake(LINK_OPTIONS) run_cmake(LINK_DIRECTORIES) run_cmake(LINK_LIBRARIES) run_cmake(SOURCES) +run_cmake(SOURCE_FILE) run_cmake(TYPE) run_cmake(USER_PROP) run_cmake(USER_PROP_INHERITED) diff --git a/Tests/RunCMake/set_property/SOURCE_FILE-result.txt b/Tests/RunCMake/set_property/SOURCE_FILE-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/set_property/SOURCE_FILE-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/set_property/SOURCE_FILE-stderr.txt b/Tests/RunCMake/set_property/SOURCE_FILE-stderr.txt new file mode 100644 index 0000000..2e0b238 --- /dev/null +++ b/Tests/RunCMake/set_property/SOURCE_FILE-stderr.txt @@ -0,0 +1,22 @@ +^CMake Error at SOURCE_FILE.cmake:1 \(set_property\): + set_property called with incorrect number of arguments no value provided to + the DIRECTORY option +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at SOURCE_FILE.cmake:2 \(set_property\): + set_property given non-existent DIRECTORY non_existing_dir +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at SOURCE_FILE.cmake:3 \(set_property\): + set_property called with incorrect number of arguments no value provided to + the TARGET_DIRECTORY option +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\) ++ +CMake Error at SOURCE_FILE.cmake:4 \(set_property\): + set_property given non-existent target for DIRECTORY_TARGET + non_existing_target +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/set_property/SOURCE_FILE.cmake b/Tests/RunCMake/set_property/SOURCE_FILE.cmake new file mode 100644 index 0000000..b1d78bd --- /dev/null +++ b/Tests/RunCMake/set_property/SOURCE_FILE.cmake @@ -0,0 +1,4 @@ +set_property(SOURCE a.txt DIRECTORY PROPERTY COMPILE_DEFINITIONS "def") +set_property(SOURCE a.txt DIRECTORY non_existing_dir PROPERTY COMPILE_DEFINITIONS "def") +set_property(SOURCE a.txt TARGET_DIRECTORY PROPERTY COMPILE_DEFINITIONS "def") +set_property(SOURCE a.txt TARGET_DIRECTORY non_existing_target PROPERTY COMPILE_DEFINITIONS "def") |