1{
2 stdenv,
3 lib,
4 composeXcodeWrapper,
5}:
6{
7 name,
8 src,
9 sdkVersion ? "13.1",
10 target ? null,
11 configuration ? null,
12 scheme ? null,
13 sdk ? null,
14 xcodeFlags ? "",
15 release ? false,
16 certificateFile ? null,
17 certificatePassword ? null,
18 provisioningProfile ? null,
19 codeSignIdentity ? null,
20 signMethod ? null,
21 generateIPA ? false,
22 generateXCArchive ? false,
23 enableWirelessDistribution ? false,
24 installURL ? null,
25 bundleId ? null,
26 appVersion ? null,
27 ...
28}@args:
29
30assert
31 release
32 ->
33 certificateFile != null
34 && certificatePassword != null
35 && provisioningProfile != null
36 && signMethod != null
37 && codeSignIdentity != null;
38assert enableWirelessDistribution -> installURL != null && bundleId != null && appVersion != null;
39
40let
41 # Set some default values here
42
43 _target = if target == null then name else target;
44
45 _configuration =
46 if configuration == null then if release then "Release" else "Debug" else configuration;
47
48 _sdk =
49 if sdk == null then
50 if release then "iphoneos" + sdkVersion else "iphonesimulator" + sdkVersion
51 else
52 sdk;
53
54 # The following is to prevent repetition
55 deleteKeychain = ''
56 security default-keychain -s login.keychain
57 security delete-keychain $keychainName
58 '';
59
60 xcodewrapperFormalArgs = builtins.functionArgs composeXcodeWrapper;
61 xcodewrapperArgs = builtins.intersectAttrs xcodewrapperFormalArgs args;
62 xcodewrapper = composeXcodeWrapper xcodewrapperArgs;
63
64 extraArgs = removeAttrs args (
65 [
66 "name"
67 "scheme"
68 "xcodeFlags"
69 "release"
70 "certificateFile"
71 "certificatePassword"
72 "provisioningProfile"
73 "signMethod"
74 "generateIPA"
75 "generateXCArchive"
76 "enableWirelessDistribution"
77 "installURL"
78 "bundleId"
79 "version"
80 ]
81 ++ builtins.attrNames xcodewrapperFormalArgs
82 );
83in
84stdenv.mkDerivation (
85 {
86 name = lib.replaceStrings [ " " ] [ "" ] name; # iOS app names can contain spaces, but in the Nix store this is not allowed
87 buildPhase = ''
88 # Be sure that the Xcode wrapper has priority over everything else.
89 # When using buildInputs this does not seem to be the case.
90 export PATH=${xcodewrapper}/bin:$PATH
91
92 ${lib.optionalString release ''
93 export HOME=/Users/$(whoami)
94 keychainName="$(basename $out)"
95
96 # Create a keychain
97 security create-keychain -p "" $keychainName
98 security default-keychain -s $keychainName
99 security unlock-keychain -p "" $keychainName
100
101 # Import the certificate into the keychain
102 security import ${certificateFile} -k $keychainName -P "${certificatePassword}" -A
103
104 # Grant the codesign utility permissions to read from the keychain
105 security set-key-partition-list -S apple-tool:,apple: -s -k "" $keychainName
106
107 # Determine provisioning ID
108 PROVISIONING_PROFILE=$(grep UUID -A1 -a ${provisioningProfile} | grep -o "[-A-Za-z0-9]\{36\}")
109
110 if [ ! -f "$HOME/Library/MobileDevice/Provisioning Profiles/$PROVISIONING_PROFILE.mobileprovision" ]
111 then
112 # Copy provisioning profile into the home directory
113 mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles"
114 cp ${provisioningProfile} "$HOME/Library/MobileDevice/Provisioning Profiles/$PROVISIONING_PROFILE.mobileprovision"
115 fi
116
117 # Check whether the identity can be found
118 security find-identity -p codesigning $keychainName
119 ''}
120
121 # Do the building
122 export LD=/usr/bin/clang # To avoid problem with -isysroot parameter that is unrecognized by the stock ld. Comparison with an impure build shows that it uses clang instead. Ugly, but it works
123
124 xcodebuild -target ${_target} -configuration ${_configuration} ${
125 lib.optionalString (scheme != null) "-scheme ${scheme}"
126 } -sdk ${_sdk} TARGETED_DEVICE_FAMILY="1, 2" ONLY_ACTIVE_ARCH=NO CONFIGURATION_TEMP_DIR=$TMPDIR CONFIGURATION_BUILD_DIR=$out ${
127 lib.optionalString (generateIPA || generateXCArchive) "-archivePath \"${name}.xcarchive\" archive"
128 } ${lib.optionalString release ''PROVISIONING_PROFILE=$PROVISIONING_PROFILE OTHER_CODE_SIGN_FLAGS="--keychain $HOME/Library/Keychains/$keychainName-db"''} ${xcodeFlags}
129
130 ${lib.optionalString release ''
131 ${lib.optionalString generateIPA ''
132 # Create export plist file
133 cat > "${name}.plist" <<EOF
134 <?xml version="1.0" encoding="UTF-8"?>
135 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
136 <plist version="1.0">
137 <dict>
138 <key>signingCertificate</key>
139 <string>${codeSignIdentity}</string>
140 <key>provisioningProfiles</key>
141 <dict>
142 <key>${bundleId}</key>
143 <string>$PROVISIONING_PROFILE</string>
144 </dict>
145 <key>signingStyle</key>
146 <string>manual</string>
147 <key>method</key>
148 <string>${signMethod}</string>
149 ${lib.optionalString (signMethod == "enterprise" || signMethod == "ad-hoc") ''
150 <key>compileBitcode</key>
151 <false/>
152 ''}
153 </dict>
154 </plist>
155 EOF
156
157 # Produce an IPA file
158 xcodebuild -exportArchive -archivePath "${name}.xcarchive" -exportOptionsPlist "${name}.plist" -exportPath $out
159
160 # Add IPA to Hydra build products
161 mkdir -p $out/nix-support
162 echo "file binary-dist \"$(echo $out/*.ipa)\"" > $out/nix-support/hydra-build-products
163
164 ${lib.optionalString enableWirelessDistribution ''
165 # Add another hacky build product that enables wireless adhoc installations
166 appname="$(basename "$(echo $out/*.ipa)" .ipa)"
167 sed -e "s|@INSTALL_URL@|${installURL}?bundleId=${bundleId}\&version=${appVersion}\&title=$appname|" ${./install.html.template} > $out/''${appname}.html
168 echo "doc install \"$out/''${appname}.html\"" >> $out/nix-support/hydra-build-products
169 ''}
170 ''}
171 ${lib.optionalString generateXCArchive ''
172 mkdir -p $out
173 mv "${name}.xcarchive" $out
174 ''}
175
176 # Delete our temp keychain
177 ${deleteKeychain}
178 ''}
179 '';
180
181 failureHook = lib.optionalString release deleteKeychain;
182
183 installPhase = "true";
184 }
185 // extraArgs
186)