From e8200497156882db1bd8c6a3b190847fc9233cac Mon Sep 17 00:00:00 2001 From: Jeremy Lorelli Date: Sat, 17 Feb 2024 22:09:08 -0800 Subject: [PATCH] feat: Start on ffmpeg, cleanup and fix fontconfig dir --- build.py | 193 ++++++++++++++++++++++++++++++++------------------ run-docker.sh | 6 +- 2 files changed, 127 insertions(+), 72 deletions(-) diff --git a/build.py b/build.py index 30cb9ad..af3ab8d 100755 --- a/build.py +++ b/build.py @@ -36,6 +36,10 @@ def get_lib_dir() -> str: return f'{get_install_dir()}/lib' +def get_pkgconf_dir() -> str: + return f'{get_lib_dir()}/pkgconfig' + + def get_global_subs() -> dict: return { 'INSTALLDIR': f'{get_install_dir()}', @@ -73,6 +77,36 @@ def download_and_extract(url: str, type: str, path: str) -> bool: return r.returncode == 0 +def add_pc_lib(pc: str, libs: list[str]) -> bool: + """ + Hack to add libs to a .pc pkg config file + + Parameters + ---------- + pc: str + Path to the pkg config file + libs: list[str] + Libs to add, including the -l part. These are just flags + + Returns + ------- + True if ok + """ + subst = ' '.join(libs) + l = [] + with open(pc, 'r') as fp: + l = fp.readlines() + + for i in range(0,len(l)): + if l[i].startswith('Libs:') and not l[i].endswith(subst): + l[i] = f'{l[i].strip()} {subst}\n' + + # Write it out + with open(pc, 'w') as fp: + fp.writelines(l) + return True + + class WorkDir(AbstractContextManager): def __init__(self, dir: str) -> None: @@ -138,6 +172,14 @@ def get_artifacts(self) -> list[str]: return [] + def get_headers(self) -> list[str]: + """ + Returns a list of directories that contain headers that should be installed + These will be copied into release/include + """ + return [] + + def get_directory(self) -> str: """ Returns the directory this dependency resides in @@ -236,7 +278,7 @@ def _execute_cmds(*args, **kwargs) -> bool: e.update(kwargs['env']) if verbose: print(f'args={a}, env={e}') - if subprocess.run(a, shell=False, env=e, capture_output=quiet).returncode != 0: + if subprocess.run(a, shell=kwargs['shell'] if 'shell' in kwargs else False, env=e, capture_output=quiet).returncode != 0: return False return True @@ -246,14 +288,12 @@ class Dep_autoconf(Dependency): def get_directory(self) -> str: return 'autoconf' - def configure(self) -> bool: return self._execute_cmds( ['./bootstrap'], ['./configure', f'--prefix={get_install_dir()}'] ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -264,7 +304,6 @@ class Dep_libffi(Dependency): def get_directory(self) -> str: return 'libffi' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh'], @@ -273,7 +312,6 @@ def configure(self) -> bool: env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -284,14 +322,12 @@ class Dep_zlib(Dependency): def get_directory(self) -> str: return 'zlib' - def configure(self) -> bool: return self._execute_cmds( ['./configure', '--static', '--64', f'--prefix={get_install_dir()}'], env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -302,7 +338,6 @@ class Dep_pcre(Dependency): def get_directory(self) -> str: return 'pcre' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh'], @@ -311,7 +346,6 @@ def configure(self) -> bool: env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -322,15 +356,12 @@ class Dep_bzip2(Dependency): def get_directory(self) -> str: return 'bzip2' - def get_artifacts(self) -> list[str]: return ['libbz2.so.1.0'] - def configure(self) -> bool: return True - def build(self) -> bool: return self._execute_cmds( ['make', 'install', f'-j{nproc()}', 'CFLAGS=-fPIC', f'PREFIX={get_install_dir()}'], @@ -347,18 +378,15 @@ class Dep_curl(Dependency): def get_directory(self) -> str: return 'curl' - def get_artifacts(self) -> list[str]: return ['libcurl.a'] - def configure(self) -> bool: return self._execute_cmds( ['cmake', '-Bbuild', '-GNinja', '-DCMAKE_BUILD_TYPE=Release', f'-DCMAKE_INSTALL_PREFIX={get_install_dir()}', '-DBUILD_SHARED_LIBS=OFF', '-DCMAKE_C_FLAGS=-fPIC', '-DCMAKE_CXX_FLAGS=-fPIC'] ) - def build(self) -> bool: return self._execute_cmds( ['ninja', '-C', 'build', 'install'] @@ -371,7 +399,6 @@ class Dep_glib(Dependency): def get_directory(self) -> str: return 'glib' - def configure(self) -> bool: return self._execute_cmds( ['meson', 'build', '--buildtype', 'release', '--default-library', 'static', '--prefix', @@ -379,7 +406,6 @@ def configure(self) -> bool: env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: if not self._execute_cmds(['ninja', '-C', 'build', 'install']): return False @@ -393,7 +419,6 @@ class Dep_pixman(Dependency): def get_directory(self) -> str: return 'pixman' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh', '--enable-gtk=no', '--enable-png=no', '--enable-shared=no', '--enable-static', @@ -401,7 +426,6 @@ def configure(self) -> bool: env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -411,7 +435,6 @@ class Dep_brotli(Dependency): def get_directory(self) -> str: return 'brotli' - def configure(self) -> bool: return self._execute_cmds( ['./bootstrap'], @@ -419,7 +442,6 @@ def configure(self) -> bool: env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -430,14 +452,12 @@ class Dep_libpng(Dependency): def get_directory(self) -> str: return 'libpng' - def configure(self) -> bool: return self._execute_cmds( ['./configure', '--enable-static', '--disable-shared', f'--prefix={get_install_dir()}'], env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: if not self._execute_cmds(['make', 'install', f'-j{nproc()}']): return False @@ -453,11 +473,9 @@ class Dep_freetype(Dependency): def get_directory(self) -> str: return 'freetype' - def get_artifacts(self) -> list[str]: return ['libfreetype.so'] - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh'], @@ -473,7 +491,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -483,14 +500,12 @@ class Dep_jsonc(Dependency): def get_directory(self) -> str: return 'json-c' - def configure(self) -> bool: return self._execute_cmds( ['cmake', '.', '-B', 'build', '-DCMAKE_BUILD_TYPE=Release', '-DBUILD_STATIC_LIBS=ON', '-DBUILD_SHARED_LIBS=OFF', f'-DCMAKE_INSTALL_PREFIX={get_install_dir()}', '-DDISABLE_EXTRA_LIBS=ON'], env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', '-C', 'build', 'install', f'-j{nproc()}']) @@ -500,7 +515,6 @@ class Dep_expat(Dependency): def get_directory(self) -> str: return 'libexpat/expat' - def configure(self) -> bool: return self._execute_cmds( ['./buildconf.sh'], @@ -508,7 +522,6 @@ def configure(self) -> bool: env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -518,15 +531,13 @@ class Dep_fontconfig(Dependency): def get_directory(self) -> str: return 'fontconfig' - def get_artifacts(self) -> list[str]: return ['libfontconfig.so'] - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh', '--enable-static=no', f'--prefix={get_install_dir()}', f'--with-expat={get_install_dir()}', '--sysconfdir=/etc', '--disable-docs', - f'--datadir={get_install_dir()}/share', '--disable-cache-build'], + f'--datadir={get_install_dir()}/share', '--disable-cache-build',], env={ 'CFLAGS': f'-fPIC -I{get_inc_dir()}', 'LDFLAGS': f'-L{get_lib_dir()} -Wl,--no-undefined', @@ -539,16 +550,19 @@ def configure(self) -> bool: } ) - def apply_patches(self) -> bool: return self._apply_patches([ 'patches/fontconfig/002-add-face-sub.patch', 'patches/fontconfig/fontpattern.diff' ]) - def build(self) -> bool: - return self._execute_cmds(['make', 'install', f'-j{nproc()}']) + return all([ + # build only! + self._execute_cmds(['make', f'-j{nproc()}', 'install-exec']), + # Manually copy the fontconfig headers and libs... can't use make install because that tries to put stuff in /etc! + #self._execute_cmds([f'cp -vf libfontconfig.so* "{get_lib_dir()}/lib"']) + ]) # Needed for fribidi @@ -557,14 +571,12 @@ class Dep_c2man(Dependency): def get_directory(self) -> str: return 'c2man' - def configure(self) -> bool: return self._execute_cmds( ['./Configure', '-s', '-d', '-e'], env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: if not self._execute_cmds(['make', f'-j{nproc()}']): return False @@ -578,14 +590,12 @@ class Dep_fribidi(Dependency): def get_directory(self) -> str: return 'fribidi' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh', f'--prefix={get_install_dir()}', '--enable-shared=no', '--enable-static'], env={'CFLAGS': '-fPIC'} ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -595,11 +605,9 @@ class Dep_cairo(Dependency): def get_directory(self) -> str: return 'cairo' - def get_artifacts(self) -> list[str]: return ['libcairo.so'] - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh', '--enable-xlib=yes', '--enable-xlib-xrender=yes', '--enable-xlib-xcb=yes', @@ -617,7 +625,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -627,7 +634,6 @@ class Dep_harfbuzz(Dependency): def get_directory(self) -> str: return 'harfbuzz' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh', f'--prefix={get_install_dir()}', '--enable-shared=no', '--enable-static'], @@ -638,7 +644,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -648,7 +653,6 @@ class Dep_libdatrie(Dependency): def get_directory(self) -> str: return 'libdatrie' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh'], @@ -660,7 +664,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -671,7 +674,6 @@ class Dep_libthai(Dependency): def get_directory(self) -> str: return 'libthai' - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh'], @@ -681,7 +683,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -692,7 +693,6 @@ class Dep_pango(Dependency): def get_directory(self) -> str: return 'pango' - def get_artifacts(self) -> list[str]: return [ 'libpango-1.0.so', @@ -700,14 +700,12 @@ def get_artifacts(self) -> list[str]: 'libpangoft2-1.0.so' ] - def apply_patches(self) -> bool: return self._apply_patches([ 'patches/pango/001-add-face-sub.patch', 'patches/pango/meson-cairo.patch' ]) - def configure(self) -> bool: return self._execute_cmds( ['meson', 'build', '--prefix', get_install_dir(), '--buildtype', 'release', @@ -734,11 +732,9 @@ class Dep_Xiph(Dependency): def __init__(self, dep: str): self.dep = dep - def get_directory(self) -> str: return self.dep - def configure(self) -> bool: return self._execute_cmds( ['./autogen.sh'], @@ -748,9 +744,22 @@ def configure(self) -> bool: } ) - def build(self) -> bool: - return self._execute_cmds(['make', 'install', f'-j{nproc()}']) + if not self._execute_cmds(['make', 'install', f'-j{nproc()}']): + return False + + match self.dep: + case 'ogg': + return add_pc_lib(f'{get_pkgconf_dir()}/ogg.pc', ['-lm']) + case 'vorbis': + return all([ + add_pc_lib(f'{get_pkgconf_dir()}/vorbis.pc', ['-logg', '-lm']), + add_pc_lib(f'{get_pkgconf_dir()}/vorbisenc.pc', ['-lvorbis', '-logg', '-lm']), + add_pc_lib(f'{get_pkgconf_dir()}/vorbisfile.pc', ['-lvorbis', '-logg', '-lm']), + ]) + case 'opus': + return add_pc_lib(f'{get_pkgconf_dir()}/opus.pc', ['-lm']) + return True class Dep_mpg123(Dependency): @@ -758,7 +767,6 @@ class Dep_mpg123(Dependency): def get_directory(self) -> str: return 'mpg123' - def configure(self) -> bool: return self._execute_cmds( ['autoreconf', '-iv'], @@ -768,7 +776,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -782,7 +789,6 @@ def download(self) -> bool: def get_directory(self) -> str: return 'mp3lame' - def configure(self) -> bool: return self._execute_cmds( ['./configure', '--enable-shared=no', '--enable-static', f'--prefix={get_install_dir()}'], @@ -791,7 +797,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: return self._execute_cmds(['make', 'install', f'-j{nproc()}']) @@ -802,11 +807,9 @@ class Dep_libsndfile(Dependency): def get_directory(self) -> str: return 'libsndfile' - def get_artifacts(self) -> list[str]: return ['libsndfile.so'] - def configure(self) -> bool: return self._execute_cmds( #['autoreconf', '-iv'], @@ -837,7 +840,6 @@ def configure(self) -> bool: } ) - def build(self) -> bool: # TODO: cmake #return self._execute_cmds(['make', '-C', 'build', 'install', f'-j{nproc()}']) @@ -845,6 +847,44 @@ def build(self) -> bool: +class Dep_ffmpeg(Dependency): + + def get_directory(self) -> str: + return 'ffmpeg' + + def get_artifacts(self) -> list[str]: + return [ + 'libavcodec.a', + 'libavformat.a', + #'libavdevice.a', # Probably don't need this + 'libavfilter.a', + 'libavutil.a', + 'libswscale.a', + 'libswresample.a' + ] + + def get_headers(self) -> list[str]: + return [ + 'libavcodec', + 'libavformat', + 'libavdevice', + 'libavfilter', + 'libavutil', + 'libswresample', + 'libswscale' + ] + + def configure(self) -> bool: + return self._execute_cmds( + ['./configure', '--disable-avx', '--disable-avx2', '--disable-sse42', '--disable-sse4', + f'--prefix={get_install_dir()}', '--enable-libvorbis', '--enable-libopus', '--disable-programs', + '--disable-doc'] + ) + + def build(self) -> bool: + return self._execute_cmds(['make', 'install', f'-j{nproc()}']) + + def get_soname(lib: str) -> str|None: result = subprocess.run(['readelf', '-d', f'{get_lib_dir()}/{lib}'], capture_output=True) if result.returncode != 0: @@ -880,13 +920,25 @@ def install_lib(lib: str): shutil.copy(f'{get_lib_dir()}/{lib}', f'release/lib/external/linux64/{lib}') +def install_headers(dir: str, dst: str): + shutil.copytree(f'{get_inc_dir()}/{dir}', f'{dst}/{dir}', dirs_exist_ok=True) + + def create_release(deps: Dict[str, Dependency]): mkdir_p('release/bin/linux64') mkdir_p('release/lib/external/linux64') + mkdir_p('release/include') + for d in deps: + # Install binary artifacts artifacts = deps[d].get_artifacts() for a in artifacts: install_lib(a) + # Install headers + headers = deps[d].get_headers() + for h in headers: + install_headers(h, 'release/include') + # Strip everything for c in glob.glob('release/**/*.so*', recursive=True): subprocess.run(['strip', '-x', c]) @@ -930,17 +982,19 @@ def main(): 'mpg123': Dep_mpg123(), 'mp3lame': Dep_mp3lame(), 'libsndfile': Dep_libsndfile(), + 'ffmpeg': Dep_ffmpeg(), } - + parser = argparse.ArgumentParser() - parser.add_argument('--only', dest='ONLY', nargs='*', choices=deps.keys(), help='Only build the specified deps') + parser.add_argument('--only', dest='ONLY', nargs='+', choices=deps.keys(), help='Only build the specified deps') parser.add_argument('--quiet', action='store_true', help='Quiet build output') parser.add_argument('--verbose', action='store_true', help='Verbose mode') parser.add_argument('--clean', action='store_true', help='Cleans the build environment') - parser.add_argument('--only-release', action='store_true', help='Only assemble a release from install/') - parser.add_argument('--skip-release', action='store_true', help='Skip assembling of tar.gz') + parser.add_argument('--only-release', action='store_true', dest='only_release', help='Only assemble a release from install/') + parser.add_argument('--skip-release', action='store_true', dest='skip_release', help='Skip assembling of tar.gz') args = parser.parse_args() - + + os.chdir(get_top()) # Clean if requested @@ -976,4 +1030,5 @@ def main(): if not args.skip_release: create_release(deps) -main() +if __name__ == '__main__': + main() diff --git a/run-docker.sh b/run-docker.sh index 3b10738..842aa73 100755 --- a/run-docker.sh +++ b/run-docker.sh @@ -1,5 +1,5 @@ -#!/bin/bash +#!/usr/bin/env bash # Runs this in a docker container for portability -docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd)":/build -w /build -e ARCH=amd64 chaos-dep-builder bash -c "./build.sh $@" - +ARGS="$@" +docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd)":/build -w /build -e ARCH=amd64 strata-steamrt bash -c "./build.py $ARGS"