From bca239882833adf0f4df0052e7ba80e76e45f668 Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Sun, 12 Jan 2025 13:11:43 +0100 Subject: [PATCH] Added recipes for some Linux system libraries. --- SConscript | 31 +++++++++++++++++++++++++++--- addons/cmake_project.py | 1 + addons/gitbranch.py | 7 +++++++ recipes/X11/recipe.py | 23 ++++++++++++++++++++++ recipes/Xft/recipe.py | 25 ++++++++++++++++++++++++ recipes/fontconfig/recipe.py | 25 ++++++++++++++++++++++++ recipes/freetype/recipe.py | 23 ++++++++++++++++++++++ recipes/nana/recipe.py | 37 ++++++++++++++++++++++++++++++++++++ recipes/zlib/recipe.py | 32 +++++++++++++++++++++++++------ 9 files changed, 195 insertions(+), 9 deletions(-) create mode 100644 recipes/X11/recipe.py create mode 100644 recipes/Xft/recipe.py create mode 100644 recipes/fontconfig/recipe.py create mode 100644 recipes/freetype/recipe.py create mode 100644 recipes/nana/recipe.py diff --git a/SConscript b/SConscript index 6f3f3cf..a1b10df 100644 --- a/SConscript +++ b/SConscript @@ -179,6 +179,30 @@ def _make_interface(env: Environment, dependencies: list = []): 'CPPDEFINES': kwargs.get('CPPDEFINES', []) } +def _exe_filename(env: Environment, name: str, type: str = 'static') -> str: + if os.name == 'posix': + return name + elif os.name == 'nt': + return f'{name}.exe' + else: + raise Exception('What OS is this?') + +def _find_executable(env: Environment, name: str, paths: 'list[str]', type : str = 'static', allow_fail: bool = False, use_glob: bool = False): + fname = _exe_filename(env, name, type) + for path in paths: + lib_path = os.path.join(path, fname) + if use_glob: + files = glob.glob(lib_path) + if len(files) == 1: + return files[0] + elif len(files) > 1: + raise Exception(f'Multiple candidates found for executable with name {name} in paths: "{", ".join(paths)}" with name: "{", ".join(files)}".') + elif os.path.exists(lib_path): + return lib_path + if allow_fail: + return None + raise Exception(f'Could not find executable with name {name} in paths: "{", ".join(paths)}" filename: "{fname}".') + def _lib_filename(env: Environment, name: str, type: str = 'static') -> str: if os.name == 'posix': ext = { @@ -239,7 +263,7 @@ def _try_merge_dicts(dictA: dict, dictB: dict) -> 'dict|None': return result -def _find_common_depenency_version(name: str, versionA: _VersionSpec, versionB: _VersionSpec) -> _VersionSpec: +def _find_common_dependency_version(name: str, versionA: _VersionSpec, versionB: _VersionSpec) -> _VersionSpec: options = _try_merge_dicts(versionA.options, versionB.options) if options is None: return None @@ -272,13 +296,13 @@ def _can_add_dependency(env: Environment, name: str, version_spec: _VersionSpec) if name not in env['SPP_DEPENDENCIES']: return True dependency = env['SPP_DEPENDENCIES'][name] - common_version_spec = _find_common_depenency_version(name, dependency.version_spec, version_spec) + common_version_spec = _find_common_dependency_version(name, dependency.version_spec, version_spec) return common_version_spec is not None def _add_dependency(env: Environment, name: str, version_spec: _VersionSpec) -> _Dependency: if name in env['SPP_DEPENDENCIES']: dependency = env['SPP_DEPENDENCIES'][name] - common_version_spec = _find_common_depenency_version(name, dependency.version_spec, version_spec) + common_version_spec = _find_common_dependency_version(name, dependency.version_spec, version_spec) if common_version_spec is None: raise Exception(f'Incompatible versions detected for {name}: {dependency.version_spec} and {version_spec}') if dependency.version_spec != common_version_spec: @@ -938,6 +962,7 @@ env.AddMethod(_rglob, 'RGlob') env.AddMethod(_deps_from_json, 'DepsFromJson') env.AddMethod(_make_interface, 'MakeInterface') env.AddMethod(_lib_filename, 'LibFilename') +env.AddMethod(_find_executable, 'FindExecutable') env.AddMethod(_find_lib, 'FindLib') env.AddMethod(_error, 'Error') env.AddMethod(_wrap_builder(env.Library, TargetType.STATIC_LIBRARY), 'Library') diff --git a/addons/cmake_project.py b/addons/cmake_project.py index c266bc2..17c1aba 100644 --- a/addons/cmake_project.py +++ b/addons/cmake_project.py @@ -98,6 +98,7 @@ def _cmake_project(env: Environment, project_root: str, generate_args: 'list[str return { 'install_dir': install_dir, + 'BINPATH': [os.path.join(install_dir, 'bin')], 'LIBPATH': libpath, 'CPPPATH': [os.path.join(install_dir, 'include')] } diff --git a/addons/gitbranch.py b/addons/gitbranch.py index 565da04..06604f3 100644 --- a/addons/gitbranch.py +++ b/addons/gitbranch.py @@ -21,19 +21,26 @@ def _clone(env: Environment, repo_name: str, remote_url: str): def _git_branch(env: Environment, repo_name: str, remote_url: str, git_ref: str = 'main') -> dict: repo, origin = _clone(env, repo_name, remote_url) worktree_dir = os.path.join(env['CLONE_DIR'], 'git', repo_name, hashlib.shake_128(git_ref.encode('utf-8')).hexdigest(6)) # TODO: commit hash would be better, right? -> not if it's a branch! + update_submodules = False if not os.path.exists(worktree_dir): print(f'Checking out into {worktree_dir}.') origin.fetch(tags=True, force=True) os.makedirs(worktree_dir) repo.git.worktree('add', worktree_dir, git_ref) + worktree_repo = Repo(worktree_dir) + update_submodules = True elif env['UPDATE_REPOSITORIES']: worktree_repo = Repo(worktree_dir) if not worktree_repo.head.is_detached: print(f'Updating git repository at {worktree_dir}') worktree_origin = worktree_repo.remotes['origin'] worktree_origin.pull() + update_submodules = True else: print(f'Not updating git repository {worktree_dir} as it is not on a branch.') + if update_submodules: + for submodule in worktree_repo.submodules: + submodule.update(init=True) return { 'checkout_root': worktree_dir, 'repo': repo, diff --git a/recipes/X11/recipe.py b/recipes/X11/recipe.py new file mode 100644 index 0000000..d74bd0b --- /dev/null +++ b/recipes/X11/recipe.py @@ -0,0 +1,23 @@ + + +import os +from SCons.Script import * + + +def available(env: Environment): + if os.name != 'posix': + return 'X11 is only available on Linux.' + +def versions(env: Environment, update: bool = False): + if os.name == 'posix': + return [(0, 0, 0)] + else: + return [] + +def dependencies(env: Environment, version) -> 'dict': + return {} + +def cook(env: Environment, version) -> dict: + return { + 'LIBS': ['X11'] + } diff --git a/recipes/Xft/recipe.py b/recipes/Xft/recipe.py new file mode 100644 index 0000000..f7533b4 --- /dev/null +++ b/recipes/Xft/recipe.py @@ -0,0 +1,25 @@ + + +import os +from SCons.Script import * + + +def available(env: Environment): + if os.name != 'posix': + return 'Xft is only available on Linux.' + +def versions(env: Environment, update: bool = False): + if os.name == 'posix': + return [(0, 0, 0)] + else: + return [] + +def dependencies(env: Environment, version) -> 'dict': + return { + 'fontconfig': {} + } + +def cook(env: Environment, version) -> dict: + return { + 'LIBS': ['Xft'] + } diff --git a/recipes/fontconfig/recipe.py b/recipes/fontconfig/recipe.py new file mode 100644 index 0000000..1f5435b --- /dev/null +++ b/recipes/fontconfig/recipe.py @@ -0,0 +1,25 @@ + + +import os +from SCons.Script import * + + +def available(env: Environment): + if os.name != 'posix': + return 'Fontconfig is only available on Linux.' + +def versions(env: Environment, update: bool = False): + if os.name == 'posix': + return [(0, 0, 0)] + else: + return [] + +def dependencies(env: Environment, version) -> 'dict': + return { + 'freetype': {} + } + +def cook(env: Environment, version) -> dict: + return { + 'LIBS': ['fontconfig'] + } diff --git a/recipes/freetype/recipe.py b/recipes/freetype/recipe.py new file mode 100644 index 0000000..774f096 --- /dev/null +++ b/recipes/freetype/recipe.py @@ -0,0 +1,23 @@ + + +import os +from SCons.Script import * + + +def available(env: Environment): + if os.name != 'posix': + return 'Freetype is only available on Linux.' + +def versions(env: Environment, update: bool = False): + if os.name == 'posix': + return [(0, 0, 0)] + else: + return [] + +def dependencies(env: Environment, version) -> 'dict': + return {} + +def cook(env: Environment, version) -> dict: + return { + 'LIBS': ['freetype'] + } diff --git a/recipes/nana/recipe.py b/recipes/nana/recipe.py new file mode 100644 index 0000000..b0c88a4 --- /dev/null +++ b/recipes/nana/recipe.py @@ -0,0 +1,37 @@ + + +import os +import re +from SCons.Script import * + + +def _git_cook(env: Environment, repo: dict) -> dict: + checkout_root = repo['checkout_root'] + build_result = env.CMakeProject(checkout_root, generate_args = ['-DNANA_CMAKE_INSTALL=ON']) + + lib_name = 'nana' + return { + 'CPPPATH': build_result['CPPPATH'], + 'LIBS': [env.FindLib(lib_name, paths=build_result['LIBPATH'])] + } + +def _dependencies(env: Environment, version) -> dict: + result = {} + if os.name == 'nt': + pass + elif os.name == 'posix': + result.update({ + 'X11': {}, + 'Xft': {} + }) + return result + +env.GitRecipe( + globals = globals(), + repo_name = 'nana', + repo_url = 'httpshttps://github.com/mewin/nana.git', + tag_pattern = re.compile(r'^v([0-9]+)\.([0-9]+)\.([0-9]+)$'), + tag_fn = lambda version: f'v{version[0]}.{version[1]}.{version[2]}', + cook_fn = _git_cook, + dependencies = _dependencies +) diff --git a/recipes/zlib/recipe.py b/recipes/zlib/recipe.py index d764b99..23a9158 100644 --- a/recipes/zlib/recipe.py +++ b/recipes/zlib/recipe.py @@ -6,6 +6,16 @@ from SCons.Script import * _REPO_NAME = 'zlib' _REPO_URL = 'https://github.com/madler/zlib.git' _TAG_PATTERN = re.compile(r'^v([0-9]+)\.([0-9]+)(?:\.([0-9]+))?$') +_VERSION_SOURCE = ''' + #include + #include + + int main(int, char**) + { + puts(ZLIB_VERSION); + return 0; + } +''' def _build_lib_name(env: Environment) -> str: if os.name == 'posix': @@ -17,19 +27,29 @@ def _build_lib_name(env: Environment) -> str: else: raise Exception('libpng is not supported yet on this OS') -def versions(env: Environment, update: bool = False): +def versions(env: Environment, update: bool = False, options: dict = {}): tags = env.GitTags(repo_name = _REPO_NAME, remote_url = _REPO_URL, force_fetch=update) result = [] - for tag in tags: - match = _TAG_PATTERN.match(tag) - if match: - result.append((int(match.groups()[0]), int(match.groups()[1]), int(match.groups()[2] or 0))) + + if options.get('use_system_library'): + version = env.VersionFromSource('pkgconf --cflags-only-I zlib', _VERSION_SOURCE) + if version: + env['SYSTEM_ZLIB_VERSION'] = version + result.append(version) + else: + for tag in tags: + match = _TAG_PATTERN.match(tag) + if match: + result.append((int(match.groups()[0]), int(match.groups()[1]), int(match.groups()[2] or 0))) return result def dependencies(env: Environment, version) -> 'dict': return {} -def cook(env: Environment, version) -> dict: +def cook(env: Environment, version, options: dict = {}) -> dict: + if options.get('use_system_library'): + return env.PkgCook('pkgconf --cflags --libs zlib') + git_ref = f'refs/tags/v{version[0]}.{version[1]}' if version[2] != 0: git_ref = git_ref + f'.{version[2]}'