From b42330be21c0d3046aa0e6fdf046a492bddef520 Mon Sep 17 00:00:00 2001 From: Mateusz Janek Date: Wed, 21 Dec 2016 15:27:49 +0100 Subject: source_group: Add options create groups matching directory tree Add `TREE` and `PREFIX` arguments to enable this behavior. --- Help/command/source_group.rst | 18 ++- Help/release/dev/source_group-tree.rst | 5 + Source/cmSourceGroupCommand.cxx | 165 ++++++++++++++++++++++ Source/cmSourceGroupCommand.h | 6 + Tests/SourceGroups/CMakeLists.txt | 13 +- Tests/SourceGroups/main.c | 7 + Tests/SourceGroups/sub1/tree_bar.c | 4 + Tests/SourceGroups/sub1/tree_baz.c | 4 + Tests/SourceGroups/sub1/tree_subdir/tree_foobar.c | 4 + Tests/SourceGroups/tree_foo.c | 4 + 10 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 Help/release/dev/source_group-tree.rst create mode 100644 Tests/SourceGroups/sub1/tree_bar.c create mode 100644 Tests/SourceGroups/sub1/tree_baz.c create mode 100644 Tests/SourceGroups/sub1/tree_subdir/tree_foobar.c create mode 100644 Tests/SourceGroups/tree_foo.c diff --git a/Help/command/source_group.rst b/Help/command/source_group.rst index 6e3829c..938ca40 100644 --- a/Help/command/source_group.rst +++ b/Help/command/source_group.rst @@ -2,15 +2,27 @@ source_group ------------ Define a grouping for source files in IDE project generation. +There are two different signatures to create source groups. -.. code-block:: cmake +:: source_group( [FILES ...] [REGULAR_EXPRESSION ]) + source_group(TREE [PREFIX ] [FILES ...]) Defines a group into which sources will be placed in project files. This is intended to set up file tabs in Visual Studio. The options are: +``TREE`` + CMake will automatically detect, from ```` files paths, source groups + it needs to create, to keep structure of source groups analogically to the + actual files and directories structure in the project. Paths of ```` + files will be cut to be relative to ````. + +``PREFIX`` + Source group and files located directly in ```` path, will be placed + in ```` source groups. + ``FILES`` Any source file specified explicitly will be placed in group ````. Relative paths are interpreted with respect to the @@ -25,11 +37,13 @@ explicitly lists the file with ``FILES`` will be favored, if any. If no group explicitly lists the file, the *last* group whose regular expression matches the file will be favored. -The ```` of the group may contain backslashes to specify subgroups: +The ```` of the group and ```` argument may contain backslashes +to specify subgroups: .. code-block:: cmake source_group(outer\\inner ...) + source_group(TREE PREFIX sources\\inc ...) For backwards compatibility, the short-hand signature diff --git a/Help/release/dev/source_group-tree.rst b/Help/release/dev/source_group-tree.rst new file mode 100644 index 0000000..c5fec1d --- /dev/null +++ b/Help/release/dev/source_group-tree.rst @@ -0,0 +1,5 @@ +source_group-tree +----------------- + +* The :command:`source_group` command gained ``TREE`` and ``PREFIX`` + options to add groups following source tree directory structure. diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx index 3f20d4e..5555199 100644 --- a/Source/cmSourceGroupCommand.cxx +++ b/Source/cmSourceGroupCommand.cxx @@ -8,6 +8,99 @@ #include "cmSourceGroup.h" #include "cmSystemTools.h" +namespace { +const size_t RootIndex = 1; +const size_t FilesWithoutPrefixKeywordIndex = 2; +const size_t FilesWithPrefixKeywordIndex = 4; +const size_t PrefixKeywordIdex = 2; + +std::vector tokenizePath(const std::string& path) +{ + return cmSystemTools::tokenize(path, "\\/"); +} + +std::string getFullFilePath(const std::string& currentPath, + const std::string& path) +{ + std::string fullPath = path; + + if (!cmSystemTools::FileIsFullPath(path.c_str())) { + fullPath = currentPath; + fullPath += "/"; + fullPath += path; + } + + return cmSystemTools::CollapseFullPath(fullPath); +} + +std::set getSourceGroupFilesPaths( + const std::string& currentPath, const std::string& root, + const std::vector& files) +{ + std::set ret; + const std::string::size_type rootLength = root.length(); + + for (size_t i = 0; i < files.size(); ++i) { + const std::string fullPath = getFullFilePath(currentPath, files[i]); + + ret.insert(fullPath.substr(rootLength + 1)); // +1 to also omnit last '/' + } + + return ret; +} + +cmSourceGroup* addSourceGroup(const std::vector& tokenizedPath, + cmMakefile& makefile) +{ + cmSourceGroup* sg; + + sg = makefile.GetSourceGroup(tokenizedPath); + if (!sg) { + makefile.AddSourceGroup(tokenizedPath); + sg = makefile.GetSourceGroup(tokenizedPath); + if (!sg) { + return CM_NULLPTR; + } + } + + return sg; +} + +bool addFilesToItsSourceGroups(const std::set& sgFilesPaths, + const std::string& prefix, cmMakefile& makefile, + std::string& errorMsg) +{ + cmSourceGroup* sg; + + for (std::set::const_iterator it = sgFilesPaths.begin(); + it != sgFilesPaths.end(); ++it) { + + std::vector tokenizedPath; + if (!prefix.empty()) { + tokenizedPath = tokenizePath(prefix + '/' + *it); + } else { + tokenizedPath = tokenizePath(*it); + } + + if (tokenizedPath.size() > 1) { + tokenizedPath.pop_back(); + + sg = addSourceGroup(tokenizedPath, makefile); + + if (!sg) { + errorMsg = "Could not create source group for file: " + *it; + return false; + } + const std::string fullPath = + getFullFilePath(makefile.GetCurrentSourceDirectory(), *it); + sg->AddGroupFile(fullPath); + } + } + + return true; +} +} + class cmExecutionStatus; // cmSourceGroupCommand @@ -19,6 +112,17 @@ bool cmSourceGroupCommand::InitialPass(std::vector const& args, return false; } + if (args[0] == "TREE") { + std::string error; + + if (!processTree(args, error)) { + this->SetError(error); + return false; + } + + return true; + } + std::string delimiter = "\\"; if (this->Makefile->GetDefinition("SOURCE_GROUP_DELIMITER")) { delimiter = this->Makefile->GetDefinition("SOURCE_GROUP_DELIMITER"); @@ -82,3 +186,64 @@ bool cmSourceGroupCommand::InitialPass(std::vector const& args, return true; } + +bool cmSourceGroupCommand::checkTreeArgumentsPreconditions( + const std::vector& args, std::string& errorMsg) const +{ + if (args.size() == 1) { + errorMsg = "TREE argument given without a root."; + return false; + } + + if (args.size() < 3) { + errorMsg = "Missing FILES arguments."; + return false; + } + + if (args[FilesWithoutPrefixKeywordIndex] != "FILES" && + args[PrefixKeywordIdex] != "PREFIX") { + errorMsg = "Unknown argument \"" + args[2] + + "\". Perhaps the FILES keyword is missing.\n"; + return false; + } + + if (args[PrefixKeywordIdex] == "PREFIX" && + (args.size() < 5 || args[FilesWithPrefixKeywordIndex] != "FILES")) { + errorMsg = "Missing FILES arguments."; + return false; + } + + return true; +} + +bool cmSourceGroupCommand::processTree(const std::vector& args, + std::string& errorMsg) +{ + if (!checkTreeArgumentsPreconditions(args, errorMsg)) { + return false; + } + + const std::string root = cmSystemTools::CollapseFullPath(args[RootIndex]); + std::string prefix; + size_t filesBegin = FilesWithoutPrefixKeywordIndex + 1; + if (args[PrefixKeywordIdex] == "PREFIX") { + prefix = args[PrefixKeywordIdex + 1]; + filesBegin = FilesWithPrefixKeywordIndex + 1; + } + + const std::vector filesVector(args.begin() + filesBegin, + args.end()); + + std::set sourceGroupPaths = getSourceGroupFilesPaths( + this->Makefile->GetCurrentSourceDirectory(), root, filesVector); + + addFilesToItsSourceGroups(sourceGroupPaths, prefix, *(this->Makefile), + errorMsg); + + if (!errorMsg.empty()) { + this->SetError(errorMsg); + return false; + } + + return true; +} diff --git a/Source/cmSourceGroupCommand.h b/Source/cmSourceGroupCommand.h index f533be1..5549096 100644 --- a/Source/cmSourceGroupCommand.h +++ b/Source/cmSourceGroupCommand.h @@ -36,6 +36,12 @@ public: * The name of the command as specified in CMakeList.txt. */ std::string GetName() const CM_OVERRIDE { return "source_group"; } + +private: + bool processTree(const std::vector& args, + std::string& errorMsg); + bool checkTreeArgumentsPreconditions(const std::vector& args, + std::string& errorMsg) const; }; #endif diff --git a/Tests/SourceGroups/CMakeLists.txt b/Tests/SourceGroups/CMakeLists.txt index 6573c82..9289e84 100644 --- a/Tests/SourceGroups/CMakeLists.txt +++ b/Tests/SourceGroups/CMakeLists.txt @@ -30,6 +30,17 @@ source_group(Base\\Sub1\\Base FILES bar.c) # a group without files, is currently not created source_group(EmptyGroup) +set(root ${CMAKE_CURRENT_SOURCE_DIR}) -add_executable(SourceGroups main.c bar.c foo.c sub1/foo.c sub1/foobar.c baz.c README.txt) +set(tree_files_without_prefix ${root}/sub1/tree_bar.c + ${root}/sub1/tree_baz.c + ${root}/sub1/tree_subdir/tree_foobar.c) +set(tree_files_with_prefix ${root}/tree_foo.c) + +source_group(TREE ${root} FILES ${tree_files_without_prefix}) + +source_group(TREE ${root} PREFIX tree_root/subgroup FILES ${tree_files_with_prefix}) + +add_executable(SourceGroups main.c bar.c foo.c sub1/foo.c sub1/foobar.c baz.c + ${tree_files_with_prefix} ${tree_files_without_prefix} README.txt) diff --git a/Tests/SourceGroups/main.c b/Tests/SourceGroups/main.c index f259f98..b88f2f8 100644 --- a/Tests/SourceGroups/main.c +++ b/Tests/SourceGroups/main.c @@ -5,10 +5,17 @@ extern int bar(void); extern int foobar(void); extern int barbar(void); extern int baz(void); +extern int tree_foo(void); +extern int tree_bar(void); +extern int tree_foobar(void); +extern int tree_baz(void); int main() { printf("foo: %d bar: %d foobar: %d barbar: %d baz: %d\n", foo(), bar(), foobar(), barbar(), baz()); + + printf("tree_foo: %d tree_bar: %d tree_foobar: %d tree_baz: %d\n", + tree_foo(), tree_bar(), tree_foobar(), tree_baz()); return 0; } diff --git a/Tests/SourceGroups/sub1/tree_bar.c b/Tests/SourceGroups/sub1/tree_bar.c new file mode 100644 index 0000000..6b79239 --- /dev/null +++ b/Tests/SourceGroups/sub1/tree_bar.c @@ -0,0 +1,4 @@ +int tree_bar(void) +{ + return 8; +} diff --git a/Tests/SourceGroups/sub1/tree_baz.c b/Tests/SourceGroups/sub1/tree_baz.c new file mode 100644 index 0000000..27ff5ab --- /dev/null +++ b/Tests/SourceGroups/sub1/tree_baz.c @@ -0,0 +1,4 @@ +int tree_baz(void) +{ + return 9; +} diff --git a/Tests/SourceGroups/sub1/tree_subdir/tree_foobar.c b/Tests/SourceGroups/sub1/tree_subdir/tree_foobar.c new file mode 100644 index 0000000..e955e04 --- /dev/null +++ b/Tests/SourceGroups/sub1/tree_subdir/tree_foobar.c @@ -0,0 +1,4 @@ +int tree_foobar(void) +{ + return 7; +} diff --git a/Tests/SourceGroups/tree_foo.c b/Tests/SourceGroups/tree_foo.c new file mode 100644 index 0000000..d392e41 --- /dev/null +++ b/Tests/SourceGroups/tree_foo.c @@ -0,0 +1,4 @@ +int tree_foo(void) +{ + return 6; +} -- cgit v0.12