diff --git a/SConscript b/SConscript index deedf00..e0739ec 100644 --- a/SConscript +++ b/SConscript @@ -1,6 +1,7 @@ import copy import glob +import inspect import json import os import psutil @@ -11,13 +12,15 @@ import time class _VersionSpec: minimum_version = None maximum_version = None + options = {} - def __init__(self, minimum_version = None, maximum_version = None): + def __init__(self, minimum_version = None, maximum_version = None, options = {}): self.minimum_version = minimum_version self.maximum_version = maximum_version + self.options = options def __str__(self): - return f'Min: {self.minimum_version}, Max: {self.maximum_version}' + return f'Min: {self.minimum_version}, Max: {self.maximum_version}, Options: {self.options}' class _Dependency: name: str = '' @@ -53,12 +56,19 @@ def _find_recipe(env: Environment, recipe_name: str): env['SPP_RECIPES'][recipe_name] = recipe return recipe +def _run_cook(dependency: _Dependency): + if not dependency.cook_result: + cook_signature = inspect.signature(dependency.recipe.cook) + kwargs = {} + if 'options' in cook_signature.parameters: + kwargs['options'] = dependency.version_spec.options + dependency.cook_result = dependency.recipe.cook(env, dependency.version, **kwargs) + def _cook(env: Environment, recipe_name: str): dependency = env['SPP_DEPENDENCIES'].get(recipe_name) if not dependency: raise Exception(f'Cannot cook {recipe_name} as it was not listed as a dependency.') - if not dependency.cook_result: - dependency.cook_result = dependency.recipe.cook(env, dependency.version) + _run_cook(dependency) return dependency.cook_result def _module(env: Environment, file: str): @@ -90,8 +100,7 @@ def _inject_dependency(dependency, kwargs: dict, add_sources: bool = True) -> No for inner_dependency in dependency['DEPENDENCIES']: _inject_dependency(inner_dependency, kwargs, False) elif isinstance(dependency, _Dependency): - if not dependency.cook_result: - dependency.cook_result = dependency.recipe.cook(env, dependency.version) + _run_cook(dependency) _inject_list(kwargs, dependency.cook_result, 'CPPPATH') _inject_list(kwargs, dependency.cook_result, 'CPPDEFINES') _inject_list(kwargs, dependency.cook_result, 'LIBPATH') @@ -185,8 +194,35 @@ def _error(env: Environment, message: str): print(message, file=sys.stderr) env.Exit(1) +def _try_merge_dicts(dictA: dict, dictB: dict) -> 'dict|None': + result = {} + for key, valueA in dictA.items(): + if key in dictB: + valueB = dictB[key] + if type(valueA) != type(valueB): + return None + elif type(valueA) == list: + result[key] = valueA + valueB + elif type(valueA) == dict: + mergedValue = _try_merge_dicts(valueA, valueB) + if mergedValue is None: + return None + result[key] = mergedValue + elif valueA != valueB: + return None + else: + result[key] = valueA + for key, valueB in dictB.items(): + if key not in result: + result[key] = valueB + return result + + def _find_common_depenency_version(name: str, versionA: _VersionSpec, versionB: _VersionSpec) -> _VersionSpec: - result_version = _VersionSpec() + options = _try_merge_dicts(versionA.options, versionB.options) + if options is None: + return None + result_version = _VersionSpec(options=options) if versionA.minimum_version is not None: if versionB.minimum_version is not None: result_version.minimum_version = max(versionA.minimum_version, versionB.minimum_version) @@ -209,7 +245,7 @@ def _find_common_depenency_version(name: str, versionA: _VersionSpec, versionB: return result_version def _parse_version_spec(version_spec: dict) -> _VersionSpec: - return _VersionSpec(version_spec.get('min'), version_spec.get('max')) + return _VersionSpec(version_spec.get('min'), version_spec.get('max'), version_spec.get('options', {})) def _can_add_dependency(env: Environment, name: str, version_spec: _VersionSpec) -> bool: if name not in env['SPP_DEPENDENCIES']: @@ -256,7 +292,11 @@ def _version_matches(version, version_spec: _VersionSpec) -> bool: def _find_version(env: Environment, dependency: _Dependency): for update in (False, True): - versions = dependency.recipe.versions(env, update=update) + versions_signature = inspect.signature(dependency.recipe.versions) + kwargs = {} + if 'options' in versions_signature.parameters: + kwargs['options'] = dependency.version_spec.options + versions = dependency.recipe.versions(env, update=update, **kwargs) _sort_versions(versions) for version in versions: if _version_matches(version, dependency.version_spec): diff --git a/addons/gitbranch.py b/addons/gitbranch.py index e20103e..a332496 100644 --- a/addons/gitbranch.py +++ b/addons/gitbranch.py @@ -2,7 +2,6 @@ from git import Repo from git.exc import GitError import hashlib -import re from SCons.Script import * Import('env') @@ -56,7 +55,9 @@ def _git_recipe(env: Environment, globals: dict, repo_name, repo_url, cook_fn, v _tag_pattern = _make_callable(tag_pattern) versions_cb = versions and _make_callable(versions) - def _versions(env: Environment, update: bool = False): + def _versions(env: Environment, update: bool = False, options: dict = {}): + if 'ref' in options: + return [(0, 0, 0)] # no versions if compiling from a branch pattern = _tag_pattern(env) if pattern: tags = env.GitTags(repo_name = _repo_name(env), remote_url = _repo_url(env), force_fetch=update) @@ -76,8 +77,10 @@ def _git_recipe(env: Environment, globals: dict, repo_name, repo_url, cook_fn, v def _dependencies(env: Environment, version) -> 'dict': return dependencies - def _cook(env: Environment, version) -> dict: - if tag_fn: + def _cook(env: Environment, version, options: dict = {}) -> dict: + if 'ref' in options: + git_ref = options['ref'] + elif tag_fn: git_ref = f'refs/tags/{tag_fn(version)}' else: assert ref_fn