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