diff --git a/lib/app-utils.js b/lib/app-utils.js index f4f42de3f..4ba7f1be5 100644 --- a/lib/app-utils.js +++ b/lib/app-utils.js @@ -27,6 +27,7 @@ const MAX_ARCHIVE_SCAN_DEPTH = 1; export const SUPPORTED_EXTENSIONS = [IPA_EXT, APP_EXT]; const MACOS_RESOURCE_FOLDER = '__MACOSX'; const SANITIZE_REPLACEMENT = '-'; +const INTEL_ARCH = 'x86_64'; /** * Verify whether the given application is compatible to the @@ -56,27 +57,47 @@ export async function verifyApplicationPlatform() { return; } - const executablePath = path.resolve(this.opts.app, await this.appInfosCache.extractExecutableName(this.opts.app)); + const executablePath = path.resolve( + this.opts.app, + await this.appInfosCache.extractExecutableName(this.opts.app) + ); const [resFile, resUname] = await B.all([ exec('lipo', ['-info', executablePath]), exec('uname', ['-m']), ]); const bundleExecutableInfo = _.trim(resFile.stdout); this.log.debug(bundleExecutableInfo); - const arch = _.trim(resUname.stdout); - const isAppleSilicon = os.cpus()[0].model.includes('Apple'); + const processArch = _.trim(resUname.stdout); + this.log.debug(`Current process architecture: ${processArch}`); + const isAppleSiliconCpu = isAppleSilicon(); + this.log.debug(`Is Apple Silicon CPU: ${isAppleSiliconCpu}`); + if (isAppleSiliconCpu && processArch === INTEL_ARCH) { + this.log.warn( + `It looks like the Appium server process is running under Rosetta emulation. ` + + `This might lead to various performance/compatibility issues while running tests on Simulator. ` + + `Consider using binaries compiled natively for the ARM64 architecture to run Appium server ` + + `with this driver.` + ); + } + if (_.includes(bundleExecutableInfo, processArch)) { + return; + } + const hasRosetta = isAppleSiliconCpu && await isRosettaInstalled(); + const isIntelApp = _.includes(bundleExecutableInfo, INTEL_ARCH); // We cannot run Simulator builds compiled for arm64 on Intel machines // Rosetta allows only to run Intel ones on arm64 if ( - !_.includes(bundleExecutableInfo, arch) && - !(isAppleSilicon && _.includes(bundleExecutableInfo, 'x86_64')) + (isIntelApp && (!isAppleSiliconCpu || hasRosetta)) || (!isIntelApp && isAppleSiliconCpu) ) { - throw new Error( - `The ${this.opts.bundleId} application does not support the ${arch} Simulator ` + - `architecture:\n${bundleExecutableInfo}\n\n` + - `Please rebuild your application to support the ${arch} platform.`, - ); + return; } + const advice = isIntelApp && isAppleSiliconCpu && !hasRosetta + ? `Please install Rosetta and try again.` + : `Please rebuild your application to support the ${processArch} platform.`; + throw new Error( + `The ${this.opts.bundleId} application does not support the ${processArch} Simulator ` + + `architecture:\n${bundleExecutableInfo}\n\n${advice}` + ); } /** @@ -666,6 +687,20 @@ export async function onPostConfigureApp({cachedAppInfo, isUrl, appPath}) { }; } +/** + * @returns {Promise} + */ +async function isRosettaInstalled() { + return await fs.exists('/Library/Apple/usr/share/rosetta/rosetta'); +} + +/** + * @returns {boolean} + */ +function isAppleSilicon() { + return os.cpus()[0].model.includes('Apple'); +} + /** * @typedef {import('./driver').XCUITestDriver} XCUITestDriver */