UNPKG

appium-xcode

Version:
281 lines 12.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getMaxTVOSSDK = exports.getMaxIOSSDK = exports.getPath = void 0; exports.getVersion = getVersion; exports.getMaxIOSSDKWithoutRetry = getMaxIOSSDKWithoutRetry; exports.getMaxTVOSSDKWithoutRetry = getMaxTVOSSDKWithoutRetry; exports.getClangVersion = getClangVersion; exports.getPathFromDeveloperDir = getPathFromDeveloperDir; exports.getPathFromXcodeSelect = getPathFromXcodeSelect; const support_1 = require("@appium/support"); const path_1 = __importDefault(require("path")); const asyncbox_1 = require("asyncbox"); const lodash_1 = __importDefault(require("lodash")); const teen_process_1 = require("teen_process"); const semver = __importStar(require("semver")); const helpers_1 = require("./helpers"); const DEFAULT_NUMBER_OF_RETRIES = 2; const XCODE_BUNDLE_ID = 'com.apple.dt.Xcode'; const log = support_1.logger.getLogger('Xcode'); /** * Retrieves the full path to Xcode Developer subfolder via xcode-select * * @param {number} timeout The maximum timeout for xcode-select execution * @returns {Promise<string>} Full path to Xcode Developer subfolder * @throws {Error} If it is not possible to retrieve a proper path */ async function getPathFromXcodeSelect(timeout = helpers_1.XCRUN_TIMEOUT) { /** * @param {string} prefix * @returns {Promise<string>} */ const generateErrorMessage = async (prefix) => { const xcodePaths = await (0, helpers_1.findAppPaths)(XCODE_BUNDLE_ID); if (lodash_1.default.isEmpty(xcodePaths)) { return `${prefix}. Consider installing Xcode to address this issue.`; } const proposals = xcodePaths.map((p) => ` sudo xcode-select -s "${path_1.default.join(p, 'Contents', 'Developer')}"`); return `${prefix}. ` + `Consider running${proposals.length > 1 ? ' any of' : ''}:\n${proposals.join('\n')}\nto address this issue.`; }; let stdout; try { ({ stdout } = await (0, teen_process_1.exec)('xcode-select', ['--print-path'], { timeout })); } catch (e) { const msg = `Cannot determine the path to Xcode by running 'xcode-select -p' command. ` + `Original error: ${e.stderr || e.message}`; log.error(msg); throw new Error(msg); } // trim and remove trailing slash const developerRoot = String(stdout).replace(/\/$/, '').trim(); if (!developerRoot) { const msg = await generateErrorMessage(`'xcode-select -p' returned an empty string`); log.error(msg); throw new Error(msg); } // xcode-select might also return a path to command line tools const { CFBundleIdentifier } = await (0, helpers_1.readXcodePlist)(developerRoot); if (CFBundleIdentifier === XCODE_BUNDLE_ID) { return developerRoot; } const msg = await generateErrorMessage(`'${developerRoot}' is not a valid Xcode path`); log.error(msg); throw msg; } /** * Retrieves the full path to Xcode Developer subfolder via `DEVELOPER_DIR` environment variable * * @returns {Promise<string>} Full path to Xcode Developer subfolder * @throws {Error} If it is not possible to retrieve a proper path * @privateRemarks This method assumes `DEVELOPER_DIR` is defined. */ async function getPathFromDeveloperDir() { const developerRoot = /** @type {string} */ (process.env.DEVELOPER_DIR); const { CFBundleIdentifier } = await (0, helpers_1.readXcodePlist)(developerRoot); if (CFBundleIdentifier === XCODE_BUNDLE_ID) { return developerRoot; } const msg = `The path to Xcode Developer dir '${developerRoot}' provided in DEVELOPER_DIR ` + `environment variable is not a valid path`; log.error(msg); throw new Error(msg); } /** * Retrieves the full path to Xcode Developer subfolder. * If `DEVELOPER_DIR` environment variable is provided then its value has a priority. * @param {number} timeout The maximum timeout for xcode-select execution * @returns {Promise<string>} Full path to Xcode Developer subfolder timeout * @throws {Error} If there was an error while retrieving the path. */ const getPath = lodash_1.default.memoize( /** * @param {number} timeout * @returns {Promise<string>} */ (timeout = helpers_1.XCRUN_TIMEOUT) => process.env.DEVELOPER_DIR ? getPathFromDeveloperDir() : getPathFromXcodeSelect(timeout)); exports.getPath = getPath; /** * Retrieves Xcode version * * @param {number} timeout [15000] Timeout of milliseconds to wait for terminal commands. * @returns {Promise<import("semver").SemVer | null>} Xcode version * @throws {Error} If there was a failure while retrieving the version */ async function getVersionWithoutRetry(timeout = helpers_1.XCRUN_TIMEOUT) { const developerPath = await getPath(timeout); // we want to read the CFBundleShortVersionString from Xcode's plist. const { CFBundleShortVersionString } = await (0, helpers_1.readXcodePlist)(developerPath); return semver.coerce(CFBundleShortVersionString); } /** * Retrieves Xcode version or the cached one if called more than once * * @param {number} retries How many retries to apply for version retrieval * @param {number} timeout Timeout of milliseconds to wait for terminal commands * @returns {Promise<import("semver").SemVer | null>} Xcode version * @throws {Error} If there was a failure while retrieving the version */ const getVersionMemoized = lodash_1.default.memoize(function getVersionMemoized(retries = DEFAULT_NUMBER_OF_RETRIES, timeout = helpers_1.XCRUN_TIMEOUT) { return (0, asyncbox_1.retry)(retries, getVersionWithoutRetry, timeout); }); /** * @typedef {Object} XcodeVersion * @property {string} versionString Xcode version as a string * @property {number} versionFloat Xcode version as a float number * @property {number} major Major number of Xcode version * @property {number} minor Minor number of Xcode version * @property {number} [patch] Patch number of Xcode version (if exists) * @property {() => string} toString Returns Xcode version as a string */ /** * Retrieves Xcode version * * @param {boolean} parse [false] Whether to parse the version to a XcodeVersion version * @param {number} retries [2] How many retries to apply for getting the version number * @param {number} timeout [15000] Timeout of milliseconds to wait for terminal commands * @returns {Promise<XcodeVersion | string>} Xcode version depending on the value of `parse` flag * @throws {Error} If there was a failure while retrieving the version */ async function getVersion(parse = false, retries = DEFAULT_NUMBER_OF_RETRIES, timeout = helpers_1.XCRUN_TIMEOUT) { const version = /** @type {import('semver').SemVer} */ (await getVersionMemoized(retries, timeout)); // xcode version strings are not exactly semver string: patch versions of 0 // are removed (e.g., '10.0.0' => '10.0') const versionString = version.patch > 0 ? version.version : `${version.major}.${version.minor}`; if (!parse) { return versionString; } return { versionString, versionFloat: parseFloat(versionString), major: version.major, minor: version.minor, patch: version.patch > 0 ? version.patch : undefined, toString() { return versionString; }, }; } /** * Check https://trac.macports.org/wiki/XcodeVersionInfo * to see the actual mapping between clang and other components. * * @returns {Promise<string|null>} The actual Clang version in x.x.x.x or x.x.x format, * which is supplied with Command Line Tools. `null` is returned * if CLT are not installed. */ async function getClangVersion() { try { await support_1.fs.which('clang'); } catch { log.info('Cannot find clang executable on the local system. ' + 'Are Xcode Command Line Tools installed?'); return null; } const { stdout } = await (0, teen_process_1.exec)('clang', ['--version']); const match = /clang-([0-9.]+)/.exec(stdout); if (!match) { log.info(`Cannot parse clang version from ${stdout}`); return null; } return match[1]; } /** * Retrieves the maximum version of iOS SDK supported by the installed Xcode * * @param {number} timeout [15000] Timeout of milliseconds to wait for terminal commands * @returns {Promise<string>} The SDK version * @throws {Error} If the SDK version number cannot be determined */ async function getMaxIOSSDKWithoutRetry(timeout = helpers_1.XCRUN_TIMEOUT) { const args = ['--sdk', 'iphonesimulator', '--show-sdk-version']; const { stdout } = await (0, helpers_1.runXcrunCommand)(args, timeout); const sdkVersion = stdout.trim(); const match = /\d.\d/.exec(stdout); if (!match) { throw new Error(`xcrun returned a non-numeric iOS SDK version: '${sdkVersion}'`); } return sdkVersion; } /** * Retrieves the maximum version of iOS SDK supported by the installed Xcode * * @param {number} timeout Timeout of milliseconds to wait for terminal commands * @param {number} retries The maximum number of retries * @returns {string} The SDK version * @throws {Error} If the SDK version number cannot be determined */ const getMaxIOSSDK = lodash_1.default.memoize(function getMaxIOSSDK(retries = DEFAULT_NUMBER_OF_RETRIES, timeout = helpers_1.XCRUN_TIMEOUT) { return (0, asyncbox_1.retry)(retries, getMaxIOSSDKWithoutRetry, timeout); }); exports.getMaxIOSSDK = getMaxIOSSDK; /** * Retrieves the maximum version of tvOS SDK supported by the installed Xcode * * @param {number} timeout Timeout of milliseconds to wait for terminal commands * @returns {Promise<string>} The SDK version * @throws {Error} If the SDK version number cannot be determined */ async function getMaxTVOSSDKWithoutRetry(timeout = helpers_1.XCRUN_TIMEOUT) { const args = ['--sdk', 'appletvsimulator', '--show-sdk-version']; const { stdout } = await (0, helpers_1.runXcrunCommand)(args, timeout); const sdkVersion = stdout.trim(); if (isNaN(parseFloat(sdkVersion))) { throw new Error(`xcrun returned a non-numeric tvOS SDK version: '${sdkVersion}'`); } return sdkVersion; } /** * Retrieves the maximum version of tvOS SDK supported by the installed Xcode * * @throws {Error} If the SDK version number cannot be determined */ const getMaxTVOSSDK = lodash_1.default.memoize( /** * @param {number} timeout Timeout of milliseconds to wait for terminal commands * @param {number} retries The maximum number of retries * @returns {Promise<string>} The SDK version */ async function getMaxTVOSSDK(retries = DEFAULT_NUMBER_OF_RETRIES, timeout = helpers_1.XCRUN_TIMEOUT) { return /** @type {string} */ (await (0, asyncbox_1.retry)(retries, getMaxTVOSSDKWithoutRetry, timeout)); }); exports.getMaxTVOSSDK = getMaxTVOSSDK; //# sourceMappingURL=xcode.js.map