diff --git a/SConscript b/SConscript index acd10e5..531f5d4 100644 --- a/SConscript +++ b/SConscript @@ -1,5 +1,6 @@ import copy +import enum import glob import inspect import json @@ -9,6 +10,10 @@ import sys import time +class TargetType(enum.Enum): + PROGRAM = 0 + LIBRARY = 1 + class _VersionSpec: minimum_version = None maximum_version = None @@ -31,6 +36,8 @@ class _Dependency: cook_result: dict = {} class _Target: + name: str + target_type: TargetType builder = None args: list = [] kwargs: dict = {} @@ -192,7 +199,7 @@ def _find_lib(env: Environment, name: str, paths: 'list[str]', type : str = 'sta def _error(env: Environment, message: str): print(message, file=sys.stderr) - env.Exit(1) + Exit(1) def _try_merge_dicts(dictA: dict, dictB: dict) -> 'dict|None': result = {} @@ -316,7 +323,7 @@ def _find_version(env: Environment, dependency: _Dependency): print(f'Required version: {dependency.version_spec}') raise Exception(f'Could not find a suitable version for dependency {dependency.name}.') -def _wrap_builder(builder, is_lib: bool = False): +def _wrap_builder(builder, target_type: TargetType): def _wrapped(env, dependencies = {}, *args, **kwargs): target_dependencies = [] for name, version_spec in dependencies.items(): @@ -343,6 +350,15 @@ def _wrap_builder(builder, is_lib: bool = False): kwargs['source'] = new_source target = _Target() + if 'name' in kwargs: + target.name = kwargs['name'] + else: + trgt = _target_entry(kwargs.get('target')) + if trgt is not None: + target.name = str(_target_entry(kwargs['target']).name) + else: + target.name = 'Unknown target' + target.target_type = target_type target.builder = builder target.args = args target.kwargs = kwargs @@ -396,6 +412,10 @@ def _version_to_string(version) -> str: return '.'.join([str(v) for v in version]) def _finalize(env: Environment): + if generate_project: + _generate_project(generate_project) + Exit(0) + version_requirements = {dep.name: { 'min': dep.version_spec.minimum_version and _version_to_string(dep.version_spec.minimum_version), 'max': dep.version_spec.maximum_version and _version_to_string(dep.version_spec.maximum_version), @@ -441,6 +461,73 @@ def _find_system_cache_dir() -> str: # fallback return _get_fallback_cache_dir() +def _target_entry(target_value): + if target_value is None: + return None + if not isinstance(target_value, list): + target_value = [target_value] + if len(target_value) < 1: + return None + if isinstance(target_value[0], str): + target_value[0] = env.Entry(target_value[0]) + return target_value[0] + +def _generate_project(project_type: str) -> None: + source_folder, target_folder = { + 'clion': (os.path.join(_spp_dir.abspath, 'util', 'clion_project_template'), Dir('#.idea').abspath) + }.get(project_type, (None, None)) + if not source_folder: + _error(None, 'Invalid project type option.') + + import uuid + def _generate_uuid() -> str: + return str(uuid.uuid4()) + + import pathlib + import shutil + import multiprocessing + + root_path = pathlib.Path(env.Dir('#').abspath) + def _get_executables() -> list: + result = [] + for target in env['SPP_TARGETS']: + if target.target_type == TargetType.PROGRAM: + trgt = _target_entry(target.kwargs['target']) + exe_path = pathlib.Path(trgt.abspath).relative_to(root_path) + result.append({ + 'name': target.name, + 'filename': str(exe_path) + }) + return result + + import jinja2 + jinja_env = jinja2.Environment() + jinja_env.globals['generate_uuid'] = _generate_uuid + jinja_env.globals['project'] = { + 'name': config['PROJECT_NAME'], + 'executables': _get_executables(), + 'build_types': ['debug', 'release_debug', 'release', 'profile'] + } + jinja_env.globals['scons_exe'] = env['ENV']['_'] + jinja_env.globals['nproc'] = multiprocessing.cpu_count() + + source_path = pathlib.Path(source_folder) + target_path = pathlib.Path(target_folder) + + for source_file in source_path.rglob('*'): + if source_file.is_file(): + target_file = target_path / (source_file.relative_to(source_path)) + target_file.parent.mkdir(parents=True, exist_ok=True) + if source_file.suffix != '.jinja': + shutil.copyfile(source_file, target_file) + continue + with source_file.open('r') as f: + templ = jinja_env.from_string(f.read()) + target_file = target_file.with_suffix('') + with target_file.open('w') as f: + f.write(templ.render()) + + Import('config') if not config.get('PROJECT_NAME'): @@ -520,6 +607,17 @@ AddOption( action = 'store_true' ) +AddOption( + '--generate_project', + dest = 'generate_project', + type = 'choice', + choices = ('clion',), + nargs = 1, + action = 'store' +) + +_spp_dir = Dir('.') + build_type = GetOption('build_type') unity_mode = GetOption('unity_mode') variant = GetOption('variant') @@ -528,6 +626,7 @@ config_file = GetOption('config_file') compiler = GetOption('compiler') update_repositories = GetOption('update_repositories') dump_env = GetOption('dump_env') +generate_project = GetOption('generate_project') default_CC = { 'gcc': 'gcc', @@ -756,17 +855,17 @@ env.AddMethod(_make_interface, 'MakeInterface') env.AddMethod(_lib_filename, 'LibFilename') env.AddMethod(_find_lib, 'FindLib') env.AddMethod(_error, 'Error') -env.AddMethod(_wrap_builder(env.Library, is_lib = True), 'Library') -env.AddMethod(_wrap_builder(env.StaticLibrary, is_lib = True), 'StaticLibrary') -env.AddMethod(_wrap_builder(env.SharedLibrary, is_lib = True), 'SharedLibrary') -env.AddMethod(_wrap_builder(env.Program), 'Program') +env.AddMethod(_wrap_builder(env.Library, TargetType.LIBRARY), 'Library') +env.AddMethod(_wrap_builder(env.StaticLibrary, TargetType.LIBRARY), 'StaticLibrary') +env.AddMethod(_wrap_builder(env.SharedLibrary, TargetType.LIBRARY), 'SharedLibrary') +env.AddMethod(_wrap_builder(env.Program, TargetType.PROGRAM), 'Program') env.AddMethod(_wrap_default(env.Default), 'Default') env.AddMethod(_wrap_depends(env.Depends), 'Depends') -env.AddMethod(_wrap_builder(env.UnityProgram), 'UnityProgram') -env.AddMethod(_wrap_builder(env.UnityLibrary, is_lib = True), 'UnityLibrary') -env.AddMethod(_wrap_builder(env.UnityStaticLibrary, is_lib = True), 'UnityStaticLibrary') -env.AddMethod(_wrap_builder(env.UnitySharedLibrary, is_lib = True), 'UnitySharedLibrary') +env.AddMethod(_wrap_builder(env.UnityProgram, TargetType.PROGRAM), 'UnityProgram') +env.AddMethod(_wrap_builder(env.UnityLibrary, TargetType.LIBRARY), 'UnityLibrary') +env.AddMethod(_wrap_builder(env.UnityStaticLibrary, TargetType.LIBRARY), 'UnityStaticLibrary') +env.AddMethod(_wrap_builder(env.UnitySharedLibrary, TargetType.LIBRARY), 'UnitySharedLibrary') env.AddMethod(_module, 'Module') env.AddMethod(_finalize, 'Finalize') @@ -780,5 +879,6 @@ if dump_env: print('==== Begin Environment Dump =====') print(env.Dump()) print('==== End Environment Dump =====') + Exit(0) Return('env') diff --git a/requirements.txt b/requirements.txt index 0fdee4b..51e32bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ GitPython psutil +Jinja2 diff --git a/util/clion_project_template/.gitignore b/util/clion_project_template/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/util/clion_project_template/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/util/clion_project_template/customTargets.xml.jinja b/util/clion_project_template/customTargets.xml.jinja new file mode 100644 index 0000000..598afbc --- /dev/null +++ b/util/clion_project_template/customTargets.xml.jinja @@ -0,0 +1,20 @@ + + + + {% for executable in project.executables %} + {% for build_type in project.build_types %} + {% set build_type_name = build_type | capitalize -%} + + + + + + + + + + + {% endfor %} + {% endfor %} + + diff --git a/util/clion_project_template/misc.xml b/util/clion_project_template/misc.xml new file mode 100644 index 0000000..8067168 --- /dev/null +++ b/util/clion_project_template/misc.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/util/clion_project_template/tools/External Tools.xml.jinja b/util/clion_project_template/tools/External Tools.xml.jinja new file mode 100644 index 0000000..b6e2606 --- /dev/null +++ b/util/clion_project_template/tools/External Tools.xml.jinja @@ -0,0 +1,21 @@ + + {% for executable in project.executables %} + {% for build_type in project.build_types %} + {% set build_type_name = build_type | capitalize -%} + + + + + + + + + {% endfor %} + {% endfor %} + diff --git a/util/clion_project_template/vcs.xml b/util/clion_project_template/vcs.xml new file mode 100644 index 0000000..b408360 --- /dev/null +++ b/util/clion_project_template/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/util/clion_project_template/workspace.xml.jinja b/util/clion_project_template/workspace.xml.jinja new file mode 100644 index 0000000..b1239fc --- /dev/null +++ b/util/clion_project_template/workspace.xml.jinja @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 5 +} + + + + + {% for executable in project.executables -%} + {% for build_type in project.build_types -%} + {% set build_type_name = build_type | capitalize -%} + + + + + {% endfor %} + {% endfor %} + + + + +