nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at litex 394 lines 14 kB view raw
1{ callPackage, stdenv, lib, fetchurl, ruby, writeText 2, licenseAccepted ? false 3}: 4 5{ cmdLineToolsVersion ? "9.0" 6, toolsVersion ? "26.1.1" 7, platformToolsVersion ? "34.0.1" 8, buildToolsVersions ? [ "33.0.2" ] 9, includeEmulator ? false 10, emulatorVersion ? "33.1.6" 11, platformVersions ? [] 12, includeSources ? false 13, includeSystemImages ? false 14, systemImageTypes ? [ "google_apis_playstore" ] 15, abiVersions ? [ "armeabi-v7a" "arm64-v8a" ] 16, cmakeVersions ? [ ] 17, includeNDK ? false 18, ndkVersion ? "25.2.9519653" 19, ndkVersions ? [ndkVersion] 20, useGoogleAPIs ? false 21, useGoogleTVAddOns ? false 22, includeExtras ? [] 23, repoJson ? ./repo.json 24, repoXmls ? null 25, extraLicenses ? [] 26}: 27 28let 29 # Determine the Android os identifier from Nix's system identifier 30 os = if stdenv.system == "x86_64-linux" then "linux" 31 else if stdenv.system == "x86_64-darwin" then "macosx" 32 else throw "No Android SDK tarballs are available for system architecture: ${stdenv.system}"; 33 34 # Uses mkrepo.rb to create a repo spec. 35 mkRepoJson = { packages ? [], images ? [], addons ? [] }: let 36 mkRepoRuby = (ruby.withPackages (pkgs: with pkgs; [ slop nokogiri ])); 37 mkRepoRubyArguments = lib.lists.flatten [ 38 (builtins.map (package: ["--packages" "${package}"]) packages) 39 (builtins.map (image: ["--images" "${image}"]) images) 40 (builtins.map (addon: ["--addons" "${addon}"]) addons) 41 ]; 42 in 43 stdenv.mkDerivation { 44 name = "androidenv-repo-json"; 45 buildInputs = [ mkRepoRuby ]; 46 preferLocalBuild = true; 47 unpackPhase = "true"; 48 buildPhase = '' 49 ruby ${./mkrepo.rb} ${lib.escapeShellArgs mkRepoRubyArguments} > repo.json 50 ''; 51 installPhase = '' 52 mv repo.json $out 53 ''; 54 }; 55 56 # Reads the repo JSON. If repoXmls is provided, will build a repo JSON into the Nix store. 57 repo = if repoXmls != null then 58 let 59 repoXmlSpec = { 60 packages = repoXmls.packages or []; 61 images = repoXmls.images or []; 62 addons = repoXmls.addons or []; 63 }; 64 in 65 lib.importJSON "${mkRepoJson repoXmlSpec}" 66 else 67 lib.importJSON repoJson; 68 69 # Converts all 'archives' keys in a repo spec to fetchurl calls. 70 fetchArchives = attrSet: 71 lib.attrsets.mapAttrsRecursive 72 (path: value: 73 if (builtins.elemAt path ((builtins.length path) - 1)) == "archives" then 74 (builtins.listToAttrs 75 (builtins.map 76 (archive: lib.attrsets.nameValuePair archive.os (fetchurl { inherit (archive) url sha1; })) value)) 77 else value 78 ) 79 attrSet; 80 81 # Converts the repo attrset into fetch calls 82 packages = fetchArchives repo.packages; 83 system-images-packages = fetchArchives repo.images; 84 addons = { 85 addons = fetchArchives repo.addons; 86 extras = fetchArchives repo.extras; 87 }; 88 89 # Converts a license name to a list of license texts. 90 mkLicenses = licenseName: repo.licenses.${licenseName}; 91 92 # Converts a list of license names to a flattened list of license texts. 93 # Just used for displaying licenses. 94 mkLicenseTexts = licenseNames: 95 lib.lists.flatten 96 (builtins.map 97 (licenseName: 98 builtins.map 99 (licenseText: "--- ${licenseName} ---\n${licenseText}") 100 (mkLicenses licenseName)) 101 licenseNames); 102 103 # Converts a license name to a list of license hashes. 104 mkLicenseHashes = licenseName: 105 builtins.map 106 (licenseText: builtins.hashString "sha1" licenseText) 107 (mkLicenses licenseName); 108 109 # The list of all license names we're accepting. Put android-sdk-license there 110 # by default. 111 licenseNames = lib.lists.unique ([ 112 "android-sdk-license" 113 ] ++ extraLicenses); 114in 115rec { 116 deployAndroidPackages = callPackage ./deploy-androidpackages.nix { 117 inherit stdenv lib mkLicenses; 118 }; 119 120 deployAndroidPackage = ({package, os ? null, buildInputs ? [], patchInstructions ? "", meta ? {}, ...}@args: 121 let 122 extraParams = removeAttrs args [ "package" "os" "buildInputs" "patchInstructions" ]; 123 in 124 deployAndroidPackages ({ 125 inherit os buildInputs meta; 126 packages = [ package ]; 127 patchesInstructions = { "${package.name}" = patchInstructions; }; 128 } // extraParams 129 )); 130 131 # put a much nicer error message that includes the available options. 132 check-version = packages: package: version: 133 if lib.hasAttrByPath [ package version ] packages then 134 packages.${package}.${version} 135 else 136 throw '' 137 The version ${version} is missing in package ${package}. 138 The only available versions are ${builtins.concatStringsSep ", " (builtins.attrNames packages.${package})}. 139 ''; 140 141 platform-tools = callPackage ./platform-tools.nix { 142 inherit deployAndroidPackage; 143 os = if stdenv.system == "aarch64-darwin" then "macosx" else os; # "macosx" is a universal binary here 144 package = check-version packages "platform-tools" platformToolsVersion; 145 }; 146 147 tools = callPackage ./tools.nix { 148 inherit deployAndroidPackage os; 149 package = check-version packages "tools" toolsVersion; 150 151 postInstall = '' 152 ${linkPlugin { name = "platform-tools"; plugin = platform-tools; }} 153 ${linkPlugin { name = "patcher"; plugin = patcher; }} 154 ${linkPlugin { name = "emulator"; plugin = emulator; }} 155 ''; 156 }; 157 158 patcher = callPackage ./patcher.nix { 159 inherit deployAndroidPackage os; 160 package = packages.patcher."1"; 161 }; 162 163 build-tools = map (version: 164 callPackage ./build-tools.nix { 165 inherit deployAndroidPackage os; 166 package = check-version packages "build-tools" version; 167 168 postInstall = '' 169 ${linkPlugin { name = "tools"; plugin = tools; check = toolsVersion != null; }} 170 ''; 171 } 172 ) buildToolsVersions; 173 174 emulator = callPackage ./emulator.nix { 175 inherit deployAndroidPackage os; 176 package = check-version packages "emulator" emulatorVersion; 177 178 postInstall = '' 179 ${linkSystemImages { images = system-images; check = includeSystemImages; }} 180 ''; 181 }; 182 183 platforms = map (version: 184 deployAndroidPackage { 185 inherit os; 186 package = check-version packages "platforms" version; 187 } 188 ) platformVersions; 189 190 sources = map (version: 191 deployAndroidPackage { 192 inherit os; 193 package = check-version packages "sources" version; 194 } 195 ) platformVersions; 196 197 system-images = lib.flatten (map (apiVersion: 198 map (type: 199 # Deploy all system images with the same systemImageType in one derivation to avoid the `null` problem below 200 # with avdmanager when trying to create an avd! 201 # 202 # ``` 203 # $ yes "" | avdmanager create avd --force --name testAVD --package 'system-images;android-33;google_apis;x86_64' 204 # Error: Package path is not valid. Valid system image paths are: 205 # null 206 # ``` 207 let 208 availablePackages = map (abiVersion: 209 system-images-packages.${apiVersion}.${type}.${abiVersion} 210 ) (builtins.filter (abiVersion: 211 lib.hasAttrByPath [apiVersion type abiVersion] system-images-packages 212 ) abiVersions); 213 214 instructions = builtins.listToAttrs (map (package: { 215 name = package.name; 216 value = lib.optionalString (lib.hasPrefix "google_apis" type) '' 217 # Patch 'google_apis' system images so they're recognized by the sdk. 218 # Without this, `android list targets` shows 'Tag/ABIs : no ABIs' instead 219 # of 'Tag/ABIs : google_apis*/*' and the emulator fails with an ABI-related error. 220 sed -i '/^Addon.Vendor/d' source.properties 221 ''; 222 }) availablePackages 223 ); 224 in 225 lib.optionals (availablePackages != []) 226 (deployAndroidPackages { 227 inherit os; 228 packages = availablePackages; 229 patchesInstructions = instructions; 230 }) 231 ) systemImageTypes 232 ) platformVersions); 233 234 cmake = map (version: 235 callPackage ./cmake.nix { 236 inherit deployAndroidPackage os; 237 package = check-version packages "cmake" version; 238 } 239 ) cmakeVersions; 240 241 # Creates a NDK bundle. 242 makeNdkBundle = ndkVersion: 243 callPackage ./ndk-bundle { 244 inherit deployAndroidPackage os platform-tools; 245 package = packages.ndk-bundle.${ndkVersion} or packages.ndk.${ndkVersion}; 246 }; 247 248 # All NDK bundles. 249 ndk-bundles = lib.optionals includeNDK (map makeNdkBundle ndkVersions); 250 251 # The "default" NDK bundle. 252 ndk-bundle = if includeNDK then lib.findFirst (x: x != null) null ndk-bundles else null; 253 254 google-apis = map (version: 255 deployAndroidPackage { 256 inherit os; 257 package = (check-version addons "addons" version).google_apis; 258 } 259 ) (builtins.filter (platformVersion: platformVersion < "26") platformVersions); # API level 26 and higher include Google APIs by default 260 261 google-tv-addons = map (version: 262 deployAndroidPackage { 263 inherit os; 264 package = (check-version addons "addons" version).google_tv_addon; 265 } 266 ) platformVersions; 267 268 # Function that automatically links all plugins for which multiple versions can coexist 269 linkPlugins = {name, plugins}: 270 lib.optionalString (plugins != []) '' 271 mkdir -p ${name} 272 ${lib.concatMapStrings (plugin: '' 273 ln -s ${plugin}/libexec/android-sdk/${name}/* ${name} 274 '') plugins} 275 ''; 276 277 # Function that automatically links all NDK plugins. 278 linkNdkPlugins = {name, plugins, rootName ? name}: 279 lib.optionalString (plugins != []) '' 280 mkdir -p ${rootName} 281 ${lib.concatMapStrings (plugin: '' 282 ln -s ${plugin}/libexec/android-sdk/${name} ${rootName}/${plugin.version} 283 '') plugins} 284 ''; 285 286 # Function that automatically links the default NDK plugin. 287 linkNdkPlugin = {name, plugin, check}: 288 lib.optionalString check '' 289 ln -s ${plugin}/libexec/android-sdk/${name} ${name} 290 ''; 291 292 # Function that automatically links a plugin for which only one version exists 293 linkPlugin = {name, plugin, check ? true}: 294 lib.optionalString check '' 295 ln -s ${plugin}/libexec/android-sdk/${name} ${name} 296 ''; 297 298 linkSystemImages = { images, check }: lib.optionalString check '' 299 mkdir -p system-images 300 ${lib.concatMapStrings (system-image: '' 301 apiVersion=$(basename $(echo ${system-image}/libexec/android-sdk/system-images/*)) 302 type=$(basename $(echo ${system-image}/libexec/android-sdk/system-images/*/*)) 303 mkdir -p system-images/$apiVersion 304 ln -s ${system-image}/libexec/android-sdk/system-images/$apiVersion/$type system-images/$apiVersion/$type 305 '') images} 306 ''; 307 308 # Links all plugins related to a requested platform 309 linkPlatformPlugins = {name, plugins, check}: 310 lib.optionalString check '' 311 mkdir -p ${name} 312 ${lib.concatMapStrings (plugin: '' 313 ln -s ${plugin}/libexec/android-sdk/${name}/* ${name} 314 '') plugins} 315 ''; # */ 316 317 # This derivation deploys the tools package and symlinks all the desired 318 # plugins that we want to use. If the license isn't accepted, prints all the licenses 319 # requested and throws. 320 androidsdk = if !licenseAccepted then throw '' 321 ${builtins.concatStringsSep "\n\n" (mkLicenseTexts licenseNames)} 322 323 You must accept the following licenses: 324 ${lib.concatMapStringsSep "\n" (str: " - ${str}") licenseNames} 325 326 a) 327 by setting nixpkgs config option 'android_sdk.accept_license = true;'. 328 b) 329 by an environment variable for a single invocation of the nix tools. 330 $ export NIXPKGS_ACCEPT_ANDROID_SDK_LICENSE=1 331 '' else callPackage ./cmdline-tools.nix { 332 inherit deployAndroidPackage os cmdLineToolsVersion; 333 334 package = check-version packages "cmdline-tools" cmdLineToolsVersion; 335 336 postInstall = '' 337 # Symlink all requested plugins 338 ${linkPlugin { name = "platform-tools"; plugin = platform-tools; }} 339 ${linkPlugin { name = "tools"; plugin = tools; check = toolsVersion != null; }} 340 ${linkPlugin { name = "patcher"; plugin = patcher; }} 341 ${linkPlugins { name = "build-tools"; plugins = build-tools; }} 342 ${linkPlugin { name = "emulator"; plugin = emulator; check = includeEmulator; }} 343 ${linkPlugins { name = "platforms"; plugins = platforms; }} 344 ${linkPlatformPlugins { name = "sources"; plugins = sources; check = includeSources; }} 345 ${linkPlugins { name = "cmake"; plugins = cmake; }} 346 ${linkNdkPlugins { name = "ndk-bundle"; rootName = "ndk"; plugins = ndk-bundles; }} 347 ${linkNdkPlugin { name = "ndk-bundle"; plugin = ndk-bundle; check = includeNDK; }} 348 ${linkSystemImages { images = system-images; check = includeSystemImages; }} 349 ${linkPlatformPlugins { name = "add-ons"; plugins = google-apis; check = useGoogleAPIs; }} 350 ${linkPlatformPlugins { name = "add-ons"; plugins = google-apis; check = useGoogleTVAddOns; }} 351 352 # Link extras 353 ${lib.concatMapStrings (identifier: 354 let 355 path = addons.extras.${identifier}.path; 356 addon = deployAndroidPackage { 357 inherit os; 358 package = addons.extras.${identifier}; 359 }; 360 in 361 '' 362 targetDir=$(dirname ${path}) 363 mkdir -p $targetDir 364 ln -s ${addon}/libexec/android-sdk/${path} $targetDir 365 '') includeExtras} 366 367 # Expose common executables in bin/ 368 mkdir -p $out/bin 369 370 for i in ${platform-tools}/bin/*; do 371 ln -s $i $out/bin 372 done 373 374 for i in ${emulator}/bin/*; do 375 ln -s $i $out/bin 376 done 377 378 find $ANDROID_SDK_ROOT/cmdline-tools/${cmdLineToolsVersion}/bin -type f -executable | while read i; do 379 ln -s $i $out/bin 380 done 381 382 # Write licenses 383 mkdir -p licenses 384 ${lib.concatMapStrings (licenseName: 385 let 386 licenseHashes = builtins.concatStringsSep "\n" (mkLicenseHashes licenseName); 387 licenseHashFile = writeText "androidenv-${licenseName}" licenseHashes; 388 in 389 '' 390 ln -s ${licenseHashFile} licenses/${licenseName} 391 '') licenseNames} 392 ''; 393 }; 394}