diff --git a/cross/crossdirect/APKBUILD b/cross/crossdirect/APKBUILD new file mode 100644 index 000000000..1cd2ba968 --- /dev/null +++ b/cross/crossdirect/APKBUILD @@ -0,0 +1,60 @@ +# Wrapper for the "crossdirect" compilation method. +# pmbootstrap mounts the native chroot in the foreign arch (e.g. armhf) chroot +# as /native. This package gets installed into the native chroot, and creates +# wrappers like: +# +# /native/usr/lib/crossdirect/armhf/gcc +# -> /native/usr/bin/armv6-alpine-linux-muslgnueabihf-gcc +# +# When building packages in the armhf chroot, PATH will get prepended with +# "/native/usr/lib/crossdirect/armhf". The end game is of course invoking the +# cross compiler from the native chroot, running at native speed, whenever +# calling the compiler from the foreign arch chroot. See crossdirect.c for +# implementation details (llvm, fakeroot, rpath). + +pkgname=crossdirect +pkgver=1 +pkgrel=0 +pkgdesc="Wrappers to launch native cross compilers in foreign chroots" +url="https://postmarketOS.org" +arch="all" +license="MIT" +options="" +source="crossdirect.c" + +build() { + cd "$srcdir" + # Architectures and binaries + _archs="x86_64 armhf armv7 aarch64" + for _arch in $_archs; do + [ "$_arch" == "$CARCH" ] && continue + _hostspec="$(arch_to_hostspec $_arch)" + $CC -o "crossdirect-$_arch" -static -DHOSTSPEC="\"$_hostspec\"" \ + crossdirect.c + done +} + +package() { + # Architectures and binaries + _archs="x86_64 armhf armv7 aarch64" + _bins="c++ cc cpp g++ gcc clang clang++" + + # Iterate over architectures + for _arch in $_archs; do + [ "$_arch" == "$CARCH" ] && continue + + # Put arch-specific crossdirect wrapper in arch-specific bin folder + _bindir="$pkgdir/usr/lib/crossdirect/$_arch" + _hostspec="$(arch_to_hostspec $_arch)" + mkdir -p "$_bindir" + cd "$_bindir" + cp "$srcdir/crossdirect-$_arch" "./" + + # Create compiler symlinks + for _bin in $_bins; do + ln -s "crossdirect-$_arch" "$_bin" + ln -s "crossdirect-$_arch" "$_hostspec-$_bin" + done + done +} +sha512sums="12801031928103bd898a0d54a5c68b33da9bded10a3d145fdf5ce8b70eb0bbbcdd50764279004b6997d85d710fa581dc8b05aa5e0eb62d50c1054cc6d66db87f crossdirect.c" diff --git a/cross/crossdirect/crossdirect.c b/cross/crossdirect/crossdirect.c new file mode 100644 index 000000000..5cff6cccb --- /dev/null +++ b/cross/crossdirect/crossdirect.c @@ -0,0 +1,77 @@ +// HOSTSPEC is defined at compile time, see APKBUILD + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include <libgen.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> + +void exit_userfriendly() { + fprintf(stderr, "Please report this at: https://gitlab.com/postmarketOS/pmaports/issues\n"); + fprintf(stderr, "As a workaround, you can compile without crossdirect.\n"); + fprintf(stderr, "See 'pmbootstrap -h' for related options.\n"); + exit(1); +} + +int main(int argc, char** argv) { + // we have a max of four extra args ("-target", "HOSTSPEC", "--sysroot=/", "-Wl,-rpath-link=/lib:/usr/lib"), plus one ending null + char* newargv[argc + 5]; + char* executableName = basename(argv[0]); + char newExecutable[PATH_MAX]; + bool isClang = (strcmp(executableName, "clang") == 0 || strcmp(executableName, "clang++") == 0); + bool startsWithHostSpec = (strncmp(HOSTSPEC, executableName, sizeof(HOSTSPEC) -1) == 0); + + if (isClang || startsWithHostSpec) { + snprintf(newExecutable, sizeof(newExecutable), "/native/usr/bin/%s", executableName); + } else { + snprintf(newExecutable, sizeof(newExecutable), "/native/usr/bin/" HOSTSPEC "-%s", executableName); + } + + char** newArgsPtr = newargv; + *newArgsPtr++ = newExecutable; + if (isClang) { + *newArgsPtr++ = "-target"; + *newArgsPtr++ = HOSTSPEC; + } + *newArgsPtr++ = "--sysroot=/"; + bool addrpath = true; + if (isClang) { + // clang gives a warning if the rpath parameter is passed when linker isn't invoked. + // to avoid this warning, only add if we're actually linking at least one library. + addrpath = false; + for (int i = 1; i < argc; i++) { + char* arg = argv[i]; + if (strlen(arg) >= 2 && arg[0] == '-' && arg[1] == 'l') { + addrpath = true; + break; + } + } + } + if (addrpath) { + *newArgsPtr++ = "-Wl,-rpath-link=/lib:/usr/lib"; + } + memcpy(newArgsPtr, argv + 1, sizeof(char*)*(argc - 1)); + newArgsPtr += (argc - 1); + *newArgsPtr = NULL; + + // new arguments prepared; now setup environmental vars + setenv("LD_LIBRARY_PATH", "/native/lib:/native/usr/lib", true); + char* ldPreload = getenv("LD_PRELOAD"); + if (ldPreload) { + if (strcmp(ldPreload, "/usr/lib/libfakeroot.so") == 0) { + setenv("LD_PRELOAD", "/native/usr/lib/libfakeroot.so", true); + } else { + fprintf(stderr, "ERROR: crossdirect: can't handle LD_PRELOAD: %s\n", ldPreload); + exit_userfriendly(); + } + } + + if (execv(newExecutable, newargv) == -1) { + fprintf(stderr, "ERROR: crossdirect: failed to execute %s: %s\n", newExecutable, strerror(errno)); + exit_userfriendly(); + } + return 1; +}