Added command line tool and removed lib folder from gitignore.
This commit is contained in:
parent
79366c9098
commit
b9335a6247
2
.gitignore
vendored
2
.gitignore
vendored
@ -17,7 +17,7 @@ dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
# lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
|
74
SConscript
74
SConscript
@ -23,7 +23,7 @@ from SCons.Script import *
|
||||
|
||||
sys.path.append(os.path.join(Dir('.').abspath, 'lib'))
|
||||
|
||||
from spp import _init_interface
|
||||
from spp import _init_interface, Module, Target, TargetType
|
||||
|
||||
_init_interface(globals=globals())
|
||||
|
||||
@ -46,12 +46,6 @@ _GCC_CPU_FEATURES_MAP = {
|
||||
'avx2': '-mavx2'
|
||||
}
|
||||
|
||||
class TargetType(enum.Enum):
|
||||
PROGRAM = 0
|
||||
STATIC_LIBRARY = 1
|
||||
SHARED_LIBRARY = 2
|
||||
MISC = 3
|
||||
|
||||
class _VersionSpec:
|
||||
minimum_version = None
|
||||
maximum_version = None
|
||||
@ -73,23 +67,6 @@ class _Dependency:
|
||||
depdeps: list = []
|
||||
cook_result: dict = {}
|
||||
|
||||
@dataclass
|
||||
class _Module:
|
||||
name: str
|
||||
folder: str
|
||||
description: str
|
||||
cxx_namespace: str
|
||||
|
||||
class _Target:
|
||||
name: str
|
||||
target_type: TargetType
|
||||
builder = None
|
||||
args: list = []
|
||||
kwargs: dict = {}
|
||||
dependencies: list = []
|
||||
target = None
|
||||
module: _Module = None
|
||||
|
||||
def _find_recipe(env: Environment, recipe_name: str):
|
||||
if recipe_name in env['SPP_RECIPES']:
|
||||
return env['SPP_RECIPES'][recipe_name]
|
||||
@ -150,7 +127,7 @@ def _module(env: Environment, file: str):
|
||||
folder = _normalize_module_path(env, env.File(file).dir.abspath)
|
||||
if folder is not None: # only include modules inside the source tree
|
||||
dirname = os.path.basename(folder)
|
||||
env.Append(SPP_MODULES = {folder: _Module(
|
||||
env.Append(SPP_MODULES = {folder: Module(
|
||||
name=dirname,
|
||||
folder=folder,
|
||||
description='',
|
||||
@ -202,7 +179,7 @@ def _inject_dependency(dependency, kwargs: dict, add_sources: bool = True) -> No
|
||||
_inject_list(kwargs, dependency.cook_result, 'LINKFLAGS')
|
||||
for depdep in dependency.depdeps:
|
||||
_inject_dependency(depdep, kwargs)
|
||||
elif isinstance(dependency, _Target):
|
||||
elif isinstance(dependency, Target):
|
||||
_inject_list(kwargs, dependency.kwargs, 'CPPPATH')
|
||||
_inject_list(kwargs, dependency.kwargs, 'CPPDEFINES')
|
||||
_inject_list(kwargs, dependency.kwargs, 'LIBPATH')
|
||||
@ -489,7 +466,7 @@ def _wrap_builder(builder, target_type: TargetType):
|
||||
if 'source' in kwargs:
|
||||
kwargs['source'] = _fix_filearg(kwargs['source'])
|
||||
|
||||
target = _Target()
|
||||
target = Target()
|
||||
if 'name' in kwargs:
|
||||
target.name = kwargs['name']
|
||||
else:
|
||||
@ -518,7 +495,7 @@ def _wrap_builder(builder, target_type: TargetType):
|
||||
|
||||
def _wrap_default(default):
|
||||
def _wrapped(env, arg):
|
||||
if isinstance(arg, _Target):
|
||||
if isinstance(arg, Target):
|
||||
env.Append(SPP_DEFAULT_TARGETS = [arg])
|
||||
elif isinstance(arg, dict) and '_target' in arg:
|
||||
default(arg['_target'])
|
||||
@ -528,7 +505,7 @@ def _wrap_default(default):
|
||||
|
||||
def _wrap_depends(depends):
|
||||
def _wrapped(env, dependant, dependency):
|
||||
if isinstance(dependant, _Target) or isinstance(dependency, _Target):
|
||||
if isinstance(dependant, Target) or isinstance(dependency, Target):
|
||||
env.Append(SPP_TARGET_DEPENDENCIES = [(dependant, dependency, depends)])
|
||||
return
|
||||
elif isinstance(dependant, dict) and '_target' in dependant:
|
||||
@ -538,7 +515,7 @@ def _wrap_depends(depends):
|
||||
depends(dependant, dependency)
|
||||
return _wrapped
|
||||
|
||||
def _build_target(target: _Target):
|
||||
def _build_target(target: Target):
|
||||
for dependency in target.dependencies:
|
||||
_inject_dependency(dependency, target.kwargs)
|
||||
if 'LIBS' in target.kwargs:
|
||||
@ -548,7 +525,7 @@ def _build_target(target: _Target):
|
||||
target.kwargs['LIBS'].remove(lib)
|
||||
target.kwargs['LIBS'].append(env.File(lib))
|
||||
pass
|
||||
elif isinstance(lib, _Target):
|
||||
elif isinstance(lib, Target):
|
||||
if not lib.target:
|
||||
_build_target(lib)
|
||||
target.kwargs['LIBS'].remove(lib)
|
||||
@ -568,6 +545,7 @@ def _finalize(env: Environment):
|
||||
_generate_project(generate_project)
|
||||
Exit(0)
|
||||
|
||||
_hook_pre_finalize.invoke()
|
||||
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),
|
||||
@ -594,13 +572,15 @@ def _finalize(env: Environment):
|
||||
for target in env['SPP_DEFAULT_TARGETS']:
|
||||
env.Default(target.target)
|
||||
for dependant, dependency, depends in env['SPP_TARGET_DEPENDENCIES']:
|
||||
if isinstance(dependant, _Target):
|
||||
if isinstance(dependant, Target):
|
||||
dependant = dependant.target
|
||||
if isinstance(dependency, _Target):
|
||||
if isinstance(dependency, Target):
|
||||
dependency = dependency.target
|
||||
depends(dependant, dependency)
|
||||
|
||||
def _find_target(env: Environment, target_name: str) -> '_Target|None':
|
||||
_hook_post_finalize.invoke()
|
||||
|
||||
def _find_target(env: Environment, target_name: str) -> 'Target|None':
|
||||
for target in env['SPP_TARGETS']:
|
||||
if target.name == target_name:
|
||||
return target
|
||||
@ -739,7 +719,7 @@ def _generate_project(project_type: str) -> None:
|
||||
|
||||
|
||||
def _get_sources(target_dict: dict) -> list[str]:
|
||||
target : _Target = target_dict['target']
|
||||
target : Target = target_dict['target']
|
||||
sources = target.kwargs.get('source')
|
||||
return [str(pathlib.Path(source.abspath).relative_to(root_path)) for source in sources]
|
||||
|
||||
@ -955,6 +935,9 @@ class _Hook:
|
||||
|
||||
_hook_pre_environment = _Hook()
|
||||
_hook_post_environment = _Hook()
|
||||
_hook_config_complete = _Hook()
|
||||
_hook_pre_finalize = _Hook()
|
||||
_hook_post_finalize = _Hook()
|
||||
|
||||
def _load_addon(modname: str, modpath: pathlib.Path) -> None:
|
||||
_debug('addons', f'Loading addon {modname} from {modpath}.')
|
||||
@ -966,12 +949,17 @@ def _load_addon(modname: str, modpath: pathlib.Path) -> None:
|
||||
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 _add_hook(func_name: str, hook: _Hook) -> None:
|
||||
if hasattr(module, func_name):
|
||||
hook.add_func(getattr(module, func_name))
|
||||
_debug('addons', f'Addon {modname} registered a {func_name} hook.')
|
||||
|
||||
_add_hook('pre_environment', _hook_pre_environment)
|
||||
_add_hook('post_environment', _hook_post_environment)
|
||||
_add_hook('config_complete', _hook_config_complete)
|
||||
_add_hook('pre_finalize', _hook_pre_finalize)
|
||||
_add_hook('post_finalize', _hook_post_finalize)
|
||||
|
||||
def _load_addons(folder: pathlib.Path) -> None:
|
||||
_debug('addons', f'Loading addons from {folder}.')
|
||||
@ -1235,7 +1223,7 @@ env['SPP_DEFAULT_TARGETS'] = []
|
||||
env['SPP_TARGET_DEPENDENCIES'] = []
|
||||
env['SPP_DEPENDENCIES'] = {}
|
||||
env['SPP_RECIPES'] = {}
|
||||
env['SPP_MODULES'] = {} # maps from folder to _Module
|
||||
env['SPP_MODULES'] = {} # maps from folder to Module
|
||||
env['SPP_CPU_FEATURES'] = config.get('USE_CPU_FEATURES', [])
|
||||
|
||||
env['OBJSUFFIX'] = f".{env['BUILD_TYPE']}{env['OBJSUFFIX']}"
|
||||
@ -1437,6 +1425,8 @@ env.AddMethod(_find_target, 'FindTarget')
|
||||
if hasattr(env, 'Gch'):
|
||||
env.AddMethod(_wrap_builder(env.Gch, TargetType.STATIC_LIBRARY), 'Gch')
|
||||
|
||||
_hook_config_complete.invoke()
|
||||
|
||||
for addon_file in env.Glob('addons/old/*.py'):
|
||||
env = SConscript(addon_file, exports = 'env')
|
||||
|
||||
|
35
addons/config_cache.py
Normal file
35
addons/config_cache.py
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from spp import get_spp, TargetType
|
||||
|
||||
spp = get_spp()
|
||||
|
||||
def _should_generate() -> bool:
|
||||
# check if any program or library target has been built
|
||||
for target in spp.targets:
|
||||
if target.target_type in (TargetType.PROGRAM, TargetType.STATIC_LIBRARY, TargetType.SHARED_LIBRARY):
|
||||
return True
|
||||
return False
|
||||
|
||||
def post_finalize(**kwargs) -> None:
|
||||
if not _should_generate():
|
||||
return
|
||||
|
||||
cache_file = Path(spp.env['CACHE_DIR']) / 'config_cache.json'
|
||||
|
||||
cache = {}
|
||||
if cache_file.exists():
|
||||
try:
|
||||
with cache_file.open('r') as f:
|
||||
cache = json.load(f)
|
||||
except Exception as e:
|
||||
spp.env.Warn(f'Error while loading config cache: {e}.')
|
||||
|
||||
cache['build_type'] = spp.env['BUILD_TYPE']
|
||||
|
||||
try:
|
||||
with cache_file.open('w') as f:
|
||||
json.dump(cache, f)
|
||||
except Exception as e:
|
||||
spp.env.Warn(f'Error while saving config cache: {e}.')
|
55
lib/spp.py
Normal file
55
lib/spp.py
Normal file
@ -0,0 +1,55 @@
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
from typing import TYPE_CHECKING
|
||||
from SCons.Script import *
|
||||
|
||||
if TYPE_CHECKING:
|
||||
class SPPEnvironment(Environment):
|
||||
def Info(self, message: str): ...
|
||||
def Warn(self, message: str): ...
|
||||
def Error(self, message: str): ...
|
||||
else:
|
||||
SPPEnvironment = Environment
|
||||
|
||||
@dataclass
|
||||
class Module:
|
||||
name: str
|
||||
folder: str
|
||||
description: str
|
||||
cxx_namespace: str
|
||||
|
||||
class TargetType(enum.Enum):
|
||||
PROGRAM = 0
|
||||
STATIC_LIBRARY = 1
|
||||
SHARED_LIBRARY = 2
|
||||
MISC = 3
|
||||
|
||||
class Target:
|
||||
name: str
|
||||
target_type: TargetType
|
||||
builder = None
|
||||
args: list = []
|
||||
kwargs: dict = {}
|
||||
dependencies: list = []
|
||||
target = None
|
||||
module: Module = None
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SPPInterface:
|
||||
globals: dict
|
||||
|
||||
@property
|
||||
def env(self) -> SPPEnvironment:
|
||||
return self.globals['env']
|
||||
|
||||
@property
|
||||
def targets(self) -> list[Target]:
|
||||
return self.env['SPP_TARGETS']
|
||||
|
||||
_spp: SPPInterface
|
||||
def _init_interface(**kwargs) -> None:
|
||||
global _spp
|
||||
_spp = SPPInterface(**kwargs)
|
||||
|
||||
def get_spp() -> SPPInterface:
|
||||
return _spp
|
0
util/__init__.py
Normal file
0
util/__init__.py
Normal file
24
util/python_module/sppcmd/__init__.py
Normal file
24
util/python_module/sppcmd/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""
|
||||
Scons++ Command Line Interface
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from .ccjson import make_ccjson_parser
|
||||
|
||||
_STDOUT_LOG_FORMAT = '%(message)s'
|
||||
|
||||
def run_spp_cmd() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--verbose', '-v', action='store_true')
|
||||
subparsers = parser.add_subparsers(required=True)
|
||||
|
||||
make_ccjson_parser(subparsers)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
logging.basicConfig(format=_STDOUT_LOG_FORMAT, level=logging.DEBUG if args.verbose else logging.INFO)
|
||||
args.handler(args)
|
||||
|
||||
return 0
|
18
util/python_module/sppcmd/ccjson.py
Normal file
18
util/python_module/sppcmd/ccjson.py
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
import argparse
|
||||
from .common import exec_spp, get_config_cache, require_project_file
|
||||
|
||||
def _cmd(args: argparse.Namespace) -> None:
|
||||
require_project_file()
|
||||
|
||||
build_type = args.build_type
|
||||
if build_type == 'auto':
|
||||
cache = get_config_cache()
|
||||
build_type = cache.get('build_type', 'debug')
|
||||
|
||||
exec_spp((f'--build_type={build_type}', '--unity=disable', 'compile_commands.json'))
|
||||
|
||||
def make_ccjson_parser(subparsers) -> None:
|
||||
parser : argparse.ArgumentParser = subparsers.add_parser('ccjson', help='Generate compile_commands.json')
|
||||
parser.set_defaults(handler=_cmd)
|
||||
parser.add_argument('--build_type', choices=('auto', 'debug', 'release_debug', 'release', 'profile'), default='auto')
|
51
util/python_module/sppcmd/common.py
Normal file
51
util/python_module/sppcmd/common.py
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Sequence
|
||||
|
||||
_project_root = Path('.').absolute()
|
||||
|
||||
def get_project_root() -> Path:
|
||||
return _project_root
|
||||
|
||||
def set_project_root(path: Path) -> None:
|
||||
global _project_root
|
||||
_project_root = path
|
||||
|
||||
def get_config_cache() -> dict:
|
||||
cache_file = get_project_root() / 'cache' / 'config_cache.json'
|
||||
if not cache_file.exists():
|
||||
return {}
|
||||
|
||||
try:
|
||||
with cache_file.open('r') as f:
|
||||
cache = json.load(f)
|
||||
if not isinstance(cache, dict):
|
||||
logging.warning('Config cache is not a dictionary, ignoring it.')
|
||||
return {}
|
||||
return cache
|
||||
except Exception as e:
|
||||
logging.error(f'Error while reading config cache: {e}.')
|
||||
return {}
|
||||
|
||||
def require_project_file() -> None:
|
||||
if not (get_project_root() / 'SConstruct').exists():
|
||||
logging.error('This command has to be run inside an existing S++ project folder. Exiting.')
|
||||
sys.exit(1)
|
||||
|
||||
def exec_checked(args: Sequence[str], **kwargs) -> None:
|
||||
logging.debug('exec_checked: "%s"', shlex.join(args))
|
||||
subprocess.run(args, stdout=sys.stdout, stderr=sys.stderr, check=True, **kwargs)
|
||||
|
||||
def exec_get_output(args: Sequence[str], **kwargs) -> str:
|
||||
logging.debug('exec_get_output: "%s"', shlex.join(args))
|
||||
return subprocess.run(args, text=True, check=True, capture_output=True, **kwargs).stdout
|
||||
|
||||
def exec_spp(args: Sequence[str], **kwargs):
|
||||
full_cmd = ('scons', '-s', '--disable_auto_update', *args)
|
||||
exec_checked(full_cmd, **kwargs)
|
10
util/spp_cmd.py
Executable file
10
util/spp_cmd.py
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'python_module'))
|
||||
|
||||
from sppcmd import run_spp_cmd
|
||||
sys.exit(run_spp_cmd())
|
Loading…
x
Reference in New Issue
Block a user