1{
2 stdenv,
3 lib,
4 unzip,
5 mkLicenses,
6 os,
7 arch,
8 meta,
9}:
10{
11 packages,
12 nativeBuildInputs ? [ ],
13 buildInputs ? [ ],
14 patchesInstructions ? { },
15 ...
16}@args:
17
18let
19 extraParams = removeAttrs args [
20 "packages"
21 "os"
22 "buildInputs"
23 "nativeBuildInputs"
24 "patchesInstructions"
25 ];
26 sortedPackages = builtins.sort (x: y: builtins.lessThan x.name y.name) packages;
27
28 mkXmlAttrs =
29 attrs: lib.concatStrings (lib.mapAttrsToList (name: value: " ${name}=\"${value}\"") attrs);
30 mkXmlValues =
31 attrs:
32 lib.concatStrings (
33 lib.mapAttrsToList (
34 name: value:
35 let
36 tag = builtins.head (builtins.match "([^:]+).*" name);
37 in
38 if builtins.typeOf value == "string" then "<${tag}>${value}</${tag}>" else mkXmlDoc name value
39 ) attrs
40 );
41 mkXmlDoc =
42 name: doc:
43 let
44 tag = builtins.head (builtins.match "([^:]+).*" name);
45 hasXmlAttrs = builtins.hasAttr "element-attributes" doc;
46 xmlValues = removeAttrs doc [ "element-attributes" ];
47 hasXmlValues = builtins.length (builtins.attrNames xmlValues) > 0;
48 in
49 if hasXmlAttrs && hasXmlValues then
50 "<${tag}${mkXmlAttrs doc.element-attributes}>${mkXmlValues xmlValues}</${tag}>"
51 else if hasXmlAttrs && !hasXmlValues then
52 "<${tag}${mkXmlAttrs doc.element-attributes}/>"
53 else if !hasXmlAttrs && hasXmlValues then
54 "<${tag}>${mkXmlValues xmlValues}</${tag}>"
55 else
56 "<${tag}/>";
57 mkXmlPackage = package: ''
58 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
59 <ns2:repository
60 xmlns:ns2="http://schemas.android.com/repository/android/common/02"
61 xmlns:ns3="http://schemas.android.com/repository/android/common/01"
62 xmlns:ns4="http://schemas.android.com/repository/android/generic/01"
63 xmlns:ns5="http://schemas.android.com/repository/android/generic/02"
64 xmlns:ns6="http://schemas.android.com/sdk/android/repo/addon2/01"
65 xmlns:ns7="http://schemas.android.com/sdk/android/repo/addon2/02"
66 xmlns:ns8="http://schemas.android.com/sdk/android/repo/addon2/03"
67 xmlns:ns9="http://schemas.android.com/sdk/android/repo/repository2/01"
68 xmlns:ns10="http://schemas.android.com/sdk/android/repo/repository2/02"
69 xmlns:ns11="http://schemas.android.com/sdk/android/repo/repository2/03"
70 xmlns:ns12="http://schemas.android.com/sdk/android/repo/sys-img2/03"
71 xmlns:ns13="http://schemas.android.com/sdk/android/repo/sys-img2/02"
72 xmlns:ns14="http://schemas.android.com/sdk/android/repo/sys-img2/01"
73 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
74 <license id="${package.license}" type="text">${lib.concatStringsSep "---" (mkLicenses package.license)}</license>
75 <localPackage path="${builtins.replaceStrings [ "/" ] [ ";" ] package.path}" obsolete="${
76 if (lib.hasAttrByPath [ "obsolete" ] package) then package.obsolete else "false"
77 }">
78 ${mkXmlDoc "type-details" package.type-details}
79 ${mkXmlDoc "revision" package.revision-details}
80 ${lib.optionalString (lib.hasAttrByPath [ "dependencies" ] package) (
81 mkXmlDoc "dependencies" package.dependencies
82 )}
83 <display-name>${package.displayName}</display-name>
84 <uses-license ref="${package.license}"/>
85 </localPackage>
86 </ns2:repository>
87 '';
88in
89stdenv.mkDerivation (
90 {
91 inherit buildInputs;
92 pname = "android-sdk-${lib.concatMapStringsSep "-" (package: package.name) sortedPackages}";
93 version = lib.concatMapStringsSep "-" (package: package.revision) sortedPackages;
94 src = lib.flatten (map (package: package.archives) packages);
95 inherit os arch;
96 nativeBuildInputs = [ unzip ] ++ nativeBuildInputs;
97 preferLocalBuild = true;
98
99 unpackPhase = ''
100 runHook preUnpack
101 if [ -z "$src" ]; then
102 echo "$pname did not have any sources available for os=$os, arch=$arch." >&2
103 echo "Are packages available for this architecture?" >&2
104 exit 1
105 fi
106 buildDir=$PWD
107 i=0
108 for srcArchive in $src; do
109 extractedZip="extractedzip-$i"
110 i=$((i+1))
111 cd "$buildDir"
112 mkdir "$extractedZip"
113 cd "$extractedZip"
114 unpackFile "$srcArchive"
115 done
116 runHook postUnpack
117 '';
118
119 installPhase = ''
120 runHook preInstall
121 ''
122 + lib.concatStrings (
123 lib.imap0 (i: package: ''
124 cd $buildDir/extractedzip-${toString i}
125
126 # Most Android Zip packages have a root folder, but some don't. We unpack
127 # the zip file in a folder and we try to discover whether it has a single root
128 # folder. If this is the case, we adjust the current working folder.
129 if [ "$(find . -mindepth 1 -maxdepth 1 -type d | wc -l)" -eq 1 ]; then
130 cd "$(find . -mindepth 1 -maxdepth 1 -type d)"
131 fi
132 extractedZip="$PWD"
133
134 packageBaseDir=$out/libexec/android-sdk/${package.path}
135 mkdir -p $packageBaseDir
136 cd $packageBaseDir
137 cp -a $extractedZip/* .
138 ${patchesInstructions.${package.name}}
139
140 if [ ! -f $packageBaseDir/package.xml ]; then
141 cat << EOF > $packageBaseDir/package.xml
142 ${mkXmlPackage package}
143 EOF
144 fi
145 '') packages
146 )
147 + ''
148 runHook postInstall
149 '';
150
151 # Some executables that have been patched with patchelf may not work any longer after they have been stripped.
152 dontStrip = true;
153 dontPatchELF = true;
154 dontAutoPatchelf = true;
155
156 inherit meta;
157 }
158 // extraParams
159)