androidenv: add shell-with-emulator.nix to have test and more examples

androidenv: replace buildInputs with nativeBuildInputs
This commit is contained in:
Hadi 2022-12-27 18:28:48 -05:00
parent e5fbfd894f
commit d511bfd0e2
6 changed files with 211 additions and 23 deletions

View file

@ -1,2 +1,3 @@
/xml
local.properties
.android

View file

@ -188,9 +188,11 @@ rec {
# 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}

View file

@ -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
@ -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 "${avdHomeDir}"
export ANDROID_SDK_HOME="${avdHomeDir}"
mkdir -p "${androidUserHome}"
export ANDROID_USER_HOME="${androidUserHome}"
''}
${if androidAvdHome == null then ''
export ANDROID_AVD_HOME=$ANDROID_USER_HOME/avd
'' else ''
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

View file

@ -0,0 +1,150 @@
{
# 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;
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"
'';
};
}

View file

@ -146,8 +146,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."
@ -156,8 +157,8 @@ 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}')

View file

@ -1,6 +1,7 @@
{ stdenv, callPackage }:
let
examples-shell = callPackage ./examples/shell.nix {};
examples-shell-with-emulator = callPackage ./examples/shell-with-emulator.nix {};
in
stdenv.mkDerivation {
name = "androidenv-test-suite";
@ -10,7 +11,8 @@ stdenv.mkDerivation {
dontConfigure = true;
dontBuild = true;
passthru.tests = { } // examples-shell.passthru.tests;
passthru.tests = examples-shell.passthru.tests //
examples-shell-with-emulator.passthru.tests;
meta.timeout = 60;
}