Update to new recipe system (S++ 2.0).

This commit is contained in:
2024-08-14 23:33:04 +02:00
parent 35b38b8b6e
commit 0c82036300
26 changed files with 646 additions and 174 deletions

View File

@@ -1,6 +1,6 @@
import copy
import enum
import json
import os
import psutil
import sys
@@ -24,6 +24,7 @@ class _Dependency:
version_spec: _VersionSpec
recipe = None
depdeps: list = []
cook_result: dict = {}
class _Target:
builder = None
@@ -46,13 +47,18 @@ def _find_recipe(env: Environment, recipe_name: str):
raise Exception(f'Could not find recipe {recipe_name}.')
spec = importlib.util.spec_from_file_location(recipe_name, source_file)
recipe = importlib.util.module_from_spec(spec)
recipe.env = env
spec.loader.exec_module(recipe)
env['SPP_RECIPES'][recipe_name] = recipe
return recipe
def _cook(env: Environment, recipe_name: str, *args, **kwargs):
recipe = _find_recipe(env, recipe_name)
return recipe.cook(env, *args, **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)
return dependency.cook_result
def _module(env: Environment, file: str):
return SConscript(file, exports = 'env', variant_dir = env['VARIANT_DIR'], src_dir = '.')
@@ -83,7 +89,14 @@ 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):
pass
if not dependency.cook_result:
dependency.cook_result = dependency.recipe.cook(env, dependency.version)
_inject_list(kwargs, dependency.cook_result, 'CPPPATH')
_inject_list(kwargs, dependency.cook_result, 'CPPDEFINES')
_inject_list(kwargs, dependency.cook_result, 'LIBPATH')
_inject_list(kwargs, dependency.cook_result, 'LIBS')
for depdep in dependency.depdeps:
_inject_dependency(depdep, kwargs)
def _rglob(env: Environment, root_path: str, pattern: str, **kwargs):
result_nodes = []
@@ -95,6 +108,14 @@ def _rglob(env: Environment, root_path: str, pattern: str, **kwargs):
result_nodes.extend(env.Glob(f'{path}/{pattern}', **kwargs))
return sorted(result_nodes)
def _deps_from_json(env: Environment, deps: dict) -> dict:
for _, dep in deps.items():
if 'min' in dep and isinstance(dep['min'], list):
dep['min'] = tuple(dep['min'])
if 'max' in dep and isinstance(dep['max'], list):
dep['max'] = tuple(dep['max'])
return deps
def _make_interface(env: Environment, dependencies: list = []):
kwargs = {}
for dependency in dependencies:
@@ -104,7 +125,7 @@ def _make_interface(env: Environment, dependencies: list = []):
'CPPDEFINES': kwargs.get('CPPDEFINES', [])
}
def _lib_filename(name: str, type: str = 'static') -> str:
def _lib_filename(env: Environment, name: str, type: str = 'static') -> str:
# TODO: windows
ext = {
'static': 'a',
@@ -112,12 +133,14 @@ def _lib_filename(name: str, type: str = 'static') -> str:
}[type]
return f'lib{name}.{ext}'
def _find_lib(env: Environment, name: str, paths: 'list[str]', type : str = 'static'):
def _find_lib(env: Environment, name: str, paths: 'list[str]', type : str = 'static', allow_fail: bool = False):
for path in paths:
lib_path = os.path.join(path, _lib_filename(name, type))
lib_path = os.path.join(path, _lib_filename(env, name, type))
if os.path.exists(lib_path):
return lib_path
return None
if allow_fail:
return None
raise Exception(f'Could not find library with name {name} in paths: "{",".join(paths)}".')
def _error(env: Environment, message: str):
print(message, file=sys.stderr)
@@ -177,12 +200,12 @@ def _add_dependency(env: Environment, name: str, version_spec: _VersionSpec) ->
def _sort_versions(versions: list) -> None:
import functools
def _compare(left, right):
if left[0] != right[0]:
return right[0] - left[0]
elif left[1] != right[1]:
return right[1] - left[1]
if left < right:
return 1
elif left == right:
return 0
else:
return right[2] - left[2]
return -1
versions.sort(key=functools.cmp_to_key(_compare))
def _version_matches(version, version_spec: _VersionSpec) -> bool:
@@ -225,12 +248,6 @@ def _wrap_builder(builder, is_lib: bool = False):
kwargs['LIBPATH'] = copy.copy(env['LIBPATH'])
if 'LIBS' not in kwargs and 'LIBS' in env:
kwargs['LIBS'] = copy.copy(env['LIBS'])
if 'LIBS' in kwargs:
libs_copy = list(kwargs['LIBS'])
for lib in libs_copy:
if isinstance(lib, str) and os.path.isabs(lib):
kwargs['LIBS'].remove(lib)
kwargs['source'].append(lib)
if 'source' in kwargs:
source = kwargs['source']
if not isinstance(source, list):
@@ -249,6 +266,8 @@ def _wrap_builder(builder, is_lib: bool = False):
target.kwargs = kwargs
target.dependencies = target_dependencies
env.Append(SPP_TARGETS = [target])
if not target.dependencies:
_build_target(target)
return target
return _wrapped
@@ -273,16 +292,47 @@ def _wrap_depends(depends):
depends(dependant, dependency)
return _wrapped
def _build_target(target: _Target):
for dependency in target.dependencies:
_inject_dependency(dependency, target.kwargs)
if 'LIBS' in target.kwargs:
libs_copy = list(target.kwargs['LIBS'])
for lib in libs_copy:
if isinstance(lib, str) and os.path.isabs(lib):
target.kwargs['LIBS'].remove(lib)
target.kwargs['source'].append(lib)
elif isinstance(lib, _Target):
if not lib.target:
_build_target(lib)
target.kwargs['LIBS'].remove(lib)
target.kwargs['LIBS'].append(lib.target)
target.target = target.builder(*target.args, **target.kwargs)
def _version_to_string(version) -> str:
return '.'.join([str(v) for v in version])
def _finalize(env: Environment):
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),
} for dep in env['SPP_DEPENDENCIES'].values()}
env['_SPP_DEPENDENCIES_OKAY'] = False
while not env['_SPP_DEPENDENCIES_OKAY']:
env['_SPP_DEPENDENCIES_OKAY'] = True
for dependency in list(env['SPP_DEPENDENCIES'].values()):
_find_version(env, dependency)
if not dependency.version:
_find_version(env, dependency)
with open('cache/versions.json', 'w') as f:
json.dump({
'requirements': version_requirements,
'selected': {
dep.name: _version_to_string(dep.version) for dep in env['SPP_DEPENDENCIES'].values()
}
}, f)
for target in env['SPP_TARGETS']:
for dependency in target.dependencies:
_inject_dependency(dependency, target.kwargs)
target.target = target.builder(*target.args, **target.kwargs)
_build_target(target)
for target in env['SPP_DEFAULT_TARGETS']:
env.Default(target.target)
@@ -575,7 +625,9 @@ elif env['COMPILER_FAMILY'] == 'clang':
env.AddMethod(_cook, 'Cook')
env.AddMethod(_parse_lib_conf, 'ParseLibConf')
env.AddMethod(_rglob, 'RGlob')
env.AddMethod(_deps_from_json, 'DepsFromJson')
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')