From 6816cd62ca112103a604b76b39939c16b5df0ee4 Mon Sep 17 00:00:00 2001
From: Tomas Pecka <peckato1@fit.cvut.cz>
Date: Sat, 24 Nov 2018 15:03:32 +0100
Subject: [PATCH] CMake generator: Project groups

---
 CMake/generate.conf   |  4 +++
 CMake/generate.py     | 72 ++++++++++++++++++++++++++++++-------------
 agui2/project.conf    |  1 +
 alib2gui/project.conf |  1 +
 aql2/project.conf     |  1 +
 5 files changed, 57 insertions(+), 22 deletions(-)

diff --git a/CMake/generate.conf b/CMake/generate.conf
index 677c346e93..a45d50ea69 100644
--- a/CMake/generate.conf
+++ b/CMake/generate.conf
@@ -8,6 +8,10 @@ patch = 1
 ProjectConfFile: project.conf
 PathToSources: ..
 
+[Groups]
+cli
+gui
+
 [CMake:Categories]
 ; projects and their templates
 ; project must belong in one of these categories
diff --git a/CMake/generate.py b/CMake/generate.py
index 8da61bfb02..2d7698a467 100755
--- a/CMake/generate.py
+++ b/CMake/generate.py
@@ -4,6 +4,7 @@ import os
 import sys
 import configparser
 import argparse
+import itertools
 
 
 # ----------------------------------------------------------------------------------------------------------------------
@@ -98,6 +99,10 @@ class Project:
         self._config = configparser.ConfigParser(allow_no_value=True)
         self._config.read(Helpers.path(self.path, conf))
 
+    @property
+    def groups(self):
+        return Config.get_array(self._config, 'General', 'groups', fallback=[])
+
     @property
     def category(self):
         ctg = Config.get(self._config, 'General', 'category')
@@ -177,6 +182,8 @@ class Project:
                     stack.append(dep)
         return visited
 
+    def __repr__(self):
+        return '<Project: {}>'.format(self.name)
 
 # ----------------------------------------------------------------------------------------------------------------------
 
@@ -270,6 +277,9 @@ PROJECTS = {
     project: Project(project, Config.get(CONFIG, 'General', 'ProjectConfFile'))
     for project in Helpers.find_dirs_with_file(ALIB_DIRECTORY, Config.get(CONFIG, 'General', 'ProjectConfFile'))
 }
