Commit 5f1923d6 authored by Jari Vetoniemi's avatar Jari Vetoniemi
Browse files

androidenv: fix android cross-compilers

These were completely broken. Even if they managed to output a binary,
it wouldn't actually run on the actual device.
parent 72f17cca
Loading
Loading
Loading
Loading
+116 −33
Original line number Diff line number Diff line
{ lib, stdenv
, makeWrapper
, runCommand, wrapBintoolsWith, wrapCCWith
, makeWrapper, python
, runCommand, wrapBintoolsWith, wrapCCWith, autoPatchelfHook
, buildAndroidndk, androidndk, targetAndroidndkPkgs
}:

@@ -11,6 +11,9 @@ let
  # N.B. The Android NDK uses slightly different LLVM-style platform triples
  # than we do. We don't just use theirs because ours are less ambiguous and
  # some builds need that clarity.
  #
  # FIXME:
  # There's some dragons here. Build host and target concepts are being mixed up.
  ndkInfoFun = { config, ... }: {
    x86_64-apple-darwin = {
      double = "darwin-x86_64";
@@ -21,65 +24,123 @@ let
    i686-unknown-linux-android = {
      triple = "i686-linux-android";
      arch = "x86";
      # LEGACY
      toolchain = "x86";
      gccVer = "4.9";
    };
    x86_64-unknown-linux-android = {
      triple = "x86_64-linux-android";
      arch = "x86_64";
      # LEGACY
      toolchain = "x86_64";
      gccVer = "4.9";
    };
    armv7a-unknown-linux-androideabi = {
      arch = "arm";
      triple = "arm-linux-androideabi";
      # LEGACY
      toolchain = "arm-linux-androideabi";
      gccVer = "4.9";
    };
    aarch64-unknown-linux-android = {
      arch = "arm64";
      triple = "aarch64-linux-android";
      # LEGACY
      toolchain = "aarch64-linux-android";
      gccVer = "4.9";
    };
  }.${config} or
    (throw "Android NDK doesn't support ${config}, as far as we know");

  buildInfo = ndkInfoFun stdenv.buildPlatform;
  hostInfo = ndkInfoFun stdenv.hostPlatform;
  targetInfo = ndkInfoFun stdenv.targetPlatform;

  prefix = lib.optionalString (stdenv.targetPlatform != stdenv.hostPlatform) (stdenv.targetPlatform.config + "-");
  inherit (stdenv.targetPlatform) sdkVer;
  suffixSalt = lib.replaceStrings ["-" "."] ["_" "_"] stdenv.targetPlatform.config;

  # targetInfo.triple is what Google thinks the toolchain should be, this is a little
  # different from what we use. We make it four parts to conform with the existing
  # standard more properly.
  targetConfig = lib.optionalString (stdenv.targetPlatform != stdenv.hostPlatform) (stdenv.targetPlatform.config);
in

rec {
  # Misc tools
  binaries = runCommand "ndk-toolchain-binutils" {
    pname = "ndk-toolchain-binutils";
  binaries = stdenv.mkDerivation {
    pname = "${targetConfig}-ndk-toolchain";
    inherit (androidndk) version;
    nativeBuildInputs = [ makeWrapper ];
    nativeBuildInputs = [ makeWrapper python autoPatchelfHook ];
    propagatedBuildInputs = [ androidndk ];
    passthru = {
      targetPrefix = prefix;
      isClang = true; # clang based cc, but bintools ld
    };
  } ''
    mkdir -p $out/bin
    dontUnpack = true;
    dontBuild = true;
    dontStrip = true;
    dontConfigure = true;
    dontPatch = true;
    autoPatchelfIgnoreMissingDeps = true;
    installPhase = ''
      if [ ! -d ${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/${buildInfo.double} ]; then
        # LEGACY: make-standalone-toolchain is deprecated
        #         https://developer.android.com/ndk/guides/standalone_toolchain
        ${androidndk}/libexec/android-sdk/ndk-bundle/build/tools/make-standalone-toolchain.sh --arch=${targetInfo.arch} --install-dir=$out/toolchain --platform=${sdkVer} --force
      else
        # https://developer.android.com/ndk/guides/other_build_systems
        mkdir -p $out
        cp -r ${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/${buildInfo.double} $out/toolchain
        find $out/toolchain -type d -exec chmod 777 {} \;
      fi

    # llvm toolchain
    for prog in ${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/${hostInfo.double}/bin/*; do
      ln -s $prog $out/bin/$(basename $prog)
      ln -s $prog $out/bin/${prefix}$(basename $prog)
    done
      if [ ! -d $out/toolchain/sysroot/usr/lib/${targetInfo.triple}/${sdkVer} ]; then
        echo "NDK does not contain libraries for SDK version ${sdkVer}";
        exit 1
      fi

    # bintools toolchain
    for prog in ${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/${targetInfo.toolchain}-${targetInfo.gccVer}/prebuilt/${hostInfo.double}/bin/*; do
      prog_suffix=$(basename $prog | sed 's/${targetInfo.triple}-//')
      ln -s $prog $out/bin/${stdenv.targetPlatform.config}-$prog_suffix
      ln -vfs $out/toolchain/sysroot/usr/lib $out/lib
      ln -s $out/toolchain/sysroot/usr/lib/${targetInfo.triple}/*.so $out/lib/
      ln -s $out/toolchain/sysroot/usr/lib/${targetInfo.triple}/*.a $out/lib/
      chmod +w $out/lib/*
      ln -s $out/toolchain/sysroot/usr/lib/${targetInfo.triple}/${sdkVer}/*.so $out/lib/
      ln -s $out/toolchain/sysroot/usr/lib/${targetInfo.triple}/${sdkVer}/*.o $out/lib/

      echo "INPUT(-lc++_static)" > $out/lib/libc++.a

      ln -s $out/toolchain/bin $out/bin
      ln -s $out/toolchain/${targetInfo.triple}/bin/* $out/bin/
      for f in $out/bin/${targetInfo.triple}-*; do
        ln -s $f ''${f/${targetInfo.triple}-/${targetConfig}-}
      done
      for f in $(find $out/toolchain -type d -name ${targetInfo.triple}); do
        ln -s $f ''${f/${targetInfo.triple}/${targetConfig}}
      done

    # shitty googly wrappers
    rm -f $out/bin/${stdenv.targetPlatform.config}-gcc $out/bin/${stdenv.targetPlatform.config}-g++
      # LEGACY: get rid of gcc and g++, otherwise wrapCCWith will use them instead of clang
      rm -f $out/bin/${targetConfig}-gcc $out/bin/${targetConfig}-g++

      # LEGACY: ld doesn't properly include transitive library dependencies.
      #         Let's use gold instead
      rm -f $out/bin/${targetConfig}-ld
      if [[ -f  $out/bin/${targetConfig}-ld.gold ]]; then
        ln -s $out/bin/${targetConfig}-ld.gold $out/bin/${targetConfig}-ld
      else
        ln -s $out/bin/lld $out/bin/${targetConfig}-ld
      fi

      (cd $out/bin;
        for tool in llvm-*; do
          ln -sf $tool ${targetConfig}-$(echo $tool | sed 's/llvm-//')
          ln -sf $tool $(echo $tool | sed 's/llvm-//')
        done)

      # handle last, as llvm-as is for llvm bytecode
      ln -sf $out/bin/${targetInfo.triple}-as $out/bin/${targetConfig}-as
      ln -sf $out/bin/${targetInfo.triple}-as $out/bin/as

      patchShebangs $out/bin
    '';
  };

  binutils = wrapBintoolsWith {
    bintools = binaries;
@@ -95,9 +156,22 @@ rec {
    libc = targetAndroidndkPkgs.libraries;
    extraBuildCommands = ''
      echo "-D__ANDROID_API__=${stdenv.targetPlatform.sdkVer}" >> $out/nix-support/cc-cflags
      echo "-target ${stdenv.targetPlatform.config}" >> $out/nix-support/cc-cflags
      echo "-resource-dir=$(echo ${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/${hostInfo.double}/lib*/clang/*)" >> $out/nix-support/cc-cflags
      if [ ! -d ${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/${hostInfo.double} ]; then
        # LEGACY: probably won't work for any recent android
        echo "--gcc-toolchain=${androidndk}/libexec/android-sdk/ndk-bundle/toolchains/${targetInfo.toolchain}-${targetInfo.gccVer}/prebuilt/${hostInfo.double}" >> $out/nix-support/cc-cflags
        echo "-fuse-ld=$out/bin/${targetConfig}-ld.gold -L${binaries}/lib" >> $out/nix-support/cc-ldflags
      else
        # Android needs executables linked with -pie since version 5.0
        # Use -fPIC for compilation, and link with -pie if no -shared flag used in ldflags
        echo "-target ${targetInfo.triple} -fPIC" >> $out/nix-support/cc-cflags
        echo "-z,noexecstack -z,relro -z,now" >> $out/nix-support/cc-ldflags
        echo 'if [[ ! " $@ " =~ " -shared " ]]; then NIX_LDFLAGS_${suffixSalt}+=" -pie"; fi' >> $out/nix-support/add-flags.sh
        echo "-Xclang -mnoexecstack" >> $out/nix-support/cc-cxxflags
      fi
      if [ ${targetInfo.triple} == arm-linux-androideabi ]; then
        # https://android.googlesource.com/platform/external/android-cmake/+/refs/heads/cmake-master-dev/android.toolchain.cmake
        echo "--fix-cortex-a8" >> $out/nix-support/cc-ldflags
      fi
    '';
  };

@@ -107,10 +181,19 @@ rec {
  # cross-compiling packages to wrap incorrectly wrap binaries we don't include
  # anyways.
  libraries = runCommand "bionic-prebuilt" {} ''
    mkdir -p $out
    cp -r ${buildAndroidndk}/libexec/android-sdk/ndk-bundle/sysroot/usr/include $out/include
    chmod +w $out/include
    cp -r ${buildAndroidndk}/libexec/android-sdk/ndk-bundle/sysroot/usr/include/${targetInfo.triple}/* $out/include
    ln -s ${buildAndroidndk}/libexec/android-sdk/ndk-bundle/platforms/android-${stdenv.hostPlatform.sdkVer}/arch-${hostInfo.arch}/usr/${if hostInfo.arch == "x86_64" then "lib64" else "lib"} $out/lib
    if [ -d ${buildAndroidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt ]; then
      lpath=${buildAndroidndk}/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/${buildInfo.double}/sysroot/usr/lib/${targetInfo.triple}/${sdkVer}
    else
      # LEGACY
      lpath=${buildAndroidndk}/libexec/android-sdk/ndk-bundle/platforms/android-${sdkVer}/arch-${hostInfo.arch}/usr/${if hostInfo.arch == "x86_64" then "lib64" else "lib"}
    fi
    if [ ! -d $lpath ]; then
      echo "NDK does not contain libraries for SDK version ${sdkVer} <$lpath>"
      exit 1
    fi
    mkdir -p $out/lib
    cp $lpath/*.so $lpath/*.a $out/lib
    chmod +w $out/lib/*
    cp $lpath/* $out/lib
  '';
}
+2 −2
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@
    import ./androidndk-pkgs.nix {
      inherit lib;
      inherit (buildPackages)
        makeWrapper;
        makeWrapper python autoPatchelfHook;
      inherit (pkgs)
        stdenv
        runCommand wrapBintoolsWith wrapCCWith;
@@ -49,7 +49,7 @@
    import ./androidndk-pkgs.nix {
      inherit lib;
      inherit (buildPackages)
        makeWrapper;
        makeWrapper python autoPatchelfHook;
      inherit (pkgs)
        stdenv
        runCommand wrapBintoolsWith wrapCCWith;
+10 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ deployAndroidPackage {
  patchInstructions = lib.optionalString (os == "linux") (''
    patchShebangs .

    # Fix the shebangs of the auto-generated scripts.
    # LEGACY: Fix the shebangs of the auto-generated scripts.
    substituteInPlace ./build/tools/make_standalone_toolchain.py \
      --replace '#!/bin/bash' '#!${pkgs.bash}/bin/bash'

@@ -29,6 +29,15 @@ deployAndroidPackage {
    # TODO: allow this stuff
    rm -rf docs tests

    # Ndk now has a prebuilt toolchains inside, the file layout has changed, we do a symlink
    # to still support the old standalone toolchains builds.
    if [ -d $out/libexec/android-sdk/ndk ] && [ ! -d $out/libexec/android-sdk/ndk-bundle ]; then
        ln -sf $out/libexec/android-sdk/ndk/${package.revision} $out/libexec/android-sdk/ndk-bundle
    else
        echo "The ndk-bundle layout has changed. The nix expressions have to be updated!"
        exit 1
    fi

    # Patch the executables of the toolchains, but not the libraries -- they are needed for crosscompiling
    if [ -d $out/libexec/android-sdk/ndk-bundle/toolchains/renderscript/prebuilt/linux-x86_64/lib64 ]; then
        addAutoPatchelfSearchPath $out/libexec/android-sdk/ndk-bundle/toolchains/renderscript/prebuilt/linux-x86_64/lib64