summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Janek <stryku2393@gmail.com>2016-12-21 15:27:49 +0100
committerBrad King <brad.king@kitware.com>2017-01-16 10:52:48 -0500
commitb42330be21c0d3046aa0e6fdf046a492bddef520 (patch)
treeefd6fbbda9401a36000f2c3b265b9d43310348e6
parent6154a2cddcdb297b1aef20f501fec9af7d7b5262 (diff)
downloadcmake-b42330be21c0d3046aa0e6fdf046a492bddef520.tar.gz
source_group: Add options create groups matching directory tree
Add `TREE` and `PREFIX` arguments to enable this behavior.
-rw-r--r--Help/command/source_group.rst18
-rw-r--r--Help/release/dev/source_group-tree.rst5
-rw-r--r--Source/cmSourceGroupCommand.cxx165
-rw-r--r--Source/cmSourceGroupCommand.h6
-rw-r--r--Tests/SourceGroups/CMakeLists.txt13
-rw-r--r--Tests/SourceGroups/main.c7
-rw-r--r--Tests/SourceGroups/sub1/tree_bar.c4
-rw-r--r--Tests/SourceGroups/sub1/tree_baz.c4
-rw-r--r--Tests/SourceGroups/sub1/tree_subdir/tree_foobar.c4
-rw-r--r--Tests/SourceGroups/tree_foo.c4
10 files changed, 227 insertions, 3 deletions
diff --git a/Help/command/source_group.rst b/Help/command/source_group.rst
index 6e3829c044..938ca40f2b 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(<name> [FILES <src>...] [REGULAR_EXPRESSION <regex>])
+ source_group(TREE <root> [PREFIX <prefix>] [FILES <src>...])
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 ``<src>`` 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 ``<src>``
+ files will be cut to be relative to ``<root>``.
+
+``PREFIX``
+ Source group and files located directly in ``<root>`` path, will be placed
+ in ``<prefix>`` source groups.
+
``FILES``
Any source file specified explicitly will be placed in group
``<name>``. 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 ``<name>`` of the group may contain backslashes to specify subgroups:
+The ``<name>`` of the group and ``<prefix>`` argument may contain backslashes
+to specify subgroups:
.. code-block:: cmake
source_group(outer\\inner ...)
+ source_group(TREE <root> 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 0000000000..c5fec1dc00
--- /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 3f20d4e056..5555199088 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<std::string> 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<std::string> getSourceGroupFilesPaths(
+ const std::string& currentPath, const std::string& root,
+ const std::vector<std::string>& files)
+{
+ std::set<std::string> 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<std::string>& 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<std::string>& sgFilesPaths,
+ const std::string& prefix, cmMakefile& makefile,
+ std::string& errorMsg)
+{
+ cmSourceGroup* sg;
+
+ for (std::set<std::string>::const_iterator it = sgFilesPaths.begin();
+ it != sgFilesPaths.end(); ++it) {
+
+ std::vector<std::string> 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<std::string> 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<std::string> const& args,
return true;
}
+
+bool cmSourceGroupCommand::checkTreeArgumentsPreconditions(
+ const std::vector<std::string>& 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<std::string>& 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<std::string> filesVector(args.begin() + filesBegin,
+ args.end());
+
+ std::set<std::string> 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 f533be15ca..55490963e5 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<std::string>& args,
+ std::string& errorMsg);
+ bool checkTreeArgumentsPreconditions(const std::vector<std::string>& args,
+ std::string& errorMsg) const;
};
#endif
diff --git a/Tests/SourceGroups/CMakeLists.txt b/Tests/SourceGroups/CMakeLists.txt
index 6573c8241b..9289e8444e 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 f259f982f4..b88f2f8b3b 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 0000000000..6b79239bb8
--- /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 0000000000..27ff5ab942
--- /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 0000000000..e955e04ea2
--- /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 0000000000..d392e41fd3
--- /dev/null
+++ b/Tests/SourceGroups/tree_foo.c
@@ -0,0 +1,4 @@
+int tree_foo(void)
+{
+ return 6;
+}