diff --git a/cerbero/bootstrap/bootstrapper.py b/cerbero/bootstrap/bootstrapper.py index 365b7f2dc..73221747b 100644 --- a/cerbero/bootstrap/bootstrapper.py +++ b/cerbero/bootstrap/bootstrapper.py @@ -86,10 +86,11 @@ def __new__(klass, config, system, toolchains, build_tools, offline, assume_yes) return bs -from cerbero.bootstrap import linux, windows, android, osx, ios # noqa: E402 +from cerbero.bootstrap import linux, windows, android, osx, ios, emscripten # noqa: E402 linux.register_all() windows.register_all() android.register_all() osx.register_all() ios.register_all() +emscripten.register_all() diff --git a/cerbero/bootstrap/emscripten.py b/cerbero/bootstrap/emscripten.py new file mode 100644 index 000000000..f6a904a88 --- /dev/null +++ b/cerbero/bootstrap/emscripten.py @@ -0,0 +1,64 @@ +# cerbero - a multi-platform build system for Open Source software +# Copyright (C) 2024 Fluendo S.A +# Authors: Maxim Dementyev +# Andoni Morales +# Jorge Zapata +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +from cerbero.bootstrap import BootstrapperBase +from cerbero.bootstrap.bootstrapper import register_toolchain_bootstrapper +from cerbero.enums import Distro +from cerbero.utils import messages as m +from cerbero.utils import shell +import os.path +import shutil + +EMSDK_VERSION = '4.0.6' +EMSDK_BUNDLE_EXT = '.tar.gz' +EMSDK_BASE_URL = 'https://github.com/emscripten-core/emsdk/archive/refs/tags/%s' + EMSDK_BUNDLE_EXT +EMSDK_CHECKSUMS = {'4.0.6' + EMSDK_BUNDLE_EXT: '2d3292d508b4f5477f490b080b38a34aaefed43e85258a1de72cb8dde3f8f3af'} + + +class EmscriptenToolchainBootstrapper(BootstrapperBase): + """ + Bootstrapper for Emscripten builds. + Installs the Emscripten SDK + """ + + def __init__(self, config, offline, assume_yes): + super().__init__(config, offline, 'emscripten') + url = EMSDK_BASE_URL % (EMSDK_VERSION) + bundle_name = 'emsdk-' + EMSDK_VERSION + EMSDK_BUNDLE_EXT + urls = (url, bundle_name, EMSDK_CHECKSUMS[os.path.basename(url)]) + self.fetch_urls.append(urls) + self.extract_steps.append((url, True, self.config.home_dir)) + + async def start(self, jobs=0): + sdk_path_after_extract = os.path.join(self.config.home_dir, 'emsdk-' + EMSDK_VERSION) + shutil.rmtree(self.config.toolchain_prefix) # to be sure it's rename and not a move + shutil.move(sdk_path_after_extract, self.config.toolchain_prefix) + m.message(f'Install Emscripten SDK {EMSDK_VERSION}...') + await shell.async_call_output( + ['./emsdk', 'install', EMSDK_VERSION], cmd_dir=self.config.toolchain_prefix, cpu_bound=False + ) + m.message('Activate Emscripten SDK...') + await shell.async_call_output( + ['./emsdk', 'activate', EMSDK_VERSION], cmd_dir=self.config.toolchain_prefix, cpu_bound=False + ) + + +def register_all(): + register_toolchain_bootstrapper(Distro.EMSCRIPTEN, EmscriptenToolchainBootstrapper) diff --git a/cerbero/build/build.py b/cerbero/build/build.py index 868d7bb33..092e69c98 100644 --- a/cerbero/build/build.py +++ b/cerbero/build/build.py @@ -344,6 +344,16 @@ class Build(object): def __init__(self): self._properties_keys = [] + # Initialize the default build dir + # The folder where the build artifacts will be generated + self.config_build_dir = os.path.abspath(os.path.join(self.config.sources, self.package_name)) + # The build dir might be different than the theoretical source dir + Path(self.config_build_dir).mkdir(parents=True, exist_ok=True) + # Initialize the default sources dir + # The folder where the actual build system's sources are located + self.sources_dir = self.config_src_dir + if self.config.target_platform == Platform.WEB: + self.library_type = LibraryType.STATIC @modify_environment def get_env(self, var, default=None): @@ -432,7 +442,6 @@ class MakefilesBase(Build, ModifyEnvBase): make_clean = None allow_parallel_build = True srcdir = '.' - requires_non_src_build = False # recipes often use shell constructs config_sh_needs_shell = True @@ -444,11 +453,8 @@ def __init__(self): if not self.using_msvc(): self.setup_buildtype_env_ops() - if self.requires_non_src_build: - self.make_dir = os.path.join(self.config_src_dir, 'cerbero-build-dir') - else: - self.make_dir = os.path.abspath(os.path.join(self.config_src_dir, self.srcdir)) - + self.sources_dir = os.path.abspath(os.path.join(self.config_src_dir, self.srcdir)) + self.configure_dir = self.sources_dir self.make = self.make or ['make', 'V=1'] self.make_install = self.make_install or ['make', 'install'] self.make_clean = self.make_clean or ['make', 'clean'] @@ -469,10 +475,8 @@ async def configure(self): When called from a method in deriverd class, that method has to be decorated with modify_environment decorator. """ - if not os.path.exists(self.make_dir): - os.makedirs(self.make_dir) - if self.requires_non_src_build: - self.config_sh = os.path.join('../', self.config_sh) + if not os.path.exists(self.config_build_dir): + os.makedirs(self.config_build_dir) substs = { 'config-sh': self.config_sh, @@ -482,8 +486,8 @@ async def configure(self): 'target': self.config.target, 'build': self.config.build, 'options': self.configure_options, - 'build_dir': self.build_dir, - 'make_dir': self.make_dir, + 'build_dir': self.config_build_dir, + 'sources_dir': self.sources_dir, } # Construct a command list when possible @@ -502,28 +506,29 @@ async def configure(self): self.maybe_add_system_libs(step='configure') - await shell.async_call(configure_cmd, self.make_dir, logfile=self.logfile, env=self.env) + await shell.async_call(configure_cmd, self.configure_dir, + logfile=self.logfile, env=self.env) @modify_environment async def compile(self): self.maybe_add_system_libs(step='compile') - await shell.async_call(self.make, self.make_dir, logfile=self.logfile, env=self.env) + await shell.async_call(self.make, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment async def install(self): self.maybe_add_system_libs(step='install') - await shell.async_call(self.make_install, self.make_dir, logfile=self.logfile, env=self.env) + await shell.async_call(self.make_install, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment def clean(self): self.maybe_add_system_libs(step='clean') - shell.new_call(self.make_clean, self.make_dir, logfile=self.logfile, env=self.env) + shell.new_call(self.make_clean, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment def check(self): if self.make_check: self.maybe_add_system_libs(step='check') - shell.new_call(self.make_check, self.build_dir, logfile=self.logfile, env=self.env) + shell.new_call(self.make_check, self.config_build_dir, logfile=self.logfile, env=self.env) class Makefile(MakefilesBase): @@ -531,6 +536,14 @@ class Makefile(MakefilesBase): Build handler for Makefile project """ + def __init__(self): + # For a generic Makefile based project, the src dir can not be different than the build dir + if self.config_src_dir != os.path.abspath(os.path.join(self.config.sources, self.package_name)): + raise FatalError('For a Makefile based project, source and build dirs must be the same') + # For a generic Makefile based project, the srcdir property is also set on the builddir + MakefilesBase.__init__(self) + self.config_build_dir = self.sources_dir + @modify_environment async def configure(self): await MakefilesBase.configure(self) @@ -557,10 +570,18 @@ class Autotools(MakefilesBase): def __init__(self): MakefilesBase.__init__(self) + # configure_sh must be called from the self.config_build_dir + self.configure_dir = self.config_build_dir + # Use the absolute path for configure as we call it from another dir + self.config_sh = os.path.join(self.sources_dir, self.config_sh) self.make_check = self.make_check or ['make', 'check'] @modify_environment async def configure(self): + # Configuring an autotools project implies: + # autoreconf_sh from self.sources_dir + # configure_sh from self.config_build_dir + # Build with PIC for static linking self.configure_tpl += ' --with-pic ' # Disable automatic dependency tracking, speeding up one-time builds @@ -580,7 +601,8 @@ async def configure(self): self.configure_tpl += ' --disable-introspection ' if self.autoreconf: - await shell.async_call(self.autoreconf_sh, self.config_src_dir, logfile=self.logfile, env=self.env) + await shell.async_call(self.autoreconf_sh, self.sources_dir, + logfile=self.logfile, env=self.env) # We don't build libtool on Windows if self.config.platform == Platform.WINDOWS: @@ -593,7 +615,7 @@ async def configure(self): if self.name != 'libtool' and self.override_libtool: cfs['ltmain.sh'] = os.path.join(self.config.build_tools_prefix, 'share/libtool/build-aux') for cf, srcdir in cfs.items(): - find_cmd = 'find {} -type f -name {}'.format(self.config_src_dir, cf) + find_cmd = 'find {} -maxdepth 0 -type f -name {}'.format(self.config_src_dir, cf) files = await shell.async_call_output(find_cmd, logfile=self.logfile, env=self.env) files = files.split('\n') files.remove('') @@ -629,6 +651,13 @@ async def configure(self): cache = os.path.join(self.config.sources, '.configure.cache') self.configure_tpl += ' --cache-file=%s' % cache + if self.library_type == LibraryType.BOTH: + self.configure_tpl += ' --enable-shared --enable-static' + elif self.library_type == LibraryType.SHARED: + self.configure_tpl += ' --enable-shared --disable-static' + elif self.library_type == LibraryType.STATIC: + self.configure_tpl += ' --disable-shared --enable-static' + # Add at the very end to allow recipes to override defaults self.configure_tpl += ' %(options)s ' @@ -643,26 +672,25 @@ class CMake(MakefilesBase): cmake_generator = 'make' config_sh_needs_shell = False config_sh = None - configure_tpl = ( - '%(config-sh)s -DCMAKE_INSTALL_PREFIX=%(prefix)s ' - '-H%(make_dir)s ' - '-B%(build_dir)s ' - '-DCMAKE_LIBRARY_OUTPUT_PATH=%(libdir)s ' - '-DCMAKE_INSTALL_BINDIR=bin ' - '-DCMAKE_INSTALL_INCLUDEDIR=include ' - '%(options)s -DCMAKE_BUILD_TYPE=Release ' - '-DCMAKE_FIND_ROOT_PATH=$CERBERO_PREFIX ' - '-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true ' - ) + configure_tpl = '%(config-sh)s -DCMAKE_INSTALL_PREFIX=%(prefix)s ' \ + '-B%(build_dir)s ' \ + '-DCMAKE_LIBRARY_OUTPUT_PATH=%(libdir)s ' \ + '-DCMAKE_INSTALL_BINDIR=bin ' \ + '-DCMAKE_INSTALL_INCLUDEDIR=include ' \ + '%(options)s -DCMAKE_BUILD_TYPE=Release '\ + '-DCMAKE_FIND_ROOT_PATH=$CERBERO_PREFIX '\ + '-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true ' def __init__(self): MakefilesBase.__init__(self) - self.build_dir = os.path.join(self.build_dir, 'b') + self.config_build_dir = os.path.join(self.config_build_dir, '_builddir') self.config_sh = 'cmake' self.configure_tpl += f'-DCMAKE_INSTALL_LIBDIR={self.config.rel_libdir} ' if self.config.distro == Distro.MSYS2: # We do not want the MSYS2 CMake because it doesn't support MSVC self.config_sh = shutil.which('cmake', path=shell.get_path_minus_msys(self.env['PATH'])) + elif self.config.target_platform == Platform.WEB: + self.configure_tpl = 'emcmake ' + self.configure_tpl @modify_environment async def configure(self): @@ -707,6 +735,8 @@ async def configure(self): system_name = 'Darwin' elif self.config.target_platform == Platform.IOS: system_name = 'iOS' + elif self.config.target_platform == Platform.WEB: + system_name = 'Emscripten' if self.config.target_platform in (Platform.DARWIN, Platform.IOS): self.configure_options += ['-DCMAKE_OSX_ARCHITECTURES=' + self.config.target_arch] @@ -733,7 +763,8 @@ async def configure(self): with open(f'{self.config_src_dir}/toolchain.cmake', 'w') as f: f.write(f'set(CMAKE_SYSTEM_NAME {system_name})\n') f.write(f'set(CMAKE_SYSTEM_PROCESSOR {self.config.target_arch})\n') - self.configure_options += [f'-DCMAKE_TOOLCHAIN_FILE={self.config_src_dir}/toolchain.cmake'] + if self.config.target_platform != Platform.WEB: + self.configure_options += [f'-DCMAKE_TOOLCHAIN_FILE={self.config_src_dir}/toolchain.cmake'] elif self.config.target_platform == Platform.WINDOWS: self.configure_options += [ f'-DCMAKE_SYSTEM_NAME={system_name}', @@ -758,17 +789,13 @@ async def configure(self): '-DLIB_SUFFIX=' + self.config.lib_suffix, ] - cmake_cache = os.path.join(self.make_dir, 'CMakeCache.txt') - cmake_files = os.path.join(self.make_dir, 'CMakeFiles') + cmake_cache = os.path.join(self.config_build_dir, 'CMakeCache.txt') + cmake_files = os.path.join(self.config_build_dir, 'CMakeFiles') if os.path.exists(cmake_cache): os.remove(cmake_cache) if os.path.exists(cmake_files): shutil.rmtree(cmake_files) await MakefilesBase.configure(self) - if not os.path.exists(self.build_dir): - os.makedirs(self.build_dir) - # as build_dir is different from source dir, makefile location will be in build_dir. - self.make_dir = self.build_dir MESON_FILE_TPL = """ @@ -854,7 +881,7 @@ def __init__(self): # Allow CMake dependencies to be found if requested if self.need_cmake: self.append_env('CMAKE_PREFIX_PATH', self.config.prefix, sep=os.pathsep) - self.meson_dir = os.path.join(self.build_dir, self.meson_builddir) + self.config_build_dir = os.path.join(self.config_build_dir, self.meson_builddir) @staticmethod def _get_option_value(opt_type, value): @@ -873,10 +900,10 @@ def _set_option(self, opt_names, variant_name): if opt_names.intersection(self.meson_options): return # Error out on invalid usage - if not os.path.isdir(self.build_dir): - raise FatalError("Build directory doesn't exist yet?") + if not os.path.isdir(self.config_build_dir): + raise FatalError('Build directory doesn\'t exist yet?') # Check if the option exists, and if so, what the type is - meson_options = os.path.join(self.build_dir, 'meson_options.txt') + meson_options = os.path.join(self.sources_dir, 'meson_options.txt') if not os.path.isfile(meson_options): return opt_name = None @@ -968,7 +995,7 @@ def _get_meson_target_file_contents(self): binaries['qmake6'] = [self.config.qt6_qmake_path] # Point meson to rustc with correct arguments to ensure it's detected when cross-compiling - if self.config.cargo_home: + if self.config.variants.rust and self.config.cargo_home: target_triple = self.config.rust_triple( self.config.target_arch, self.config.target_platform, self.using_msvc() ) @@ -1019,8 +1046,13 @@ def _get_meson_target_file_contents(self): for k, v in binaries.items(): extra_binaries += '{} = {}\n'.format(k, str(v)) + system = self.config.target_platform + if self.config.target_distro == Distro.EMSCRIPTEN: + system = 'emscripten' + extra_binaries += "exe_wrapper = ['node']\n" + contents = MESON_FILE_TPL.format( - system=self.config.target_platform, + system=system, cpu=self.config.target_arch, cpu_family=self._get_target_cpu_family(), # Assume all supported target archs are little endian @@ -1123,23 +1155,20 @@ def _get_meson_dummy_file_contents(self): return contents def _write_meson_file(self, contents, fname): - fpath = os.path.join(self.meson_dir, fname) + fpath = os.path.join(self.config_build_dir, fname) with open(fpath, 'w', encoding='utf-8') as f: f.write(contents) return fpath @modify_environment async def configure(self): - # self.build_dir is different on each call to configure() when doing universal builds - self.meson_dir = os.path.join(self.build_dir, self.meson_builddir) - if os.path.exists(self.meson_dir): + if os.path.exists(self.config_build_dir): # Only remove if it's not empty - if os.listdir(self.meson_dir): - shutil.rmtree(self.meson_dir) - os.makedirs(self.meson_dir) + if os.listdir(self.config_build_dir): + shutil.rmtree(self.config_build_dir) + os.makedirs(self.config_build_dir) else: - os.makedirs(self.meson_dir) - + os.makedirs(self.config_build_dir) # Explicitly enable/disable introspection, same as Autotools self._set_option({'introspection', 'gir'}, 'gi') # Control python support using the variant @@ -1207,6 +1236,9 @@ async def configure(self): for key, value in self.meson_options.items(): meson_cmd += ['-D%s=%s' % (key, str(value))] + # Set the source and build dirs + meson_cmd += [self.config_build_dir, self.sources_dir] + # We export the target toolchain with env vars, but that confuses Meson # when cross-compiling (it will pick the env vars for the build # machine). We always set this using the cross file or native file as @@ -1217,27 +1249,27 @@ async def configure(self): self.unset_toolchain_env() self.maybe_add_system_libs(step='configure') - await shell.async_call(meson_cmd, self.meson_dir, logfile=self.logfile, env=self.env) + await shell.async_call(meson_cmd, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment async def compile(self): self.maybe_add_system_libs(step='compile') - await shell.async_call(self.make, self.meson_dir, logfile=self.logfile, env=self.env) + await shell.async_call(self.make, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment async def install(self): self.maybe_add_system_libs(step='install') - await shell.async_call(self.make_install, self.meson_dir, logfile=self.logfile, env=self.env) + await shell.async_call(self.make_install, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment def clean(self): self.maybe_add_system_libs(step='clean') - shell.new_call(self.make_clean, self.meson_dir, logfile=self.logfile, env=self.env) + shell.new_call(self.make_clean, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment def check(self): self.maybe_add_system_libs(step='check') - shell.new_call(self.make_check, self.meson_dir, logfile=self.logfile, env=self.env) + shell.new_call(self.make_check, self.config_build_dir, logfile=self.logfile, env=self.env) class Cargo(Build, ModifyEnvBase): @@ -1264,9 +1296,11 @@ def __init__(self): if not self.using_msvc(): self.setup_buildtype_env_ops() - self.config_src_dir = os.path.abspath(os.path.join(self.build_dir, self.srcdir)) - self.cargo_dir = os.path.join(self.config_src_dir, 'b') - self.cargo = os.path.join(self.config.cargo_home, 'bin', 'cargo' + self.config._get_exe_suffix()) + self.sources_dir = os.path.abspath(os.path.join(self.config_src_dir, + self.srcdir)) + self.config_build_dir = os.path.join(self.config_build_dir, '_builddir') + self.cargo = os.path.join(self.config.cargo_home, 'bin', + 'cargo' + self.config._get_exe_suffix()) # Debuginfo is enormous, about 0.5GB per plugin, so it's split out # where enabled by default (macOS and MSVC) and stripped everywhere @@ -1291,7 +1325,7 @@ def __init__(self): '--target', self.target_triple, '--target-dir', - self.cargo_dir, + self.config_build_dir, ] jobs = self.num_of_cpus() @@ -1345,13 +1379,13 @@ def get_cargo_toml_version(self): return data['package']['version'] async def configure(self): - if os.path.exists(self.cargo_dir): + if os.path.exists(self.config_build_dir): # Only remove if it's not empty - if os.listdir(self.cargo_dir): - shutil.rmtree(self.cargo_dir) - os.makedirs(self.cargo_dir) + if os.listdir(self.config_build_dir): + shutil.rmtree(self.config_build_dir) + os.makedirs(self.config_build_dir) else: - os.makedirs(self.cargo_dir) + os.makedirs(self.config_build_dir) # TODO: Ideally we should strip while packaging, not while linking if self.rustc_debuginfo == 'strip': @@ -1448,13 +1482,13 @@ def get_cargoc_args(self): async def compile(self): self.maybe_add_system_libs(step='configure+compile') cmd = [self.cargo, 'cbuild'] + self.get_cargoc_args() - await self.retry_run(shell.async_call, cmd, self.cargo_dir, logfile=self.logfile, env=self.env) + await self.retry_run(shell.async_call, cmd, self.config_build_dir, logfile=self.logfile, env=self.env) @modify_environment async def install(self): self.maybe_add_system_libs(step='configure+install') cmd = [self.cargo, 'cinstall'] + self.get_cargoc_args() - await self.retry_run(shell.async_call, cmd, self.cargo_dir, logfile=self.logfile, env=self.env) + await self.retry_run(shell.async_call, cmd, self.config_build_dir, logfile=self.logfile, env=self.env) class BuildType(object): diff --git a/cerbero/build/filesprovider.py b/cerbero/build/filesprovider.py index 0e2c4b241..dc47e3cc6 100644 --- a/cerbero/build/filesprovider.py +++ b/cerbero/build/filesprovider.py @@ -185,6 +185,14 @@ class FilesProvider(object): 'pext': '.so', 'srext': '.dylib', }, + Platform.WEB: { + 'bext': '.js', + 'sregex': _DYLIB_REGEX, + 'mext': '.so', + 'smext': '.a', + 'pext': '.so', + 'srext': '.dylib', + }, } # Match static gstreamer plugins, GIO modules, etc. diff --git a/cerbero/build/oven.py b/cerbero/build/oven.py index 0bd287655..c10cab3ee 100644 --- a/cerbero/build/oven.py +++ b/cerbero/build/oven.py @@ -142,6 +142,10 @@ async def start_cooking(self): if self.deps_only: ordered_recipes = [x for x in ordered_recipes if x not in recipes] + if not ordered_recipes: + m.message(N_('No dependencies to build.')) + return + m.message(N_('Building the following recipes: %s') % ' '.join([x.name for x in ordered_recipes])) steps = [step[1] for step in recipes[0].steps] @@ -478,13 +482,12 @@ async def _cook_recipe_step_with_prompt(self, recipe, step, count): if be.step == BuildSteps.EXTRACT[1]: source_dir = recipe.get_for_arch(be.arch, 'config_src_dir') else: - source_dir = recipe.get_for_arch(be.arch, 'build_dir') - shell.enter_build_environment( - self.config.target_platform, be.arch, self.config.distro, source_dir, env=environ - ) + source_dir = recipe.get_for_arch(be.arch, 'config_build_dir') + shell.enter_build_environment(self.config.target_platform, + be.arch, self.config.distro, source_dir, env=environ) raise be elif action == RecoveryActions.RETRY_ALL: - shutil.rmtree(recipe.get_for_arch(be.arch, 'build_dir')) + shutil.rmtree(recipe.get_for_arch(be.arch, 'config_build_dir')) self.cookbook.reset_recipe_status(recipe.name) # propagate up to the task manager to retry the recipe entirely raise RetryRecipeError() diff --git a/cerbero/build/recipe.py b/cerbero/build/recipe.py index 8880667f3..41d22a99b 100644 --- a/cerbero/build/recipe.py +++ b/cerbero/build/recipe.py @@ -281,12 +281,7 @@ class Recipe(FilesProvider, metaclass=MetaRecipe): def __init__(self, config, env): self.config = config if self.package_name is None: - self.package_name = '%s-%s' % (self.name, self.version) - if not hasattr(self, 'repo_dir'): - self.repo_dir = os.path.join(self.config.local_sources, self.package_name) - self.repo_dir = os.path.abspath(self.repo_dir) - self.build_dir = os.path.abspath(os.path.join(self.config.sources, self.package_name)) - self.config_src_dir = self.build_dir + self.package_name = "%s-%s" % (self.name, self.version) self.deps = self.deps or [] if self.config.prefix_is_build_tools(): if self.btype == build.BuildType.MESON: @@ -671,7 +666,7 @@ def _install_datadir_license(self, lobj, install_dir): raise RuntimeError(msg.format(self.name)) fname = lobj.acronym + '.txt' dest = str(install_dir / fname) - src = os.path.join(self.config.data_dir, 'licenses', lobj.acronym + '.txt') + src = self.config.get_data_file(os.path.join('licenses', lobj.acronym + '.txt')) if shell.DRY_RUN: print('Copying {!r} to {!r}'.format(src, dest)) else: diff --git a/cerbero/build/source.py b/cerbero/build/source.py index a2b0ec5eb..caf4baf8e 100644 --- a/cerbero/build/source.py +++ b/cerbero/build/source.py @@ -74,6 +74,8 @@ def __init__(self): if not self.version: raise InvalidRecipeError(self, N_("'version' attribute is missing in the recipe")) + self.config_src_dir = os.path.abspath(os.path.join(self.config.sources, self.package_name)) + self.repo_dir = os.path.abspath(os.path.join(self.config.local_sources, self.package_name)) @property def check_cert(self): @@ -464,7 +466,6 @@ async def extract_impl(self, fetching=False): m.action(N_('Extracting tarball to %s') % self.config_src_dir, logfile=get_logfile(self)) if os.path.exists(self.config_src_dir): shutil.rmtree(self.config_src_dir) - unpack_dir = self.config.sources if self.tarball_is_bomb: unpack_dir = self.config_src_dir @@ -720,9 +721,30 @@ def built_version(self): return '%s+svn~%s' % (self.version, svn.revision(self.repo_dir)) +class LocalDir(Source): + ''' + Source handler for local files + ''' + path = None + + def __init__(self): + if not self.path: + raise FatalError('Missing path. It is mandatory') + Source.__init__(self) + # Overwrite the source dir to not be relative to the prefix + self.config_src_dir = os.path.abspath(self.path) + + def fetch_impl(self): + pass + + async def extract(self): + pass + + class SourceType(object): CUSTOM = CustomSource TARBALL = Tarball GIT = Git GIT_TARBALL = GitExtractedTarball SVN = Svn + LOCAL_DIR = LocalDir diff --git a/cerbero/commands/gensdkshell.py b/cerbero/commands/gensdkshell.py index 227586cba..ffbad06a8 100644 --- a/cerbero/commands/gensdkshell.py +++ b/cerbero/commands/gensdkshell.py @@ -96,7 +96,8 @@ def runargs( self._putvar('LD_LIBRARY_PATH', '%s${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}' % libdir) self._putvar( 'PKG_CONFIG_PATH', - '%s/pkgconfig:%s/share/pkgconfig' '${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}' % (libdir, prefix_env), + f'{libdir}/pkgconfig:{libdir}/gstreamer-1.0/pkgconfig:{prefix_env}/share/pkgconfig' + + '${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}', ) self._putvar('XDG_DATA_DIRS', '%s/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}' % prefix_env) self._putvar('XDG_CONFIG_DIRS', '%s/etc/xdg${XDG_CONFIG_DIRS:+:$XDG_CONFIG_DIRS}' % prefix_env) diff --git a/cerbero/config.py b/cerbero/config.py index 309b23c2a..d2e5212b7 100644 --- a/cerbero/config.py +++ b/cerbero/config.py @@ -20,6 +20,7 @@ import sys import copy import sysconfig +from importlib import resources from functools import lru_cache from pathlib import PurePath, Path @@ -62,6 +63,7 @@ (Platform.WINDOWS, Architecture.ARM64, 'msvc'): 'aarch64-pc-windows-msvc', (Platform.WINDOWS, Architecture.X86, 'gnu'): 'i686-pc-windows-gnu', (Platform.WINDOWS, Architecture.X86, 'msvc'): 'i686-pc-windows-msvc', + (Platform.WEB, Architecture.WASM32): 'wasm32-unknown-emscripten', } @@ -250,7 +252,7 @@ class Config(object): 'git_root', 'distro', 'target_distro', - 'environ_dir', + 'config_dir', 'cache_file', 'toolchain_prefix', 'toolchain_version', @@ -631,6 +633,7 @@ def get_env(self, prefix, libdir): PkgConfig.set_executable(env, self) PkgConfig.set_default_search_dir(os.path.join(prefix, 'share', 'pkgconfig'), env, self) PkgConfig.add_search_dir(os.path.join(libdir, 'pkgconfig'), env, self) + PkgConfig.add_search_dir(os.path.join(libdir, 'gstreamer-1.0', 'pkgconfig'), env, self) # Some autotools recipes will call the native (non-cross) compiler to # build generators, and we don't want it to use these. We will set the @@ -684,7 +687,7 @@ def load_defaults(self): self.set_property('exe_suffix', self._get_exe_suffix()) self.set_property('data_dir', self._find_data_dir()) self.set_property('cached_sources', self._relative_path('sources')) - self.set_property('environ_dir', self._relative_path('config')) + self.set_property('config_dir', self._relative_path('config')) self.set_property('recipes_dir', self._relative_path('recipes')) self.set_property('packages_dir', self._relative_path('packages')) self.set_property('allow_system_libs', True) @@ -732,6 +735,13 @@ def get_packages_repos(self): packages_dir[name] = (path, priority) return packages_dir + def get_data_file(self, filepath): + if self.uninstalled: + p = os.path.join(self.data_dir, filepath) + return os.path.abspath(p) + else: + return self.data_dir.joinpath(filepath) + def recipe_commit(self, recipe_name): if self.force_git_commit: return self.force_git_commit @@ -957,8 +967,8 @@ def _load_cmd_config(self, filenames): raise ConfigurationError(_("Configuration file %s doesn't " 'exist') % f) def _load_platform_config(self): - platform_config = os.path.join(self.environ_dir, '%s.config' % self.target_platform) - arch_config = os.path.join(self.environ_dir, '%s_%s.config' % (self.target_platform, self.target_arch)) + platform_config = os.path.join(self.config_dir, '%s.config' % self.target_platform) + arch_config = os.path.join(self.config_dir, '%s_%s.config' % (self.target_platform, self.target_arch)) for config_path in [platform_config, arch_config]: if os.path.exists(config_path): @@ -1025,22 +1035,15 @@ def _find_data_dir(self): if self.uninstalled: self.data_dir = os.path.join(os.path.dirname(__file__), '..', 'data') self.data_dir = os.path.abspath(self.data_dir) - return - curdir = os.path.dirname(__file__) - while not os.path.exists(os.path.join(curdir, 'share', 'cerbero', 'config')): - curdir = os.path.abspath(os.path.join(curdir, '..')) - if curdir == '/' or curdir[1:] == ':/': - # We reached the root without finding the data dir, which - # shouldn't happen - raise FatalError('Data dir not found') - self.data_dir = os.path.join(curdir, 'share', 'cerbero') + else: + self.data_dir = resources.files('cerbero-share.data') def _relative_path(self, path): - if not self.uninstalled: - p = os.path.join(self.data_dir, path) - else: + if self.uninstalled: p = os.path.join(os.path.dirname(__file__), '..', path) - return os.path.abspath(p) + return os.path.abspath(p) + else: + return resources.files('cerbero-share').joinpath(path) def _default_home_dir(self): if self.uninstalled: diff --git a/cerbero/enums.py b/cerbero/enums.py index 989296391..b31fbb169 100644 --- a/cerbero/enums.py +++ b/cerbero/enums.py @@ -31,6 +31,7 @@ class Platform: DARWIN = 'darwin' ANDROID = 'android' IOS = 'ios' + WEB = 'web' class Architecture: @@ -43,6 +44,9 @@ class Architecture: ARMv7 = 'armv7' ARMv7S = 'armv7s' ARM64 = 'arm64' + WASM = 'wasm' + WASM32 = 'wasm32' + WASM64 = 'wasm64' @staticmethod def is_arm(arch): @@ -72,6 +76,7 @@ class Distro: ANDROID = 'android' GENTOO = 'gentoo' NONE = 'none' + EMSCRIPTEN = 'emscripten' class DistroVersion: diff --git a/config/cross-web-emscripten-wasm32.cbc b/config/cross-web-emscripten-wasm32.cbc new file mode 100644 index 000000000..82b5244f5 --- /dev/null +++ b/config/cross-web-emscripten-wasm32.cbc @@ -0,0 +1,10 @@ +# FLUENDO S.A. +# Copyright (C) <2024> +# Cross-compilation config file for Emscripten WASM 32 bits. + +from cerbero.config import Platform, Architecture, Distro + +target_platform = Platform.WEB +target_distro = Distro.EMSCRIPTEN +target_distro_version = None +target_arch = Architecture.WASM32 diff --git a/config/cross-web-emscripten-wasm64.cbc b/config/cross-web-emscripten-wasm64.cbc new file mode 100644 index 000000000..ebb96c2b7 --- /dev/null +++ b/config/cross-web-emscripten-wasm64.cbc @@ -0,0 +1,10 @@ +# FLUENDO S.A. +# Copyright (C) <2024> +# Cross-compilation config file for Emscripten WASM 64 bits. + +from cerbero.config import Platform, Architecture, Distro + +target_platform = Platform.WEB +target_distro = Distro.EMSCRIPTEN +target_distro_version = None +target_arch = Architecture.WASM64 diff --git a/config/web.config b/config/web.config new file mode 100644 index 000000000..85f1e5e85 --- /dev/null +++ b/config/web.config @@ -0,0 +1,71 @@ +# FLUENDO S.A. +# Copyright (C) <2024> +# Platform config file for the Web platform. +# It contains sensitive enviroment configuration that +# shouldn't be modified unless you know what you are doing. +# PLEASE, DO NOT EDIT THIS FILE + +import os +import os.path +from cerbero.config import Distro, FatalError +from cerbero.utils import EnvValueCmd + +if target_distro != Distro.EMSCRIPTEN: + raise FatalError('EMSCRIPTEN is the only Distro supported for the WEB platform') + +# These variants are enabled by default, but not available on our platform +variants.rust = False + +# Init build envvars in the way it is done in linux, darwin, etc. +# And do += later +for f in ['CPPFLAGS', 'CFLAGS', 'CCASFLAGS', 'CXXFLAGS', 'LDFLAGS', + 'OBJCFLAGS']: + env[f] = env.get(f, '') + +COMMON_FLAGS=" -O3 -pthread" + +# https://github.com/WebAssembly/exception-handling +COMMON_FLAGS += " -fexceptions" + +env['CFLAGS'] += COMMON_FLAGS +env['CFLAGS'] += " -mnontrapping-fptoint" + +# SIMD, https://github.com/WebAssembly/simd +env['CFLAGS'] += " -msimd128 -DWASM_SIMD_COMPAT_SLOW" + +# BIGINT, https://github.com/WebAssembly/JS-BigInt-integration +env['CFLAGS'] += " -DWASM_BIGINT" +env['LDFLAGS'] += " -sWASM_BIGINT" + +# PIC +env['CFLAGS'] += " -fPIC" +env['CFLAGS'] += " -I" + os.path.join(prefix, "include") + +env['CXXFLAGS'] += env['CFLAGS'] + +env['LDFLAGS'] += COMMON_FLAGS +env['LDFLAGS'] += " -sAUTO_JS_LIBRARIES=0 -sAUTO_NATIVE_LIBRARIES=0" + +if not toolchain_prefix: + toolchain_prefix = os.path.join(home_dir, "emsdk") + +dot_emscripten_config = os.path.join(toolchain_prefix, '.emscripten') +# This file might be missing if we are bootstrapping +if os.path.exists(dot_emscripten_config): + # Parse the .emscripten config file to fetch the installed + # sdk enviroment. + # after execution, this config file defines: + # BINARYEN_ROOT, EMSCRIPTEN_ROOT, LLVM_ROOT, NODE_JS + os.environ['EM_CONFIG'] = dot_emscripten_config + exec(open(dot_emscripten_config).read(), globals()) + + def add_cmd(var, prefix, command): + env[var] = os.path.join(prefix, command) + + add_cmd('CC', EMSCRIPTEN_ROOT, 'emcc') + add_cmd('CXX', EMSCRIPTEN_ROOT, 'em++') + add_cmd('AR', EMSCRIPTEN_ROOT, 'emar') + add_cmd('LD', EMSCRIPTEN_ROOT, 'emcc') + add_cmd('NM', LLVM_ROOT, 'llvm-nm') + add_cmd('LDSHARED', EMSCRIPTEN_ROOT, 'emcc'), + add_cmd('RANLIB', EMSCRIPTEN_ROOT, 'emranlib') diff --git a/packages/__init__.py b/packages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/recipes/__init__.py b/recipes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/recipes/angle-uwp.recipe b/recipes/angle-uwp.recipe index 21212860c..fe3684b91 100644 --- a/recipes/angle-uwp.recipe +++ b/recipes/angle-uwp.recipe @@ -34,7 +34,7 @@ class Recipe(recipe.Recipe): def install(self): bindir = Path(self.config.prefix) / 'bin' - bdir = Path(self.build_dir) + bdir = Path(self.config_build_dir) if self.config.target_arch == Architecture.ARM64: dlldir = bdir / 'bin/UAP/ARM64' elif self.config.target_arch == Architecture.X86_64: diff --git a/recipes/build-tools/autoconf.recipe b/recipes/build-tools/autoconf.recipe index c01cb5eaf..943f20698 100644 --- a/recipes/build-tools/autoconf.recipe +++ b/recipes/build-tools/autoconf.recipe @@ -3,11 +3,11 @@ class Recipe(recipe.Recipe): name = 'autoconf' - version = '2.69' + version = '2.72' licenses = [License.GPLv2Plus] stype = SourceType.TARBALL url = 'gnu://' - tarball_checksum = '64ebcec9f8ac5b2487125a86a7760d2591ac9e1d3dbd59489633f9de62a57684' + tarball_checksum = 'ba885c1319578d6c94d46e9b0dceb4014caafe2490e437a0dbca3f270a223f5a' deps = ['m4'] use_msys_perl = True diff --git a/recipes/build-tools/bison.recipe b/recipes/build-tools/bison.recipe index daa4c263e..332c7042c 100644 --- a/recipes/build-tools/bison.recipe +++ b/recipes/build-tools/bison.recipe @@ -16,6 +16,6 @@ class Recipe(recipe.Recipe): files_bins = ['bison', 'yacc'] async def extract(self): - if os.path.exists(self.build_dir): - shutil.rmtree(self.build_dir) + if os.path.exists(self.config_build_dir): + shutil.rmtree(self.config_build_dir) await super(recipe.Recipe, self).extract() diff --git a/recipes/build-tools/cmake.recipe b/recipes/build-tools/cmake.recipe index 796e2bd64..35396cfe6 100644 --- a/recipes/build-tools/cmake.recipe +++ b/recipes/build-tools/cmake.recipe @@ -38,5 +38,5 @@ class Recipe(recipe.Recipe): pass await super(recipe.Recipe, self).configure() # Windows XP does not have _mkgmtime64 - shell.replace(os.path.join(self.build_dir, 'CMakeCache.txt'), + shell.replace(os.path.join(self.config_build_dir, 'CMakeCache.txt'), {'HAVE__MKGMTIME64:INTERNAL=1':'HAVE__MKGMTIME64:INTERNAL='}) diff --git a/recipes/build-tools/gas-preprocessor.recipe b/recipes/build-tools/gas-preprocessor.recipe index 033aeb846..51a17600a 100644 --- a/recipes/build-tools/gas-preprocessor.recipe +++ b/recipes/build-tools/gas-preprocessor.recipe @@ -14,7 +14,7 @@ class Recipe(recipe.Recipe): async def install(self): if not os.path.exists(os.path.join(self.config.prefix, 'bin')): os.makedirs(os.path.join(self.config.prefix, 'bin')) - shutil.copy (os.path.join(self.build_dir, 'gas-preprocessor.pl'), + shutil.copy (os.path.join(self.config_build_dir, 'gas-preprocessor.pl'), os.path.join(self.config.prefix, 'bin')) await shell.async_call ('chmod +x %s' % os.path.join(self.config.prefix, 'bin', 'gas-preprocessor.pl'), env=self.env) diff --git a/recipes/build-tools/gettext-m4.recipe b/recipes/build-tools/gettext-m4.recipe index 7a3159a2e..6d722def3 100644 --- a/recipes/build-tools/gettext-m4.recipe +++ b/recipes/build-tools/gettext-m4.recipe @@ -40,11 +40,11 @@ class Recipe(recipe.Recipe): ] def post_install(self): - m4dir = os.path.join(self.build_dir, 'gettext-runtime', 'm4') + m4dir = os.path.join(self.config_build_dir, 'gettext-runtime', 'm4') for f in [x for x in os.listdir(m4dir) if x.endswith('.m4')]: shutil.copy(os.path.join(m4dir, f), os.path.join(self.config.prefix, 'share', 'aclocal')) - m4dir = os.path.join(self.build_dir, 'gettext-runtime', 'gnulib-m4') + m4dir = os.path.join(self.config_build_dir, 'gettext-runtime', 'gnulib-m4') for f in ['lib-ld.m4', 'lib-prefix.m4', 'lib-link.m4']: shutil.copy(os.path.join(m4dir, f), os.path.join(self.config.prefix, 'share', 'aclocal')) diff --git a/recipes/build-tools/gobject-introspection-m4.recipe b/recipes/build-tools/gobject-introspection-m4.recipe index 8cc7b0e8f..2d521f1c7 100644 --- a/recipes/build-tools/gobject-introspection-m4.recipe +++ b/recipes/build-tools/gobject-introspection-m4.recipe @@ -19,5 +19,5 @@ class Recipe(recipe.Recipe): override_libtool = False async def install(self): - shutil.copy(os.path.join(self.build_dir, 'm4', 'introspection.m4'), + shutil.copy(os.path.join(self.config_build_dir, 'm4', 'introspection.m4'), os.path.join(self.config.prefix, 'share', 'aclocal')) diff --git a/recipes/build-tools/gtk-doc-lite.recipe b/recipes/build-tools/gtk-doc-lite.recipe index e43bac8c9..0d2fa97ef 100644 --- a/recipes/build-tools/gtk-doc-lite.recipe +++ b/recipes/build-tools/gtk-doc-lite.recipe @@ -27,7 +27,7 @@ class Recipe(recipe.Recipe): bin_dir = os.path.join(self.config.prefix, 'bin') if not os.path.exists(bin_dir): os.makedirs(bin_dir) - autotools_dir = os.path.join(self.build_dir, 'buildsystems/autotools') + autotools_dir = os.path.join(self.config_build_dir, 'buildsystems/autotools') shutil.copy(os.path.join(autotools_dir, 'gtk-doc.m4'), os.path.join(aclocal_dir, 'gtk-doc.m4')) shutil.copy(os.path.join(autotools_dir, 'gtk-doc.make'), diff --git a/recipes/build-tools/libtool.recipe b/recipes/build-tools/libtool.recipe index 1416ee9b6..28dcda41f 100644 --- a/recipes/build-tools/libtool.recipe +++ b/recipes/build-tools/libtool.recipe @@ -25,12 +25,12 @@ class Recipe(recipe.Recipe): 'share/aclocal/lt~obsolete.m4'] async def extract(self): - if os.path.exists(self.build_dir): - shutil.rmtree(self.build_dir) + if os.path.exists(self.config_build_dir): + shutil.rmtree(self.config_build_dir) await super(recipe.Recipe, self).extract() async def configure(self): - shell.touch(os.path.join(self.build_dir, 'doc', 'libtool.1')) - shell.touch(os.path.join(self.build_dir, 'doc', 'libtoolize.1')) + shell.touch(os.path.join(self.config_build_dir, 'doc', 'libtool.1')) + shell.touch(os.path.join(self.config_build_dir, 'doc', 'libtoolize.1')) await super(recipe.Recipe, self).configure() diff --git a/recipes/build-tools/meson.recipe b/recipes/build-tools/meson.recipe index 6e94bb8b3..4b15395bf 100644 --- a/recipes/build-tools/meson.recipe +++ b/recipes/build-tools/meson.recipe @@ -34,7 +34,7 @@ class Recipe(recipe.Recipe): prefix = self.config.prefix python_exe = os.path.join(self.config.build_tools_prefix, 'bin', 'python') await shell.async_call([python_exe, '-m', 'pip', 'install', '--prefix', prefix, '.'], - cmd_dir=self.build_dir, env=self.env, logfile=self.logfile) + cmd_dir=self.config_build_dir, env=self.env, logfile=self.logfile) if self.config.platform == Platform.WINDOWS: # Python insists on using Scripts instead of bin on Windows for # scripts. Insist back, and use bin again. diff --git a/recipes/build-tools/moltenvk-tools.recipe b/recipes/build-tools/moltenvk-tools.recipe index 18abd27d6..261bb9826 100644 --- a/recipes/build-tools/moltenvk-tools.recipe +++ b/recipes/build-tools/moltenvk-tools.recipe @@ -42,7 +42,7 @@ class Recipe(recipe.Recipe): if os.path.exists(srcdir): shutil.rmtree(srcdir) # https://vulkan.lunarg.com/doc/sdk/1.3.283.0/mac/getting_started.html - vulkan_installer = os.path.join(self.build_dir, 'InstallVulkan.app/Contents/MacOS/InstallVulkan') + vulkan_installer = os.path.join(self.config_build_dir, 'InstallVulkan.app/Contents/MacOS/InstallVulkan') await shell.async_call([vulkan_installer, '--root', self.config.moltenvk_prefix, '--accept-licenses', diff --git a/recipes/build-tools/ninja.recipe b/recipes/build-tools/ninja.recipe index 9564c28a6..6a6f96665 100644 --- a/recipes/build-tools/ninja.recipe +++ b/recipes/build-tools/ninja.recipe @@ -14,7 +14,7 @@ class Recipe(recipe.Recipe): def configure(self): shell.new_call([self.config.python_exe, 'configure.py', '--bootstrap', '--verbose'], - self.build_dir, logfile=self.logfile, env=self.env) + self.config_build_dir, logfile=self.logfile, env=self.env) async def install(self): ninja = 'ninja' @@ -23,5 +23,5 @@ class Recipe(recipe.Recipe): bindir = os.path.join (self.config.prefix, "bin") if not os.path.exists(bindir): os.makedirs(bindir) - os.replace(os.path.join(self.build_dir, ninja), + os.replace(os.path.join(self.config_build_dir, ninja), os.path.join(bindir, ninja)) diff --git a/recipes/build-tools/vala-m4.recipe b/recipes/build-tools/vala-m4.recipe index b4bd22b9f..ba292ca38 100644 --- a/recipes/build-tools/vala-m4.recipe +++ b/recipes/build-tools/vala-m4.recipe @@ -19,14 +19,14 @@ class Recipe(recipe.Recipe): 'share/vala/Makefile.vapigen'] async def install(self): - shutil.copy(os.path.join(self.build_dir, 'vapigen', 'vapigen.m4'), + shutil.copy(os.path.join(self.config_build_dir, 'vapigen', 'vapigen.m4'), os.path.join(self.config.prefix, 'share', 'aclocal', 'vapigen.m4')) - shutil.copy(os.path.join(self.build_dir, 'vala.m4'), + shutil.copy(os.path.join(self.config_build_dir, 'vala.m4'), os.path.join(self.config.prefix, 'share', 'aclocal', 'vala.m4')) destdir = os.path.join(self.config.prefix, 'share', 'vala') if not os.path.exists(destdir): os.makedirs(destdir) - shutil.copy(os.path.join(self.build_dir, 'vapigen', 'Makefile.vapigen'), + shutil.copy(os.path.join(self.config_build_dir, 'vapigen', 'Makefile.vapigen'), os.path.join(destdir, 'Makefile.vapigen')) diff --git a/recipes/build-tools/winetricks.recipe b/recipes/build-tools/winetricks.recipe index 3e068392e..9ad02b04d 100644 --- a/recipes/build-tools/winetricks.recipe +++ b/recipes/build-tools/winetricks.recipe @@ -14,5 +14,5 @@ class Recipe(recipe.Recipe): async def install(self): winetricks_tool = os.path.join(self.config.prefix, 'bin', self.name) - shutil.copy(os.path.join(self.build_dir, 'src', self.name), winetricks_tool) + shutil.copy(os.path.join(self.config_build_dir, 'src', self.name), winetricks_tool) await shell.async_call(['chmod', '+x', winetricks_tool]) diff --git a/recipes/cdparanoia.recipe b/recipes/cdparanoia.recipe index 5b7b8dad4..03b8e854c 100644 --- a/recipes/cdparanoia.recipe +++ b/recipes/cdparanoia.recipe @@ -5,6 +5,7 @@ import shutil from fnmatch import fnmatch +from cerbero.build.build import modify_environment from cerbero.build.filesprovider import FilesProvider from cerbero.tools.libtool import LibtoolLibrary @@ -20,7 +21,6 @@ class Recipe(recipe.Recipe): # binaries are GPL and we don't distribute them licenses = [License.LGPLv2_1Plus] allow_parallel_build = False - config_sh = 'CFLAGS="$CFLAGS -fPIC" ./configure' patches = ['cdparanoia/0001-configure.in-Always-use-AC_PROG_CC.patch'] files_libs = ['libcdda_paranoia', 'libcdda_interface'] @@ -30,6 +30,7 @@ class Recipe(recipe.Recipe): make_check = None async def configure(self): + self.append_env('CFLAGS', '-fPIC') autotools_dir = os.path.join(self.config._relative_path('data'), 'autotools') shutil.copy(os.path.join(autotools_dir, 'config.guess'), os.path.join(self.config_src_dir, 'configure.guess')) shutil.copy(os.path.join(autotools_dir, 'config.sub'), os.path.join(self.config_src_dir, 'configure.sub')) diff --git a/recipes/custom.py b/recipes/custom.py index 4eec6f3a0..a436d589a 100644 --- a/recipes/custom.py +++ b/recipes/custom.py @@ -58,7 +58,8 @@ def __init__(self, config, env): subproject = self.name.replace('-1.0', '') self.repo_dir = os.path.abspath(os.path.join(self.config.local_sources, 'gstreamer-1.0')) self.config_src_dir = os.path.abspath(os.path.join(self.config.sources, 'gstreamer-1.0')) - self.build_dir = os.path.join(self.config_src_dir, 'subprojects', subproject) + self.config_build_dir = os.path.join(self.config_src_dir, 'subprojects', subproject, '_builddir') + self.sources_dir = os.path.join(self.config_src_dir, 'subprojects', subproject) # Force using the commit/remotes from 'gstreamer-1.0' recipe, if set # in the config, on all gstreamer recipes because they share the same # git repository. @@ -73,7 +74,10 @@ def enable_plugin(self, plugin, category, variant=None, option=None, dep=None): option = plugin if variant is None or getattr(self.config.variants, variant): if dep is not None: - self.deps.append(dep) + if not isinstance(dep, list): + dep = [dep] + for d in dep: + self.deps.append(d) plugin = '%(libdir)s/gstreamer-1.0/libgst' + plugin if not hasattr(self, 'files_plugins_' + category): setattr(self, 'files_plugins_' + category, []) @@ -117,8 +121,12 @@ def _remove_plugin_file(self, plugin, category): def disable_plugin(self, plugin, category, option=None, dep=None, library_name=None): if option is None: option = plugin - if dep is not None and dep in self.deps: - self.deps.remove(dep) + if dep is not None: + if not isinstance(dep, list): + dep = [dep] + for d in dep: + if d in self.deps: + self.deps.remove(d) self._remove_plugin_file(plugin, category) if library_name is not None: library = 'libgst' + library_name + '-1.0' diff --git a/recipes/docbook-xml.recipe b/recipes/docbook-xml.recipe index 5dd8ffb01..e19da0df6 100644 --- a/recipes/docbook-xml.recipe +++ b/recipes/docbook-xml.recipe @@ -17,7 +17,7 @@ class Recipe(recipe.Recipe): if not os.path.exists(etc_path): os.makedirs(etc_path) etc_catalog_path = os.path.join(etc_path, 'catalog.xml') - new_catalog_path = os.path.join(self.build_dir, 'catalog.xml') + new_catalog_path = os.path.join(self.config_build_dir, 'catalog.xml') def read_catalog(path): try: diff --git a/recipes/docbook-xsl.recipe b/recipes/docbook-xsl.recipe index 8c7bb361f..2c18de230 100644 --- a/recipes/docbook-xsl.recipe +++ b/recipes/docbook-xsl.recipe @@ -16,7 +16,7 @@ class Recipe(recipe.Recipe): if not os.path.exists(etc_path): os.makedirs(etc_path) etc_catalog_path = os.path.join(etc_path, 'catalog.xml') - new_catalog_path = os.path.join(self.build_dir, 'catalog.xml') + new_catalog_path = os.path.join(self.config_build_dir, 'catalog.xml') def read_catalog(path): try: diff --git a/recipes/gst-plugins-bad-1.0.recipe b/recipes/gst-plugins-bad-1.0.recipe index e7adab643..cacc815a4 100644 --- a/recipes/gst-plugins-bad-1.0.recipe +++ b/recipes/gst-plugins-bad-1.0.recipe @@ -88,7 +88,7 @@ class Recipe(custom.GStreamer): 'libsrtp', 'libdca', 'libdvdnav', 'libnice', 'soundtouch', 'vo-aacenc', 'openjpeg', 'pango', 'spandsp', 'webrtc-audio-processing', 'sbc', 'ladspa', - 'srt', 'zbar', 'libltc', 'qrencode', 'json-glib'] + 'srt', 'zbar', 'libltc'] use_system_libs = True files_lang = ['gst-plugins-bad-1.0'] @@ -512,8 +512,10 @@ class Recipe(custom.GStreamer): self.enable_plugin('svtav1', 'codecs', dep='svt-av1') self.enable_plugin('x265', 'codecs', dep='x265') + self.enable_plugin('qroverlay', 'effects', dep=['qrencode', 'json-glib']) + self.meson_options['qroverlay'] = 'enabled' - if self.config.target_platform not in [Platform.IOS, Platform.ANDROID]: + if self.config.target_platform not in [Platform.IOS, Platform.ANDROID, Platform.WEB]: # These need plugins self.enable_plugin('frei0r', 'effects', dep='frei0r-plugins') @@ -606,9 +608,39 @@ class Recipe(custom.GStreamer): # dtls plugin needs openssl, and we pick up the system openssl if on # Linux and not cross-compiling. - if self.config.target_platform != Platform.LINUX or self.config.cross_compiling(): + if self.config.target_platform != Platform.WEB and \ + (self.config.target_platform != Platform.LINUX or self.config.cross_compiling()): self.deps.append('openssl') + if self.config.target_platform == Platform.WEB: + self.disable_plugin('aes', 'effects') + self.disable_plugin('assrender', 'codecs', dep='libass') + self.disable_plugin('closedcaption', 'effects', dep='pango') + self.disable_plugin('dash', 'codecs', dep='libxml2') + self.disable_plugin('dtsdec', 'codecs_restricted', option='dts', dep='libdca') + self.disable_plugin('dtls', 'net') + self.disable_plugin('hls', 'codecs') + self.disable_plugin('ladspa', 'effects', dep='ladspa') + self.disable_plugin('openjpeg', 'codecs', dep='openjpeg') + self.disable_plugin('openh264', 'codecs', dep='openh264') + self.disable_plugin('resindvd', 'dvd', dep='libdvdnav') + self.disable_plugin('rtmp', 'net_restricted', option='rtmp', dep='librtmp') + self.disable_plugin('sbc', 'codecs', dep='sbc') + self.disable_plugin('sctp', 'net') + self.meson_options['sctp-internal-usrsctp'] = 'disabled' + self.disable_plugin('soundtouch', 'effects', dep='soundtouch') + self.disable_plugin('spandsp', 'codecs', dep='spandsp') + self.disable_plugin('smoothstreaming', 'codecs', dep='libxml2') + self.disable_plugin('srt', 'net', dep='srt') + self.disable_plugin('voaacenc', 'codecs_restricted', dep='vo-aacenc') + self.disable_plugin('webrtc', 'net', dep='webrtc-audio-processing') + self.disable_plugin('webrtcdsp', 'effects', dep='libnice') + self.disable_plugin('x265', 'codecs', dep='x265') + self.disable_plugin('zbar', 'codecs', dep='zbar') + self.disable_plugin('qroverlay', 'effects', dep=['qrencode', 'json-glib']) + self.meson_options['qroverlay'] = 'disabled' + self.meson_options['nls'] = 'disabled' + if self.using_uwp(): # Uses unavailable APIs self.disable_plugin('d3d', 'sys', option='d3dvideosink') diff --git a/recipes/gst-plugins-base-1.0.recipe b/recipes/gst-plugins-base-1.0.recipe index 52f918cee..73cc14713 100644 --- a/recipes/gst-plugins-base-1.0.recipe +++ b/recipes/gst-plugins-base-1.0.recipe @@ -177,6 +177,17 @@ class Recipe(custom.GStreamer): if self.config.target_platform == Platform.LINUX: self.enable_plugin('alsa', 'sys', 'alsa') + elif self.config.target_platform == Platform.WEB: + self.meson_options['gl_platform'] = 'egl' + self.meson_options['gl_winsys'] = 'egl' + self.disable_plugin('pango', 'core', dep='pango') + self.meson_options['gl-png'] = 'disabled' + self.deps.remove('libpng') + self.meson_options['tests'] = 'disabled' + self.meson_options['examples'] = 'disabled' + self.meson_options['tools'] = 'disabled' + self.meson_options['nls'] = 'disabled' + if self.using_uwp(): # All these can be ported to Meson/UWP self.disable_plugin('ogg', 'codecs', dep='libogg') diff --git a/recipes/gst-plugins-good-1.0.recipe b/recipes/gst-plugins-good-1.0.recipe index bc9af2811..fe1920c74 100644 --- a/recipes/gst-plugins-good-1.0.recipe +++ b/recipes/gst-plugins-good-1.0.recipe @@ -242,6 +242,18 @@ class Recipe(custom.GStreamer): self.meson_options['osxaudio'] = 'enabled' elif self.config.target_platform == Platform.LINUX: self.enable_plugin('jack', 'sys') + elif self.config.target_platform == Platform.WEB: + self.meson_options['nls'] = 'disabled' + self.disable_plugin('adaptivedemux2', 'codecs') + self.disable_plugin('avi', 'codecs') + self.disable_plugin('cairo', 'effects', dep='cairo') + self.disable_plugin('dv', 'codecs', dep='libdv') + self.disable_plugin('gdkpixbuf', 'effects', option='gdk-pixbuf', dep='gdk-pixbuf') + self.disable_plugin('lame', 'codecs', dep='lame') + self.disable_plugin('png', 'codecs', dep='libpng') + self.disable_plugin('soup', 'net', dep='libsoup') + self.disable_plugin('taglib', 'codecs', dep='taglib') + self.disable_plugin('wavpack', 'codecs', dep='wavpack') self.enable_plugin('video4linux2', 'capture', variant='v4l2', option='v4l2') self.enable_plugin('ximagesrc', 'capture', variant='x11') diff --git a/recipes/gstreamer-1.0.recipe b/recipes/gstreamer-1.0.recipe index 5210756ee..b185f88b9 100644 --- a/recipes/gstreamer-1.0.recipe +++ b/recipes/gstreamer-1.0.recipe @@ -79,3 +79,9 @@ class Recipe(custom.GStreamer): self.config.target_platform not in [Platform.IOS, Platform.ANDROID]: self.meson_options['ptp-helper'] = 'enabled' self.files_misc.append('libexec/gstreamer-1.0/gst-ptp-helper%(bext)s') + + if self.config.target_platform == Platform.WEB: + self.meson_options['nls'] = 'disabled' + self.meson_options['tools'] = 'disabled' + self.meson_options['tests'] = 'disabled' + self.meson_options['examples'] = 'disabled' \ No newline at end of file diff --git a/recipes/ladspa.recipe b/recipes/ladspa.recipe index babcc2403..33672c2d6 100644 --- a/recipes/ladspa.recipe +++ b/recipes/ladspa.recipe @@ -24,5 +24,5 @@ class Recipe(recipe.Recipe): if not os.path.exists(include_path): os.makedirs(include_path) - ladspa_h = os.path.join(self.build_dir, 'src', 'ladspa.h') + ladspa_h = os.path.join(self.config_build_dir, 'src', 'ladspa.h') shutil.copy(ladspa_h, include_path) diff --git a/recipes/libsrtp.recipe b/recipes/libsrtp.recipe index 065bf246c..584de8ba8 100644 --- a/recipes/libsrtp.recipe +++ b/recipes/libsrtp.recipe @@ -29,7 +29,7 @@ class Recipe(recipe.Recipe): await super(Recipe, self).install() libdir = os.path.join(self.config.prefix, 'lib' + self.config.lib_suffix) - shutil.copy(os.path.join(self.build_dir, 'srtp.def'), libdir) + shutil.copy(os.path.join(self.sources_dir, 'srtp.def'), libdir) def post_install(self): libtool_la = LibtoolLibrary('srtp2', None, None, None, self.config.libdir, diff --git a/recipes/openssl.recipe b/recipes/openssl.recipe index dd27e2e93..52f4baf7c 100644 --- a/recipes/openssl.recipe +++ b/recipes/openssl.recipe @@ -234,7 +234,7 @@ class Recipe(recipe.Recipe): else: self.files_bins = ['openssl'] - await shell.async_call(config_sh + self.openssl_platform, self.build_dir, + await shell.async_call(config_sh + self.openssl_platform, self.config_build_dir, logfile=self.logfile, env=self.env) def post_install(self): diff --git a/recipes/opus.recipe b/recipes/opus.recipe index b370fee31..5c5c1ce72 100644 --- a/recipes/opus.recipe +++ b/recipes/opus.recipe @@ -31,6 +31,9 @@ class Recipe(recipe.Recipe): # Always have NEON on ARM64, don't need to detect the CPU at runtime. if self.config.target_arch == Architecture.ARM64: self.meson_options['rtcd'] = 'disabled' + if self.config.target_platform == Platform.WEB: + self.meson_options['intrinsics'] = 'disabled' + self.meson_options['rtcd'] = 'disabled' def post_install(self): major, minor, micro = get_libtool_versions(self.version) diff --git a/recipes/webview2.recipe b/recipes/webview2.recipe index 6399a4299..ad0d9f433 100644 --- a/recipes/webview2.recipe +++ b/recipes/webview2.recipe @@ -20,7 +20,7 @@ class Recipe(recipe.Recipe): can_msvc = True def install(self): - bdir = Path(self.build_dir) / 'build/native' + bdir = Path(self.config_build_dir) / 'build/native' if self.config.target_arch == Architecture.ARM64: libdir = bdir / 'arm64' elif self.config.target_arch == Architecture.X86_64: diff --git a/recipes/x265-10bit.recipe b/recipes/x265-10bit.recipe index 359e9d62d..be0e0839a 100644 --- a/recipes/x265-10bit.recipe +++ b/recipes/x265-10bit.recipe @@ -29,13 +29,12 @@ class Recipe(recipe.Recipe): elif self.config.target_arch == Architecture.X86: # on x86 it tries to use the high bank of AVX self.configure_options += ' -DENABLE_ASSEMBLY=OFF ' - self.config_src_dir = os.path.join(self.config.sources, f'x265-src-{self.version}', 'source') - self.build_dir = os.path.join(self.config_src_dir, 'b-10bit') - self.make_dir = self.config_src_dir + self.sources_dir = os.path.join(self.config.sources, f'x265-src-{self.version}', 'source') + self.configure_dir = self.sources_dir # Set up the HDR library - DO NOT EXECUTE any kind of install async def install(self): prefix = '' if self.using_msvc() else 'lib' - src = Path(self.build_dir) / f'{prefix}x265.a' + src = Path(self.config_build_dir) / f'{prefix}x265.a' dest = src.with_stem(f"{'' if self.using_msvc() else 'lib'}{self.name}") src.replace(dest) diff --git a/recipes/x265-12bit.recipe b/recipes/x265-12bit.recipe index 6f475f3f6..16f8709de 100644 --- a/recipes/x265-12bit.recipe +++ b/recipes/x265-12bit.recipe @@ -29,14 +29,12 @@ class Recipe(recipe.Recipe): elif self.config.target_arch == Architecture.X86: # on x86 it tries to use the high bank of AVX self.configure_options += ' -DENABLE_ASSEMBLY=OFF ' - self.config_src_dir = os.path.join(self.config.sources, f'x265-src-{self.version}', 'source') - self.build_dir = os.path.join(self.config_src_dir, 'b-12bit') - self.make_dir = self.config_src_dir - Path(self.build_dir).mkdir(parents=True, exist_ok=True) + self.sources_dir = os.path.join(self.config.sources, f'x265-src-{self.version}', 'source') + self.configure_dir = self.sources_dir # Set up the HDR library - DO NOT EXECUTE any kind of install async def install(self): prefix = '' if self.using_msvc() else 'lib' - src = Path(self.build_dir) / f'{prefix}x265.a' + src = Path(self.config_build_dir) / f'{prefix}x265.a' dest = src.with_stem(f"{'' if self.using_msvc() else 'lib'}{self.name}") src.replace(dest) diff --git a/recipes/x265.recipe b/recipes/x265.recipe index dbd66f8b8..ed0145619 100644 --- a/recipes/x265.recipe +++ b/recipes/x265.recipe @@ -37,14 +37,12 @@ class Recipe(recipe.Recipe): elif self.config.target_arch == Architecture.X86: # on x86 it tries to use the high bank of AVX self.configure_options += ' -DENABLE_ASSEMBLY=OFF ' - self.config_src_dir = os.path.join(self.config.sources, f'x265-src-{self.version}', 'source') - self.build_dir = os.path.join(self.config_src_dir, 'b') - self.make_dir = self.config_src_dir - Path(self.build_dir).mkdir(parents=True, exist_ok=True) prefix = '' if self.using_msvc() else 'lib' - self.hdr10lib = Path(self.config_src_dir) / 'b-10bit' / f'{prefix}x265-10bit.a' - self.hdr12lib = Path(self.config_src_dir) / 'b-12bit' / f'{prefix}x265-12bit.a' + self.sources_dir = os.path.join(self.config.sources, f'x265-src-{self.version}', 'source') + self.configure_dir = self.sources_dir + self.hdr10lib = Path(self.config.sources) / f'x265-10bit-{self.version}' / '_builddir' / f'{prefix}x265-10bit.a' + self.hdr12lib = Path(self.config.sources) / f'x265-12bit-{self.version}' / '_builddir' / f'{prefix}x265-12bit.a' self.configure_options += f' -DEXTRA_LIB={self.hdr10lib.as_posix()};{self.hdr12lib.as_posix()} ' def post_install(self): diff --git a/setup.py b/setup.py index 94dc23446..bd0f05463 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,7 @@ import os import sys -import shutil from setuptools import setup, find_packages -from setuptools.command import sdist as setuptools_sdist -from cerbero.utils import shell from cerbero.enums import CERBERO_VERSION -from distutils.dir_util import copy_tree -import distutils.log sys.path.insert(0, './cerbero') @@ -16,113 +11,6 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() -# Utility function to parse directories -def parse_dir(dirpath, extension=None): - if os.path.exists('.git'): - files = shell.check_output(['git', 'ls-files', dirpath]).split('\n') - files.remove('') - else: - files = shell.check_output(['find', dirpath, '-type', 'f']).split('\n') - files.remove('') - if extension is None: - return files - return [f for f in files if f.endswith(extension)] - - -# Utility function to create the list of data files -def datafiles(prefix): - files = [] - datadir = os.path.join(prefix, 'share', 'cerbero') - for dirname, extension in [('recipes', '.recipe'), ('packages', '.package')]: - for f in parse_dir(dirname, extension): - files.append((os.path.join(datadir, dirname), [f])) - for dirname in ['config']: - for f in parse_dir(dirname): - files.append((os.path.join(datadir, dirname), [f])) - for dirname in ['data']: - for f in parse_dir(dirname): - dirpath = os.path.split(f.split('/', 1)[1])[0] - files.append((os.path.join(datadir, dirpath), [f])) - return files - - -# Intercept packages and recipes -packages = [x[len('--package=') :] for x in sys.argv if x.startswith('--package=')] -recipes = [x[len('--recipe=') :] for x in sys.argv if x.startswith('--recipe=')] -if len(packages) == 0: - packages = None -if len(recipes) == 0: - recipes = None -sys.argv = [x for x in sys.argv if not x.startswith('--package=') and not x.startswith('--recipe=')] - - -# Fill manifest -shutil.copy('MANIFEST.in.in', 'MANIFEST.in') -with open('MANIFEST.in', 'a+') as f: - for dirname in ['data', 'config', 'tools']: - f.write('\n'.join(['include %s' % x for x in parse_dir(dirname)])) - f.write('\n') - - for dirname, suffix in [('packages', '.package'), ('recipes', '.recipe')]: - filenames = parse_dir(dirname) - requested = globals()[dirname] - if requested: - requested_filenames = tuple([os.sep + x + suffix for x in requested]) - - # Add special directories - if dirname == 'packages': - requested_dir = requested + ['gstreamer-1.0'] - else: - requested_dir = requested + ['build-tools', 'toolchain'] - requested_directories = tuple(os.path.join(dirname, x, '') for x in requested_dir) - - filenames = [ - p - for p in filenames - if p.startswith(requested_directories) or p.endswith(requested_filenames) or p.endswith('.py') - ] - - missing_files = [p for p in requested_filenames if not [True for m in filenames if m.endswith(p)]] - assert not missing_files, 'Not all %s from the command line (%s) exist' % ( - dirname, - ', '.join(missing_files), - ) - f.write('\n'.join(['include %s' % x for x in filenames])) - f.write('\n') - - -# Intercept prefix -prefix = [x for x in sys.argv if x.startswith('--prefix=')] -if len(prefix) == 1: - prefix = prefix[0].split('--prefix=')[1] -else: - prefix = '/usr/local' - - -class extended_sdist(setuptools_sdist.sdist): - user_options = setuptools_sdist.sdist.user_options + [ - ('source-dirs=', None, 'Comma-separated list of source directories to add to the package'), - ('package=', None, 'Specific package to include, other packages are not included'), - ('recipe=', None, 'Specific recipe to include, other recipes are not included'), - ] - - def initialize_options(self): - self.source_dirs = [] - setuptools_sdist.sdist.initialize_options(self) - - def finalize_options(self): - self.ensure_string_list('source_dirs') - setuptools_sdist.sdist.finalize_options(self) - - def make_release_tree(self, base_dir, files): - setuptools_sdist.sdist.make_release_tree(self, base_dir, files) - for d in self.source_dirs: - src = d.rstrip().rstrip(os.sep) - dest = os.path.join(base_dir, 'sources', os.path.basename(src)) - distutils.log.info('Copying %s -> %s', src, dest) - copy_tree(src, dest, update=not self.force, verbose=0, dry_run=self.dry_run) - - setup( name='cerbero', version=CERBERO_VERSION, @@ -131,16 +19,32 @@ def make_release_tree(self, base_dir, files): description=('Multi platform build system for Open Source projects'), license='LGPL', url='http://gstreamer.freedesktop.org/', - packages=find_packages(exclude=['tests']), + packages=[ + 'cerbero-share.config', + 'cerbero-share.data', + 'cerbero-share.packages', + 'cerbero-share.recipes', + ] + + find_packages(exclude=['config', 'data', 'packages', 'recipes', 'test']), + package_dir={ + 'cerbero-share.config': 'config', + 'cerbero-share.data': 'data', + 'cerbero-share.packages': 'packages', + 'cerbero-share.recipes': 'recipes', + }, + package_data={ + 'cerbero-share.config': ['**'], + 'cerbero-share.data': ['**'], + 'cerbero-share.packages': ['**'], + 'cerbero-share.recipes': ['**'], + }, long_description=read('README.md'), zip_safe=False, include_package_data=True, - data_files=datafiles(prefix), entry_points=""" [console_scripts] cerbero = cerbero.main:main""", classifiers=[ 'License :: OSI Approved :: LGPL License', ], - cmdclass={'sdist': extended_sdist}, ) diff --git a/test/projects/autotools/Makefile.am b/test/projects/autotools/Makefile.am new file mode 100644 index 000000000..f4af9ea36 --- /dev/null +++ b/test/projects/autotools/Makefile.am @@ -0,0 +1,9 @@ +MAINTAINERCLEANFILES = \ +Makefile.in \ +aclocal.m4 \ +configure \ +configure~ \ +install-sh \ +missing + +SUBDIRS = data src diff --git a/test/projects/autotools/autogen.sh b/test/projects/autotools/autogen.sh new file mode 100755 index 000000000..790d8a601 --- /dev/null +++ b/test/projects/autotools/autogen.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +aclocal +automake --add-missing +autoconf diff --git a/test/projects/autotools/configure.ac b/test/projects/autotools/configure.ac new file mode 100644 index 000000000..14fd97f97 --- /dev/null +++ b/test/projects/autotools/configure.ac @@ -0,0 +1,9 @@ +AC_INIT([Cerbero Autotools Project],[0.0.1]) +AC_CONFIG_SRCDIR(configure.ac) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_FILES([ + Makefile + data/Makefile + src/Makefile +]) +AC_OUTPUT diff --git a/test/projects/autotools/data/Makefile.am b/test/projects/autotools/data/Makefile.am new file mode 100644 index 000000000..518bfd9fd --- /dev/null +++ b/test/projects/autotools/data/Makefile.am @@ -0,0 +1,2 @@ +filesdir = $(datadir)/autotools-cerbero +files_DATA = file1.txt diff --git a/test/projects/autotools/data/file1.txt b/test/projects/autotools/data/file1.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/projects/autotools/src/Makefile.am b/test/projects/autotools/src/Makefile.am new file mode 100644 index 000000000..e69de29bb diff --git a/test/projects/cmake/CMakeLists.txt b/test/projects/cmake/CMakeLists.txt new file mode 100644 index 000000000..b6b79ba0e --- /dev/null +++ b/test/projects/cmake/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.22) +project(cerbero-cmake VERSION 0.0.1 DESCRIPTION "Cerbero CMake Project") +install(FILES data/file1.txt DESTINATION share/cmake-cerbero) diff --git a/test/projects/cmake/data/file1.txt b/test/projects/cmake/data/file1.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/projects/meson/data/file1.txt b/test/projects/meson/data/file1.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/projects/meson/meson.build b/test/projects/meson/meson.build new file mode 100644 index 000000000..66bb2cffa --- /dev/null +++ b/test/projects/meson/meson.build @@ -0,0 +1,3 @@ +project('meson-cerbero', 'c', version : '0.1') + +install_data(sources: 'data/file1.txt', install_dir: join_paths(get_option('datadir'), 'meson-cerbero')) diff --git a/test/recipes/autotools-cerbero.recipe b/test/recipes/autotools-cerbero.recipe new file mode 100644 index 000000000..8942fff66 --- /dev/null +++ b/test/recipes/autotools-cerbero.recipe @@ -0,0 +1,10 @@ +# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python + +class Recipe(recipe.Recipe): + name = 'autotools-cerbero' + version = '0.0.1' + stype = SourceType.LOCAL_DIR + btype = BuildType.AUTOTOOLS + path = os.path.join(os.path.dirname(__file__), '..', 'projects', 'autotools') + + files_share = ['share/autotools-cerbero/file1.txt'] diff --git a/test/recipes/cmake-cerbero.recipe b/test/recipes/cmake-cerbero.recipe new file mode 100644 index 000000000..29e671f6b --- /dev/null +++ b/test/recipes/cmake-cerbero.recipe @@ -0,0 +1,10 @@ +# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python + +class Recipe(recipe.Recipe): + name = 'cmake-cerbero' + version = '0.0.1' + stype = SourceType.LOCAL_DIR + btype = BuildType.CMAKE + path = os.path.join(os.path.dirname(__file__), '..', 'projects', 'cmake') + + files_share = ['share/cmake-cerbero/file1.txt'] diff --git a/test/recipes/meson-cerbero.recipe b/test/recipes/meson-cerbero.recipe new file mode 100644 index 000000000..d5d7f998b --- /dev/null +++ b/test/recipes/meson-cerbero.recipe @@ -0,0 +1,10 @@ +# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python + +class Recipe(recipe.Recipe): + name = 'meson-cerbero' + version = '0.0.1' + stype = SourceType.LOCAL_DIR + btype = BuildType.MESON + path = os.path.join(os.path.dirname(__file__), '..', 'projects', 'meson') + + files_share = ['share/meson-cerbero/file1.txt'] diff --git a/test/test_cerbero_build_build.py b/test/test_cerbero_build_build.py index a5ac02acf..756f220ff 100644 --- a/test/test_cerbero_build_build.py +++ b/test/test_cerbero_build_build.py @@ -23,15 +23,12 @@ from cerbero.build import build -class MakefilesBase(build.MakefilesBase): - srcdir = '' - build_dir = '' - +class ModifyEnvBase(build.ModifyEnvBase): def __init__(self, config): + self.env = {} self.config = config self.config_src_dir = os.path.join(config.sources, 'test') - self.env = {} - build.MakefilesBase.__init__(self) + build.ModifyEnvBase.__init__(self) @build.modify_environment def get_env_var(self, var): @@ -49,7 +46,7 @@ def setUp(self): self.var = 'TEST_VAR' self.val1 = 'test' self.val2 = 'test2' - self.mk = MakefilesBase(DummyConfig()) + self.mk = ModifyEnvBase(DummyConfig()) def testAppendEnv(self): self.mk.env[self.var] = self.val1 diff --git a/test/test_cerbero_build_recipe.py b/test/test_cerbero_build_recipe.py index b13452f70..e4f9e2532 100644 --- a/test/test_cerbero_build_recipe.py +++ b/test/test_cerbero_build_recipe.py @@ -33,6 +33,7 @@ class Class1(build.Build): def __init__(self): self.test = 'CODEPASS' + super().__init__() class1_method = lambda x: None @@ -109,7 +110,7 @@ def testSources(self): build_dir = os.path.abspath(build_dir) self.assertEqual(self.recipe.repo_dir, repo_dir) - self.assertEqual(self.recipe.build_dir, build_dir) + self.assertEqual(self.recipe.config_build_dir, build_dir) def testListDeps(self): self.recipe.config.target_platform = Platform.LINUX diff --git a/test/test_cerbero_build_source_localdir.py b/test/test_cerbero_build_source_localdir.py new file mode 100644 index 000000000..ea9186f94 --- /dev/null +++ b/test/test_cerbero_build_source_localdir.py @@ -0,0 +1,58 @@ +# cerbero - a multi-platform build system for Open Source software +# Copyright (C) 2012 Andoni Morales Alastruey +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +import unittest +import os +import tempfile +import shutil + +from test.test_common import DummyConfig +from cerbero.build.cookbook import CookBook +from cerbero.build.oven import Oven +from cerbero.utils import run_until_complete + + +class LocalDirTest(unittest.TestCase): + def setUp(self): + self.config = DummyConfig() + self.tmp = tempfile.mkdtemp() + self.config.prefix = self.tmp + self.config.external_recipes = {'test': (os.path.join(os.path.dirname(__file__), 'recipes'), 1)} + self.cookbook = CookBook(self.config) + + def tearDown(self): + shutil.rmtree(self.tmp) + + def LocalDirProject(self, name, file): + # Build the autotools project + recipe_name = self.cookbook.get_closest_recipe(name) + recipe = self.cookbook.get_recipe(recipe_name) + oven = Oven([recipe_name], self.cookbook, force=True) + run_until_complete(oven.start_cooking()) + files = recipe.files_list() + # Check for the existance of file1.txt + self.assertEqual(files, [file]) + + def testLocalDirAutotools(self): + self.LocalDirProject('autotools-cerbero', 'share/autotools-cerbero/file1.txt') + + def testLocalDirMeson(self): + self.LocalDirProject('meson-cerbero', 'share/meson-cerbero/file1.txt') + + def testLocalDirCMake(self): + self.LocalDirProject('cmake-cerbero', 'share/cmake-cerbero/file1.txt')