Unverified Commit 117fc28b authored by Artturi's avatar Artturi Committed by GitHub
Browse files

Merge pull request #213871 from hadilq/androidenv/fix-create-avd

parents b1da4778 a05928d7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
/xml
local.properties
.android
+37 −15
Original line number Diff line number Diff line
@@ -185,20 +185,38 @@ rec {

  system-images = lib.flatten (map (apiVersion:
    map (type:
      map (abiVersion:
        lib.optionals (lib.hasAttrByPath [apiVersion type abiVersion] system-images-packages) (
          deployAndroidPackage {
            inherit os;
            package = system-images-packages.${apiVersion}.${type}.${abiVersion};
      # Deploy all system images with the same  systemImageType in one derivation to avoid the `null` problem below
      # with avdmanager when trying to create an avd!
      #
      # ```
      # $ yes "" | avdmanager create avd --force --name testAVD --package 'system-images;android-33;google_apis;x86_64'
      # Error: Package path is not valid. Valid system image paths are:
      # null
      # ```
      let
        availablePackages = map (abiVersion:
          system-images-packages.${apiVersion}.${type}.${abiVersion}
        ) (builtins.filter (abiVersion:
          lib.hasAttrByPath [apiVersion type abiVersion] system-images-packages
        ) abiVersions);

        instructions = builtins.listToAttrs (map (package: {
            name = package.name;
            value = lib.optionalString (lib.hasPrefix "google_apis" type) ''
              # Patch 'google_apis' system images so they're recognized by the sdk.
              # Without this, `android list targets` shows 'Tag/ABIs : no ABIs' instead
              # of 'Tag/ABIs : google_apis*/*' and the emulator fails with an ABI-related error.
            patchInstructions = lib.optionalString (lib.hasPrefix "google_apis" type) ''
              sed -i '/^Addon.Vendor/d' source.properties
            '';
          }
        )
      ) abiVersions
          }) availablePackages
        );
      in
      lib.optionals (availablePackages != [])
        (deployAndroidPackages {
          inherit os;
          packages = availablePackages;
          patchesInstructions = instructions;
        })
    ) systemImageTypes
  ) platformVersions);

@@ -271,8 +289,8 @@ rec {
    ${lib.concatMapStrings (system-image: ''
      apiVersion=$(basename $(echo ${system-image}/libexec/android-sdk/system-images/*))
      type=$(basename $(echo ${system-image}/libexec/android-sdk/system-images/*/*))
      mkdir -p system-images/$apiVersion/$type
      ln -s ${system-image}/libexec/android-sdk/system-images/$apiVersion/$type/* system-images/$apiVersion/$type
      mkdir -p system-images/$apiVersion
      ln -s ${system-image}/libexec/android-sdk/system-images/$apiVersion/$type system-images/$apiVersion/$type
    '') images}
  '';

@@ -294,7 +312,11 @@ rec {
    You must accept the following licenses:
    ${lib.concatMapStringsSep "\n" (str: "  - ${str}") licenseNames}

    a)
      by setting nixpkgs config option 'android_sdk.accept_license = true;'.
    b)
      by an environment variable for a single invocation of the nix tools.
        $ export NIXPKGS_ACCEPT_ANDROID_SDK_LICENSE=1
  '' else callPackage ./cmdline-tools.nix {
    inherit deployAndroidPackage os cmdLineToolsVersion;

+1 −1
Original line number Diff line number Diff line
{ config, pkgs ? import <nixpkgs> {}
, licenseAccepted ? config.android_sdk.accept_license or false
, licenseAccepted ? config.android_sdk.accept_license or (builtins.getEnv "NIXPKGS_ACCEPT_ANDROID_SDK_LICENSE" == "1")
}:

rec {
+50 −18
Original line number Diff line number Diff line
{ composeAndroidPackages, stdenv, lib, runtimeShell }:
{ name, app ? null
, platformVersion ? "16", abiVersion ? "armeabi-v7a", systemImageType ? "default"
, enableGPU ? false, extraAVDFiles ? []
, package ? null, activity ? null
, avdHomeDir ? null, sdkExtraArgs ? {}
, platformVersion ? "33"
, abiVersion ? "armeabi-v7a"
, systemImageType ? "default"
, enableGPU ? false
, extraAVDFiles ? []
, package ? null
, activity ? null
, androidUserHome ? null
, avdHomeDir ? null # Support old variable with non-standard naming!
, androidAvdHome ? avdHomeDir
, sdkExtraArgs ? {}
, androidAvdFlags ? null
, androidEmulatorFlags ? null
}:

let
  sdkArgs = {
    toolsVersion = "26.1.1";
    platformVersions = [ platformVersion ];
    includeEmulator = true;
    includeSystemImages = true;
  } // sdkExtraArgs // {
    cmdLineToolsVersion = "8.0";
    platformVersions = [ platformVersion ];
    systemImageTypes = [ systemImageType ];
    abiVersions = [ abiVersion ];
  } // sdkExtraArgs;
  };

  sdk = (composeAndroidPackages sdkArgs).androidsdk;
in
@@ -33,24 +43,45 @@ stdenv.mkDerivation {
        export TMPDIR=/tmp
    fi

    ${if avdHomeDir == null then ''
    ${if androidUserHome == null then ''
      # Store the virtual devices somewhere else, instead of polluting a user's HOME directory
      export ANDROID_SDK_HOME=$(mktemp -d $TMPDIR/nix-android-vm-XXXX)
      export ANDROID_USER_HOME=$(mktemp -d $TMPDIR/nix-android-user-home-XXXX)
    '' else ''
      mkdir -p "${androidUserHome}"
      export ANDROID_USER_HOME="${androidUserHome}"
    ''}

    ${if androidAvdHome == null then ''
      export ANDROID_AVD_HOME=$ANDROID_USER_HOME/avd
    '' else ''
      mkdir -p "${avdHomeDir}"
      export ANDROID_SDK_HOME="${avdHomeDir}"
      mkdir -p "${androidAvdHome}"
      export ANDROID_AVD_HOME="${androidAvdHome}"
    ''}

    # We need to specify the location of the Android SDK root folder
    export ANDROID_SDK_ROOT=${sdk}/libexec/android-sdk

    ${lib.optionalString (androidAvdFlags != null) ''
      # If NIX_ANDROID_AVD_FLAGS is empty
      if [[ -z "$NIX_ANDROID_AVD_FLAGS" ]]; then
        NIX_ANDROID_AVD_FLAGS="${androidAvdFlags}"
      fi
    ''}

    ${lib.optionalString (androidEmulatorFlags != null) ''
      # If NIX_ANDROID_EMULATOR_FLAGS is empty
      if [[ -z "$NIX_ANDROID_EMULATOR_FLAGS" ]]; then
        NIX_ANDROID_EMULATOR_FLAGS="${androidEmulatorFlags}"
      fi
    ''}

    # We have to look for a free TCP port

    echo "Looking for a free TCP port in range 5554-5584" >&2

    for i in $(seq 5554 2 5584)
    do
        if [ -z "$(${sdk}/libexec/android-sdk/platform-tools/adb devices | grep emulator-$i)" ]
        if [ -z "$(${sdk}/bin/adb devices | grep emulator-$i)" ]
        then
            port=$i
            break
@@ -68,25 +99,26 @@ stdenv.mkDerivation {
    export ANDROID_SERIAL="emulator-$port"

    # Create a virtual android device for testing if it does not exist
    ${sdk}/libexec/android-sdk/tools/bin/avdmanager list target
    ${sdk}/bin/avdmanager list target

    if [ "$(${sdk}/libexec/android-sdk/tools/android list avd | grep 'Name: device')" = "" ]
    if [ "$(${sdk}/bin/avdmanager list avd | grep 'Name: device')" = "" ]
    then
        # Create a virtual android device
        yes "" | ${sdk}/libexec/android-sdk/tools/bin/avdmanager create avd -n device -k "system-images;android-${platformVersion};${systemImageType};${abiVersion}" $NIX_ANDROID_AVD_FLAGS
        yes "" | ${sdk}/bin/avdmanager create avd --force -n device -k "system-images;android-${platformVersion};${systemImageType};${abiVersion}" -p $ANDROID_AVD_HOME $NIX_ANDROID_AVD_FLAGS

        ${lib.optionalString enableGPU ''
          # Enable GPU acceleration
          echo "hw.gpu.enabled=yes" >> $ANDROID_SDK_HOME/.android/avd/device.avd/config.ini
          echo "hw.gpu.enabled=yes" >> $ANDROID_AVD_HOME/device.avd/config.ini
        ''}

        ${lib.concatMapStrings (extraAVDFile: ''
          ln -sf ${extraAVDFile} $ANDROID_SDK_HOME/.android/avd/device.avd
          ln -sf ${extraAVDFile} $ANDROID_AVD_HOME/device.avd
        '') extraAVDFiles}
    fi

    # Launch the emulator
    ${sdk}/libexec/android-sdk/emulator/emulator -avd device -no-boot-anim -port $port $NIX_ANDROID_EMULATOR_FLAGS &
    echo "\nLaunch the emulator"
    $ANDROID_SDK_ROOT/emulator/emulator -avd device -no-boot-anim -port $port $NIX_ANDROID_EMULATOR_FLAGS &

    # Wait until the device has completely booted
    echo "Waiting until the emulator has booted the device and the package manager is ready..." >&2
+151 −0
Original line number Diff line number Diff line
{
  # To test your changes in androidEnv run `nix-shell android-sdk-with-emulator-shell.nix`

  # If you copy this example out of nixpkgs, use these lines instead of the next.
  # This example pins nixpkgs: https://nix.dev/tutorials/towards-reproducibility-pinning-nixpkgs.html
  /*nixpkgsSource ? (builtins.fetchTarball {
    name = "nixpkgs-20.09";
    url = "https://github.com/NixOS/nixpkgs/archive/20.09.tar.gz";
    sha256 = "1wg61h4gndm3vcprdcg7rc4s1v3jkm5xd7lw8r2f67w502y94gcy";
  }),
  pkgs ? import nixpkgsSource {
    config.allowUnfree = true;
  },
  */

  # If you want to use the in-tree version of nixpkgs:
  pkgs ? import ../../../../.. {
    config.allowUnfree = true;
  },

  config ? pkgs.config
}:

# Copy this file to your Android project.
let
  # Declaration of versions for everything. This is useful since these
  # versions may be used in multiple places in this Nix expression.
  android = {
    platforms = [ "33" ];
    systemImageTypes = [ "google_apis" ];
    abis = [ "arm64-v8a" "x86_64" ];
  };

  # If you copy this example out of nixpkgs, something like this will work:
  /*androidEnvNixpkgs = fetchTarball {
    name = "androidenv";
    url = "https://github.com/NixOS/nixpkgs/archive/<fill me in from Git>.tar.gz";
    sha256 = "<fill me in with nix-prefetch-url --unpack>";
  };

  androidEnv = pkgs.callPackage "${androidEnvNixpkgs}/pkgs/development/mobile/androidenv" {
    inherit config pkgs;
    licenseAccepted = true;
  };*/

  # Otherwise, just use the in-tree androidenv:
  androidEnv = pkgs.callPackage ./.. {
    inherit config pkgs;
    # You probably need to uncomment below line to express consent.
    # licenseAccepted = true;
  };

  sdkArgs = {
    platformVersions = android.platforms;
    abiVersions = android.abis;
    systemImageTypes = android.systemImageTypes;

    includeSystemImages = true;
    includeEmulator = true;

    # Accepting more licenses declaratively:
    extraLicenses = [
      # Already accepted for you with the global accept_license = true or
      # licenseAccepted = true on androidenv.
      # "android-sdk-license"

      # These aren't, but are useful for more uncommon setups.
      "android-sdk-preview-license"
      "android-googletv-license"
      "android-sdk-arm-dbt-license"
      "google-gdk-license"
      "intel-android-extra-license"
      "intel-android-sysimage-license"
      "mips-android-sysimage-license"
    ];
  };

  androidComposition = androidEnv.composeAndroidPackages sdkArgs;
  androidEmulator = androidEnv.emulateApp {
    name = "android-sdk-emulator-demo";
    sdkExtraArgs = sdkArgs;
  };
  androidSdk = androidComposition.androidsdk;
  platformTools = androidComposition.platform-tools;
  jdk = pkgs.jdk;
in
pkgs.mkShell rec {
  name = "androidenv-demo";
  packages = [ androidSdk platformTools androidEmulator jdk pkgs.android-studio ];

  LANG = "C.UTF-8";
  LC_ALL = "C.UTF-8";
  JAVA_HOME = jdk.home;

  # Note: ANDROID_HOME is deprecated. Use ANDROID_SDK_ROOT.
  ANDROID_SDK_ROOT = "${androidSdk}/libexec/android-sdk";
  ANDROID_NDK_ROOT = "${ANDROID_SDK_ROOT}/ndk-bundle";

  shellHook = ''
    # Write out local.properties for Android Studio.
    cat <<EOF > local.properties
    # This file was automatically generated by nix-shell.
    sdk.dir=$ANDROID_SDK_ROOT
    ndk.dir=$ANDROID_NDK_ROOT
    EOF
  '';

  passthru.tests = {

    shell-with-emulator-sdkmanager-packages-test = pkgs.runCommand "shell-with-emulator-sdkmanager-packages-test" {
      nativeBuildInputs = [ androidSdk jdk ];
    } ''
      output="$(sdkmanager --list)"
      installed_packages_section=$(echo "''${output%%Available Packages*}" | awk 'NR>4 {print $1}')
      echo "installed_packages_section: ''${installed_packages_section}"

      packages=(
        "build-tools;33.0.1" "cmdline-tools;8.0" \
        "emulator" "patcher;v4" "platform-tools" "platforms;android-33" \
        "system-images;android-33;google_apis;arm64-v8a" \
        "system-images;android-33;google_apis;x86_64"
      )

      for package in "''${packages[@]}"; do
        if [[ ! $installed_packages_section =~ "$package" ]]; then
          echo "$package package was not installed."
          exit 1
        fi
      done

      touch "$out"
    '';

    shell-with-emulator-avdmanager-create-avd-test = pkgs.runCommand "shell-with-emulator-avdmanager-create-avd-test" {
      nativeBuildInputs = [ androidSdk androidEmulator jdk ];
    } ''
      avdmanager delete avd -n testAVD || true
      echo "" | avdmanager create avd --force --name testAVD --package 'system-images;android-33;google_apis;x86_64'
      result=$(avdmanager list avd)

      if [[ ! $result =~ "Name: testAVD" ]]; then
        echo "avdmanager couldn't create the avd! The output is :''${result}"
        exit 1
      fi

      avdmanager delete avd -n testAVD || true
      touch "$out"
    '';
  };
}
Loading