stim 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{android-DJWiTBkz.mjs → android-DWssqHQ8.mjs} +165 -661
- package/dist/{android-sobvO9ia.mjs → android-Da-G_o3n.mjs} +646 -548
- package/dist/{android-BW8i_YxU.mjs → android-DeYdjjBP.mjs} +6 -2
- package/dist/{android-cas-CSS8gjR7.mjs → android-cas-SWIjzeuL.mjs} +54 -18
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/{app-install--PAocWvq.mjs → app-install-DJg7bVv-.mjs} +36 -10
- package/dist/build-lock-CgtsB6TV.mjs +604 -0
- package/dist/{build-slots-CuPszJDu.mjs → build-slots-C-oAHRT1.mjs} +2 -2
- package/dist/{cache-manifest-uoOKhgUD.mjs → cache-manifest-CYJi8-Hx.mjs} +2 -2
- package/dist/cache-manifest.d.mts +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +14 -14
- package/dist/collector-run.d.mts +2 -0
- package/dist/collector-run.mjs +7 -7
- package/dist/{command-output-BuIb_ZOI.mjs → command-output-B3ycpsYv.mjs} +2 -2
- package/dist/{config-DMP7L7H6.mjs → config-B7iyKWkL.mjs} +56 -16
- package/dist/{dependency-state-CTL8A9p3.mjs → dependency-state-D0M7oKA7.mjs} +2 -2
- package/dist/{deps-BAGOnrc7.mjs → deps-oCXVNEqQ.mjs} +6 -6
- package/dist/detached-entry-Bpo6ZY_I.mjs +65 -0
- package/dist/{dev-client-BoifdqkU.mjs → dev-client-DDUkWgJL.mjs} +12 -12
- package/dist/{device-BUw4JBuH.mjs → device-CBSaC6BU.mjs} +7 -7
- package/dist/{device-DvKJ454A.mjs → device-CLeDp-KH.mjs} +17 -15
- package/dist/{device-lease-r7yEQ1R8.mjs → device-lease-Ffz_M5t0.mjs} +3 -3
- package/dist/{device-pool-DU4FQKp5.mjs → device-pool-DNSHL5z8.mjs} +4 -4
- package/dist/{device-remote-BTUqbl0i.mjs → device-remote-BprxZp-m.mjs} +11 -11
- package/dist/{doctor-Di_B26uh.mjs → doctor-CTAx9gMu.mjs} +90 -46
- package/dist/{error-diagnostics-CTePWAzW.mjs → error-diagnostics-D1Kaf-eT.mjs} +23 -17
- package/dist/{exec-CyylIdq9.mjs → exec-B29XNuhP.mjs} +14 -2
- package/dist/{gc-C20dWw_8.mjs → gc-CX0KRfLG.mjs} +17 -16
- package/dist/{guide-sAnoty2y.mjs → guide-CPmAG9me.mjs} +61 -22
- package/dist/{guide-status-BF6RsJJp.mjs → guide-status-Craq9z0t.mjs} +3 -3
- package/dist/{ios-BdpkncL6.mjs → ios-BRXYSxSc.mjs} +34 -37
- package/dist/{ios-device-DJOi9FR5.mjs → ios-device-CDbvn5H2.mjs} +2 -2
- package/dist/{ios-device-D3pzTKQE.mjs → ios-device-CFXA46ez.mjs} +1 -1
- package/dist/{ios-Deh5js5V.mjs → ios-lEJ5pKsq.mjs} +3 -3
- package/dist/{logs-BfQdIhiF.mjs → logs-CakdBTtm.mjs} +6 -6
- package/dist/{logs-query-Cdp5aaCH.mjs → logs-query-BGL-Pl12.mjs} +1 -1
- package/dist/metro-C7bcJ0uo.mjs +233 -0
- package/dist/{named-ports-DH9SXd0f.mjs → named-ports-DzMOlmaX.mjs} +57 -42
- package/dist/{ownership-CD_XnpDu.mjs → ownership-Cp52QAK1.mjs} +3 -3
- package/dist/{ownership-CVY9cqCr.mjs → ownership-D5vkdm8H.mjs} +13 -13
- package/dist/{ports-A6uTJSPj.mjs → ports-BIbJaKNW.mjs} +3 -3
- package/dist/{process-output-C7IlIzd7.mjs → process-output-Dl5uKE03.mjs} +1 -1
- package/dist/{project-B_SDt4ip.mjs → project-DDZWf_16.mjs} +3 -3
- package/dist/{reclaim-C_rB25f4.mjs → reclaim-D614jI6_.mjs} +11 -11
- package/dist/{reload-CYBJbJfI.mjs → reload-ChW46VDb.mjs} +9 -9
- package/dist/{server-bare-H3pAhbMs.mjs → server-bare-BQkyxa_b.mjs} +1 -1
- package/dist/{server-expo-Ds8_mfng.mjs → server-expo-BVAA1OqJ.mjs} +23 -11
- package/dist/server-expo-YDDG3eP7.mjs +2 -0
- package/dist/{settings-kO2ZqIKy.mjs → settings-BXXorVM2.mjs} +8 -5
- package/dist/{slot-launch-BpHiHH6A.mjs → slot-launch-DbLvyrup.mjs} +4 -4
- package/dist/{start-DiEfqEWk.mjs → start-TTVILlUi.mjs} +111 -38
- package/dist/{state-CnWzvCWP.mjs → state-DZobzxLW.mjs} +2 -2
- package/dist/{stats-Fo9ACIv7.mjs → stats-BYcZ6n1h.mjs} +1 -1
- package/dist/{stats-DHrO0frH.mjs → stats-CLg5RuDS.mjs} +4 -4
- package/dist/{status-J5LAXpag.mjs → status-DUNy_HZs.mjs} +14 -15
- package/dist/{stop-CM1fejKW.mjs → stop-CnUpnFT5.mjs} +2 -2
- package/dist/{stop-0jJNahwN.mjs → stop-ZrfODZ51.mjs} +13 -13
- package/dist/supervisor-run.mjs +7 -5
- package/dist/{teardown-CAVcWeRD.mjs → teardown-5J5UXz2P.mjs} +14 -17
- package/dist/{workspace-process-lock-Bz19_R9R.mjs → workspace-process-lock-BYfZz_S-.mjs} +1 -1
- package/dist/{workspace-state-WVwWw_hB.mjs → workspace-state-BT5E3SA2.mjs} +3 -3
- package/dist/{worktree-CeUKDAQw.mjs → worktree-BfE4lzHK.mjs} +21 -16
- package/dist/{worktree-DJnBbQZ9.mjs → worktree-FG_ZKKdb.mjs} +13 -9
- package/dist/{xcode-BqDeRkpT.mjs → xcode-B6YaWeg0.mjs} +92 -17
- package/package.json +12 -8
- package/shim/android-optimizations.gradle +6 -0
- package/dist/metro-ato6ef3J.mjs +0 -99
- package/dist/ports-BZprTJRa.mjs +0 -90
- package/dist/server-expo-CSwUVYOa.mjs +0 -2
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-
|
|
2
|
-
import { C as withConfigLock, E as ownership_claim_exports, F as deviceSlotPlatforms, P as
|
|
1
|
+
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
+
import { B as validateDeviceSlot, C as withConfigLock, E as ownership_claim_exports, F as deviceSlotKey, I as deviceSlotPlatforms, P as deviceSlotFileKey, S as upsertProject, b as setDevice, f as getProject, g as loadConfig, l as getConcurrencyLimits, y as saveConfig } from "./config-B7iyKWkL.mjs";
|
|
3
3
|
import { r as createNdjsonWriter } from "./ndjson-BZwI1NBv.mjs";
|
|
4
|
-
import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState, s as emulatorLogFile } from "./workspace-state-
|
|
5
|
-
import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-
|
|
6
|
-
import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, n as detectAndroidPackage, r as detectBundleId, t as appProjectProblem } from "./project-
|
|
7
|
-
import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-
|
|
8
|
-
import { r as waitForChild, t as createLineReader } from "./process-output-
|
|
9
|
-
import {
|
|
10
|
-
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { t as
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { t as
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { r as
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { a as
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
4
|
+
import { _ as workspaceLogsDir, h as workspaceDir, o as writeWorkspaceState, r as readWorkspaceState, s as emulatorLogFile } from "./workspace-state-BT5E3SA2.mjs";
|
|
5
|
+
import { T as removeTemporaryEntry, g as repoRoot, s as gitCommonDir, w as makeTemporaryDirectory } from "./worktree-FG_ZKKdb.mjs";
|
|
6
|
+
import { a as findProjectRoot, c as projectShortcut, i as detectIsExpo, n as detectAndroidPackage, r as detectBundleId, t as appProjectProblem } from "./project-DDZWf_16.mjs";
|
|
7
|
+
import { n as MODE_EXPO, p as writeWorkspaceLaunch, t as MODE_BARE } from "./state-DZobzxLW.mjs";
|
|
8
|
+
import { r as waitForChild, t as createLineReader } from "./process-output-Dl5uKE03.mjs";
|
|
9
|
+
import { a as resolveProjectMetro, i as pidExists, o as signalProcessTree } from "./metro-C7bcJ0uo.mjs";
|
|
10
|
+
import { n as verifyCollectorOwnership, r as readCollectors } from "./ownership-Cp52QAK1.mjs";
|
|
11
|
+
import { A as resolveSettings, B as artifactCachePolicy, D as remoteDeviceSettingError, E as remoteAndroidSetting, F as tunnelModeSetting, H as optimizationBuildProfile, I as unknownSettingKeys, N as settingShapeErrors, R as detectProviders, T as publicUrlSetting, U as resolveOptimizations, V as compilerCacheFallbackMessage, a as SETTING_SHAPE_REMEDY, d as cacheProviderSettingError, j as settingFile, k as resolveCacheProviderConfig, r as REMOTE_DEVICE_BACKENDS, s as androidAvdConfigSettingError, u as androidDataPartitionSizeGbSettingError, x as metroWarmupUrlSetting } from "./settings-BXXorVM2.mjs";
|
|
12
|
+
import { B as waitForBoot, E as memoizeEmulatorProbe, F as resetAdoptedAvd, I as resolveOwnedAvdSerial, L as resolvePhysicalDevice, M as physicalDeviceModel, P as probeEmulatorSerial, S as listAdbDevices, _ as emulatorFailureRemedy, a as androidPoolNoCandidatesRefusal, g as emulatorDiskSpaceRemedy, i as androidPoolCandidates, n as androidDeviceAbi, o as androidSystemImageAbi, r as androidHome, s as androidToolCwd, t as androidBuildToolName, v as extractEmulatorFailure, w as listInstalledSystemImages, y as findBuildTool } from "./android-DWssqHQ8.mjs";
|
|
13
|
+
import { _ as launchErrorPreview, c as launchErrorReport, g as stepTimer, h as stepClock, i as formatDuration, l as phaseLine, n as appReadinessMessage, p as shortHash } from "./command-output-B3ycpsYv.mjs";
|
|
14
|
+
import { C as untrackedNativeFiles, D as readAssetManifest, E as compareAssetManifests, S as untrackedMissLine, T as captureAssetManifest, _ as providerUploadOutcome, b as storeBuild, c as buildCacheKey, d as filesystemBuildCapability, f as fingerprintDiffRecord, g as providerDownloadPath, h as prepareProviderDownloadDir, i as releaseBuildLock, l as describeFingerprintMiss, m as fingerprintProject, n as acquireBuildLock, o as takeoverLine, p as fingerprintDiffSuffix, s as waitForBuild, t as WAIT_CEILING_MS, v as refingerprintAfterMutation, w as assetDiffReason, x as storedAssetManifest, y as resolveBuild } from "./build-lock-CgtsB6TV.mjs";
|
|
15
|
+
import { t as cleanLine } from "./server-expo-BVAA1OqJ.mjs";
|
|
16
|
+
import { C as resolveRemote, S as resolveEasCliBin, _ as exitAfterFlush, f as RESOLVE_TIMEOUT_MS, g as easAuthNote, h as checkEasAuth, l as parkedMaxSetting, m as cacheLevel, p as UPLOAD_TIMEOUT_MS, v as isEasAuthFailureText, w as uploadRemote, y as loadProjectProvider } from "./teardown-5J5UXz2P.mjs";
|
|
17
|
+
import { c as ensureWorkspaceStorageSafely, d as launchOutcomeRecord, f as noMetroMessage, g as warmMetro, i as detectEntryFile, m as resolveMetroWithRetry, n as pickDevClientScheme, o as needsPrebuild, p as noMetroRemedy, s as runPrebuild, t as devClientScheme, u as isPhysicalDeviceRequest, v as isEasBuildFailure, y as resolveEasDevelopmentBuild } from "./dev-client-DDUkWgJL.mjs";
|
|
18
|
+
import { t as withWorkspaceProcessLock } from "./workspace-process-lock-BYfZz_S-.mjs";
|
|
19
|
+
import { D as verifyAndroidReleaseLaunch, E as unverifiedLaunchLines, O as verifyLaunch, S as launchAndroidReleaseApp, _ as installConflictKind, c as RELEASE_VERIFY_WAIT_MS, d as androidDevClientUrl, g as installAndroidApp, h as deviceShellArg, i as LAUNCH_BUNDLING, l as VERIFY_TIMEOUT_MS, n as DEFAULT_METRO_PORT, o as LAUNCH_FATAL, s as LAUNCH_UNVERIFIED, t as ADB_INSTALL_TIMEOUT_MS, u as androidAppProcess, x as launchAndroidApp } from "./app-install-DJg7bVv-.mjs";
|
|
20
|
+
import { a as lostLine, c as releaseLeaseOnSignal, i as leaseExpiryText, l as runLease, n as DEBUG_VERIFY_STEP_MS, o as lostRefusal, r as acquireRunLease, s as parseDeviceWait, t as selectFromPool, u as waitFlagConflict } from "./device-pool-DNSHL5z8.mjs";
|
|
21
|
+
import { r as releaseBuildSlot, t as acquireBuildSlot } from "./build-slots-C-oAHRT1.mjs";
|
|
22
|
+
import { a as statsProjectKey, i as recordRunStats, n as readRunEstimates, t as createRunRecorder } from "./stats-BYcZ6n1h.mjs";
|
|
23
|
+
import { i as ensureOwnedDevice, l as AvdBootError, o as unknownAndroidSystemImageRefusal, r as ensureBooted, t as checkDeviceCapacity, u as AvdRecoveryError } from "./device-CLeDp-KH.mjs";
|
|
24
|
+
import { a as ensureMetroReachable, b as ownedSessionName, l as resolveRemoteContext, n as binOnPath, o as ensureRemoteBootOwned, s as remoteAndroidDeps } from "./device-remote-BprxZp-m.mjs";
|
|
25
|
+
import { a as buildAndroid, c as CCACHE_NOT_RUN, f as resolveCcache, l as CCACHE_UNAVAILABLE, n as resolveAndroidCas, o as productFlavorRefusal, p as formatDiagnostic, r as resolveAndroidCompilerCache, s as readProductFlavors, u as ccacheActivityLine } from "./android-cas-SWIjzeuL.mjs";
|
|
26
|
+
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B6YaWeg0.mjs";
|
|
27
|
+
import { n as nativeRunCommand, t as launchSlotScope } from "./slot-launch-DbLvyrup.mjs";
|
|
27
28
|
import { t as spawnEntry } from "./spawn-entry-nCE6m770.mjs";
|
|
28
|
-
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-
|
|
29
|
-
import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
29
|
+
import { a as printNativeCrashReport, r as captureNativeCrashes, t as errorDiagnostics } from "./error-diagnostics-D1Kaf-eT.mjs";
|
|
30
|
+
import { constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
30
31
|
import { InvalidArgumentError } from "commander";
|
|
31
|
-
import { basename, dirname, join, relative } from "node:path";
|
|
32
|
+
import { basename, dirname, isAbsolute, join, relative } from "node:path";
|
|
32
33
|
import { createWarnOnce, loadCacheProvider, resolveTieredBuild, storeTieredBuild } from "@stim-cli/cache";
|
|
33
34
|
import chalk from "chalk";
|
|
34
35
|
//#region src/engine/apk-swap.ts
|
|
35
36
|
const ANDROID_BUNDLE_NAME = "index.android.bundle";
|
|
36
|
-
const ANDROID_BUNDLE_ENTRY = `assets/${ANDROID_BUNDLE_NAME}`;
|
|
37
37
|
const LAST_LINES = 5;
|
|
38
38
|
function hermesEnabledFromGradleProperties(text) {
|
|
39
39
|
if (typeof text !== "string") return true;
|
|
@@ -54,16 +54,21 @@ function readAndroidHermesEnabled(root) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
function hermescBinDir(platform = process.platform) {
|
|
57
|
-
|
|
57
|
+
if (platform === "darwin") return "osx-bin";
|
|
58
|
+
return platform === "win32" ? "win64-bin" : "linux64-bin";
|
|
59
|
+
}
|
|
60
|
+
function hermescName(platform) {
|
|
61
|
+
return platform === "win32" ? "hermesc.exe" : "hermesc";
|
|
58
62
|
}
|
|
59
63
|
function hermescCandidates(root, { platform = process.platform, reactNativePath = null } = {}) {
|
|
60
64
|
const bin = hermescBinDir(platform);
|
|
65
|
+
const name = hermescName(platform);
|
|
61
66
|
const rn = reactNativePath ?? join(root, "node_modules", "react-native");
|
|
62
67
|
return [.../* @__PURE__ */ new Set([
|
|
63
|
-
join(dirname(rn), "hermes-compiler", "hermesc", bin,
|
|
64
|
-
join(root, "node_modules", "hermes-compiler", "hermesc", bin,
|
|
65
|
-
join(root, "node_modules", "react-native", "sdks", "hermesc", bin,
|
|
66
|
-
join(root, "node_modules", "react-native", "sdks", "hermes", "build", "bin",
|
|
68
|
+
join(dirname(rn), "hermes-compiler", "hermesc", bin, name),
|
|
69
|
+
join(root, "node_modules", "hermes-compiler", "hermesc", bin, name),
|
|
70
|
+
join(root, "node_modules", "react-native", "sdks", "hermesc", bin, name),
|
|
71
|
+
join(root, "node_modules", "react-native", "sdks", "hermes", "build", "bin", name)
|
|
67
72
|
])];
|
|
68
73
|
}
|
|
69
74
|
function androidHermescPath(root, { exists = existsSync, platform = process.platform, reactNativePath = null } = {}) {
|
|
@@ -117,8 +122,8 @@ function androidBundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
|
|
|
117
122
|
]
|
|
118
123
|
};
|
|
119
124
|
}
|
|
120
|
-
function
|
|
121
|
-
return
|
|
125
|
+
function jarPath({ javaHome = process.env.JAVA_HOME } = {}) {
|
|
126
|
+
return javaHome ? join(javaHome, "bin", "jar") : "jar";
|
|
122
127
|
}
|
|
123
128
|
function zipalignArgs({ buildToolsMajor, input, output }) {
|
|
124
129
|
return [
|
|
@@ -141,7 +146,7 @@ function resolveKeystore(root, settings) {
|
|
|
141
146
|
const bag = android && typeof android === "object" && !Array.isArray(android) ? android : {};
|
|
142
147
|
const configured = bag["keystore"];
|
|
143
148
|
return {
|
|
144
|
-
path: typeof configured === "string" && configured.trim() !== "" ? configured.trim()
|
|
149
|
+
path: typeof configured === "string" && configured.trim() !== "" ? isAbsolute(configured.trim()) ? configured.trim() : join(root, configured.trim()) : join(root, "android", "app", "debug.keystore"),
|
|
145
150
|
pass: keystorePassArg(bag["keystorePassword"])
|
|
146
151
|
};
|
|
147
152
|
}
|
|
@@ -155,7 +160,7 @@ function apksignerArgs({ keystore, apkPath }) {
|
|
|
155
160
|
apkPath
|
|
156
161
|
];
|
|
157
162
|
}
|
|
158
|
-
async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter = null, exec = null, spawnFn = null, mkdtemp = () => makeTemporaryDirectory(cachedApkPath, "stim-apk-swap-"), exists = existsSync, hermesEnabled = null, buildTools = null, findTool = findBuildTool, storedAssets = null, readManifest = readAssetManifest, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, onHeartbeat = (line) => console.error(line) }) {
|
|
163
|
+
async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter = null, exec = null, spawnFn = null, mkdtemp = () => makeTemporaryDirectory(cachedApkPath, "stim-apk-swap-"), exists = existsSync, hermesEnabled = null, buildTools = null, findTool = findBuildTool, storedAssets = null, readManifest = readAssetManifest, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, onHeartbeat = (line) => console.error(line), platform = process.platform }) {
|
|
159
164
|
const e = exec || getExecutor();
|
|
160
165
|
const startedAt = now();
|
|
161
166
|
const elapsed = () => now() - startedAt;
|
|
@@ -183,15 +188,7 @@ async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter
|
|
|
183
188
|
const base = basename(cachedApkPath);
|
|
184
189
|
work = join(tmp, `unaligned-${base}`);
|
|
185
190
|
final = join(tmp, base);
|
|
186
|
-
|
|
187
|
-
e.runFile("cp", [
|
|
188
|
-
"-c",
|
|
189
|
-
cachedApkPath,
|
|
190
|
-
work
|
|
191
|
-
]);
|
|
192
|
-
} catch {
|
|
193
|
-
e.runFile("cp", [cachedApkPath, work]);
|
|
194
|
-
}
|
|
191
|
+
copyApk(e, cachedApkPath, work, platform);
|
|
195
192
|
} catch (err) {
|
|
196
193
|
return fail("copy", `could not copy ${cachedApkPath} aside: ${describe(err)}`);
|
|
197
194
|
}
|
|
@@ -287,7 +284,7 @@ async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter
|
|
|
287
284
|
bundle: bundleOutput,
|
|
288
285
|
out: hbc
|
|
289
286
|
}));
|
|
290
|
-
|
|
287
|
+
renameSync(hbc, bundleOutput);
|
|
291
288
|
hermes = true;
|
|
292
289
|
} catch (err) {
|
|
293
290
|
return fail("hermesc", `hermesc failed on ${bundleOutput}: ${describe(err)}`);
|
|
@@ -315,24 +312,19 @@ async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter
|
|
|
315
312
|
if (!fresh) return fail("assets", `could not hash the assets emitted into ${assetsDest}, so the asset set cannot be verified`);
|
|
316
313
|
const diff = compareAssetManifests(fresh, storedAssets);
|
|
317
314
|
if (!diff.same) return refuse(assetDiffReason(diff), diff);
|
|
315
|
+
const jar = jarPath();
|
|
318
316
|
try {
|
|
319
|
-
e.runFile(
|
|
320
|
-
"
|
|
321
|
-
|
|
322
|
-
ANDROID_BUNDLE_ENTRY
|
|
323
|
-
]);
|
|
324
|
-
} catch (err) {
|
|
325
|
-
if (!isNothingToDelete(describe(err))) return fail("zip", `zip -d ${ANDROID_BUNDLE_ENTRY} failed on ${work}: ${describe(err)}`);
|
|
326
|
-
}
|
|
327
|
-
try {
|
|
328
|
-
e.runFile("zip", [
|
|
329
|
-
"-0",
|
|
330
|
-
"-r",
|
|
317
|
+
e.runFile(jar, [
|
|
318
|
+
"--update",
|
|
319
|
+
"--file",
|
|
331
320
|
work,
|
|
321
|
+
"--no-compress",
|
|
322
|
+
"-C",
|
|
323
|
+
stage,
|
|
332
324
|
"assets"
|
|
333
|
-
]
|
|
325
|
+
]);
|
|
334
326
|
} catch (err) {
|
|
335
|
-
return fail("zip",
|
|
327
|
+
return fail("zip", `${jar} --update ${work} failed: ${describe(err)}`);
|
|
336
328
|
}
|
|
337
329
|
const tools = buildTools ?? findTool(["zipalign"]);
|
|
338
330
|
if (!tools) return fail("zipalign", "no zipalign found under the Android SDK build-tools; install one with `sdkmanager \"build-tools;36.0.0\"`");
|
|
@@ -347,7 +339,7 @@ async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter
|
|
|
347
339
|
}
|
|
348
340
|
const signer = buildTools ? {
|
|
349
341
|
...buildTools,
|
|
350
|
-
path: join(dirname(buildTools.path), "apksigner")
|
|
342
|
+
path: join(dirname(buildTools.path), androidBuildToolName("apksigner"))
|
|
351
343
|
} : findTool(["apksigner"]);
|
|
352
344
|
if (!signer) return fail("apksigner", "no apksigner found under the Android SDK build-tools; install one with `sdkmanager \"build-tools;36.0.0\"`");
|
|
353
345
|
try {
|
|
@@ -374,6 +366,17 @@ async function swapApkBundle({ root, isExpo, cachedApkPath, keystore, logWriter
|
|
|
374
366
|
if (note) result.note = note;
|
|
375
367
|
return result;
|
|
376
368
|
}
|
|
369
|
+
function copyApk(e, from, to, platform) {
|
|
370
|
+
if (platform === "darwin") try {
|
|
371
|
+
e.runFile("cp", [
|
|
372
|
+
"-c",
|
|
373
|
+
from,
|
|
374
|
+
to
|
|
375
|
+
]);
|
|
376
|
+
return;
|
|
377
|
+
} catch {}
|
|
378
|
+
copyFileSync(from, to, constants.COPYFILE_FICLONE);
|
|
379
|
+
}
|
|
377
380
|
function describe(err) {
|
|
378
381
|
const e = err;
|
|
379
382
|
const streams = [e?.stdout, e?.stderr].map((s) => s ? String(s).trim() : "").filter(Boolean).join("\n");
|
|
@@ -630,6 +633,519 @@ function androidBuildOptions({ release, physical, device, variant, deviceAbi, co
|
|
|
630
633
|
};
|
|
631
634
|
}
|
|
632
635
|
//#endregion
|
|
636
|
+
//#region src/commands/android/artifact.ts
|
|
637
|
+
const FALLBACK_LINES = 5;
|
|
638
|
+
function fail$1(code, message, remedy, extra = {}) {
|
|
639
|
+
return {
|
|
640
|
+
code,
|
|
641
|
+
message,
|
|
642
|
+
remedy,
|
|
643
|
+
extra
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async function acquireAndroidArtifact({ root, buildLog, writer, settings, isExpo, device, physical, buildPlan, cacheProviderConfig, requestedBuildCache, easBuild, androidPackage: initialPackage, record, maxBuilds, progress }, { deviceAbi, fingerprint, untracked, resolveCached, storeCached, storedAssets, captureAssets, acquireLock, releaseLock, waitForBuild, loadProvider, easAuth, resolveRemoteBuild, uploadRemoteBuild, loadCacheProviderModule, acquireSlot, releaseSlot, needsPrebuildFor, prebuild, build, ccacheFor, swapApk, readState, now }) {
|
|
647
|
+
const { phase, out, estimates, stats } = progress;
|
|
648
|
+
const { variant, release, profile: buildProfile, cas, cache: cachePolicy } = buildPlan;
|
|
649
|
+
const useBuildCache = cachePolicy.read;
|
|
650
|
+
let androidPackage = initialPackage;
|
|
651
|
+
let ccacheActivity = CCACHE_NOT_RUN;
|
|
652
|
+
let phaseFailure = null;
|
|
653
|
+
const refused = () => ({
|
|
654
|
+
ok: false,
|
|
655
|
+
failure: phaseFailure,
|
|
656
|
+
ccache: ccacheActivity
|
|
657
|
+
});
|
|
658
|
+
let buildLock = null;
|
|
659
|
+
const releaseHeldLock = () => {
|
|
660
|
+
if (!buildLock) return;
|
|
661
|
+
const held = buildLock;
|
|
662
|
+
buildLock = null;
|
|
663
|
+
try {
|
|
664
|
+
releaseLock(held);
|
|
665
|
+
} catch (err) {
|
|
666
|
+
out(phaseLine("build", chalk.dim(`could not release the build lock at ${held.path}: ${err?.message || err}`)));
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
let buildSlot = null;
|
|
670
|
+
const releaseHeldSlot = () => {
|
|
671
|
+
if (!buildSlot) return;
|
|
672
|
+
const held = buildSlot;
|
|
673
|
+
buildSlot = null;
|
|
674
|
+
try {
|
|
675
|
+
releaseSlot(held);
|
|
676
|
+
} catch (err) {
|
|
677
|
+
out(phaseLine("build", chalk.dim(`could not release the build slot: ${err?.message || err}`)));
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
const { abi: buildAbi, runOptions: buildRunOptions, remoteRunOptions } = androidBuildOptions({
|
|
681
|
+
release,
|
|
682
|
+
physical,
|
|
683
|
+
device,
|
|
684
|
+
variant,
|
|
685
|
+
deviceAbi,
|
|
686
|
+
compiler: cas?.id,
|
|
687
|
+
buildProfile,
|
|
688
|
+
targetAbiOnly: buildPlan.targetAbiOnly
|
|
689
|
+
});
|
|
690
|
+
let hash = "";
|
|
691
|
+
let providerUpload = null;
|
|
692
|
+
let providerName = null;
|
|
693
|
+
let providerLoad = null;
|
|
694
|
+
const cacheWarn = createWarnOnce((line) => phase("cache", chalk.yellow(line)));
|
|
695
|
+
const loadTieredProvider = cachePolicy.remote && cacheProviderConfig ? () => providerLoad ??= loadCacheProviderModule({
|
|
696
|
+
projectRoot: root,
|
|
697
|
+
config: cacheProviderConfig
|
|
698
|
+
}) : null;
|
|
699
|
+
let fingerprintSources = [];
|
|
700
|
+
let cacheKey = "";
|
|
701
|
+
let storeHash = "";
|
|
702
|
+
let storeKey = "";
|
|
703
|
+
let storeSources = [];
|
|
704
|
+
let apkPath = null;
|
|
705
|
+
async function resolveInitialFingerprint() {
|
|
706
|
+
if (easBuild?.ok) {
|
|
707
|
+
hash = storeHash = easBuild.fingerprint;
|
|
708
|
+
cacheKey = storeKey = easBuild.cacheKey;
|
|
709
|
+
apkPath = easBuild.path;
|
|
710
|
+
record.fingerprint = hash;
|
|
711
|
+
record.cacheKey = cacheKey;
|
|
712
|
+
record.cacheHit = easBuild.cacheHit;
|
|
713
|
+
record.cacheSkipped = !useBuildCache;
|
|
714
|
+
providerName = "eas";
|
|
715
|
+
stats.setCacheKey(cacheKey);
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
const fingerprintTimer = stepTimer(now);
|
|
719
|
+
try {
|
|
720
|
+
const computed = await fingerprint(root, { platform: PLATFORM });
|
|
721
|
+
hash = computed?.hash ?? "";
|
|
722
|
+
fingerprintSources = computed?.sources ?? [];
|
|
723
|
+
} catch (err) {
|
|
724
|
+
phaseFailure = fail$1(NO_FINGERPRINT, `@expo/fingerprint could not fingerprint ${root}: ${err?.message || err}`, "Fix the @expo/fingerprint error above, then retry.");
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
if (!hash) {
|
|
728
|
+
phaseFailure = fail$1(NO_FINGERPRINT, `@expo/fingerprint returned no hash for ${root}, so the build cache cannot be addressed.`, "Check the project native inputs and the @expo/fingerprint error above, then retry.");
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
record.fingerprint = hash;
|
|
732
|
+
cacheKey = buildCacheKey(PLATFORM, hash, buildRunOptions);
|
|
733
|
+
stats.setCacheKey(cacheKey);
|
|
734
|
+
record.cacheKey = cacheKey;
|
|
735
|
+
storeHash = hash;
|
|
736
|
+
storeKey = cacheKey;
|
|
737
|
+
storeSources = fingerprintSources;
|
|
738
|
+
const found = await resolveTieredBuild({
|
|
739
|
+
local: filesystemBuildCapability({
|
|
740
|
+
resolve: resolveCached,
|
|
741
|
+
store: storeCached,
|
|
742
|
+
sources: fingerprintSources
|
|
743
|
+
}),
|
|
744
|
+
loadProvider: loadTieredProvider,
|
|
745
|
+
target: {
|
|
746
|
+
projectRoot: root,
|
|
747
|
+
platform: PLATFORM,
|
|
748
|
+
key: cacheKey
|
|
749
|
+
},
|
|
750
|
+
destinationDir: providerDownloadPath(workspaceDir(root)),
|
|
751
|
+
ensureDestination: prepareProviderDownloadDir,
|
|
752
|
+
skipRead: !useBuildCache,
|
|
753
|
+
warn: cacheWarn
|
|
754
|
+
});
|
|
755
|
+
const cached = found?.tier === "local" ? found.path : null;
|
|
756
|
+
record.cacheHit = cached ? "local" : false;
|
|
757
|
+
record.cacheSkipped = !useBuildCache;
|
|
758
|
+
let missDiff = "";
|
|
759
|
+
let missUntracked = null;
|
|
760
|
+
if (!cached) {
|
|
761
|
+
const lastBuild = readState(root)?.lastBuild ?? null;
|
|
762
|
+
const miss = describeFingerprintMiss({
|
|
763
|
+
platform: PLATFORM,
|
|
764
|
+
current: {
|
|
765
|
+
hash,
|
|
766
|
+
sources: fingerprintSources
|
|
767
|
+
},
|
|
768
|
+
lastBuild
|
|
769
|
+
});
|
|
770
|
+
if (miss) {
|
|
771
|
+
missDiff = fingerprintDiffSuffix(miss.changed);
|
|
772
|
+
writer.write(fingerprintDiffRecord({
|
|
773
|
+
changed: miss.changed,
|
|
774
|
+
previousHash: miss.previousHash,
|
|
775
|
+
hash
|
|
776
|
+
}));
|
|
777
|
+
} else if (useBuildCache) missUntracked = untrackedMissLine(untracked({ projectRoot: root }));
|
|
778
|
+
}
|
|
779
|
+
phase("fingerprint", `${shortHash(hash)} ${cached ? "hit" : "miss"}${useBuildCache ? "" : !requestedBuildCache ? " (--no-build-cache)" : " (cache reuse off in config)"} ${fingerprintTimer()}${missDiff}`);
|
|
780
|
+
if (missUntracked) phase("fingerprint", chalk.dim(missUntracked));
|
|
781
|
+
if (found?.tier === "provider") {
|
|
782
|
+
record.cacheHit = "remote";
|
|
783
|
+
providerName = found.providerName ?? null;
|
|
784
|
+
phase("cache", `provider hit (${providerName})${found.storedLocally ? " -> stored locally" : ""}`);
|
|
785
|
+
}
|
|
786
|
+
apkPath = found?.path ?? null;
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
if (!await resolveInitialFingerprint()) return refused();
|
|
790
|
+
let remote = null;
|
|
791
|
+
let abandonedRemote = false;
|
|
792
|
+
let uploadPending = null;
|
|
793
|
+
async function resolveRemoteArtifact() {
|
|
794
|
+
if (buildAbi || cas || buildProfile || !cachePolicy.remote) return;
|
|
795
|
+
if (!apkPath) {
|
|
796
|
+
const loaded = await loadProvider(root, { isExpo });
|
|
797
|
+
if (loaded?.unavailable) phase("cache", chalk.yellow(`provider not usable: ${loaded.unavailable}`));
|
|
798
|
+
else if (loaded?.provider) remote = loaded;
|
|
799
|
+
if (remote?.name === "eas") {
|
|
800
|
+
const auth = easAuth({
|
|
801
|
+
projectRoot: root,
|
|
802
|
+
owner: loaded?.owner || null
|
|
803
|
+
});
|
|
804
|
+
const authNote = easAuthNote(auth);
|
|
805
|
+
if (authNote) phase("cache", chalk.yellow(authNote));
|
|
806
|
+
if (auth?.code === "logged-out") remote = null;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (remote && useBuildCache) {
|
|
810
|
+
const remoteTimer = stepTimer(now);
|
|
811
|
+
const hit = await resolveRemoteBuild({
|
|
812
|
+
logWriter: writer,
|
|
813
|
+
provider: remote.provider,
|
|
814
|
+
platform: PLATFORM,
|
|
815
|
+
projectRoot: root,
|
|
816
|
+
fingerprintHash: hash,
|
|
817
|
+
runOptions: remoteRunOptions
|
|
818
|
+
});
|
|
819
|
+
if (hit?.appPath) {
|
|
820
|
+
let stored = null;
|
|
821
|
+
try {
|
|
822
|
+
stored = storeCached(PLATFORM, cacheKey, hit.appPath, { sources: fingerprintSources });
|
|
823
|
+
} catch (err) {
|
|
824
|
+
phase("cache", chalk.yellow(`remote hit could not be stored locally: ${err?.message || err}`));
|
|
825
|
+
}
|
|
826
|
+
apkPath = stored || hit.appPath;
|
|
827
|
+
record.cacheHit = "remote";
|
|
828
|
+
phase("cache", `remote hit (${remote.name})${stored ? " -> stored locally" : ""} ${remoteTimer()}`);
|
|
829
|
+
} else if (hit?.timedOut) {
|
|
830
|
+
abandonedRemote = true;
|
|
831
|
+
phase("cache", chalk.yellow(`${remote.name} did not answer within ${formatDuration(RESOLVE_TIMEOUT_MS)}; building instead`));
|
|
832
|
+
} else if (hit?.failed) {
|
|
833
|
+
const authNote = remote.name === "eas" && isEasAuthFailureText(hit.failed) ? easAuthNote({
|
|
834
|
+
code: "logged-out",
|
|
835
|
+
reason: hit.failed
|
|
836
|
+
}) : null;
|
|
837
|
+
phase("cache", chalk.yellow(authNote || `${remote.name} could not be used: ${hit.failed}; building instead`));
|
|
838
|
+
} else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (!easBuild) await resolveRemoteArtifact();
|
|
842
|
+
let waitedForBuild = null;
|
|
843
|
+
let releasedWait = null;
|
|
844
|
+
async function waitForSharedBuild() {
|
|
845
|
+
if (!useBuildCache) return true;
|
|
846
|
+
const waitStarted = now();
|
|
847
|
+
let failedHolder;
|
|
848
|
+
while (!apkPath) {
|
|
849
|
+
let attempt = null;
|
|
850
|
+
try {
|
|
851
|
+
attempt = acquireLock({
|
|
852
|
+
platform: PLATFORM,
|
|
853
|
+
key: cacheKey,
|
|
854
|
+
root,
|
|
855
|
+
logFile: buildLog
|
|
856
|
+
});
|
|
857
|
+
} catch (err) {
|
|
858
|
+
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
859
|
+
if (refusal) {
|
|
860
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
phase("build", chalk.yellow(`could not take the build lock: ${err?.message || err}; building anyway`));
|
|
864
|
+
}
|
|
865
|
+
if (attempt?.acquired) {
|
|
866
|
+
buildLock = attempt;
|
|
867
|
+
const previous = attempt.tookOver ?? failedHolder;
|
|
868
|
+
if (previous) phase("build", chalk.yellow(takeoverLine(previous)));
|
|
869
|
+
break;
|
|
870
|
+
} else if (attempt?.held) {
|
|
871
|
+
releasedWait = null;
|
|
872
|
+
const holder = attempt.held;
|
|
873
|
+
const who = holder.projectRoot || "another workspace";
|
|
874
|
+
phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""} -- stim guide lifecycle concurrency`);
|
|
875
|
+
let waited = null;
|
|
876
|
+
try {
|
|
877
|
+
const ceilingMs = WAIT_CEILING_MS - (now() - waitStarted);
|
|
878
|
+
if (ceilingMs <= 0) throw Object.assign(/* @__PURE__ */ new Error(`Waited ${formatDuration(now() - waitStarted)} for shared builds without an artifact; ${who} (pid ${holder.pid}) holds ${attempt.path}.`), {
|
|
879
|
+
code: "STIM_BUILD_WAIT_TIMEOUT",
|
|
880
|
+
lockPath: attempt.path
|
|
881
|
+
});
|
|
882
|
+
waited = await waitForBuild({
|
|
883
|
+
platform: PLATFORM,
|
|
884
|
+
key: cacheKey,
|
|
885
|
+
out,
|
|
886
|
+
ceilingMs
|
|
887
|
+
});
|
|
888
|
+
} catch (err) {
|
|
889
|
+
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
890
|
+
if (refusal) {
|
|
891
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
const wtErr = err;
|
|
895
|
+
if (wtErr?.code !== "STIM_BUILD_WAIT_TIMEOUT") throw err;
|
|
896
|
+
phaseFailure = fail$1("STIM_BUILD_WAIT_TIMEOUT", wtErr.message, `Check pid ${holder.pid}; if it is not really building, remove ${wtErr.lockPath} and run \`stim android\` again.`, { lastBuildStatus: true });
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
if (waited?.hit) {
|
|
900
|
+
apkPath = waited.hit ?? null;
|
|
901
|
+
record.cacheHit = "local";
|
|
902
|
+
waitedForBuild = {
|
|
903
|
+
pid: holder.pid,
|
|
904
|
+
ms: waited.waitedMs
|
|
905
|
+
};
|
|
906
|
+
phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
907
|
+
} else if (waited?.lockReleased) {
|
|
908
|
+
failedHolder = void 0;
|
|
909
|
+
releasedWait = {
|
|
910
|
+
facts: {
|
|
911
|
+
pid: holder.pid,
|
|
912
|
+
ms: waited.waitedMs
|
|
913
|
+
},
|
|
914
|
+
who
|
|
915
|
+
};
|
|
916
|
+
phase("build", `${who}'s build lock was released; rechecking this workspace before building`);
|
|
917
|
+
} else {
|
|
918
|
+
phase("build", chalk.yellow(`${who}'s build ended without an artifact (${waited?.builderFailed}); retrying the build lock`));
|
|
919
|
+
failedHolder = holder;
|
|
920
|
+
}
|
|
921
|
+
} else break;
|
|
922
|
+
}
|
|
923
|
+
return true;
|
|
924
|
+
}
|
|
925
|
+
if (!easBuild && !await waitForSharedBuild()) return refused();
|
|
926
|
+
let swapDir = null;
|
|
927
|
+
let swapFellBack = false;
|
|
928
|
+
const installableCachedApk = async (key, cachedPath) => {
|
|
929
|
+
if (!release) return cachedPath;
|
|
930
|
+
phase("swap", `regenerating this workspace's JS for the cached ${variant} APK`);
|
|
931
|
+
const swap = await swapApk({
|
|
932
|
+
root,
|
|
933
|
+
isExpo,
|
|
934
|
+
cachedApkPath: cachedPath,
|
|
935
|
+
keystore: resolveKeystore(root, settings),
|
|
936
|
+
logWriter: writer,
|
|
937
|
+
storedAssets: storedAssets(PLATFORM, key)
|
|
938
|
+
});
|
|
939
|
+
if (swap?.ok && swap.apkPath) {
|
|
940
|
+
if (swap.note) phase("swap", chalk.yellow(swap.note));
|
|
941
|
+
swapDir = swap.tmpDir ?? null;
|
|
942
|
+
phase("swap", `${swap.hermes ? "hermes bytecode" : "plain JS"} repacked (store), zipaligned and re-signed (${formatDuration(swap.durationMs)})`);
|
|
943
|
+
return swap.apkPath;
|
|
944
|
+
}
|
|
945
|
+
if (swap?.assetMismatch) phase("swap", chalk.yellow(`${swap.reason} -- building fresh instead` + (swap.assetDiff ? " (an APK cannot be made to carry an asset AAPT did not package)" : "")));
|
|
946
|
+
else phase("swap", chalk.yellow(`failed at ${swap?.step || "unknown step"}: ${swap?.reason || "unknown reason"} -- building fresh instead (a cached ${variant} APK carries its builder's JS; it is never installed after a failed swap)`));
|
|
947
|
+
for (const line of swap?.lastLines ?? []) phase("", chalk.dim(line));
|
|
948
|
+
swapFellBack = true;
|
|
949
|
+
return null;
|
|
950
|
+
};
|
|
951
|
+
async function prepareCachedArtifact() {
|
|
952
|
+
if (apkPath && record.cacheHit) {
|
|
953
|
+
const prepared = await installableCachedApk(cacheKey, apkPath);
|
|
954
|
+
apkPath = prepared;
|
|
955
|
+
if (!prepared) {
|
|
956
|
+
record.cacheHit = false;
|
|
957
|
+
waitedForBuild = null;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
async function buildArtifact() {
|
|
962
|
+
if (!apkPath) try {
|
|
963
|
+
if (maxBuilds) try {
|
|
964
|
+
buildSlot = await acquireSlot({
|
|
965
|
+
max: maxBuilds,
|
|
966
|
+
root,
|
|
967
|
+
logFile: buildLog,
|
|
968
|
+
out
|
|
969
|
+
});
|
|
970
|
+
} catch (err) {
|
|
971
|
+
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
972
|
+
if (refusal) {
|
|
973
|
+
phaseFailure = fail$1(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
|
|
977
|
+
}
|
|
978
|
+
if (needsPrebuildFor(root, "android", isExpo)) {
|
|
979
|
+
const pre = await prebuild(root, PLATFORM, writer, { isExpo });
|
|
980
|
+
if (pre.failed) {
|
|
981
|
+
phaseFailure = fail$1(pre.code, pre.reason, pre.remedy, {
|
|
982
|
+
lastBuildStatus: true,
|
|
983
|
+
lines: tail(pre.lastLines),
|
|
984
|
+
logPath: displayPath(root, buildLog)
|
|
985
|
+
});
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
phase("prebuild", `android/ generated (${formatDuration(pre.durationMs)})`);
|
|
989
|
+
androidPackage = androidPackage || detectAndroidPackage(root);
|
|
990
|
+
record.bundleId = androidPackage;
|
|
991
|
+
const after = await refingerprintAfterMutation({
|
|
992
|
+
projectRoot: root,
|
|
993
|
+
platform: PLATFORM,
|
|
994
|
+
previousHash: hash,
|
|
995
|
+
fingerprint
|
|
996
|
+
});
|
|
997
|
+
if (after?.moved) {
|
|
998
|
+
storeHash = after.hash;
|
|
999
|
+
storeSources = after.sources;
|
|
1000
|
+
storeKey = buildCacheKey(PLATFORM, after.hash, buildRunOptions);
|
|
1001
|
+
record.fingerprint = storeHash;
|
|
1002
|
+
record.cacheKey = storeKey;
|
|
1003
|
+
phase("fingerprint", chalk.dim(`${shortHash(hash)} -> ${shortHash(storeHash)} (after prebuild)`));
|
|
1004
|
+
const late = useBuildCache ? resolveCached(PLATFORM, storeKey) : null;
|
|
1005
|
+
if (late) {
|
|
1006
|
+
const prepared = await installableCachedApk(storeKey, late);
|
|
1007
|
+
if (prepared) {
|
|
1008
|
+
apkPath = prepared;
|
|
1009
|
+
record.cacheHit = "local";
|
|
1010
|
+
phase("cache", `hit ${shortHash(storeHash)} (post-prebuild key)`);
|
|
1011
|
+
if (releasedWait) {
|
|
1012
|
+
waitedForBuild = releasedWait.facts;
|
|
1013
|
+
phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
if (!apkPath) {
|
|
1020
|
+
phase("build", `compiling ${variant || "debug"} with Gradle`);
|
|
1021
|
+
const built = await build({
|
|
1022
|
+
root,
|
|
1023
|
+
logWriter: writer,
|
|
1024
|
+
variant,
|
|
1025
|
+
abi: buildAbi
|
|
1026
|
+
}, {
|
|
1027
|
+
estimateMs: estimates().coldBuildMs,
|
|
1028
|
+
ccache: buildPlan.compilerCache === "ccache" ? ccacheFor({
|
|
1029
|
+
root,
|
|
1030
|
+
onNote: out
|
|
1031
|
+
}) : null,
|
|
1032
|
+
cas,
|
|
1033
|
+
buildCache: buildPlan.gradleBuildCache,
|
|
1034
|
+
pch: buildPlan.pch,
|
|
1035
|
+
compilerCacheDisabled: buildPlan.compilerCache === "none"
|
|
1036
|
+
});
|
|
1037
|
+
ccacheActivity = built.ccache ?? CCACHE_UNAVAILABLE;
|
|
1038
|
+
phase("cache", `compilation cache ${ccacheActivityLine(ccacheActivity)}`);
|
|
1039
|
+
if (!built.ok) {
|
|
1040
|
+
const diagnostics = built.diagnostics;
|
|
1041
|
+
for (const diag of diagnostics) writer.write({
|
|
1042
|
+
src: "build",
|
|
1043
|
+
level: "error",
|
|
1044
|
+
event: "gradle_diagnostic",
|
|
1045
|
+
msg: formatDiagnostic(diag)
|
|
1046
|
+
});
|
|
1047
|
+
phase("build", chalk.red(`FAILED after ${formatDuration(built.durationMs)}`));
|
|
1048
|
+
const extracted = diagnostics.map(formatDiagnostic);
|
|
1049
|
+
if (built.truncated > 0) extracted.push(`... and ${built.truncated} more diagnostic(s) in the log`);
|
|
1050
|
+
phaseFailure = fail$1(built.code, built.reason, diagnostics.find((d) => d.remedy)?.remedy || built.remedy || null, {
|
|
1051
|
+
lastBuildStatus: true,
|
|
1052
|
+
diagnostics: extracted,
|
|
1053
|
+
lines: extracted.length ? [] : tail(built.lastLines),
|
|
1054
|
+
logPath: displayPath(root, buildLog)
|
|
1055
|
+
});
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
apkPath = built.apkPath;
|
|
1059
|
+
stats.setBuildMs(built.durationMs);
|
|
1060
|
+
phase("build", `ok (${formatDuration(built.durationMs)})`);
|
|
1061
|
+
if (built.apkNote) phase("build", chalk.yellow(built.apkNote));
|
|
1062
|
+
const beforeBuildHash = storeHash;
|
|
1063
|
+
const afterBuild = await refingerprintAfterMutation({
|
|
1064
|
+
projectRoot: root,
|
|
1065
|
+
platform: PLATFORM,
|
|
1066
|
+
previousHash: beforeBuildHash,
|
|
1067
|
+
fingerprint
|
|
1068
|
+
});
|
|
1069
|
+
if (!afterBuild) {
|
|
1070
|
+
record.fingerprint = null;
|
|
1071
|
+
record.cacheKey = null;
|
|
1072
|
+
phase("fingerprint", chalk.yellow("unavailable after Gradle; the build will be installed but not cached"));
|
|
1073
|
+
} else {
|
|
1074
|
+
if (afterBuild.moved) {
|
|
1075
|
+
storeHash = afterBuild.hash;
|
|
1076
|
+
storeSources = afterBuild.sources;
|
|
1077
|
+
storeKey = buildCacheKey(PLATFORM, afterBuild.hash, buildRunOptions);
|
|
1078
|
+
record.fingerprint = storeHash;
|
|
1079
|
+
record.cacheKey = storeKey;
|
|
1080
|
+
phase("fingerprint", chalk.dim(`${shortHash(beforeBuildHash)} -> ${shortHash(storeHash)} (after Gradle)`));
|
|
1081
|
+
}
|
|
1082
|
+
if (cachePolicy.write) {
|
|
1083
|
+
const assetManifest = release ? captureAssets(root, { variant }) : null;
|
|
1084
|
+
try {
|
|
1085
|
+
const stored = await storeTieredBuild({
|
|
1086
|
+
local: filesystemBuildCapability({
|
|
1087
|
+
resolve: resolveCached,
|
|
1088
|
+
store: storeCached,
|
|
1089
|
+
sources: storeSources,
|
|
1090
|
+
assetManifest
|
|
1091
|
+
}),
|
|
1092
|
+
loadProvider: loadTieredProvider,
|
|
1093
|
+
target: {
|
|
1094
|
+
projectRoot: root,
|
|
1095
|
+
platform: PLATFORM,
|
|
1096
|
+
key: storeKey
|
|
1097
|
+
},
|
|
1098
|
+
sourcePath: apkPath,
|
|
1099
|
+
overwrite: !useBuildCache || swapFellBack,
|
|
1100
|
+
warn: cacheWarn
|
|
1101
|
+
});
|
|
1102
|
+
providerUpload = stored.providerUpload;
|
|
1103
|
+
providerName = stored.providerName ?? providerName;
|
|
1104
|
+
} catch (err) {
|
|
1105
|
+
phase("cache", chalk.yellow(`could not store the build: ${err?.message || err}`));
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (remote) uploadPending = uploadRemoteBuild({
|
|
1109
|
+
logWriter: writer,
|
|
1110
|
+
provider: remote.provider,
|
|
1111
|
+
platform: PLATFORM,
|
|
1112
|
+
projectRoot: root,
|
|
1113
|
+
fingerprintHash: storeHash,
|
|
1114
|
+
buildPath: apkPath,
|
|
1115
|
+
runOptions: remoteRunOptions
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
} finally {
|
|
1120
|
+
releaseHeldLock();
|
|
1121
|
+
releaseHeldSlot();
|
|
1122
|
+
}
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
if (!easBuild) {
|
|
1126
|
+
await prepareCachedArtifact();
|
|
1127
|
+
if (!await buildArtifact()) return refused();
|
|
1128
|
+
}
|
|
1129
|
+
return {
|
|
1130
|
+
ok: true,
|
|
1131
|
+
artifact: {
|
|
1132
|
+
apkPath,
|
|
1133
|
+
androidPackage,
|
|
1134
|
+
swapDir,
|
|
1135
|
+
waitedForBuild,
|
|
1136
|
+
ccache: ccacheActivity,
|
|
1137
|
+
uploadPending,
|
|
1138
|
+
providerUpload,
|
|
1139
|
+
providerName,
|
|
1140
|
+
remote,
|
|
1141
|
+
abandonedRemote
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
function tail(lines, n = FALLBACK_LINES) {
|
|
1146
|
+
return (Array.isArray(lines) ? lines : []).slice(-n);
|
|
1147
|
+
}
|
|
1148
|
+
//#endregion
|
|
633
1149
|
//#region src/commands/android/result.ts
|
|
634
1150
|
function androidFacts({ slot, serial, avdName = null, deviceName = null, systemImage = null, fingerprint, cacheKey = null, variant = null, metroPort = null, cacheHit, cacheSkipped = false, waitedForBuild = null, appPath, bundleId, installSkipped = false, launched, ccache = CCACHE_UNAVAILABLE, logs, debugHttpHost = null, debugHttpHostNote = null, devClientUrl = null, durationMs, lease }) {
|
|
635
1151
|
return {
|
|
@@ -775,11 +1291,11 @@ function collectorEntry() {
|
|
|
775
1291
|
return spawnEntry("collector-run");
|
|
776
1292
|
}
|
|
777
1293
|
function collectorLogFile(root, slot = "default") {
|
|
778
|
-
return join(workspaceLogsDir(root), `collector-${
|
|
1294
|
+
return join(workspaceLogsDir(root), `collector-${deviceSlotFileKey(PLATFORM, slot)}.log`);
|
|
779
1295
|
}
|
|
780
1296
|
const COLLECTOR_EXIT_WAIT_MS = 2e3;
|
|
781
1297
|
const COLLECTOR_POLL_MS = 25;
|
|
782
|
-
function killPreviousCollector(root, { platform = PLATFORM, slot = "default", kill = (pid, signal) =>
|
|
1298
|
+
function killPreviousCollector(root, { platform = PLATFORM, slot = "default", kill = (pid, signal) => signalProcessTree(pid, signal), collectors = null, verify = verifyCollectorOwnership, isAlive = pidExists, note = (_line) => {} } = {}) {
|
|
783
1299
|
const record = (collectors ?? readCollectors(root))?.[deviceSlotKey(platform, slot)];
|
|
784
1300
|
const pid = Number(record?.pid);
|
|
785
1301
|
if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid) return null;
|
|
@@ -837,7 +1353,7 @@ async function startCollector({ root, slot = "default", serial, packageName, spa
|
|
|
837
1353
|
"--package",
|
|
838
1354
|
packageName
|
|
839
1355
|
], {
|
|
840
|
-
cwd: root,
|
|
1356
|
+
cwd: androidToolCwd() ?? root,
|
|
841
1357
|
detached: true,
|
|
842
1358
|
stdio,
|
|
843
1359
|
env: process.env
|
|
@@ -965,6 +1481,7 @@ async function verifyAndroidRun({ root, slot, release, remoteRelease, remoteDevi
|
|
|
965
1481
|
serial,
|
|
966
1482
|
component,
|
|
967
1483
|
devClientUrl: scheme ? androidDevClientUrl(scheme, metroPort ?? 8081, physical) : null,
|
|
1484
|
+
devClient: Boolean(scheme),
|
|
968
1485
|
mode: isExpo ? MODE_EXPO : MODE_BARE
|
|
969
1486
|
})) phase("", chalk.yellow(line));
|
|
970
1487
|
return { state: LAUNCH_UNVERIFIED };
|
|
@@ -1411,7 +1928,6 @@ function resolveAndroidRunPlan({ settings, settingsContext, slot, easProfile, va
|
|
|
1411
1928
|
}
|
|
1412
1929
|
//#endregion
|
|
1413
1930
|
//#region src/commands/android.ts
|
|
1414
|
-
const FALLBACK_LINES = 5;
|
|
1415
1931
|
function androidCommand(program) {
|
|
1416
1932
|
registerAndroid(program);
|
|
1417
1933
|
}
|
|
@@ -1624,7 +2140,7 @@ async function runAndroid(options = {}) {
|
|
|
1624
2140
|
};
|
|
1625
2141
|
}
|
|
1626
2142
|
const logsDir = workspaceLogsDir(root);
|
|
1627
|
-
const buildLog = join(logsDir, `build-${
|
|
2143
|
+
const buildLog = join(logsDir, `build-${deviceSlotFileKey("android", slot)}.ndjson`);
|
|
1628
2144
|
const writer = createWriter(buildLog, {
|
|
1629
2145
|
truncate: true,
|
|
1630
2146
|
fields: { slot }
|
|
@@ -1638,28 +2154,6 @@ async function runAndroid(options = {}) {
|
|
|
1638
2154
|
avdName: null,
|
|
1639
2155
|
deviceName: null
|
|
1640
2156
|
};
|
|
1641
|
-
let buildLock = null;
|
|
1642
|
-
const releaseHeldLock = () => {
|
|
1643
|
-
if (!buildLock) return;
|
|
1644
|
-
const held = buildLock;
|
|
1645
|
-
buildLock = null;
|
|
1646
|
-
try {
|
|
1647
|
-
releaseLock(held);
|
|
1648
|
-
} catch (err) {
|
|
1649
|
-
out(phaseLine("build", chalk.dim(`could not release the build lock at ${held.path}: ${err?.message || err}`)));
|
|
1650
|
-
}
|
|
1651
|
-
};
|
|
1652
|
-
let buildSlot = null;
|
|
1653
|
-
const releaseHeldSlot = () => {
|
|
1654
|
-
if (!buildSlot) return;
|
|
1655
|
-
const held = buildSlot;
|
|
1656
|
-
buildSlot = null;
|
|
1657
|
-
try {
|
|
1658
|
-
releaseSlot(held);
|
|
1659
|
-
} catch (err) {
|
|
1660
|
-
out(phaseLine("build", chalk.dim(`could not release the build slot: ${err?.message || err}`)));
|
|
1661
|
-
}
|
|
1662
|
-
};
|
|
1663
2157
|
const phase = (label, text) => out(phaseLine(label, text));
|
|
1664
2158
|
const stats = createRunRecorder({
|
|
1665
2159
|
platform: PLATFORM,
|
|
@@ -1745,7 +2239,7 @@ async function runAndroid(options = {}) {
|
|
|
1745
2239
|
if (!planned.ok) return fail(planned.code, planned.message, planned.remedy, { lines: planned.lines });
|
|
1746
2240
|
const { plan } = planned;
|
|
1747
2241
|
const { build: buildPlan, target, isExpo, cacheProviderConfig } = plan;
|
|
1748
|
-
const { variant, release,
|
|
2242
|
+
const { variant, release, cache: cachePolicy } = buildPlan;
|
|
1749
2243
|
const useBuildCache = cachePolicy.read;
|
|
1750
2244
|
const physical = target.kind === "physical";
|
|
1751
2245
|
const remoteBackend = target.kind === "remote" ? target.backend : null;
|
|
@@ -1936,115 +2430,6 @@ async function runAndroid(options = {}) {
|
|
|
1936
2430
|
record.avdName = device.avdName ?? null;
|
|
1937
2431
|
record.deviceName = device.deviceName ?? device.avdName ?? null;
|
|
1938
2432
|
record.systemImage = device.systemImage;
|
|
1939
|
-
const { abi: buildAbi, runOptions: buildRunOptions, remoteRunOptions } = androidBuildOptions({
|
|
1940
|
-
release,
|
|
1941
|
-
physical,
|
|
1942
|
-
device,
|
|
1943
|
-
variant,
|
|
1944
|
-
deviceAbi,
|
|
1945
|
-
compiler: cas?.id,
|
|
1946
|
-
buildProfile,
|
|
1947
|
-
targetAbiOnly: buildPlan.targetAbiOnly
|
|
1948
|
-
});
|
|
1949
|
-
let hash = "";
|
|
1950
|
-
let providerUpload = null;
|
|
1951
|
-
let providerName = null;
|
|
1952
|
-
let providerLoad = null;
|
|
1953
|
-
const cacheWarn = createWarnOnce((line) => phase("cache", chalk.yellow(line)));
|
|
1954
|
-
const loadTieredProvider = cachePolicy.remote && cacheProviderConfig ? () => providerLoad ??= loadCacheProviderModule({
|
|
1955
|
-
projectRoot: root,
|
|
1956
|
-
config: cacheProviderConfig
|
|
1957
|
-
}) : null;
|
|
1958
|
-
let fingerprintSources = [];
|
|
1959
|
-
let cacheKey = "";
|
|
1960
|
-
let storeHash = "";
|
|
1961
|
-
let storeKey = "";
|
|
1962
|
-
let storeSources = [];
|
|
1963
|
-
let apkPath = null;
|
|
1964
|
-
async function resolveInitialFingerprint() {
|
|
1965
|
-
if (easBuild?.ok) {
|
|
1966
|
-
hash = storeHash = easBuild.fingerprint;
|
|
1967
|
-
cacheKey = storeKey = easBuild.cacheKey;
|
|
1968
|
-
apkPath = easBuild.path;
|
|
1969
|
-
record.fingerprint = hash;
|
|
1970
|
-
record.cacheKey = cacheKey;
|
|
1971
|
-
record.cacheHit = easBuild.cacheHit;
|
|
1972
|
-
record.cacheSkipped = !useBuildCache;
|
|
1973
|
-
providerName = "eas";
|
|
1974
|
-
stats.setCacheKey(cacheKey);
|
|
1975
|
-
return true;
|
|
1976
|
-
}
|
|
1977
|
-
const fingerprintTimer = stepTimer(now);
|
|
1978
|
-
try {
|
|
1979
|
-
const computed = await fingerprint(root, { platform: PLATFORM });
|
|
1980
|
-
hash = computed?.hash ?? "";
|
|
1981
|
-
fingerprintSources = computed?.sources ?? [];
|
|
1982
|
-
} catch (err) {
|
|
1983
|
-
phaseFailure = fail(NO_FINGERPRINT, `@expo/fingerprint could not fingerprint ${root}: ${err?.message || err}`, "Fix the @expo/fingerprint error above, then retry.");
|
|
1984
|
-
return false;
|
|
1985
|
-
}
|
|
1986
|
-
if (!hash) {
|
|
1987
|
-
phaseFailure = fail(NO_FINGERPRINT, `@expo/fingerprint returned no hash for ${root}, so the build cache cannot be addressed.`, "Check the project native inputs and the @expo/fingerprint error above, then retry.");
|
|
1988
|
-
return false;
|
|
1989
|
-
}
|
|
1990
|
-
record.fingerprint = hash;
|
|
1991
|
-
cacheKey = buildCacheKey(PLATFORM, hash, buildRunOptions);
|
|
1992
|
-
stats.setCacheKey(cacheKey);
|
|
1993
|
-
record.cacheKey = cacheKey;
|
|
1994
|
-
storeHash = hash;
|
|
1995
|
-
storeKey = cacheKey;
|
|
1996
|
-
storeSources = fingerprintSources;
|
|
1997
|
-
const found = await resolveTieredBuild({
|
|
1998
|
-
local: filesystemBuildCapability({
|
|
1999
|
-
resolve: resolveCached,
|
|
2000
|
-
store: storeCached,
|
|
2001
|
-
sources: fingerprintSources
|
|
2002
|
-
}),
|
|
2003
|
-
loadProvider: loadTieredProvider,
|
|
2004
|
-
target: {
|
|
2005
|
-
projectRoot: root,
|
|
2006
|
-
platform: PLATFORM,
|
|
2007
|
-
key: cacheKey
|
|
2008
|
-
},
|
|
2009
|
-
destinationDir: providerDownloadPath(workspaceDir(root)),
|
|
2010
|
-
ensureDestination: prepareProviderDownloadDir,
|
|
2011
|
-
skipRead: !useBuildCache,
|
|
2012
|
-
warn: cacheWarn
|
|
2013
|
-
});
|
|
2014
|
-
const cached = found?.tier === "local" ? found.path : null;
|
|
2015
|
-
record.cacheHit = cached ? "local" : false;
|
|
2016
|
-
record.cacheSkipped = !useBuildCache;
|
|
2017
|
-
let missDiff = "";
|
|
2018
|
-
let missUntracked = null;
|
|
2019
|
-
if (!cached) {
|
|
2020
|
-
const lastBuild = readState(root)?.lastBuild ?? null;
|
|
2021
|
-
const miss = describeFingerprintMiss({
|
|
2022
|
-
platform: PLATFORM,
|
|
2023
|
-
current: {
|
|
2024
|
-
hash,
|
|
2025
|
-
sources: fingerprintSources
|
|
2026
|
-
},
|
|
2027
|
-
lastBuild
|
|
2028
|
-
});
|
|
2029
|
-
if (miss) {
|
|
2030
|
-
missDiff = fingerprintDiffSuffix(miss.changed);
|
|
2031
|
-
writer.write(fingerprintDiffRecord({
|
|
2032
|
-
changed: miss.changed,
|
|
2033
|
-
previousHash: miss.previousHash,
|
|
2034
|
-
hash
|
|
2035
|
-
}));
|
|
2036
|
-
} else if (useBuildCache) missUntracked = untrackedMissLine(untracked({ projectRoot: root }));
|
|
2037
|
-
}
|
|
2038
|
-
phase("fingerprint", `${shortHash(hash)} ${cached ? "hit" : "miss"}${useBuildCache ? "" : !requestedBuildCache ? " (--no-build-cache)" : " (cache reuse off in config)"} ${fingerprintTimer()}${missDiff}`);
|
|
2039
|
-
if (missUntracked) phase("fingerprint", chalk.dim(missUntracked));
|
|
2040
|
-
if (found?.tier === "provider") {
|
|
2041
|
-
record.cacheHit = "remote";
|
|
2042
|
-
providerName = found.providerName ?? null;
|
|
2043
|
-
phase("cache", `provider hit (${providerName})${found.storedLocally ? " -> stored locally" : ""}`);
|
|
2044
|
-
}
|
|
2045
|
-
apkPath = found?.path ?? null;
|
|
2046
|
-
return true;
|
|
2047
|
-
}
|
|
2048
2433
|
const runFromFingerprint = async () => {
|
|
2049
2434
|
if (metroCheck && metroPort !== null && plan.metroWarmup) prewarmMetro({
|
|
2050
2435
|
port: metroPort,
|
|
@@ -2053,346 +2438,62 @@ async function runAndroid(options = {}) {
|
|
|
2053
2438
|
appId: androidPackage,
|
|
2054
2439
|
bundleUrl: metroWarmupUrlSetting(settings, "android")
|
|
2055
2440
|
});
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
if (remote && useBuildCache) {
|
|
2077
|
-
const remoteTimer = stepTimer(now);
|
|
2078
|
-
const hit = await resolveRemoteBuild({
|
|
2079
|
-
logWriter: writer,
|
|
2080
|
-
provider: remote.provider,
|
|
2081
|
-
platform: PLATFORM,
|
|
2082
|
-
projectRoot: root,
|
|
2083
|
-
fingerprintHash: hash,
|
|
2084
|
-
runOptions: remoteRunOptions
|
|
2085
|
-
});
|
|
2086
|
-
if (hit?.appPath) {
|
|
2087
|
-
let stored = null;
|
|
2088
|
-
try {
|
|
2089
|
-
stored = storeCached(PLATFORM, cacheKey, hit.appPath, { sources: fingerprintSources });
|
|
2090
|
-
} catch (err) {
|
|
2091
|
-
phase("cache", chalk.yellow(`remote hit could not be stored locally: ${err?.message || err}`));
|
|
2092
|
-
}
|
|
2093
|
-
apkPath = stored || hit.appPath;
|
|
2094
|
-
record.cacheHit = "remote";
|
|
2095
|
-
phase("cache", `remote hit (${remote.name})${stored ? " -> stored locally" : ""} ${remoteTimer()}`);
|
|
2096
|
-
} else if (hit?.timedOut) {
|
|
2097
|
-
abandonedRemote = true;
|
|
2098
|
-
phase("cache", chalk.yellow(`${remote.name} did not answer within ${formatDuration(RESOLVE_TIMEOUT_MS)}; building instead`));
|
|
2099
|
-
} else if (hit?.failed) {
|
|
2100
|
-
const authNote = remote.name === "eas" && isEasAuthFailureText(hit.failed) ? easAuthNote({
|
|
2101
|
-
code: "logged-out",
|
|
2102
|
-
reason: hit.failed
|
|
2103
|
-
}) : null;
|
|
2104
|
-
phase("cache", chalk.yellow(authNote || `${remote.name} could not be used: ${hit.failed}; building instead`));
|
|
2105
|
-
} else phase("cache", `remote miss (${remote.name}) ${remoteTimer()}`);
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
if (!easBuild) await resolveRemoteArtifact();
|
|
2109
|
-
let waitedForBuild = null;
|
|
2110
|
-
let releasedWait = null;
|
|
2111
|
-
async function waitForSharedBuild() {
|
|
2112
|
-
if (!useBuildCache) return true;
|
|
2113
|
-
const waitStarted = now();
|
|
2114
|
-
let failedHolder;
|
|
2115
|
-
while (!apkPath) {
|
|
2116
|
-
let attempt = null;
|
|
2117
|
-
try {
|
|
2118
|
-
attempt = acquireLock({
|
|
2119
|
-
platform: PLATFORM,
|
|
2120
|
-
key: cacheKey,
|
|
2121
|
-
root,
|
|
2122
|
-
logFile: buildLog
|
|
2123
|
-
});
|
|
2124
|
-
} catch (err) {
|
|
2125
|
-
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
2126
|
-
if (refusal) {
|
|
2127
|
-
phaseFailure = fail(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
2128
|
-
return false;
|
|
2129
|
-
}
|
|
2130
|
-
phase("build", chalk.yellow(`could not take the build lock: ${err?.message || err}; building anyway`));
|
|
2131
|
-
}
|
|
2132
|
-
if (attempt?.acquired) {
|
|
2133
|
-
buildLock = attempt;
|
|
2134
|
-
const previous = attempt.tookOver ?? failedHolder;
|
|
2135
|
-
if (previous) phase("build", chalk.yellow(takeoverLine(previous)));
|
|
2136
|
-
break;
|
|
2137
|
-
} else if (attempt?.held) {
|
|
2138
|
-
releasedWait = null;
|
|
2139
|
-
const holder = attempt.held;
|
|
2140
|
-
const who = holder.projectRoot || "another workspace";
|
|
2141
|
-
phase("build", `${who} is already building ${shortHash(hash)} (pid ${holder.pid})${holder.logFile ? ` -- tail ${holder.logFile}` : ""} -- stim guide lifecycle concurrency`);
|
|
2142
|
-
let waited = null;
|
|
2143
|
-
try {
|
|
2144
|
-
const ceilingMs = WAIT_CEILING_MS - (now() - waitStarted);
|
|
2145
|
-
if (ceilingMs <= 0) throw Object.assign(/* @__PURE__ */ new Error(`Waited ${formatDuration(now() - waitStarted)} for shared builds without an artifact; ${who} (pid ${holder.pid}) holds ${attempt.path}.`), {
|
|
2146
|
-
code: "STIM_BUILD_WAIT_TIMEOUT",
|
|
2147
|
-
lockPath: attempt.path
|
|
2148
|
-
});
|
|
2149
|
-
waited = await waitForBuild({
|
|
2150
|
-
platform: PLATFORM,
|
|
2151
|
-
key: cacheKey,
|
|
2152
|
-
out,
|
|
2153
|
-
ceilingMs
|
|
2154
|
-
});
|
|
2155
|
-
} catch (err) {
|
|
2156
|
-
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
2157
|
-
if (refusal) {
|
|
2158
|
-
phaseFailure = fail(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
2159
|
-
return false;
|
|
2160
|
-
}
|
|
2161
|
-
const wtErr = err;
|
|
2162
|
-
if (wtErr?.code !== "STIM_BUILD_WAIT_TIMEOUT") throw err;
|
|
2163
|
-
phaseFailure = fail("STIM_BUILD_WAIT_TIMEOUT", wtErr.message, `Check pid ${holder.pid}; if it is not really building, remove ${wtErr.lockPath} and run \`stim android\` again.`, { lastBuildStatus: true });
|
|
2164
|
-
return false;
|
|
2165
|
-
}
|
|
2166
|
-
if (waited?.hit) {
|
|
2167
|
-
apkPath = waited.hit ?? null;
|
|
2168
|
-
record.cacheHit = "local";
|
|
2169
|
-
waitedForBuild = {
|
|
2170
|
-
pid: holder.pid,
|
|
2171
|
-
ms: waited.waitedMs
|
|
2172
|
-
};
|
|
2173
|
-
phase("build", `waited ${formatDuration(waited.waitedMs)} for ${who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
2174
|
-
} else if (waited?.lockReleased) {
|
|
2175
|
-
failedHolder = void 0;
|
|
2176
|
-
releasedWait = {
|
|
2177
|
-
facts: {
|
|
2178
|
-
pid: holder.pid,
|
|
2179
|
-
ms: waited.waitedMs
|
|
2180
|
-
},
|
|
2181
|
-
who
|
|
2182
|
-
};
|
|
2183
|
-
phase("build", `${who}'s build lock was released; rechecking this workspace before building`);
|
|
2184
|
-
} else {
|
|
2185
|
-
phase("build", chalk.yellow(`${who}'s build ended without an artifact (${waited?.builderFailed}); retrying the build lock`));
|
|
2186
|
-
failedHolder = holder;
|
|
2187
|
-
}
|
|
2188
|
-
} else break;
|
|
2189
|
-
}
|
|
2190
|
-
return true;
|
|
2191
|
-
}
|
|
2192
|
-
if (!easBuild && !await waitForSharedBuild()) return phaseFailure;
|
|
2193
|
-
let swapDir = null;
|
|
2194
|
-
let swapFellBack = false;
|
|
2195
|
-
const installableCachedApk = async (key, cachedPath) => {
|
|
2196
|
-
if (!release) return cachedPath;
|
|
2197
|
-
phase("swap", `regenerating this workspace's JS for the cached ${variant} APK`);
|
|
2198
|
-
const swap = await swapApk({
|
|
2199
|
-
root,
|
|
2200
|
-
isExpo,
|
|
2201
|
-
cachedApkPath: cachedPath,
|
|
2202
|
-
keystore: resolveKeystore(root, settings),
|
|
2203
|
-
logWriter: writer,
|
|
2204
|
-
storedAssets: storedAssets(PLATFORM, key)
|
|
2205
|
-
});
|
|
2206
|
-
if (swap?.ok && swap.apkPath) {
|
|
2207
|
-
if (swap.note) phase("swap", chalk.yellow(swap.note));
|
|
2208
|
-
swapDir = swap.tmpDir ?? null;
|
|
2209
|
-
phase("swap", `${swap.hermes ? "hermes bytecode" : "plain JS"} repacked (store), zipaligned and re-signed (${formatDuration(swap.durationMs)})`);
|
|
2210
|
-
return swap.apkPath;
|
|
2211
|
-
}
|
|
2212
|
-
if (swap?.assetMismatch) phase("swap", chalk.yellow(`${swap.reason} -- building fresh instead` + (swap.assetDiff ? " (an APK cannot be made to carry an asset AAPT did not package)" : "")));
|
|
2213
|
-
else phase("swap", chalk.yellow(`failed at ${swap?.step || "unknown step"}: ${swap?.reason || "unknown reason"} -- building fresh instead (a cached ${variant} APK carries its builder's JS; it is never installed after a failed swap)`));
|
|
2214
|
-
for (const line of swap?.lastLines ?? []) phase("", chalk.dim(line));
|
|
2215
|
-
swapFellBack = true;
|
|
2216
|
-
return null;
|
|
2217
|
-
};
|
|
2218
|
-
async function prepareCachedArtifact() {
|
|
2219
|
-
if (apkPath && record.cacheHit) {
|
|
2220
|
-
const prepared = await installableCachedApk(cacheKey, apkPath);
|
|
2221
|
-
apkPath = prepared;
|
|
2222
|
-
if (!prepared) {
|
|
2223
|
-
record.cacheHit = false;
|
|
2224
|
-
waitedForBuild = null;
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
async function buildArtifact() {
|
|
2229
|
-
if (!apkPath) try {
|
|
2230
|
-
if (limits.maxBuilds) try {
|
|
2231
|
-
buildSlot = await acquireSlot({
|
|
2232
|
-
max: limits.maxBuilds,
|
|
2233
|
-
root,
|
|
2234
|
-
logFile: buildLog,
|
|
2235
|
-
out
|
|
2236
|
-
});
|
|
2237
|
-
} catch (err) {
|
|
2238
|
-
const refusal = (0, ownership_claim_exports.claimFailure)(err, "stim android");
|
|
2239
|
-
if (refusal) {
|
|
2240
|
-
phaseFailure = fail(refusal.code, refusal.message, refusal.remedy, { lastBuildStatus: true });
|
|
2241
|
-
return false;
|
|
2242
|
-
}
|
|
2243
|
-
phase("build", chalk.yellow(`could not take a build slot: ${err?.message || err}; building anyway`));
|
|
2244
|
-
}
|
|
2245
|
-
if (needsPrebuildFor(root, "android", isExpo)) {
|
|
2246
|
-
const pre = await prebuild(root, PLATFORM, writer, { isExpo });
|
|
2247
|
-
if (pre.failed) {
|
|
2248
|
-
phaseFailure = fail(pre.code, pre.reason, pre.remedy, {
|
|
2249
|
-
lastBuildStatus: true,
|
|
2250
|
-
lines: tail(pre.lastLines),
|
|
2251
|
-
logPath: displayPath(root, buildLog)
|
|
2252
|
-
});
|
|
2253
|
-
return false;
|
|
2254
|
-
}
|
|
2255
|
-
phase("prebuild", `android/ generated (${formatDuration(pre.durationMs)})`);
|
|
2256
|
-
androidPackage = androidPackage || detectAndroidPackage(root);
|
|
2257
|
-
record.bundleId = androidPackage;
|
|
2258
|
-
const after = await refingerprintAfterMutation({
|
|
2259
|
-
projectRoot: root,
|
|
2260
|
-
platform: PLATFORM,
|
|
2261
|
-
previousHash: hash,
|
|
2262
|
-
fingerprint
|
|
2263
|
-
});
|
|
2264
|
-
if (after?.moved) {
|
|
2265
|
-
storeHash = after.hash;
|
|
2266
|
-
storeSources = after.sources;
|
|
2267
|
-
storeKey = buildCacheKey(PLATFORM, after.hash, buildRunOptions);
|
|
2268
|
-
record.fingerprint = storeHash;
|
|
2269
|
-
record.cacheKey = storeKey;
|
|
2270
|
-
phase("fingerprint", chalk.dim(`${shortHash(hash)} -> ${shortHash(storeHash)} (after prebuild)`));
|
|
2271
|
-
const late = useBuildCache ? resolveCached(PLATFORM, storeKey) : null;
|
|
2272
|
-
if (late) {
|
|
2273
|
-
const prepared = await installableCachedApk(storeKey, late);
|
|
2274
|
-
if (prepared) {
|
|
2275
|
-
apkPath = prepared;
|
|
2276
|
-
record.cacheHit = "local";
|
|
2277
|
-
phase("cache", `hit ${shortHash(storeHash)} (post-prebuild key)`);
|
|
2278
|
-
if (releasedWait) {
|
|
2279
|
-
waitedForBuild = releasedWait.facts;
|
|
2280
|
-
phase("build", `waited ${formatDuration(waitedForBuild.ms)} for ${releasedWait.who}'s build -> installed from cache -- stim guide lifecycle concurrency`);
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
if (!apkPath) {
|
|
2287
|
-
phase("build", `compiling ${variant || "debug"} with Gradle`);
|
|
2288
|
-
const built = await build({
|
|
2289
|
-
root,
|
|
2290
|
-
logWriter: writer,
|
|
2291
|
-
variant,
|
|
2292
|
-
abi: buildAbi
|
|
2293
|
-
}, {
|
|
2294
|
-
estimateMs: estimates().coldBuildMs,
|
|
2295
|
-
ccache: buildPlan.compilerCache === "ccache" ? ccacheFor({
|
|
2296
|
-
root,
|
|
2297
|
-
onNote: out
|
|
2298
|
-
}) : null,
|
|
2299
|
-
cas,
|
|
2300
|
-
buildCache: buildPlan.gradleBuildCache,
|
|
2301
|
-
pch: buildPlan.pch,
|
|
2302
|
-
compilerCacheDisabled: buildPlan.compilerCache === "none"
|
|
2303
|
-
});
|
|
2304
|
-
ccacheActivity = built.ccache ?? CCACHE_UNAVAILABLE;
|
|
2305
|
-
phase("cache", `compilation cache ${ccacheActivityLine(ccacheActivity)}`);
|
|
2306
|
-
if (!built.ok) {
|
|
2307
|
-
const diagnostics = built.diagnostics;
|
|
2308
|
-
for (const diag of diagnostics) writer.write({
|
|
2309
|
-
src: "build",
|
|
2310
|
-
level: "error",
|
|
2311
|
-
event: "gradle_diagnostic",
|
|
2312
|
-
msg: formatDiagnostic(diag)
|
|
2313
|
-
});
|
|
2314
|
-
phase("build", chalk.red(`FAILED after ${formatDuration(built.durationMs)}`));
|
|
2315
|
-
const extracted = diagnostics.map(formatDiagnostic);
|
|
2316
|
-
if (built.truncated > 0) extracted.push(`... and ${built.truncated} more diagnostic(s) in the log`);
|
|
2317
|
-
phaseFailure = fail(built.code, built.reason, diagnostics.find((d) => d.remedy)?.remedy || built.remedy || null, {
|
|
2318
|
-
lastBuildStatus: true,
|
|
2319
|
-
diagnostics: extracted,
|
|
2320
|
-
lines: extracted.length ? [] : tail(built.lastLines),
|
|
2321
|
-
logPath: displayPath(root, buildLog)
|
|
2322
|
-
});
|
|
2323
|
-
return false;
|
|
2324
|
-
}
|
|
2325
|
-
apkPath = built.apkPath;
|
|
2326
|
-
stats.setBuildMs(built.durationMs);
|
|
2327
|
-
phase("build", `ok (${formatDuration(built.durationMs)})`);
|
|
2328
|
-
if (built.apkNote) phase("build", chalk.yellow(built.apkNote));
|
|
2329
|
-
const beforeBuildHash = storeHash;
|
|
2330
|
-
const afterBuild = await refingerprintAfterMutation({
|
|
2331
|
-
projectRoot: root,
|
|
2332
|
-
platform: PLATFORM,
|
|
2333
|
-
previousHash: beforeBuildHash,
|
|
2334
|
-
fingerprint
|
|
2335
|
-
});
|
|
2336
|
-
if (!afterBuild) {
|
|
2337
|
-
record.fingerprint = null;
|
|
2338
|
-
record.cacheKey = null;
|
|
2339
|
-
phase("fingerprint", chalk.yellow("unavailable after Gradle; the build will be installed but not cached"));
|
|
2340
|
-
} else {
|
|
2341
|
-
if (afterBuild.moved) {
|
|
2342
|
-
storeHash = afterBuild.hash;
|
|
2343
|
-
storeSources = afterBuild.sources;
|
|
2344
|
-
storeKey = buildCacheKey(PLATFORM, afterBuild.hash, buildRunOptions);
|
|
2345
|
-
record.fingerprint = storeHash;
|
|
2346
|
-
record.cacheKey = storeKey;
|
|
2347
|
-
phase("fingerprint", chalk.dim(`${shortHash(beforeBuildHash)} -> ${shortHash(storeHash)} (after Gradle)`));
|
|
2348
|
-
}
|
|
2349
|
-
if (cachePolicy.write) {
|
|
2350
|
-
const assetManifest = release ? captureAssets(root, { variant }) : null;
|
|
2351
|
-
try {
|
|
2352
|
-
const stored = await storeTieredBuild({
|
|
2353
|
-
local: filesystemBuildCapability({
|
|
2354
|
-
resolve: resolveCached,
|
|
2355
|
-
store: storeCached,
|
|
2356
|
-
sources: storeSources,
|
|
2357
|
-
assetManifest
|
|
2358
|
-
}),
|
|
2359
|
-
loadProvider: loadTieredProvider,
|
|
2360
|
-
target: {
|
|
2361
|
-
projectRoot: root,
|
|
2362
|
-
platform: PLATFORM,
|
|
2363
|
-
key: storeKey
|
|
2364
|
-
},
|
|
2365
|
-
sourcePath: apkPath,
|
|
2366
|
-
overwrite: !useBuildCache || swapFellBack,
|
|
2367
|
-
warn: cacheWarn
|
|
2368
|
-
});
|
|
2369
|
-
providerUpload = stored.providerUpload;
|
|
2370
|
-
providerName = stored.providerName ?? providerName;
|
|
2371
|
-
} catch (err) {
|
|
2372
|
-
phase("cache", chalk.yellow(`could not store the build: ${err?.message || err}`));
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
if (remote) uploadPending = uploadRemoteBuild({
|
|
2376
|
-
logWriter: writer,
|
|
2377
|
-
provider: remote.provider,
|
|
2378
|
-
platform: PLATFORM,
|
|
2379
|
-
projectRoot: root,
|
|
2380
|
-
fingerprintHash: storeHash,
|
|
2381
|
-
buildPath: apkPath,
|
|
2382
|
-
runOptions: remoteRunOptions
|
|
2383
|
-
});
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
} finally {
|
|
2387
|
-
releaseHeldLock();
|
|
2388
|
-
releaseHeldSlot();
|
|
2441
|
+
const acquiredArtifact = await acquireAndroidArtifact({
|
|
2442
|
+
root,
|
|
2443
|
+
buildLog,
|
|
2444
|
+
writer,
|
|
2445
|
+
settings,
|
|
2446
|
+
isExpo,
|
|
2447
|
+
device,
|
|
2448
|
+
physical,
|
|
2449
|
+
buildPlan,
|
|
2450
|
+
cacheProviderConfig,
|
|
2451
|
+
requestedBuildCache,
|
|
2452
|
+
easBuild,
|
|
2453
|
+
androidPackage,
|
|
2454
|
+
record,
|
|
2455
|
+
maxBuilds: limits.maxBuilds,
|
|
2456
|
+
progress: {
|
|
2457
|
+
phase,
|
|
2458
|
+
out,
|
|
2459
|
+
estimates,
|
|
2460
|
+
stats
|
|
2389
2461
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2462
|
+
}, {
|
|
2463
|
+
deviceAbi,
|
|
2464
|
+
fingerprint,
|
|
2465
|
+
untracked,
|
|
2466
|
+
resolveCached,
|
|
2467
|
+
storeCached,
|
|
2468
|
+
storedAssets,
|
|
2469
|
+
captureAssets,
|
|
2470
|
+
acquireLock,
|
|
2471
|
+
releaseLock,
|
|
2472
|
+
waitForBuild,
|
|
2473
|
+
loadProvider,
|
|
2474
|
+
easAuth,
|
|
2475
|
+
resolveRemoteBuild,
|
|
2476
|
+
uploadRemoteBuild,
|
|
2477
|
+
loadCacheProviderModule,
|
|
2478
|
+
acquireSlot,
|
|
2479
|
+
releaseSlot,
|
|
2480
|
+
needsPrebuildFor,
|
|
2481
|
+
prebuild,
|
|
2482
|
+
build,
|
|
2483
|
+
ccacheFor,
|
|
2484
|
+
swapApk,
|
|
2485
|
+
readState,
|
|
2486
|
+
now
|
|
2487
|
+
});
|
|
2488
|
+
if (!acquiredArtifact.ok) {
|
|
2489
|
+
ccacheActivity = acquiredArtifact.ccache;
|
|
2490
|
+
const { failure } = acquiredArtifact;
|
|
2491
|
+
return fail(failure.code, failure.message, failure.remedy, failure.extra);
|
|
2395
2492
|
}
|
|
2493
|
+
const { artifact } = acquiredArtifact;
|
|
2494
|
+
const { apkPath } = artifact;
|
|
2495
|
+
ccacheActivity = artifact.ccache;
|
|
2496
|
+
androidPackage = artifact.androidPackage;
|
|
2396
2497
|
record.appPath = apkPath;
|
|
2397
2498
|
let leaseHandle = null;
|
|
2398
2499
|
let stopLeaseSignals = null;
|
|
@@ -2459,15 +2560,15 @@ async function runAndroid(options = {}) {
|
|
|
2459
2560
|
bootDuration: () => bootDuration,
|
|
2460
2561
|
apkPath,
|
|
2461
2562
|
androidPackage,
|
|
2462
|
-
swapDir,
|
|
2563
|
+
swapDir: artifact.swapDir,
|
|
2463
2564
|
record,
|
|
2464
|
-
waitedForBuild,
|
|
2565
|
+
waitedForBuild: artifact.waitedForBuild,
|
|
2465
2566
|
ccache: ccacheActivity,
|
|
2466
|
-
uploadPending,
|
|
2467
|
-
providerUpload,
|
|
2468
|
-
providerName,
|
|
2469
|
-
remote,
|
|
2470
|
-
abandonedRemote,
|
|
2567
|
+
uploadPending: artifact.uploadPending,
|
|
2568
|
+
providerUpload: artifact.providerUpload,
|
|
2569
|
+
providerName: artifact.providerName,
|
|
2570
|
+
remote: artifact.remote,
|
|
2571
|
+
abandonedRemote: artifact.abandonedRemote,
|
|
2471
2572
|
started,
|
|
2472
2573
|
startedAt,
|
|
2473
2574
|
writer,
|
|
@@ -2505,8 +2606,5 @@ async function runAndroid(options = {}) {
|
|
|
2505
2606
|
throw error;
|
|
2506
2607
|
}
|
|
2507
2608
|
}
|
|
2508
|
-
function tail(lines, n = FALLBACK_LINES) {
|
|
2509
|
-
return (Array.isArray(lines) ? lines : []).slice(-n);
|
|
2510
|
-
}
|
|
2511
2609
|
//#endregion
|
|
2512
2610
|
export { NO_DEVICE, NO_FINGERPRINT, NO_METRO, androidDevClientScheme, androidFacts, androidSystemImageSetting, androidVariantSetting, apkDevClientFacts, apkPackage, collectorLogFile, androidCommand as default, displayPath, dumpApkManifest, findAapt, formatDuration, isReleaseVariant, killPreviousCollector, lastBuildRecord, noDeviceDiagnostic, parseXmltree, phaseLine, registerAndroid, resolveSystemImage, resolveVariant, runAndroid, shortHash, startCollector };
|