From b590f8a9b1ddb5f07215d92b97f20fb474fdf4a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Uhl=C3=ADk?= <jan@uhlik.me>
Date: Thu, 29 Mar 2018 22:08:17 +0200
Subject: [PATCH] add CMake building option by Jan Uhlik

---
 .gitignore                      |   4 +
 .gitlab-ci.yml                  |  13 ++
 CMake/CMakeLists_executable.txt |  23 +++
 CMake/CMakeLists_library.txt    |  50 +++++++
 CMake/CMakeLists_root.txt       |  92 ++++++++++++
 CMake/Findcppunit.cmake         |  75 ++++++++++
 CMake/Findreadline.cmake        |  47 ++++++
 CMake/Findtclap.cmake           |  11 ++
 CMake/LICENSE                   |  21 +++
 CMake/NOTES.md                  |   4 +
 CMake/README.md                 |  10 ++
 CMake/__init__.py               |   0
 CMake/__main__.py               |   3 +
 CMake/alib_cmake.py             | 245 ++++++++++++++++++++++++++++++++
 CMake/requirements.txt          |   1 +
 cmake-debug.sh                  |  21 +++
 16 files changed, 620 insertions(+)
 create mode 100644 CMake/CMakeLists_executable.txt
 create mode 100644 CMake/CMakeLists_library.txt
 create mode 100644 CMake/CMakeLists_root.txt
 create mode 100644 CMake/Findcppunit.cmake
 create mode 100644 CMake/Findreadline.cmake
 create mode 100644 CMake/Findtclap.cmake
 create mode 100644 CMake/LICENSE
 create mode 100644 CMake/NOTES.md
 create mode 100644 CMake/README.md
 create mode 100644 CMake/__init__.py
 create mode 100644 CMake/__main__.py
 create mode 100755 CMake/alib_cmake.py
 create mode 100644 CMake/requirements.txt
 create mode 100755 cmake-debug.sh

diff --git a/.gitignore b/.gitignore
index aa8e34b614..a190b9e440 100644
--- a/.gitignore
+++ b/.gitignore
@@ -95,6 +95,10 @@ docs/userGuide/res/*
 # CMake
 cmake-build-debug/
 
+# CMakeList.txt files are generated
+**/CMakeLists.txt
+CMakeLists.txt
+
 # Mongo Explorer plugin:
 .idea/**/mongoSettings.xml
 
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 64bde69775..76ba0abc5c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,6 +47,19 @@ build-gcc:
 build-clang:
   <<: *build
 
+build-cmake:
+  variables:
+    EXTRA_PKGS: cmake python3 py3-click
+  script:
+    - cd CMake
+    - ./alib_cmake.py -w -m
+    - cd ..
+    - mkdir release
+    - cd release
+    - cmake -DCMAKE_BUILD_TYPE=Release ..
+    - make
+  stage: build
+
 build-doc:
   variables:
     EXTRA_PKGS: doxygen graphviz
