source_group: Add options create groups matching directory tree

Add `TREE` and `PREFIX` arguments to enable this behavior.
This commit is contained in:
Mateusz Janek 2016-12-21 15:27:49 +01:00 committed by Brad King
parent 6154a2cddc
commit b42330be21
10 changed files with 227 additions and 3 deletions

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
int tree_bar(void)
{
return 8;
}

View File

@ -0,0 +1,4 @@
int tree_baz(void)
{
return 9;
}

View File

@ -0,0 +1,4 @@
int tree_foobar(void)
{
return 7;
}

View File

@ -0,0 +1,4 @@
int tree_foo(void)
{
return 6;
}