Skip to content

Commit

Permalink
Merge pull request #213871 from hadilq/androidenv/fix-create-avd
Browse files Browse the repository at this point in the history
  • Loading branch information
Artturin authored Mar 3, 2023
2 parents b1da477 + a05928d commit 117fc28
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 46 deletions.
1 change: 1 addition & 0 deletions pkgs/development/mobile/androidenv/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/xml
local.properties
.android
52 changes: 37 additions & 15 deletions pkgs/development/mobile/androidenv/compose-android-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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};
# 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) ''
# 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.
sed -i '/^Addon.Vendor/d' source.properties
'';
}
)
) abiVersions
}) availablePackages
);
in
lib.optionals (availablePackages != [])
(deployAndroidPackages {
inherit os;
packages = availablePackages;
patchesInstructions = instructions;
})
) systemImageTypes
) platformVersions);

Expand Down Expand Up @@ -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}
'';

Expand All @@ -294,7 +312,11 @@ rec {
You must accept the following licenses:
${lib.concatMapStringsSep "\n" (str: " - ${str}") licenseNames}
by setting nixpkgs config option 'android_sdk.accept_license = true;'.
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;

Expand Down
2 changes: 1 addition & 1 deletion pkgs/development/mobile/androidenv/default.nix
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{ 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 {
Expand Down
68 changes: 50 additions & 18 deletions pkgs/development/mobile/androidenv/emulate-app.nix
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
{ 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
Expand All @@ -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
Expand All @@ -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
Expand Down
151 changes: 151 additions & 0 deletions pkgs/development/mobile/androidenv/examples/shell-with-emulator.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{
# 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"
'';
};
}

14 changes: 8 additions & 6 deletions pkgs/development/mobile/androidenv/examples/shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ let
# Otherwise, just use the in-tree androidenv:
androidEnv = pkgs.callPackage ./.. {
inherit config pkgs;
licenseAccepted = true;
# You probably need to uncomment below line to express consent.
# licenseAccepted = true;
};

androidComposition = androidEnv.composeAndroidPackages {
Expand Down Expand Up @@ -146,8 +147,9 @@ pkgs.mkShell rec {
'';

passthru.tests = {
sdkmanager-licenses-test = pkgs.runCommand "sdkmanager-licenses-test" {
buildInputs = [ androidSdk jdk ];

shell-sdkmanager-licenses-test = pkgs.runCommand "shell-sdkmanager-licenses-test" {
nativeBuildInputs = [ androidSdk jdk ];
} ''
if [[ ! "$(sdkmanager --licenses)" =~ "All SDK package licenses accepted." ]]; then
echo "At least one of SDK package licenses are not accepted."
Expand All @@ -156,14 +158,14 @@ pkgs.mkShell rec {
touch $out
'';

sdkmanager-packages-test = pkgs.runCommand "sdkmanager-packages-test" {
buildInputs = [ androidSdk jdk ];
shell-sdkmanager-packages-test = pkgs.runCommand "shell-sdkmanager-packages-test" {
nativeBuildInputs = [ androidSdk jdk ];
} ''
output="$(sdkmanager --list)"
installed_packages_section=$(echo "''${output%%Available Packages*}" | awk 'NR>4 {print $1}')
packages=(
"build-tools;30.0.3" "ndk-bundle" "platform-tools" \
"build-tools;30.0.3" "platform-tools" \
"platforms;android-23" "platforms;android-24" "platforms;android-25" "platforms;android-26" \
"platforms;android-27" "platforms;android-28" "platforms;android-29" "platforms;android-30" \
"platforms;android-31" "platforms;android-32" "platforms;android-33" \
Expand Down
Loading

0 comments on commit 117fc28

Please sign in to comment.