diff --git a/CMake/CMakeLists_executable.txt b/CMake/CMakeLists_executable.txt
new file mode 100644
index 0000000000..29ee106644
--- /dev/null
+++ b/CMake/CMakeLists_executable.txt
@@ -0,0 +1,23 @@
+project({project_name})
+
+set(PROJECT_NAME {project_name})
+
+set(SOURCE_FILES
+{source_files}        )
+
+# Add executable target
+add_executable(${{PROJECT_NAME}} ${{SOURCE_FILES}})
+
+# Target link libraries
+target_link_libraries(${{PROJECT_NAME}} {target_test_libs} {target_libs})
+
+set_target_properties(${{PROJECT_NAME}} PROPERTIES
+        CXX_STANDARD 14
+        CXX_STANDARD_REQUIRED ON
+        LINKER_LANGUAGE CXX
+
+        INTERFACE_POSITION_INDEPENDENT_CODE ON
+        )
+
+# Install
+install(TARGETS ${{PROJECT_NAME}} RUNTIME DESTINATION bin)
diff --git a/CMake/CMakeLists_library.txt b/CMake/CMakeLists_library.txt
new file mode 100644
index 0000000000..702d7469d7
--- /dev/null
+++ b/CMake/CMakeLists_library.txt
@@ -0,0 +1,50 @@
+project({project_name})
+
+set(PROJECT_NAME {project_name})
+
+set(SOURCE_FILES
+{source_files}        )
+
+# Add library target
+add_library(${{PROJECT_NAME}} SHARED ${{SOURCE_FILES}})
+
+# Target link libraries
+target_link_libraries(${{PROJECT_NAME}} LINK_PUBLIC {target_libs})
+
+# Include dependencies directories
+target_include_directories(${{PROJECT_NAME}}
+        PUBLIC ${{CMAKE_CURRENT_SOURCE_DIR}}/src # anything that depends on this should include src also, hence public
+        {xml_dep} # place for XML dependency
+        )
+
+set_target_properties(${{PROJECT_NAME}} PROPERTIES
+        CXX_STANDARD 14
+        CXX_STANDARD_REQUIRED ON
+        LINKER_LANGUAGE CXX
+
+        INTERFACE_POSITION_INDEPENDENT_CODE ON
+        )
+
+# Install
+install(TARGETS ${{PROJECT_NAME}} LIBRARY DESTINATION bin)
+
+########################################################################################################################
+# cppunit tests
+
+set(PROJECT_NAME_TEST test-{project_name})
+
+set(SOURCE_FILES_TEST
+{source_files_test}        )
+
+add_executable(${{PROJECT_NAME_TEST}} ${{SOURCE_FILES_TEST}})
+set_target_properties(${{PROJECT_NAME_TEST}} PROPERTIES
+        CXX_STANDARD 14
+        CXX_STANDARD_REQUIRED ON
+        )
+
+target_link_libraries(${{PROJECT_NAME_TEST}} ${{PROJECT_NAME}} {target_test_libs} ${{CPPUNIT_LIBRARY}})
+
+enable_testing()
+add_test(NAME ${{PROJECT_NAME}}
+        COMMAND $<TARGET_FILE:${{PROJECT_NAME_TEST}}>
+        )
diff --git a/CMake/CMakeLists_root.txt b/CMake/CMakeLists_root.txt
new file mode 100644
index 0000000000..14990f191e
--- /dev/null
+++ b/CMake/CMakeLists_root.txt
@@ -0,0 +1,92 @@
+cmake_minimum_required(VERSION 3.8)
+
+# Project name
+project(alib2)
+
+
+################
+# Immediately fail if not UNIX
+# TODO: Add suport for other platforms
+# FIXME: Apple is subset of UNIX, check.
+if (NOT UNIX)
+    message(FATAL_ERROR "Unavailable if not Unix")
+endif ()
+
+#################
+# ALIB Versioning
+set(ALIB_VERSION_MAJOR 2)
+set(ALIB_VERSION_MINOR 0)
+set(ALIB_VERSION_PATCH 0)
+set(ALIB_VERSION ${{ALIB_VERSION_MAJOR}}.${{ALIB_VERSION_MINOR}}.${{ALIB_VERSION_PATCH}})
+mark_as_advanced(ALIB_VERSION)
+
+######################
+# Some Makefile tuning
+set(CMAKE_COLOR_MAKEFILE ON)
+# set(CMAKE_VERBOSE_MAKEFILE ON)
+
+####################
+# CMake include path
+set(CMAKE_MODULE_PATH ${{CMAKE_SOURCE_DIR}}/CMake)
+# set(CMAKE_TEMPLATE_PATH ${{CMAKE_SOURCE_DIR}}/CMakeTemplates)
+
+#########################################################
+# If not specified whether Debug or Release, select debug
+# cmake -DCMAKE_BUILD_TYPE=Release
+# cmake -DCMAKE_BUILD_TYPE=Debug
+
+if (NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE Debug)
+endif ()
+
+###########################################
+# Flags according to Debug/Release decision
+#  - CMake uses -g on Debug implicitly
+#  - fPIC for libraries will be enabled explicitly
+set(ALIB_BUILD_FLAGS_COMMON -Wall -pedantic -pipe -Wextra -Werror)
+set(ALIB_BUILD_FLAGS_DEBUG ${{ALIB_BUILD_FLAGS_COMMON}} -Og)
+set(ALIB_BUILD_FLAGS_RELEASE ${{ALIB_BUILD_FLAGS_COMMON}} -O3)
+
+if (${{CMAKE_CXX_COMPILER_ID}} STREQUAL "Clang")
+    set(ALIB_BUILD_FLAGS_DEBUG ${{ALIB_BUILD_FLAGS_COMMON}} -O0)
+endif ()
+
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+    add_definitions(-DDEBUG)
+    add_compile_options(${{ALIB_BUILD_FLAGS_DEBUG}})
+elseif (CMAKE_BUILD_TYPE STREQUAL "Release")
+    remove_definitions(-DNDEBUG)
+    add_definitions(-DRELEASE)
+    add_compile_options(${{ALIB_BUILD_FLAGS_RELEASE}})
+endif ()
+
+######################################
+# check required external dependencies
+# - we will deal with includes and library links in each project explicitly
+find_package(LibXml2 REQUIRED)
+find_package(cppunit REQUIRED)
+find_package(tclap REQUIRED)
+find_package(readline REQUIRED)
+
+# Cpp version
+set(CMAKE_CXX_STANDARD {{cpp_version}})
+
+set(ALIB_MODULES_LIB
+        {alib_modules_lib}
+        )
+
+set(ALIB_MODULES_EXE
+        {alib_modules_exe}
+        )
+
+##################
+# Register modules
+foreach (module ${{ALIB_MODULES_LIB}} ${{ALIB_MODULES_EXE}})
+    add_subdirectory(${{module}})
+endforeach ()
+
+#######
+# Tests
+#  - enable
+enable_testing()
+# include(CTest) # we do not need full CTest support now
diff --git a/CMake/Findcppunit.cmake b/CMake/Findcppunit.cmake
new file mode 100644
index 0000000000..3689e47cda
--- /dev/null
+++ b/CMake/Findcppunit.cmake
@@ -0,0 +1,75 @@
+# - try to find cppunit library
+#
+# Cache Variables: (probably not for direct use in your scripts)
+#  CPPUNIT_INCLUDE_DIR
+#  CPPUNIT_LIBRARY
+#
+# Non-cache variables you might use in your CMakeLists.txt:
+#  CPPUNIT_FOUND
+#  CPPUNIT_INCLUDE_DIRS
+#  CPPUNIT_LIBRARIES
+#
+# Requires these CMake modules:
+#  SelectLibraryConfigurations (included with CMake >= 2.8.0)
+#  FindPackageHandleStandardArgs (known included with CMake >=2.6.2)
+#
+# Original Author:
+# 2009-2011 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2011.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(CPPUNIT_ROOT_DIR
+        "${CPPUNIT_ROOT_DIR}"
+        CACHE
+        PATH
+        "Directory to search")
+
+find_library(CPPUNIT_LIBRARY_RELEASE
+        NAMES
+        cppunit
+        HINTS
+        "${CPPUNIT_ROOT_DIR}")
+
+find_library(CPPUNIT_LIBRARY_DEBUG
+        NAMES
+        cppunitd
+        HINTS
+        "${CPPUNIT_ROOT_DIR}")
+
+include(SelectLibraryConfigurations)
+select_library_configurations(CPPUNIT)
+
+# Might want to look close to the library first for the includes.
+get_filename_component(_libdir "${CPPUNIT_LIBRARY_RELEASE}" PATH)
+
+find_path(CPPUNIT_INCLUDE_DIR
+        NAMES
+        cppunit/TestCase.h
+        HINTS
+        "${_libdir}/.."
+        PATHS
+        "${CPPUNIT_ROOT_DIR}"
+        PATH_SUFFIXES
+        include/)
+
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(cppunit
+        DEFAULT_MSG
+        CPPUNIT_LIBRARY
+        CPPUNIT_INCLUDE_DIR)
+
+if (CPPUNIT_FOUND)
+    set(CPPUNIT_LIBRARIES ${CPPUNIT_LIBRARY} ${CMAKE_DL_LIBS})
+    set(CPPUNIT_INCLUDE_DIRS "${CPPUNIT_INCLUDE_DIR}")
+    mark_as_advanced(CPPUNIT_ROOT_DIR)
+endif ()
+
+mark_as_advanced(CPPUNIT_INCLUDE_DIR
+        CPPUNIT_LIBRARY_RELEASE
+        CPPUNIT_LIBRARY_DEBUG)
diff --git a/CMake/Findreadline.cmake b/CMake/Findreadline.cmake
new file mode 100644
index 0000000000..c3e32b79f4
--- /dev/null
+++ b/CMake/Findreadline.cmake
@@ -0,0 +1,47 @@
+# - Try to find readline include dirs and libraries
+#
+# Usage of this module as follows:
+#
+#     find_package(Readline)
+#
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+#  Readline_ROOT_DIR         Set this variable to the root installation of
+#                            readline if the module has problems finding the
+#                            proper installation path.
+#
+# Variables defined by this module:
+#
+#  READLINE_FOUND            System has readline, include and lib dirs found
+#  Readline_INCLUDE_DIR      The readline include directories.
+#  Readline_LIBRARY          The readline library.
+
+find_path(Readline_ROOT_DIR
+        NAMES include/readline/readline.h
+        )
+
+find_path(Readline_INCLUDE_DIR
+        NAMES readline/readline.h
+        HINTS ${Readline_ROOT_DIR}/include
+        )
+
+find_library(Readline_LIBRARY
+        NAMES readline
+        HINTS ${Readline_ROOT_DIR}/lib
+        )
+
+if (Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+    set(READLINE_FOUND TRUE)
+else (Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+    FIND_LIBRARY(Readline_LIBRARY NAMES readline)
+    include(FindPackageHandleStandardArgs)
+    FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY)
+    MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
+endif (Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+
+mark_as_advanced(
+        Readline_ROOT_DIR
+        Readline_INCLUDE_DIR
+        Readline_LIBRARY
+)
diff --git a/CMake/Findtclap.cmake b/CMake/Findtclap.cmake
new file mode 100644
index 0000000000..2bfa5cd6e2
--- /dev/null
+++ b/CMake/Findtclap.cmake
@@ -0,0 +1,11 @@
+find_path(TCLAP_INCLUDE_DIR CmdLine.h PATHS
+        $ENV{HOME}/build/include
+        PATH_SUFFIXES
+        tclap
+        include/)
+
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(TCLAP
+        DEFAULT_MSG
+        TCLAP_INCLUDE_DIR)
diff --git a/CMake/LICENSE b/CMake/LICENSE
new file mode 100644
index 0000000000..d51344849e
--- /dev/null
+++ b/CMake/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 ctu-fit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/CMake/NOTES.md b/CMake/NOTES.md
new file mode 100644
index 0000000000..26994fa60b
--- /dev/null
+++ b/CMake/NOTES.md
@@ -0,0 +1,4 @@
+CMake extension
+===============
+
+This module is a support module for compiling with `cmake` which is neccesary for correct syntax highlight in `CLion`. `CMakeLists.txt` can be generated with simple python script [`alib-cmake`]. Original author is Jan UhlĂ­k and the script is available on [`gitlab`](https://gitlab.com/ctu-fit/alib-cmake).
diff --git a/CMake/README.md b/CMake/README.md
new file mode 100644
index 0000000000..461328d61f
--- /dev/null
+++ b/CMake/README.md
@@ -0,0 +1,10 @@
+# CMakeLists generator for Algorithms library
+This package generate `CMakeLists.txt` for *Algorithms library*.
+Library dependencies are parsed from `makefile.conf`.
+
+## Run
+```
+alib-cmake -w
+```
+
+See `--help` for details.
diff --git a/CMake/__init__.py b/CMake/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/CMake/__main__.py b/CMake/__main__.py
new file mode 100644
index 0000000000..2141f99ff5
--- /dev/null
+++ b/CMake/__main__.py
@@ -0,0 +1,3 @@
+from alib_cmake import main
+
+main()
diff --git a/CMake/alib_cmake.py b/CMake/alib_cmake.py
new file mode 100755
index 0000000000..04b1d2eedc
--- /dev/null
+++ b/CMake/alib_cmake.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+import click
+
+alib_path = '..'
+
+alib_modules_lib = [
+    # Libraries
+    'alib2algo',
+    'alib2algo_experimental',
+    'alib2aux',
+    'alib2cli',
+    'alib2common',
+    'alib2data',
+    'alib2data_experimental',
+    'alib2elgo',
+    'alib2measure',
+    'alib2raw',
+    'alib2raw_cli_integration',
+    'alib2std',
+    'alib2str',
+    'alib2str_cli_integration',
+    'alib2xml',
+    'alib2abstraction',
+    'alib2dummy',
+    'alib2graph_data',
+    'alib2graph_algo',
+]
+
+alib_modules_exe = [
+    # Binaries
+    'aaccess2',
+    'aarbology2',
+    'acast2',
+    'acompaction2',
+    'acompare2',
+    'aconversions2',
+    'aconvert2',
+    'aderivation2',
+    'adeterminize2',
+    'aecho2',
+    'aepsilon2',
+    'agenerate2',
+    'aintegral2',
+    'aintrospection2',
+    'alangop2',
+    'aminimize2',
+    'anormalize2',
+    'aql2',
+    'aquery2',
+    'arand2',
+    'araw2',
+    'arename2',
+    'areverse2',
+    'arun2',
+    'astat2',
+    'astringology2',
+    'atrim2',
+    'tniceprint',
+]
+
+root_template = open(os.path.join(os.path.dirname(__file__), "CMakeLists_root.txt"), 'r').read()
+library_template = open(os.path.join(os.path.dirname(__file__), "CMakeLists_library.txt"), 'r').read()
+executable_template = open(os.path.join(os.path.dirname(__file__), "CMakeLists_executable.txt"), 'r').read()
+
+separate_size = 20
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+def create_root_cmakelist(debug):
+    alib_modules_exe_str = '\n        '.join(alib_modules_exe)
+    alib_modules_lib_str = '\n        '.join(alib_modules_lib)
+
+    if not debug:
+        print('CMakeLists.txt')
+        print('-' * separate_size)
+        print(root_template.format(
+            alib_modules_lib=alib_modules_lib_str,
+            alib_modules_exe=alib_modules_exe_str
+        ))
+        print('-' * separate_size)
+    else:
+        with open(f'{alib_path}/CMakeLists.txt', 'w') as file:
+            file.write(root_template.format(
+                alib_modules_lib=alib_modules_lib_str,
+                alib_modules_exe=alib_modules_exe_str
+            ))
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+def get_source_files(project_name, directory):
+    # Recursively find all source file(s) and directories
+    source_files = []
+    for dp, dn, fn in os.walk(f'{alib_path}/{project_name}/{directory}'):
+        for file in fn:
+            if file.endswith(".cxx") or file.endswith(".hxx") or file.endswith(".txx"):
+                continue
+            source_files.append(os.path.join(dp, file))
+
+    # Create strings from lists for file(s)
+    source_files_str = ""
+    for file in source_files:
+        source_files_str += f'        {file[len(alib_path)+1+len(project_name)+1:]}\n'
+
+    return source_files_str
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+def parse_makeconfig(file):
+    link_libs = []
+    link_test_libs = []
+    xml_dep = ''
+
+    # Read and parse makefile.conf
+    with open(file, 'r') as file:
+        for line in file:
+            if line.startswith('LINK_LIBRARIES'):
+                link_libs = line[len('LINK_LIBRARIES='):].split()
+            elif line.startswith('SYSTEM_LIBRARIES'):
+                sys_libs = line[len('SYSTEM_LIBRARIES='):].split()
+                for lib in sys_libs:
+                    if lib == 'xml2':
+                        link_libs.append('${LIBXML2_LIBRARIES}')
+                        xml_dep = 'PUBLIC ${LIBXML2_INCLUDE_DIR}'
+                    else:
+                        link_libs.append(lib)
+            elif line.startswith('TEST_LINK_LIBRARIES'):
+                link_test_libs = line[len('TEST_LINK_LIBRARIES='):].split()
+
+    return ' '.join(link_libs), ' '.join(link_test_libs), xml_dep
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+def create_library_package(project_name, write):
+    # Parse config file
+    link_libs, link_test_libs, xml_dep = parse_makeconfig(f'{alib_path}/{project_name}/makefile.conf')
+
+    # Get source files
+    source_files = get_source_files(project_name, 'src')
+
+    # Get source files test
+    source_files_test = get_source_files(project_name, 'test-src')
+
+    if not write:
+        print(f'{project_name}/CMakeLists.txt')
+        print('-' * separate_size)
+        print(library_template.format(
+            project_name=project_name,
+            target_libs=link_libs,
+            target_test_libs=link_test_libs,
+            xml_dep=xml_dep,
+            source_files=source_files,
+            source_files_test=source_files_test,
+        ))
+        print('-' * separate_size)
+    else:
+        with open(f'{alib_path}/{project_name}/CMakeLists.txt', 'w') as file:
+            file.write(library_template.format(
+                project_name=project_name,
+                target_libs=link_libs,
+                target_test_libs=link_test_libs,
+                xml_dep=xml_dep,
+                source_files=source_files,
+                source_files_test=source_files_test,
+            ))
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+def create_executable_package(project_name, write):
+    # Parse config file
+    link_libs, link_test_libs, xml_dep = parse_makeconfig(f'{alib_path}/{project_name}/makefile.conf')
+
+    # Get source files
+    source_files = get_source_files(project_name, 'src')
+
+    if not write:
+        print(f'{project_name}/CMakeLists.txt')
+        print('-' * separate_size)
+        print(executable_template.format(
+            project_name=project_name,
+            target_libs=link_libs,
+            target_test_libs=link_test_libs,
+            xml_dep=xml_dep,
+            source_files=source_files,
+        ))
+        print('-' * separate_size)
+    else:
+        with open(f'{alib_path}/{project_name}/CMakeLists.txt', 'w') as file:
+            file.write(executable_template.format(
+                project_name=project_name,
+                target_libs=link_libs,
+                target_test_libs=link_test_libs,
+                xml_dep=xml_dep,
+                source_files=source_files,
+            ))
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+@click.command()
+@click.option('--packages', '-p', help='Specify packages', default=alib_modules_lib + alib_modules_exe, multiple=True)
+@click.option('--write', '-w', help='Write output to file(s).', is_flag=True, default=False)
+@click.option('--main_file', '-m', help='Generate also main CMakeLists.txt', is_flag=True, default=False)
+def main(write, main_file, packages):
+    packages_cnt = len(packages)
+    libs_cnt = len(list(set(packages) & set(alib_modules_lib)))
+    exes_cnt = len(list(set(packages) & set(alib_modules_exe)))
+
+    # Increment counter
+    if main_file:
+        packages_cnt += 1
+
+    # Generate for library package
+    for i, package in enumerate(list(set(packages) & set(alib_modules_lib)), 1):
+        try:
+            create_library_package(package, write)
+            print(f'[{i}/{packages_cnt}] Generated library package {package}', file=sys.stderr)
+        except FileNotFoundError as e:
+            print(f'[{i}/{packages_cnt}] Skipping library package {package}: {e}', file=sys.stderr)
+
+    # Generate for executable package
+    for i, package in enumerate(list(set(packages) & set(alib_modules_exe)), libs_cnt + 1):
+        try:
+            create_executable_package(package, write)
+            print(f'[{i}/{packages_cnt}] Generated executable package {package}', file=sys.stderr)
+        except FileNotFoundError as e:
+            print(f'[{i}/{packages_cnt}] Skipping executable package {package}: {e}', file=sys.stderr)
+
+    # Generate root file
+    if main_file:
+        create_root_cmakelist(write)
+        print(f'[{packages_cnt}/{packages_cnt}] Generated main CMakeLists.txt', file=sys.stderr)
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+if __name__ == '__main__':
+    main()
diff --git a/CMake/requirements.txt b/CMake/requirements.txt
new file mode 100644
index 0000000000..3af12f5c89
--- /dev/null
+++ b/CMake/requirements.txt
@@ -0,0 +1 @@
+click==6.7
diff --git a/cmake-debug.sh b/cmake-debug.sh
new file mode 100755
index 0000000000..56cd0b8365
--- /dev/null
+++ b/cmake-debug.sh
@@ -0,0 +1,21 @@
+THREADS=${1:-5}
+cd CMake
+./alib_cmake.py -w -m
+cd ..
+
+if [ ! -d debug ]; then
+  mkdir debug
+fi
+
+if [ -L debug ]; then
+  DIRECTORY=$(pwd)
+  cd $(readlink debug)
+  cmake ${DIRECTORY}
+  cd ${DIRECTORY}
+  cd debug
+else
+  cd debug
+  cmake ..
+fi
+
+CXX=clang++ make -j${THREADS}
-- 
GitLab