(WIP) Restructuring of the project, rework of addons.
This commit is contained in:
156
SConscript
156
SConscript
@@ -1,8 +1,10 @@
|
||||
|
||||
from collections.abc import Callable
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import glob
|
||||
import importlib.util
|
||||
import inspect
|
||||
import json
|
||||
import multiprocessing
|
||||
@@ -17,6 +19,18 @@ from typing import Any
|
||||
import uuid
|
||||
|
||||
from SCons.Node import Node
|
||||
from SCons.Script import *
|
||||
|
||||
sys.path.append(os.path.join(Dir('.').abspath, 'lib'))
|
||||
|
||||
from spp import _init_interface
|
||||
|
||||
_init_interface(globals=globals())
|
||||
|
||||
_SPP_VERSION = (1, 1, 0)
|
||||
_DEBUG = {
|
||||
'addons': False
|
||||
}
|
||||
|
||||
_GCC_CPU_FEATURES_MAP = {
|
||||
'mmx': '-mmmx',
|
||||
@@ -36,6 +50,7 @@ class TargetType(enum.Enum):
|
||||
PROGRAM = 0
|
||||
STATIC_LIBRARY = 1
|
||||
SHARED_LIBRARY = 2
|
||||
MISC = 3
|
||||
|
||||
class _VersionSpec:
|
||||
minimum_version = None
|
||||
@@ -78,11 +93,17 @@ class _Target:
|
||||
def _find_recipe(env: Environment, recipe_name: str):
|
||||
if recipe_name in env['SPP_RECIPES']:
|
||||
return env['SPP_RECIPES'][recipe_name]
|
||||
import importlib.util
|
||||
source_file = None
|
||||
|
||||
if not env['SPP_RECIPES_FOLDERS']:
|
||||
env.Error('No recipes repositories set. Add one using env.RecipeRepo(<name>, <url>, <branch>).')
|
||||
# for compatibility
|
||||
if '_SPP_FALLBACK_RECIPE_REPO' in env:
|
||||
repo_args: dict = env['_SPP_FALLBACK_RECIPE_REPO']
|
||||
env.Warn('No recipes repositories set. Add one using env.RecipeRepo(<name>, <url>, <branch>).')
|
||||
env.Warn(f'Falling back to default recipe repository ({repo_args["repo_name"]} at {repo_args["remote_url"]} ref={repo_args.get("git_ref", "master")}).')
|
||||
env.RecipeRepo(**repo_args)
|
||||
else:
|
||||
env.Error('No recipes repositories set. Add one using env.RecipeRepo(<name>, <url>, <branch>).')
|
||||
for folder in env['SPP_RECIPES_FOLDERS']:
|
||||
from SCons import Node
|
||||
if folder is Node:
|
||||
@@ -293,16 +314,19 @@ def _find_lib(env: Environment, name: str, paths: 'list[str]', type : str = 'sta
|
||||
return None
|
||||
raise Exception(f'Could not find library with name {name} in paths: "{", ".join(paths)}" filename: "{fname}".')
|
||||
|
||||
def _debug(cond: str, msg: str) -> None:
|
||||
if _DEBUG.get(cond):
|
||||
print(f'[DEBUG] [{cond}] {msg}')
|
||||
|
||||
def _info(env: Environment, message: str) -> None:
|
||||
def _info(env: Environment|None, message: str) -> None:
|
||||
if not GetOption('silent'):
|
||||
print(message)
|
||||
print(f'[INFO] {message}')
|
||||
|
||||
def _warn(env: Environment, message: str) -> None:
|
||||
print(message, file=sys.stderr)
|
||||
def _warn(env: Environment|None, message: str) -> None:
|
||||
print(f'[WARN] {message}', file=sys.stderr)
|
||||
|
||||
def _error(env: Environment, message: str) -> None:
|
||||
print(message, file=sys.stderr)
|
||||
def _error(env: Environment|None, message: str) -> None:
|
||||
print(f'[ERROR] {message}', file=sys.stderr)
|
||||
Exit(1)
|
||||
|
||||
def _try_merge_dicts(dictA: dict, dictB: dict) -> 'dict|None':
|
||||
@@ -450,17 +474,20 @@ def _wrap_builder(builder, target_type: TargetType):
|
||||
kwargs['LIBPATH'] = copy.copy(env['LIBPATH'])
|
||||
if 'LIBS' not in kwargs and 'LIBS' in env:
|
||||
kwargs['LIBS'] = copy.copy(env['LIBS'])
|
||||
if 'source' in kwargs:
|
||||
source = kwargs['source']
|
||||
if not isinstance(source, list):
|
||||
source = [source]
|
||||
new_source = []
|
||||
for src in source:
|
||||
if isinstance(src, str):
|
||||
new_source.append(env.Entry(src))
|
||||
|
||||
def _fix_filearg(filearg: str|Entry|list[str|Entry]) -> list[Entry]:
|
||||
if not isinstance(filearg, list):
|
||||
filearg = (filearg,)
|
||||
result = []
|
||||
for ele in filearg:
|
||||
if isinstance(ele, str):
|
||||
result.append(env.Entry(ele))
|
||||
else:
|
||||
new_source.append(src)
|
||||
kwargs['source'] = new_source
|
||||
result.append(ele)
|
||||
return result
|
||||
|
||||
if 'source' in kwargs:
|
||||
kwargs['source'] = _fix_filearg(kwargs['source'])
|
||||
|
||||
target = _Target()
|
||||
if 'name' in kwargs:
|
||||
@@ -502,7 +529,7 @@ def _wrap_default(default):
|
||||
def _wrap_depends(depends):
|
||||
def _wrapped(env, dependant, dependency):
|
||||
if isinstance(dependant, _Target) or isinstance(dependency, _Target):
|
||||
env.Append(SPP_TARGET_DEPENDENCIES = [(dependant, dependency)])
|
||||
env.Append(SPP_TARGET_DEPENDENCIES = [(dependant, dependency, depends)])
|
||||
return
|
||||
elif isinstance(dependant, dict) and '_target' in dependant:
|
||||
dependant = dependant['_target']
|
||||
@@ -527,7 +554,7 @@ def _build_target(target: _Target):
|
||||
target.kwargs['LIBS'].remove(lib)
|
||||
target.kwargs['LIBS'].append(lib.target)
|
||||
new_kwargs = target.kwargs.copy()
|
||||
if 'target' in new_kwargs: # there should always be a target, right?
|
||||
if 'target' in new_kwargs and target.target_type != TargetType.MISC: # there should always be a target, right?
|
||||
new_kwargs['target'] = f"{new_kwargs['target']}-{build_type}"
|
||||
if os.name == 'nt' and 'PDB' not in new_kwargs:
|
||||
new_kwargs['PDB'] = f'{new_kwargs["target"]}.pdb'
|
||||
@@ -566,12 +593,12 @@ def _finalize(env: Environment):
|
||||
_build_target(target)
|
||||
for target in env['SPP_DEFAULT_TARGETS']:
|
||||
env.Default(target.target)
|
||||
for dependant, dependency in env['SPP_TARGET_DEPENDENCIES']:
|
||||
for dependant, dependency, depends in env['SPP_TARGET_DEPENDENCIES']:
|
||||
if isinstance(dependant, _Target):
|
||||
dependant = dependant.target
|
||||
if isinstance(dependency, _Target):
|
||||
dependency = dependency.target
|
||||
env.Depends(dependant, dependency)
|
||||
depends(dependant, dependency)
|
||||
|
||||
def _find_target(env: Environment, target_name: str) -> '_Target|None':
|
||||
for target in env['SPP_TARGETS']:
|
||||
@@ -915,6 +942,51 @@ def _dump() -> None:
|
||||
print(dump_fn(data))
|
||||
Exit(0)
|
||||
|
||||
class _Hook:
|
||||
def __init__(self) -> None:
|
||||
self._funcs: list[Callable] = []
|
||||
|
||||
def add_func(self, func: Callable) -> None:
|
||||
self._funcs.append(func)
|
||||
|
||||
def invoke(self, **kwargs) -> None:
|
||||
for func in self._funcs:
|
||||
func(**kwargs)
|
||||
|
||||
_hook_pre_environment = _Hook()
|
||||
_hook_post_environment = _Hook()
|
||||
|
||||
def _load_addon(modname: str, modpath: pathlib.Path) -> None:
|
||||
_debug('addons', f'Loading addon {modname} from {modpath}.')
|
||||
|
||||
spec = importlib.util.spec_from_file_location(modname, modpath)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
if hasattr(module, 'available') and not module.available():
|
||||
_debug('addons', f'Addon {modname} is not available and will not be loaded.')
|
||||
return
|
||||
if hasattr(module, 'pre_environment'):
|
||||
_hook_pre_environment.add_func(module.pre_environment)
|
||||
_debug('addons', f'Addon {modname} registered a pre_environment hook.')
|
||||
if hasattr(module, 'post_environment'):
|
||||
_hook_post_environment.add_func(module.post_environment)
|
||||
_debug('addons', f'Addon {modname} registered a post_environment hook.')
|
||||
|
||||
def _load_addons(folder: pathlib.Path) -> None:
|
||||
_debug('addons', f'Loading addons from {folder}.')
|
||||
for script_file in folder.glob('*.py'):
|
||||
_load_addon(script_file.name[:-3], script_file)
|
||||
|
||||
_ALLOWED_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
|
||||
def _sanitize_identifier(name: str) -> str:
|
||||
chrs = []
|
||||
for chr in name:
|
||||
if chr in _ALLOWED_CHARS:
|
||||
chrs.append(chr)
|
||||
else:
|
||||
chrs.append('_')
|
||||
return ''.join(chrs)
|
||||
|
||||
Import('config')
|
||||
|
||||
@@ -925,7 +997,9 @@ if not config.get('CXX_STANDARD'):
|
||||
if not config.get('CXX_NO_EXCEPTIONS'):
|
||||
config['CXX_NO_EXCEPTIONS'] = False
|
||||
if not config.get('PREPROCESSOR_PREFIX'):
|
||||
config['PREPROCESSOR_PREFIX'] = config['PROJECT_NAME'].upper() # TODO: may be nicer?
|
||||
config['PREPROCESSOR_PREFIX'] = _sanitize_identifier(config['PROJECT_NAME']).upper() # TODO: may be nicer?
|
||||
if not config.get('SPP_TARGET_VERSION'):
|
||||
config['SPP_TARGET_VERSION'] = (1, 0, 0)
|
||||
|
||||
if 'COMPILATIONDB_FILTER_FILES' not in config:
|
||||
config['COMPILATIONDB_FILTER_FILES'] = True
|
||||
@@ -1080,9 +1154,21 @@ if 'VARIABLES' in config:
|
||||
for vardef in config['VARIABLES']:
|
||||
vars.Add(*vardef)
|
||||
|
||||
tools = ['default', 'compilation_db', 'unity_build']
|
||||
tools = ['default', 'compilation_db']
|
||||
if 'TOOLS' in config:
|
||||
assert isinstance(config['TOOLS'], list)
|
||||
tools.extend(config['TOOLS'])
|
||||
addon_dirs = [pathlib.Path(Dir('.').abspath) / 'addons']
|
||||
if 'ADDON_DIRS' in config:
|
||||
assert isinstance(config['ADDON_DIRS'], list)
|
||||
addon_dirs.extend(config['ADDON_DIRS'])
|
||||
|
||||
for addon_dir in addon_dirs:
|
||||
if not isinstance(addon_dir, pathlib.Path):
|
||||
addon_dir = pathlib.Path(addon_dir)
|
||||
_load_addons(addon_dir)
|
||||
|
||||
_hook_pre_environment.invoke()
|
||||
|
||||
env = Environment(tools = tools, variables = vars, ENV = os.environ)
|
||||
env['SPP_RECIPES_FOLDERS'] = []
|
||||
@@ -1156,6 +1242,8 @@ env['OBJSUFFIX'] = f".{env['BUILD_TYPE']}{env['OBJSUFFIX']}"
|
||||
if variant:
|
||||
env['OBJSUFFIX'] = f".{variant}{env['OBJSUFFIX']}"
|
||||
|
||||
_hook_post_environment.invoke()
|
||||
|
||||
# create the cache dir
|
||||
os.makedirs(env['CACHE_DIR'], exist_ok=True)
|
||||
cache_gitignore = f'{env["CACHE_DIR"]}/.gitignore'
|
||||
@@ -1324,17 +1412,23 @@ env.AddMethod(_find_lib, 'FindLib')
|
||||
env.AddMethod(_info, 'Info')
|
||||
env.AddMethod(_warn, 'Warn')
|
||||
env.AddMethod(_error, 'Error')
|
||||
env.AddMethod(_wrap_builder(env.Program, TargetType.PROGRAM), 'Program')
|
||||
env.AddMethod(_wrap_builder(env.Library, TargetType.STATIC_LIBRARY), 'Library')
|
||||
env.AddMethod(_wrap_builder(env.StaticLibrary, TargetType.STATIC_LIBRARY), 'StaticLibrary')
|
||||
env.AddMethod(_wrap_builder(env.SharedLibrary, TargetType.SHARED_LIBRARY), 'SharedLibrary')
|
||||
env.AddMethod(_wrap_builder(env.Program, TargetType.PROGRAM), 'Program')
|
||||
env.AddMethod(_wrap_builder(env.AstJson, TargetType.MISC), 'AstJson')
|
||||
|
||||
if 'unity_build' in tools:
|
||||
env.AddMethod(_wrap_builder(env.UnityProgram, TargetType.PROGRAM), 'UnityProgram')
|
||||
env.AddMethod(_wrap_builder(env.UnityLibrary, TargetType.STATIC_LIBRARY), 'UnityLibrary')
|
||||
env.AddMethod(_wrap_builder(env.UnityStaticLibrary, TargetType.STATIC_LIBRARY), 'UnityStaticLibrary')
|
||||
env.AddMethod(_wrap_builder(env.UnitySharedLibrary, TargetType.SHARED_LIBRARY), 'UnitySharedLibrary')
|
||||
|
||||
env.AddMethod(_wrap_default(env.Default), 'Default')
|
||||
env.AddMethod(_wrap_depends(env.Depends), 'Depends')
|
||||
env.AddMethod(_wrap_depends(env.Ignore), 'Ignore')
|
||||
env.AddMethod(_wrap_depends(env.Requires), 'Requires')
|
||||
|
||||
env.AddMethod(_wrap_builder(env.UnityProgram, TargetType.PROGRAM), 'UnityProgram')
|
||||
env.AddMethod(_wrap_builder(env.UnityLibrary, TargetType.STATIC_LIBRARY), 'UnityLibrary')
|
||||
env.AddMethod(_wrap_builder(env.UnityStaticLibrary, TargetType.STATIC_LIBRARY), 'UnityStaticLibrary')
|
||||
env.AddMethod(_wrap_builder(env.UnitySharedLibrary, TargetType.SHARED_LIBRARY), 'UnitySharedLibrary')
|
||||
env.AddMethod(_module, 'Module')
|
||||
env.AddMethod(_module_config, 'ModuleConfig')
|
||||
env.AddMethod(_finalize, 'Finalize')
|
||||
@@ -1343,7 +1437,7 @@ env.AddMethod(_find_target, 'FindTarget')
|
||||
if hasattr(env, 'Gch'):
|
||||
env.AddMethod(_wrap_builder(env.Gch, TargetType.STATIC_LIBRARY), 'Gch')
|
||||
|
||||
for addon_file in env.Glob('addons/*.py'):
|
||||
for addon_file in env.Glob('addons/old/*.py'):
|
||||
env = SConscript(addon_file, exports = 'env')
|
||||
|
||||
Return('env')
|
||||
|
||||
Reference in New Issue
Block a user