├── layout ├── vexpress │ ├── data │ │ └── .keep │ └── etc │ │ └── fstab ├── pandaboard │ └── etc │ │ └── fstab └── common │ └── etc │ └── init.d │ ├── S00boot.sh │ └── rcS ├── README ├── packages ├── sysroot.package ├── system-image-aarch64.package ├── system-image-vexpress.package ├── libgmp.package ├── zlib.package ├── valgrind.package ├── lzo.package ├── lxc.package ├── libtommath.package ├── libnl.package ├── libtomcrypt.package ├── openssl.package ├── libunwind.package ├── libdwarf.package ├── libjpeg-turbo.package ├── bzip2.package ├── perf.package ├── e2fsprogs.package ├── gdb.package ├── wpa-supplicant.files │ └── configure ├── minimal-init.files │ └── etc │ │ └── init.d │ │ ├── S00boot.sh │ │ └── rcS ├── wpa-supplicant.package ├── busybox.package ├── libelfutils.package ├── libpng.package ├── freetype.package ├── minimal-init.package ├── freetype.files │ └── enable-lcd-rendering.patch └── linux.package ├── .gitignore ├── byo ├── __init__.py ├── toolchain.py ├── process.py ├── package.py └── builder.py └── byobuild /layout/vexpress/data/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | usage: ./byobuild , e.g. /usr/bin/armv7a-hardfloat-linux-gnueabi- 2 | -------------------------------------------------------------------------------- /packages/sysroot.package: -------------------------------------------------------------------------------- 1 | InstallDirectory: "{SystemRootDirectory}" 2 | InstallIntoCommonRoot: No 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .sources 2 | .build 3 | .install 4 | .rootfs 5 | root.tar* 6 | __pycache__ 7 | *.py[co] 8 | build.*/ 9 | -------------------------------------------------------------------------------- /layout/pandaboard/etc/fstab: -------------------------------------------------------------------------------- 1 | /dev/mmcblk0p1 /boot vfat noauto,noatime 1 2 2 | /dev/mmcblk0p2 / ext4 noatime 0 1 3 | -------------------------------------------------------------------------------- /layout/vexpress/etc/fstab: -------------------------------------------------------------------------------- 1 | /dev/mmcblk0p1 / squashfs noatime 0 1 2 | /dev/mmcblk0p2 /data auto noatime 0 0 3 | -------------------------------------------------------------------------------- /packages/system-image-aarch64.package: -------------------------------------------------------------------------------- 1 | Depends-On: 2 | - sysroot 3 | - linux-aarch64 4 | - busybox 5 | - minimal-init 6 | -------------------------------------------------------------------------------- /packages/system-image-vexpress.package: -------------------------------------------------------------------------------- 1 | Depends-On: 2 | - sysroot 3 | - linux-vexpress 4 | - busybox 5 | - minimal-init 6 | -------------------------------------------------------------------------------- /byo/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os.path 3 | from shlex import quote as escape 4 | 5 | root = os.path.abspath(os.path.dirname(sys.argv[0])) 6 | -------------------------------------------------------------------------------- /packages/libgmp.package: -------------------------------------------------------------------------------- 1 | Version: 2 | Fetch: https://gmplib.org/download/gmp/gmp-6.2.1.tar.xz 3 | 4 | Build: 5 | - ./configure --prefix=/usr --host={Host} 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/zlib.package: -------------------------------------------------------------------------------- 1 | Version: 1.2.13 2 | Fetch: https://zlib.net/zlib-{Version}.tar.gz 3 | 4 | Build: 5 | - CROSS_PREFIX={CrossCompilePrefix} ./configure --prefix=/usr 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/valgrind.package: -------------------------------------------------------------------------------- 1 | Version: 3.14.0 2 | Fetch: http://www.valgrind.org/downloads/valgrind-{Version}.tar.bz2 3 | 4 | Build: 5 | - ./configure --prefix=/usr --host={Host} 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/lzo.package: -------------------------------------------------------------------------------- 1 | Version: '2.10' 2 | Fetch: https://www.oberhumer.com/opensource/lzo/download/lzo-{Version}.tar.gz 3 | 4 | Build: 5 | - ./configure --prefix=/usr --host={Host} 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/lxc.package: -------------------------------------------------------------------------------- 1 | Version: 3.0.2 2 | Fetch: https://github.com/lxc/lxc/archive/lxc-{Version}.tar.gz 3 | 4 | Build: 5 | - ./autogen.sh 6 | - ./configure --prefix=/usr --host={Host} 7 | - make -j{Jobs} 8 | - make install DESTDIR={InstallDirectory} 9 | -------------------------------------------------------------------------------- /packages/libtommath.package: -------------------------------------------------------------------------------- 1 | Version: 1.2.0 2 | Fetch: https://github.com/libtom/libtommath/releases/download/v{Version}/ltm-{Version}.tar.xz 3 | 4 | Build: 5 | - make -j{Jobs} CC={CCompiler} CXX={CXXCompiler} DESTDIR={InstallDirectory} PREFIX=/usr install 6 | -------------------------------------------------------------------------------- /packages/libnl.package: -------------------------------------------------------------------------------- 1 | Version: 1.1.4 2 | Fetch: https://www.infradead.org/~tgr/libnl/files/libnl-{Version}.tar.gz 3 | 4 | Build: 5 | - ./configure --host={Host} --prefix=/usr 6 | - make -j{Jobs} CC={CCompiler} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/libtomcrypt.package: -------------------------------------------------------------------------------- 1 | Version: 1.18.2 2 | Fetch: https://github.com/libtom/libtomcrypt/releases/download/v{Version}/crypt-{Version}.tar.xz 3 | 4 | Build: 5 | - make -j{Jobs} CC={CCompiler} CXX={CXXCompiler} DESTDIR={InstallDirectory} PREFIX=/usr install 6 | -------------------------------------------------------------------------------- /packages/openssl.package: -------------------------------------------------------------------------------- 1 | Version: 1.1.1t 2 | Fetch: https://www.openssl.org/source/openssl-{Version}.tar.gz 3 | 4 | Build: 5 | - MACHINE={Host} ./config --prefix=/usr --cross-compile-prefix={CrossCompilePrefix} 6 | - make depend 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/libunwind.package: -------------------------------------------------------------------------------- 1 | Version: 1.6.2 2 | Fetch: http://download.savannah.nongnu.org/releases/libunwind/libunwind-{Version}.tar.gz 3 | 4 | Build: 5 | - CROSS_PREFIX={CrossCompilePrefix} ./configure --prefix=/usr 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/libdwarf.package: -------------------------------------------------------------------------------- 1 | Version: 0.6.0 2 | Fetch: https://www.prevanders.net/libdwarf-{Version}.tar.xz 3 | 4 | Build: 5 | - CC={CCompiler} CPPFLAGS=-I{TargetDevelopmentRoot}/usr/include LDFLAGS=-L{TargetDevelopmentRoot}/usr/lib ./configure --prefix=/usr --host={Host} 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | -------------------------------------------------------------------------------- /packages/libjpeg-turbo.package: -------------------------------------------------------------------------------- 1 | Version: 2.1.5.1 2 | Fetch: https://downloads.sourceforge.net/project/libjpeg-turbo/{Version}/libjpeg-turbo-{Version}.tar.gz 3 | 4 | Build: 5 | - cmake -DCMAKE_C_COMPILER={CCompiler} -DCMAKE_FIND_ROOT_PATH={TargetDevelopmentRoot} -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX={InstallDirectory} 6 | - make -j{Jobs} 7 | - make install 8 | -------------------------------------------------------------------------------- /layout/common/etc/init.d/S00boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "mounting /dev" 4 | mount -t devtmpfs dev /dev 5 | echo "mounting /proc" 6 | mount -t proc proc /proc 7 | echo "mounting /sys" 8 | mount -t sysfs sysfs /sys 9 | echo "switching to mdev" 10 | echo /sbin/mdev > /proc/sys/kernel/hotplug 11 | echo "populating devices from sysfs" 12 | mdev -s 13 | echo "boot done" 14 | -------------------------------------------------------------------------------- /packages/bzip2.package: -------------------------------------------------------------------------------- 1 | Version: 1.0.8 2 | Fetch: https://sourceware.org/pub/bzip2/bzip2-{Version}.tar.gz 3 | Signature: https://sourceware.org/pub/bzip2/bzip2-{Version}.tar.gz.sign 4 | Public-Key: https://sourceware.org/pub/bzip2/gpgkey-5C1D1AA44BE649DE760A.gpg 5 | 6 | 7 | Build: 8 | - make -j{Jobs} install CC={CCompiler} PREFIX={InstallDirectory}/usr 9 | 10 | Install: 11 | usr/bin: tools 12 | -------------------------------------------------------------------------------- /byo/toolchain.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def find_in_dir(path): 4 | if not os.path.isdir(path) or not os.access(path, os.R_OK): 5 | return 6 | for f in os.listdir(path): 7 | if f.endswith('gcc'): 8 | yield (path, f[:-3]) 9 | 10 | def find_in_dirs(dirs): 11 | for dir in dirs: 12 | yield from find_in_dir(dir) 13 | 14 | def find_in_path(): 15 | yield from find_in_dirs(os.getenv('PATH').split(':')) 16 | -------------------------------------------------------------------------------- /packages/perf.package: -------------------------------------------------------------------------------- 1 | Version: 4.16.17 2 | Fetch: https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-{Version}.tar.xz 3 | Signature: https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-{Version}.tar.sign 4 | 5 | Depends: 6 | - libelfutils 7 | - libdwarf 8 | - libelf 9 | 10 | Build: 11 | - ARCH={LinuxPlatform} CROSS_COMPILE={CrossCompilePrefix} make WERROR=0 DESTDIR={InstallDirectory} V=1 JOBS={Jobs} -C tools/perf install 12 | -------------------------------------------------------------------------------- /packages/e2fsprogs.package: -------------------------------------------------------------------------------- 1 | Version: 1.47.0 2 | Fetch: https://www.kernel.org/pub/linux/kernel/people/tytso/e2fsprogs/v{Version}/e2fsprogs-{Version}.tar.xz 3 | 4 | Build: 5 | - ./configure --prefix=/usr --host={Host} CPPFLAGS=-I{TargetDevelopmentRoot}/usr/include LDFLAGS=-L{TargetDevelopmentRoot}/usr/lib 6 | - make -j{Jobs} 7 | - make install DESTDIR={InstallDirectory} 8 | - make -j{Jobs} -C lib/uuid install DESTDIR={InstallDirectory} 9 | -------------------------------------------------------------------------------- /packages/gdb.package: -------------------------------------------------------------------------------- 1 | Version: 13.1 2 | Fetch: http://ftp.gnu.org/gnu/gdb/gdb-{Version}.tar.xz 3 | Signature: http://ftp.gnu.org/gnu/gdb/gdb-{Version}.tar.xz.sig 4 | 5 | Tag: devel 6 | 7 | Depends-On: 8 | - libgmp 9 | 10 | Build: 11 | - ./configure --host={Host} --prefix=/usr --with-libgmp-prefix={TargetDevelopmentRoot}/usr 12 | - make -j{Jobs} CC={CCompiler} CXX={CXXCompiler} 13 | - make install DESTDIR={InstallDirectory} 14 | -------------------------------------------------------------------------------- /packages/wpa-supplicant.files/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ROOT=$1 4 | pushd wpa_supplicant 5 | echo "CFLAGS += -I${ROOT}/usr/include" > .config 6 | echo "LIBS += -L${ROOT}/usr/lib" >> .config 7 | echo "LIBS_p += -L${ROOT}/usr/lib -ldl" >> .config 8 | echo "export LIBDIR=/usr/lib" >> .config 9 | echo "export BINDIR=/usr/bin" >> .config 10 | echo "export CONFIG_TLS=internal" >> .config 11 | cat defconfig >> .config 12 | popd 13 | 14 | -------------------------------------------------------------------------------- /packages/minimal-init.files/etc/init.d/S00boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "mounting /tmp" 4 | mount -t tmpfs tmp /tmp 5 | echo "mounting /dev" 6 | mount -t devtmpfs dev /dev 7 | echo "mounting /proc" 8 | mount -t proc proc /proc 9 | echo "mounting /sys" 10 | mount -t sysfs sysfs /sys 11 | echo "switching to mdev" 12 | echo /sbin/mdev > /proc/sys/kernel/hotplug 13 | echo "populating devices from sysfs" 14 | mdev -s 15 | echo "boot done" 16 | -------------------------------------------------------------------------------- /packages/wpa-supplicant.package: -------------------------------------------------------------------------------- 1 | Version: 2.10 2 | Fetch: https://w1.fi/releases/wpa_supplicant-{Version}.tar.gz 3 | 4 | Build: 5 | - cp "{AuxFilesDirectory}/configure" "{WorkDirectory}" 6 | - ./configure {TargetDevelopmentRoot} 7 | - make -C wpa_supplicant -j{Jobs} CC={CCompiler} 8 | - make -C wpa_supplicant install DESTDIR={InstallDirectory} LIBDIR=lib/ BINDIR=bin/ 9 | 10 | Depends-On: 11 | - libtommath 12 | - libnl 13 | - linux 14 | -------------------------------------------------------------------------------- /packages/busybox.package: -------------------------------------------------------------------------------- 1 | Version: 1.33.2 2 | Fetch: https://www.busybox.net/downloads/busybox-{Version}.tar.bz2 3 | Signature: https://www.busybox.net/downloads/busybox-{Version}.tar.bz2.sign 4 | Public-Key: https://busybox.net/~vda/vda_pubkey.gpg 5 | 6 | Build: 7 | - make defconfig 8 | - make -j{Jobs} CROSS_COMPILE={CrossCompilePrefix} 9 | - make -j{Jobs} CROSS_COMPILE={CrossCompilePrefix} install 10 | 11 | InstallDirectory: _install 12 | 13 | Install: 14 | - make install 15 | -------------------------------------------------------------------------------- /packages/libelfutils.package: -------------------------------------------------------------------------------- 1 | Version: 0.173 2 | Fetch: http://sourceware.org/pub/elfutils/{Version}/elfutils-{Version}.tar.bz2 3 | 4 | Depends-On: 5 | - zlib 6 | - bzip2 7 | 8 | Build: 9 | - CC={CCompiler} CPPFLAGS=-I{TargetDevelopmentRoot}/usr/include LDFLAGS="-L{TargetDevelopmentRoot}/usr/lib -Wl,-rpath-link,{TargetDevelopmentRoot}/usr/lib" ./configure --prefix=/usr --host={Host} --disable-textrelcheck 10 | - make -j{Jobs} 11 | - make install DESTDIR={InstallDirectory} 12 | -------------------------------------------------------------------------------- /packages/libpng.package: -------------------------------------------------------------------------------- 1 | Version: 1.6.39 2 | Fetch: https://sourceforge.net/projects/libpng/files/libpng16/{Version}/libpng-{Version}.tar.xz 3 | Signature: https://sourceforge.net/projects/libpng/files/libpng16/{Version}/libpng-{Version}.tar.xz 4 | 5 | Build: 6 | - ./configure --prefix=/usr --host={Host} CPPFLAGS=-I{TargetDevelopmentRoot}/usr/include LDFLAGS=-L{TargetDevelopmentRoot}/usr/lib 7 | - make -j{Jobs} 8 | - make install DESTDIR={InstallDirectory} 9 | 10 | Depends-On: 11 | - zlib 12 | 13 | Install: 14 | usr/bin/png-fix-itxt: tools, devel 15 | usr/bin/pngfix: tools, devel 16 | -------------------------------------------------------------------------------- /layout/common/etc/init.d/rcS: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "starting init..." 4 | # Start all init scripts in /etc/init.d 5 | # executing them in numerical order. 6 | # 7 | for i in /etc/init.d/S??* ;do 8 | 9 | echo "processing $i" 10 | # Ignore dangling symlinks (if any). 11 | [ ! -f "$i" ] && continue 12 | 13 | case "$i" in 14 | *.sh) 15 | # Source shell script for speed. 16 | ( 17 | trap - INT QUIT TSTP 18 | set start 19 | . $i 20 | ) 21 | ;; 22 | *) 23 | # No sh extension, so fork subprocess. 24 | $i start 25 | ;; 26 | esac 27 | 28 | done 29 | -------------------------------------------------------------------------------- /packages/freetype.package: -------------------------------------------------------------------------------- 1 | Version: 2.13.0 2 | Fetch: https://sourceforge.net/projects/freetype/files/freetype2/{Version}/freetype-{Version}.tar.xz/download 3 | Signature: https://sourceforge.net/projects/freetype/files/freetype2/{Version}/freetype-{Version}.tar.xz.sig/download 4 | Depends-On: 5 | - libpng 6 | 7 | Build: 8 | - patch -p1 -i "{AuxFilesDirectory}/enable-lcd-rendering.patch" 9 | - CPPFLAGS=-I{TargetDevelopmentRoot}/usr/include LDFLAGS=-L{TargetDevelopmentRoot}/usr/lib ./configure --prefix=/usr --host={Host} --enable-static --without-zlib --without-bzip2 --with-png=no 10 | - make -j{Jobs} 11 | - make install DESTDIR={InstallDirectory} 12 | -------------------------------------------------------------------------------- /packages/minimal-init.files/etc/init.d/rcS: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "starting init..." 4 | # Start all init scripts in /etc/init.d 5 | # executing them in numerical order. 6 | # 7 | for i in /etc/init.d/S??* ;do 8 | 9 | echo "processing $i" 10 | # Ignore dangling symlinks (if any). 11 | [ ! -f "$i" ] && continue 12 | 13 | case "$i" in 14 | *.sh) 15 | # Source shell script for speed. 16 | ( 17 | trap - INT QUIT TSTP 18 | set start 19 | . $i 20 | ) 21 | ;; 22 | *) 23 | # No sh extension, so fork subprocess. 24 | $i start 25 | ;; 26 | esac 27 | 28 | done 29 | -------------------------------------------------------------------------------- /packages/minimal-init.package: -------------------------------------------------------------------------------- 1 | Build: 2 | - mkdir -p {InstallDirectory}/dev 3 | - touch {InstallDirectory}/dev/.keep 4 | - mkdir -p {InstallDirectory}/etc 5 | - touch {InstallDirectory}/etc/.keep 6 | - mkdir -p {InstallDirectory}/media 7 | - touch {InstallDirectory}/media/.keep 8 | - mkdir -p {InstallDirectory}/sys 9 | - touch {InstallDirectory}/sys/.keep 10 | - mkdir -p {InstallDirectory}/proc 11 | - touch {InstallDirectory}/proc/.keep 12 | - mkdir -p {InstallDirectory}/tmp 13 | - touch {InstallDirectory}/tmp/.keep 14 | - mkdir -p {InstallDirectory}/var 15 | - touch {InstallDirectory}/var/.keep 16 | - cp -ar "{AuxFilesDirectory}/." "{InstallDirectory}" 17 | -------------------------------------------------------------------------------- /packages/freetype.files/enable-lcd-rendering.patch: -------------------------------------------------------------------------------- 1 | diff -ru freetype.old/include/freetype/config/ftoption.h freetype/include/freetype/config/ftoption.h 2 | --- freetype.old/include/freetype/config/ftoption.h 2017-08-23 05:55:39.000000000 +0100 3 | +++ freetype/include/freetype/config/ftoption.h 2018-06-28 01:08:07.522264480 +0100 4 | @@ -123,7 +123,7 @@ 5 | * When this macro is not defined, FreeType offers alternative LCD 6 | * rendering technology that produces excellent output. 7 | */ 8 | -/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ 9 | +#define FT_CONFIG_OPTION_SUBPIXEL_RENDERING 10 | 11 | 12 | /************************************************************************** 13 | -------------------------------------------------------------------------------- /packages/linux.package: -------------------------------------------------------------------------------- 1 | Release: 6 2 | Version: "{Release}.2.11" 3 | Fetch: https://cdn.kernel.org/pub/linux/kernel/v{Release}.x/linux-{Version}.tar.xz 4 | Signature: https://cdn.kernel.org/pub/linux/kernel/v{Release}.x/linux-{Version}.tar.sign 5 | 6 | Build: 7 | - ARCH={LinuxPlatform} CROSS_COMPILE={CrossCompilePrefix} make defconfig 8 | - ARCH={LinuxPlatform} CROSS_COMPILE={CrossCompilePrefix} make -j{Jobs} 9 | - mkdir -p {InstallDirectory}/boot 10 | - ARCH={LinuxPlatform} CROSS_COMPILE={CrossCompilePrefix} INSTALL_MOD_STRIP=1 make zinstall dtbs_install headers_install modules_install INSTALL_HDR_PATH={InstallDirectory}/usr INSTALL_MOD_PATH={InstallDirectory} INSTALL_PATH={InstallDirectory}/boot INSTALL_DTBS_PATH={InstallDirectory}/boot 11 | -------------------------------------------------------------------------------- /byobuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from argparse import ArgumentParser 4 | from byo import escape 5 | from byo.toolchain import find_in_path as find_toolchains_in_path 6 | from byo.builder import build 7 | import sys 8 | import logging 9 | 10 | parser = ArgumentParser("build tool for byo rootfs project") 11 | parser.add_argument('prefix', help = 'cross-compile toolchain prefix, e.g. armv7-linux-gnueabi-', nargs='?') 12 | parser.add_argument('target', help = 'target', default=[], nargs='*') 13 | parser.add_argument('-l', '--list', help = 'list toolchains available in PATH', action='store_true') 14 | parser.add_argument('-f', '--force', help = 'force rebuilding current target', action='store_true') 15 | parser.add_argument('-k', '--keep', help = 'keep sources in tmp/package directory after installation', action='store_true') 16 | parser.add_argument('-v', '--verbose', help = 'be verbose', action='store_true') 17 | parser.add_argument('-e', '--extract', help = 'extract rootfs with given tags(comma-separated)', nargs='+', default=[]) 18 | args = parser.parse_args() 19 | 20 | logging.basicConfig(level = logging.DEBUG if args.verbose else logging.INFO) 21 | 22 | if args.list: 23 | print("possible prefixes listed below:") 24 | for path, prefix in find_toolchains_in_path(): 25 | print('\t%s %s' %(sys.argv[0], escape(prefix))) 26 | 27 | if args.prefix is not None: 28 | build(args.prefix, *args.target, force = args.force, keep = args.keep, extract = args.extract) 29 | -------------------------------------------------------------------------------- /byo/process.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import subprocess 4 | import shutil 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | class Environment(object): 9 | def __init__(self, root, name): 10 | self.root = root 11 | self.name = name 12 | self.log_dir = self.create_dir('tmp', name) 13 | self.log_path = os.path.join(self.log_dir, "build.log") 14 | with open(self.log_path, "wt") as log: 15 | pass 16 | 17 | def create_dir(self, *args): 18 | path = os.path.join(self.root, *args) 19 | try: 20 | os.makedirs(path) 21 | except: 22 | pass 23 | return path 24 | 25 | def get_output(self, cwd, *args, **kw): 26 | completed = subprocess.run(args, stdout = subprocess.PIPE, bufsize = 256 * 1024, cwd = cwd) 27 | if completed.returncode != 0: 28 | raise Exception("command %s failed with code %d, cwd: %s" %(" ".join(args), completed.returncode, cwd)) 29 | return completed.stdout.decode('utf-8') 30 | 31 | 32 | def exec(self, cwd, *args, **kw): 33 | if cwd is None: 34 | raise Exception("you must provide current directory for your command") 35 | 36 | env = os.environ.copy() 37 | 38 | mod_args = [] 39 | can_be_var = True 40 | for arg in args: 41 | if can_be_var and '=' in arg: #var 42 | pos = arg.index('=') 43 | key, value = arg[:pos], arg[pos + 1:] 44 | logger.debug('setting %s to %s' %(key, value)) 45 | env[key] = value 46 | else: 47 | can_be_var = False 48 | mod_args.append(arg) 49 | 50 | args = mod_args 51 | cmd = " ".join(args) 52 | logger.debug("running %s", cmd) 53 | with open(self.log_path, "at") as log: 54 | log.write("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nrunning %s\n" %cmd) 55 | completed = subprocess.run(args, stderr = subprocess.STDOUT, stdout = log, bufsize = 256 * 1024, cwd = cwd, env = env) 56 | if completed.returncode != 0: 57 | raise Exception("command %s failed with code %d, cwd: %s" %(cmd, completed.returncode, cwd)) 58 | 59 | def cleanup(self): 60 | tmp = os.path.join(self.root, 'tmp', self.name) 61 | if os.path.exists(tmp): 62 | logger.info('cleaning up...') 63 | shutil.rmtree(tmp) 64 | logger.info('all done') 65 | -------------------------------------------------------------------------------- /byo/package.py: -------------------------------------------------------------------------------- 1 | import byo 2 | import logging 3 | import os.path 4 | import yaml 5 | import json 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | from enum import IntEnum 10 | 11 | class State(IntEnum): 12 | NOT_PRESENT = 0 13 | DOWNLOADED = 1 14 | UNPACKED = 2 15 | BUILT = 3 16 | INSTALLED = 4 17 | 18 | class PackageState(object): 19 | def __init__(self, package_state_dir): 20 | self.state_dir = package_state_dir 21 | self.__state_file = os.path.join(package_state_dir, 'state') 22 | self.__files_file = os.path.join(package_state_dir, 'files') 23 | self.__load() 24 | 25 | @property 26 | def state(self): 27 | return self.__state 28 | 29 | @state.setter 30 | def state(self, state): 31 | self.__state = state 32 | with open(self.__state_file, "w") as f: 33 | f.write(state.name) 34 | 35 | def __load(self): 36 | self.__state = State.NOT_PRESENT 37 | if not os.path.exists(self.__state_file): 38 | return 39 | try: 40 | with open(self.__state_file) as f: 41 | self.__state = State[f.read()] 42 | except: 43 | logger.exception("failed loading current state") 44 | 45 | def reset(self): 46 | self.__state = State.NOT_PRESENT 47 | if os.path.exists(self.__state_file): 48 | os.unlink(self.__state_file) 49 | if os.path.exists(self.__files_file): 50 | os.unlink(self.__files_file) 51 | 52 | @property 53 | def files(self): 54 | with open(self.__files_file) as f: 55 | return json.loads(f.read()) 56 | 57 | 58 | @files.setter 59 | def files(self, files): 60 | with open(self.__files_file, "w") as f: 61 | f.write(json.dumps(files)) 62 | 63 | 64 | class Metadata(object): 65 | def __init__(self, data): 66 | self.data = data 67 | 68 | def __format(self, text): 69 | while True: 70 | next = text.format(*[], **self.data) 71 | if next == text: 72 | break 73 | text = next 74 | return next 75 | 76 | 77 | def __format_var(self, name): 78 | url = self.data.get(name, None) 79 | if url is not None: 80 | return self.__format(url) 81 | 82 | @property 83 | def depends(self): 84 | return self.data.get('Depends-On', []) 85 | 86 | @property 87 | def public_key(self): 88 | return self.data.get('Public-Key', None) 89 | 90 | @property 91 | def signature(self): 92 | return self.__format_var('Signature') 93 | 94 | @property 95 | def fetch_url(self): 96 | return self.__format_var('Fetch') 97 | 98 | @property 99 | def install_dir(self): 100 | return self.__format_var('InstallDirectory') 101 | 102 | @property 103 | def copy_to_root(self): 104 | return self.data.get('InstallIntoCommonRoot', True) 105 | 106 | @property 107 | def build(self): 108 | return map(self.__format, self.data.get('Build', [])) 109 | 110 | def read_metadata(name): 111 | root = os.path.join(byo.root, 'packages') 112 | with open(os.path.join(root, name + ".package")) as f: 113 | data = yaml.safe_load(f) 114 | logger.debug('read metadata %s %s', name, data) 115 | data = Metadata(data) 116 | return data 117 | 118 | def get_package_queue(name): 119 | queue = [name] 120 | result = [] 121 | visited = set() 122 | while queue: 123 | current = queue 124 | queue = [] 125 | for name in current: 126 | logger.debug('processing %s...' %name) 127 | if name not in visited: 128 | visited.add(name) 129 | result.append(name) 130 | data = read_metadata(name) 131 | queue += data.depends 132 | return reversed(result) 133 | 134 | def get_installed_packages(prefix): 135 | root = os.path.join(byo.root, 'build.' + prefix.strip('-')) 136 | logger.debug('enumerating installed packages in %s', root) 137 | r = [] 138 | packages = os.path.join(root, 'packages') 139 | for package in os.listdir(packages): 140 | if (package.startswith('.')): 141 | continue 142 | state = PackageState(os.path.join(packages, package)) 143 | if state.state != State.INSTALLED: 144 | logger.warning('skipping package %s, %s', package, state.state) 145 | r.append(package) 146 | return r 147 | -------------------------------------------------------------------------------- /byo/builder.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import byo.package 3 | import urllib.request 4 | import urllib.parse 5 | import shutil 6 | import shlex 7 | #import gpg 8 | import os.path 9 | import byo 10 | from byo.process import Environment 11 | from byo.package import State, PackageState, get_installed_packages 12 | import multiprocessing 13 | import sys 14 | 15 | cpu_count = multiprocessing.cpu_count() 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | def get_file_tags(path): #fixme: put into base package script 20 | if path.startswith('boot'): 21 | return ('boot', ) 22 | if path.endswith('.pdf') \ 23 | or path.endswith('.man') \ 24 | or path.endswith('.info'): 25 | return ('doc', ) 26 | if path.startswith('usr/include') \ 27 | or path.startswith('usr/man') \ 28 | or path.endswith('Makefile') \ 29 | or path.startswith('usr/share/man') \ 30 | or path.startswith('usr/share/doc') \ 31 | or path.startswith('usr/lib/pkgconfig') \ 32 | or path.startswith('etc/portage') \ 33 | or (path.startswith('usr/bin') and path.endswith('-config')) \ 34 | or path.endswith('.a') \ 35 | or path.endswith('.m4') \ 36 | or path.endswith('.la'): 37 | return ('devel', ) 38 | else: 39 | return ('core', ) 40 | 41 | 42 | class Builder(object): 43 | def __init__(self, prefix, target, options): 44 | self.prefix = prefix 45 | self.target = target 46 | self.options = options 47 | self.metadata = byo.package.read_metadata(target) 48 | #self.gpg = gpg.Context() 49 | self.root = os.path.join(byo.root, 'build.' + prefix.strip('-')) 50 | self.archive = None 51 | self.env = Environment(self.root, target) 52 | 53 | self.__force = options.get('force', False) 54 | self.__keep = options.get('keep', False) 55 | if self.__force: 56 | self.env.cleanup() 57 | 58 | self.packages_dir = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'packages') 59 | self.root_dir = self.env.create_dir('root') 60 | self.work_dir = self.env.create_dir('tmp', self.target, 'work') 61 | 62 | self.__update_vars(self.metadata.data) 63 | install_dir = self.metadata.install_dir 64 | if install_dir: #custom installation directory, e.g. busybox _install 65 | logger.debug('custom install directory %s', install_dir) 66 | if not os.path.isabs(install_dir): #prepend work directory if it's not absolute 67 | install_dir = os.path.join(self.work_dir, install_dir) 68 | else: 69 | install_dir = self.env.create_dir('tmp', self.target, 'root') 70 | 71 | self.install_dir = install_dir 72 | self.__state = PackageState(self.env.create_dir('packages', self.target)) 73 | if self.__force: 74 | self.__state.reset() 75 | self.metadata.data['InstallDirectory'] = self.install_dir 76 | 77 | def __update_vars(self, vars): 78 | vars['Jobs'] = cpu_count 79 | vars['CrossCompilePrefix'] = self.prefix 80 | linux_platforms = (('aarch64', 'arm64'), ('arm', 'arm'), ('mips', 'mips'), ('x86_64', 'x84_64'), ('riscv32', 'riscv'), ('riscv64', 'riscv')) 81 | for name, platform in linux_platforms: 82 | if name in self.prefix: 83 | vars['LinuxPlatform'] = platform 84 | break 85 | vars['Host'] = self.prefix.rstrip('-') 86 | vars['TargetRoot'] = self.root_dir 87 | vars['TargetDevelopmentRoot'] = self.root_dir 88 | vars['WorkDirectory'] = self.work_dir 89 | vars['AuxFilesDirectory'] = os.path.join(self.packages_dir, self.target + ".files") 90 | c_compiler = self.prefix + 'gcc' 91 | vars['SystemRootDirectory'] = self.env.get_output(self.work_dir, c_compiler, '-print-sysroot').strip() 92 | vars['CCompiler'] = c_compiler 93 | vars['CXXCompiler'] = self.prefix + 'g++' 94 | vars['Assembler'] = self.prefix + 'as' 95 | vars['Archiver'] = self.prefix + 'ar' 96 | vars['Linker'] = self.prefix + 'ld' 97 | 98 | def __fetch_cache(self, url, fname): 99 | downloads = self.env.create_dir('..', 'build.downloads') 100 | cached = os.path.join(downloads, fname) 101 | if os.path.exists(cached): 102 | return cached 103 | 104 | logger.info('downloading url %s → %s', url, fname) 105 | 106 | with urllib.request.urlopen(url) as response, open(cached, 'wb') as out_file: 107 | shutil.copyfileobj(response, out_file) 108 | 109 | logger.info('downloading finished') 110 | return cached 111 | 112 | def _fetch(self): 113 | url = self.metadata.fetch_url 114 | if url is None: 115 | self.__state.state = State.DOWNLOADED 116 | return 117 | parsed = urllib.parse.urlparse(url) 118 | fname = str(os.path.basename(parsed.path)) 119 | self.archive = self.__fetch_cache(url, fname) 120 | if self.__state.state >= State.DOWNLOADED: 121 | return 122 | self.__state.state = State.DOWNLOADED 123 | 124 | def _unpack(self): 125 | if self.__state.state >= State.UNPACKED: 126 | return 127 | 128 | if self.archive: 129 | logger.info('unpacking...') 130 | self.env.exec(self.work_dir, 'tar', '--strip=1', '-xf', self.archive) 131 | self.__state.state = State.UNPACKED 132 | 133 | def _build(self): 134 | if self.__state.state >= State.BUILT: 135 | return 136 | 137 | logger.info('building...') 138 | for cmd in self.metadata.build: 139 | cmd = shlex.split(cmd) 140 | self.env.exec(self.work_dir, *cmd) 141 | self.__state.state = State.BUILT 142 | 143 | def __link(self, dst_dir, src_dir, file, warn_overwrite = True): 144 | src_file = os.path.join(src_dir, file) 145 | dst_file = os.path.join(dst_dir, file) 146 | real_dst_dir = os.path.dirname(dst_file) 147 | if os.path.exists(dst_file) or os.path.islink(dst_file): 148 | if warn_overwrite: 149 | logger.warn('overwriting %s', dst_file) 150 | os.unlink(dst_file) 151 | if not os.path.exists(real_dst_dir): 152 | os.makedirs(real_dst_dir) 153 | 154 | if os.path.islink(src_file): 155 | linkto = os.readlink(src_file) 156 | os.symlink(linkto, dst_file) 157 | else: 158 | try: 159 | os.link(src_file, dst_file) 160 | except OSError as e: 161 | if e.errno == 18 or e.errno == 1: #x-device link/permission denied 162 | try: 163 | shutil.copy(src_file, dst_file) 164 | except FileNotFoundError: 165 | logger.warn('skipping broken symlink at %s', file) 166 | else: 167 | raise 168 | 169 | 170 | def _install(self): 171 | if self.__state.state >= State.INSTALLED: 172 | return 173 | 174 | copy_to_root = self.metadata.copy_to_root 175 | logger.info('installing...') 176 | registry = {} 177 | for src_dir, src_dirs, files in os.walk(self.install_dir, topdown = True): 178 | dirname = os.path.relpath(src_dir, self.install_dir) 179 | dst_dir = os.path.join(self.root_dir, dirname) 180 | self.env.create_dir(dst_dir) 181 | for file in files: 182 | fullname = os.path.join(dirname, file) 183 | tags = get_file_tags(fullname) 184 | for tag in tags: 185 | registry_files = registry.setdefault(tag, []) 186 | registry_files.append(fullname) 187 | 188 | if copy_to_root: 189 | logger.debug("installing %s %s", fullname, tags) 190 | self.__link(dst_dir, src_dir, file) 191 | 192 | self.__state.files = registry 193 | self.__state.state = State.INSTALLED 194 | 195 | def _cleanup(self): 196 | if not self.__keep: 197 | self.env.cleanup() 198 | 199 | def _extract(self): 200 | extract = self.options.get('extract', []) 201 | if not extract: 202 | return 203 | 204 | logger.debug('extract tags %s', extract) 205 | dst_dir = self.env.create_dir('root.' + '+'.join(extract)) 206 | logger.debug('extract to %s', dst_dir) 207 | 208 | copy_to_root = self.metadata.copy_to_root 209 | src_dir = self.root_dir if copy_to_root else self.install_dir 210 | logger.debug('extracting from %s', src_dir) 211 | for tag in extract: 212 | files = self.__state.files 213 | tagged = files.get(tag, []) 214 | for file in tagged: 215 | logger.debug("extracting %s", file) 216 | self.__link(dst_dir, src_dir, file, warn_overwrite = False) 217 | 218 | def build(self): 219 | try: 220 | self._fetch() 221 | self._unpack() 222 | self._build() 223 | self._install() 224 | self._extract() 225 | self._cleanup() 226 | except: 227 | logger.exception('build failed') 228 | raise 229 | 230 | 231 | def _build(prefix, target, **options): 232 | logger.info('building %s for %s...' %(target, prefix)) 233 | logger.debug('package options %s', options) 234 | builder = Builder(prefix, target, options) 235 | builder.build() 236 | 237 | def build(prefix, *targets, **options): 238 | global_options = {} 239 | global_options['extract'] = options['extract'] 240 | logger.debug('targets %s', targets) 241 | logger.debug('options %s', options) 242 | if not targets and options['extract']: 243 | targets = get_installed_packages(prefix) 244 | 245 | for target in targets: 246 | for package in byo.package.get_package_queue(target): 247 | _build(prefix, package, ** (options if target == package else global_options)) 248 | --------------------------------------------------------------------------------