+GROUPS = {
+    group: [p for n, p in PROJECTS.items() if group in p.groups] for group in Config.get_section(CONFIG, 'Groups').keys()
+}
 CATEGORIES = {
     category: SmartOpen(template, directory=SCRIPT_DIRECTORY, mode='r').read()
     for category, template in Config.get_section(CONFIG, 'CMake:Categories').items()
@@ -278,29 +288,41 @@ CATEGORIES = {
 
 # ----------------------------------------------------------------------------------------------------------------------
 
+def compute_packages(packages, groups, no_dependencies, verbose):
+    # filter out invalid
+    for g in groups:
+        if g not in GROUPS:
+            print("Warning: Package group {} is not a valid group. Skipping".format(g), file=sys.stderr)
+
+    for p in packages:
+        if p not in PROJECTS:
+            print("Warning: Package {} is not a valid project. Skipping".format(p), file=sys.stderr)
+
+    packages = [p for p in packages if p in PROJECTS]
+    groups = [g for g in groups if g in GROUPS]
+    packages = [PROJECTS[p] for p in packages] + list(itertools.chain.from_iterable([v for k, v in GROUPS.items() if k in groups]))
+
+    res = set()
+
+    # no packages specified, i.e. return all
+    if len(packages) is 0:
+        return PROJECTS.values()
 
-def main(dry_run, main_file, packages_requested, no_dependencies, only_show_dependencies):
-    if packages_requested is None:
-        packages = PROJECTS.values()
-    else:
-        # Filter out invalid packages and possibly compute dependencies
-        packages = set()
-        for p in packages_requested:
-            if p not in PROJECTS:
-                print("Warning: Package {} is not a valid project. Skipping".format(p), file=sys.stderr)
-                continue
+    # possibly compute dependencies
+    for p in packages:
+        if verbose:
+            p_dependencies_str = map(lambda s: "'{}'".format(s), PROJECTS[p].dependencies)
+            print("Project '{}' depends on: {}".format(p, Helpers.join(p_dependencies_str, ', ')), file=sys.stderr)
 
-            if only_show_dependencies:
-                pdeps = map(lambda s: "'{}'".format(s), PROJECTS[p].dependencies)
-                print("Project '{}' depends on: {}".format(p, Helpers.join(pdeps, ', ')), file=sys.stderr)
+        if no_dependencies:
+            res.add(p)
+        else:
+            res |= {PROJECTS[d] for d in p.recursive_dependencies()}
+    return res
 
-            if no_dependencies:
-                packages.add(p)
-            else:
-                for dep in PROJECTS[p].recursive_dependencies():
-                    packages.add(dep)
 
-        packages = [PROJECTS[p] for p in set(packages)]
+def main(dry_run, main_file, packages_requested, groups_requested, no_dependencies, only_show_dependencies):
+    packages = compute_packages(packages_requested, groups_requested, no_dependencies, only_show_dependencies)
 
     print("The following packages will be generated:", file=sys.stderr)
     print(", ".join(sorted([p.name for p in packages])), file=sys.stderr)
@@ -333,9 +355,10 @@ if __name__ == '__main__':
     parser = argparse.ArgumentParser(description='CMakeLists generator for Algorithms Library Toolkit')
     parser.add_argument('-w', '--write', action='store_true', default=False, help="Write files")
     parser.add_argument('-m', '--main', action='store_true', default=False, help="Generate main CMakeLists.txt")
-    parser.add_argument('-p', '--package', action='append', help='Specify packages to build. For multiple packages, use -p multiple times')
+    parser.add_argument('-p', '--package', action='append', help='Specify packages to build. For multiple packages, use -p multiple times. Can be combined with -g')
+    parser.add_argument('-g', '--group', action='append', help='Specify package group to build. For multiple groups, use -g multiple times. Can be combined with -p')
     parser.add_argument('--no-deps', action='store_true', default=False, help="Don't generate dependencies")
-    parser.add_argument('--show-deps', action='store_true', default=False, help='Only show dependencies of selected packages')
+    parser.add_argument('--show-deps', action='store_true', default=False, help='Only show dependencies of selected packages or groups')
     parser.add_argument('-v', '--version', action='store_true', default=False, help="Show current version")
 
     args = parser.parse_args()
@@ -346,4 +369,9 @@ if __name__ == '__main__':
 	        Config.get(CONFIG, 'Versioning', 'patch')))
         sys.exit(0)
 
-    sys.exit(main(not args.write, args.main, args.package, args.no_deps, args.show_deps))
+    sys.exit(main(not args.write,
+                  args.main,
+                  args.package if args.package else [],
+                  args.group if args.group else [],
+                  args.no_deps,
+                  args.show_deps))
diff --git a/agui2/project.conf b/agui2/project.conf
index 9b9617c589..dbaa4230b6 100644
--- a/agui2/project.conf
+++ b/agui2/project.conf
@@ -1,5 +1,6 @@
 [General]
 category: executable
+groups: gui
 
 [Dependencies]
 project: alib2algo alib2aux alib2data alib2str alib2gui
diff --git a/alib2gui/project.conf b/alib2gui/project.conf
index 54ccc707e3..dfd77298f9 100644
--- a/alib2gui/project.conf
+++ b/alib2gui/project.conf
@@ -1,5 +1,6 @@
 [General]
 category: library
+groups: gui
 
 [Dependencies]
 project: alib2str alib2xml alib2common alib2abstraction alib2measure alib2std
diff --git a/aql2/project.conf b/aql2/project.conf
index fe8b512e0a..2207bc5e0d 100644
--- a/aql2/project.conf
+++ b/aql2/project.conf
@@ -1,5 +1,6 @@
 [General]
 category: executable
+groups: cli
 
 [Dependencies]
 project: alib2elgo alib2graph_algo alib2algo alib2aux alib2raw_cli_integration alib2raw alib2str_cli_integration alib2str alib2graph_data alib2data alib2dummy alib2cli alib2xml alib2common alib2abstraction alib2measure alib2std
-- 
GitLab