diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index 8c3102669e..e519dab0ec 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -1,6 +1,7 @@ import * as path from "path"; import * as semver from "semver"; import { EOL } from "os"; +import { cache } from "./common/decorators"; export class AndroidToolsInfo implements IAndroidToolsInfo { private static ANDROID_TARGET_PREFIX = "android"; @@ -13,19 +14,8 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { private showWarningsAsErrors: boolean; private toolsInfo: IAndroidToolsInfoData; private selectedCompileSdk: number; - private installedTargetsCache: string[] = null; - private androidHome = process.env["ANDROID_HOME"]; - private pathToAndroidExecutable: string; - private _androidExecutableName: string; - private get androidExecutableName(): string { - if (!this._androidExecutableName) { - this._androidExecutableName = "android"; - if (this.$hostInfo.isWindows) { - this._androidExecutableName += ".bat"; - } - } - - return this._androidExecutableName; + private get androidHome(): string { + return process.env["ANDROID_HOME"]; } constructor(private $childProcess: IChildProcess, @@ -34,54 +24,17 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { private $hostInfo: IHostInfo, private $logger: ILogger, private $options: IOptions, - private $adb: Mobile.IAndroidDebugBridge, protected $staticConfig: Config.IStaticConfig) { } - public async getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): Promise { - if (options) { - this.showWarningsAsErrors = options.showWarningsAsErrors; - } - if (!this.pathToAndroidExecutable) { - if (this.validateAndroidHomeEnvVariable(this.androidHome)) { - let androidPath = path.join(this.androidHome, "tools", this.androidExecutableName); - if (!await this.trySetAndroidPath(androidPath) && !await this.trySetAndroidPath(this.androidExecutableName)) { - this.printMessage(`Unable to find "${this.androidExecutableName}" executable file. Make sure you have set ANDROID_HOME environment variable correctly.`); - } - } else { - this.$logger.trace("ANDROID_HOME environment variable is not set correctly."); - } - } - - return this.pathToAndroidExecutable; - } - - private async trySetAndroidPath(androidPath: string): Promise { - let isAndroidPathCorrect = true; - try { - let result = await this.$adb.executeCommand(["--help"], { returnChildProcess: true }); - if (result && result.stdout) { - this.$logger.trace(result.stdout); - this.pathToAndroidExecutable = androidPath; - } else { - this.$logger.trace(`Unable to find android executable from '${androidPath}'.`); - isAndroidPathCorrect = false; - } - } catch (err) { - this.$logger.trace(`Error occurred while checking androidExecutable from '${androidPath}'. ${err.message}`); - isAndroidPathCorrect = false; - } - - return isAndroidPathCorrect; - } - - public async getToolsInfo(): Promise { + @cache() + public getToolsInfo(): IAndroidToolsInfoData { if (!this.toolsInfo) { let infoData: IAndroidToolsInfoData = Object.create(null); infoData.androidHomeEnvVar = this.androidHome; - infoData.compileSdkVersion = await this.getCompileSdk(); - infoData.buildToolsVersion = await this.getBuildToolsVersion(); - infoData.targetSdkVersion = await this.getTargetSdk(); - infoData.supportRepositoryVersion = await this.getAndroidSupportRepositoryVersion(); + infoData.compileSdkVersion = this.getCompileSdkVersion(); + infoData.buildToolsVersion = this.getBuildToolsVersion(); + infoData.targetSdkVersion = this.getTargetSdk(); + infoData.supportRepositoryVersion = this.getAndroidSupportRepositoryVersion(); infoData.generateTypings = this.shouldGenerateTypings(); this.toolsInfo = infoData; @@ -90,14 +43,14 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return this.toolsInfo; } - public async validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): Promise { + public validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): boolean { let detectedErrors = false; this.showWarningsAsErrors = options && options.showWarningsAsErrors; - let toolsInfoData = await this.getToolsInfo(); - let isAndroidHomeValid = this.validateAndroidHomeEnvVariable(toolsInfoData.androidHomeEnvVar); + let toolsInfoData = this.getToolsInfo(); + let isAndroidHomeValid = this.validateAndroidHomeEnvVariable(); if (!toolsInfoData.compileSdkVersion) { this.printMessage(`Cannot find a compatible Android SDK for compilation. To be able to build for Android, install Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later.`, - "Run `$ android` to manage your Android SDK versions."); + `Run \`\$ ${this.getPathToSdkManagementTool()}\` to manage your Android SDK versions.`); detectedErrors = true; } @@ -111,7 +64,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { message = `You have to install version ${versionRangeMatches[1]}.`; } - let invalidBuildToolsAdditionalMsg = 'Run `android` from your command-line to install required `Android Build Tools`.'; + let invalidBuildToolsAdditionalMsg = `Run \`\$ ${this.getPathToSdkManagementTool()}\` from your command-line to install required \`Android Build Tools\`.`; if (!isAndroidHomeValid) { invalidBuildToolsAdditionalMsg += ' In case you already have them installed, make sure `ANDROID_HOME` environment variable is set correctly.'; } @@ -121,7 +74,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { } if (!toolsInfoData.supportRepositoryVersion) { - let invalidSupportLibAdditionalMsg = 'Run `$ android` to manage the Android Support Repository.'; + let invalidSupportLibAdditionalMsg = `Run \`\$ ${this.getPathToSdkManagementTool()}\` to manage the Android Support Repository.`; if (!isAndroidHomeValid) { invalidSupportLibAdditionalMsg += ' In case you already have it installed, make sure `ANDROID_HOME` environment variable is set correctly.'; } @@ -153,6 +106,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { if (options) { this.showWarningsAsErrors = options.showWarningsAsErrors; } + let additionalMessage = "You will not be able to build your projects for Android." + EOL + "To be able to build for Android, verify that you have installed The Java Development Kit (JDK) and configured it according to system requirements as" + EOL + " described in " + this.$staticConfig.SYS_REQUIREMENTS_LINK; @@ -186,6 +140,51 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return null; } + @cache() + public validateAndroidHomeEnvVariable(options?: { showWarningsAsErrors: boolean }): boolean { + if (options) { + this.showWarningsAsErrors = options.showWarningsAsErrors; + } + + const expectedDirectoriesInAndroidHome = ["build-tools", "tools", "platform-tools", "extras"]; + let androidHomeValidationResult = true; + + if (!this.androidHome || !this.$fs.exists(this.androidHome)) { + this.printMessage("The ANDROID_HOME environment variable is not set or it points to a non-existent directory. You will not be able to perform any build-related operations for Android.", + "To be able to perform Android build-related operations, set the `ANDROID_HOME` variable to point to the root of your Android SDK installation directory."); + androidHomeValidationResult = false; + } else if (!_.some(expectedDirectoriesInAndroidHome.map(dir => this.$fs.exists(path.join(this.androidHome, dir))))) { + this.printMessage("The ANDROID_HOME environment variable points to incorrect directory. You will not be able to perform any build-related operations for Android.", + "To be able to perform Android build-related operations, set the `ANDROID_HOME` variable to point to the root of your Android SDK installation directory, " + + "where you will find `tools` and `platform-tools` directories."); + androidHomeValidationResult = false; + } + + return androidHomeValidationResult; + } + + @cache() + private getPathToSdkManagementTool(): string { + const sdkManagerName = "sdkmanager"; + let sdkManagementToolPath = sdkManagerName; + + const isAndroidHomeValid = this.validateAndroidHomeEnvVariable(); + + if (isAndroidHomeValid) { + // In case ANDROID_HOME is correct, check if sdkmanager exists and if not it means the SDK has not been updated. + // In this case user shoud use `android` from the command-line instead of sdkmanager. + const pathToSdkManager = path.join(this.androidHome, "tools", "bin", sdkManagerName); + const pathToAndroidExecutable = path.join(this.androidHome, "tools", "android"); + const pathToExecutable = this.$fs.exists(pathToSdkManager) ? pathToSdkManager : pathToAndroidExecutable; + + this.$logger.trace(`Path to Android SDK Management tool is: ${pathToExecutable}`); + + sdkManagementToolPath = pathToExecutable.replace(this.androidHome, this.$hostInfo.isWindows ? "%ANDROID_HOME%" : "$ANDROID_HOME"); + } + + return sdkManagementToolPath; + } + private shouldGenerateTypings(): boolean { return this.$options.androidTypings; } @@ -211,11 +210,11 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { } } - private async getCompileSdk(): Promise { + private getCompileSdkVersion(): number { if (!this.selectedCompileSdk) { let userSpecifiedCompileSdk = this.$options.compileSdk; if (userSpecifiedCompileSdk) { - let installedTargets = await this.getInstalledTargets(); + let installedTargets = this.getInstalledTargets(); let androidCompileSdk = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${userSpecifiedCompileSdk}`; if (!_.includes(installedTargets, androidCompileSdk)) { this.$errors.failWithoutHelp(`You have specified '${userSpecifiedCompileSdk}' for compile sdk, but it is not installed on your system.`); @@ -223,7 +222,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { this.selectedCompileSdk = userSpecifiedCompileSdk; } else { - let latestValidAndroidTarget = await this.getLatestValidAndroidTarget(); + let latestValidAndroidTarget = this.getLatestValidAndroidTarget(); if (latestValidAndroidTarget) { let integerVersion = this.parseAndroidSdkString(latestValidAndroidTarget); @@ -237,8 +236,8 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return this.selectedCompileSdk; } - private async getTargetSdk(): Promise { - let targetSdk = this.$options.sdk ? parseInt(this.$options.sdk) : await this.getCompileSdk(); + private getTargetSdk(): number { + let targetSdk = this.$options.sdk ? parseInt(this.$options.sdk) : this.getCompileSdkVersion(); this.$logger.trace(`Selected targetSdk is: ${targetSdk}`); return targetSdk; } @@ -273,7 +272,7 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return `${AndroidToolsInfo.REQUIRED_BUILD_TOOLS_RANGE_PREFIX} <=${this.getMaxSupportedVersion()}`; } - private async getBuildToolsVersion(): Promise { + private getBuildToolsVersion(): string { let buildToolsVersion: string; if (this.androidHome) { let pathToBuildTools = path.join(this.androidHome, "build-tools"); @@ -284,8 +283,8 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return buildToolsVersion; } - private async getAppCompatRange(): Promise { - let compileSdkVersion = await this.getCompileSdk(); + private getAppCompatRange(): string { + let compileSdkVersion = this.getCompileSdkVersion(); let requiredAppCompatRange: string; if (compileSdkVersion) { requiredAppCompatRange = `>=${compileSdkVersion} <${compileSdkVersion + 1}`; @@ -294,9 +293,9 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return requiredAppCompatRange; } - private async getAndroidSupportRepositoryVersion(): Promise { + private getAndroidSupportRepositoryVersion(): string { let selectedAppCompatVersion: string; - let requiredAppCompatRange = await this.getAppCompatRange(); + let requiredAppCompatRange = this.getAppCompatRange(); if (this.androidHome && requiredAppCompatRange) { let pathToAppCompat = path.join(this.androidHome, "extras", "android", "m2repository", "com", "android", "support", "appcompat-v7"); selectedAppCompatVersion = this.getMatchingDir(pathToAppCompat, requiredAppCompatRange); @@ -306,8 +305,8 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return selectedAppCompatVersion; } - private async getLatestValidAndroidTarget(): Promise { - let installedTargets = await this.getInstalledTargets(); + private getLatestValidAndroidTarget(): string { + let installedTargets = this.getInstalledTargets(); return _.findLast(AndroidToolsInfo.SUPPORTED_TARGETS.sort(), supportedTarget => _.includes(installedTargets, supportedTarget)); } @@ -315,47 +314,21 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return parseInt(androidSdkString.replace(`${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-`, "")); } - private async getInstalledTargets(): Promise { - if (!this.installedTargetsCache) { - try { - let pathToAndroidExecutable = await this.getPathToAndroidExecutable(); - if (pathToAndroidExecutable) { - let result = await this.$childProcess.spawnFromEvent(pathToAndroidExecutable, ["list", "targets"], "close", {}, { throwError: false }); - if (result && result.stdout) { - this.$logger.trace(result.stdout); - this.installedTargetsCache = []; - result.stdout.replace(/id: \d+ or "(.+)"/g, (m: string, p1: string) => (this.installedTargetsCache.push(p1), m)); - } - } - } catch (err) { - this.$logger.trace("Unable to get Android targets. Error is: " + err); - } + @cache() + private getInstalledTargets(): string[] { + let installedTargets: string[] = []; + const pathToInstalledTargets = path.join(this.androidHome, "platforms"); + if (this.$fs.exists(pathToInstalledTargets)) { + installedTargets = this.$fs.readDirectory(pathToInstalledTargets); } - return this.installedTargetsCache; + + this.$logger.trace("Installed Android Targets are: ", installedTargets); + + return installedTargets; } private getMaxSupportedVersion(): number { return this.parseAndroidSdkString(_.last(AndroidToolsInfo.SUPPORTED_TARGETS.sort())); } - - private _cachedAndroidHomeValidationResult: boolean = null; - private validateAndroidHomeEnvVariable(androidHomeEnvVar: string): boolean { - if (this._cachedAndroidHomeValidationResult === null) { - this._cachedAndroidHomeValidationResult = true; - let expectedDirectoriesInAndroidHome = ["build-tools", "tools", "platform-tools", "extras"]; - if (!androidHomeEnvVar || !this.$fs.exists(androidHomeEnvVar)) { - this.printMessage("The ANDROID_HOME environment variable is not set or it points to a non-existent directory. You will not be able to perform any build-related operations for Android.", - "To be able to perform Android build-related operations, set the `ANDROID_HOME` variable to point to the root of your Android SDK installation directory."); - this._cachedAndroidHomeValidationResult = false; - } else if (!_.some(expectedDirectoriesInAndroidHome.map(dir => this.$fs.exists(path.join(androidHomeEnvVar, dir))))) { - this.printMessage("The ANDROID_HOME environment variable points to incorrect directory. You will not be able to perform any build-related operations for Android.", - "To be able to perform Android build-related operations, set the `ANDROID_HOME` variable to point to the root of your Android SDK installation directory, " + - "where you will find `tools` and `platform-tools` directories."); - this._cachedAndroidHomeValidationResult = false; - } - } - - return this._cachedAndroidHomeValidationResult; - } } $injector.register("androidToolsInfo", AndroidToolsInfo); diff --git a/lib/common b/lib/common index 6bcc88c5b2..340713d7c9 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 6bcc88c5b2b88438d855681fedb891b445dbafdf +Subproject commit 340713d7c92e03ed8032261691b25ad91d5d2214 diff --git a/lib/declarations.ts b/lib/declarations.ts index 78bd9d1eaa..15d7d76d90 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -220,14 +220,14 @@ interface IAndroidToolsInfo { * and ANDROID_HOME environement variable. * @return {IAndroidToolsInfoData} Information about installed Android Tools and SDKs. */ - getToolsInfo(): Promise; + getToolsInfo(): IAndroidToolsInfoData; /** * Validates the information about required Android tools and SDK versions. * @param {any} options Defines if the warning messages should treated as error and if the targetSdk value should be validated as well. * @return {boolean} True if there are detected issues, false otherwise. */ - validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): Promise; + validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): boolean; /** * Validates the information about required JAVA version. @@ -238,12 +238,11 @@ interface IAndroidToolsInfo { validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): Promise; /** - * Returns the path to `android` executable. It should be `$ANDROID_HOME/tools/android`. - * In case ANDROID_HOME is not defined, check if `android` is part of $PATH. + * Validates if ANDROID_HOME environment variable is set correctly. * @param {any} options Defines if the warning messages should treated as error. - * @return {string} Path to the `android` executable. + * @returns {boolean} true in case ANDROID_HOME is correctly set, false otherwise. */ - getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): Promise; + validateAndroidHomeEnvVariable(options?: { showWarningsAsErrors: boolean }): boolean; /** * Gets the path to `adb` executable from ANDROID_HOME. It should be `$ANDROID_HOME/platform-tools/adb` in case it exists. diff --git a/lib/definitions/emulator-platform-service.d.ts b/lib/definitions/emulator-platform-service.d.ts index bed97e0282..259bbabbb2 100644 --- a/lib/definitions/emulator-platform-service.d.ts +++ b/lib/definitions/emulator-platform-service.d.ts @@ -11,6 +11,6 @@ interface IEmulatorPlatformService { listAvailableEmulators(platform: string): Promise; getEmulatorInfo(platform: string, nameOfId: string): Promise; getiOSEmulators(): Promise; - getAndroidEmulators(): Promise; + getAndroidEmulators(): IEmulatorInfo[]; startEmulator(info: IEmulatorInfo, projectData: IProjectData): Promise; } \ No newline at end of file diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 5063f4f8d1..e8c0da0acf 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -160,7 +160,7 @@ interface IPlatformProjectService extends NodeJS.EventEmitter { validate(projectData: IProjectData): Promise; createProject(frameworkDir: string, frameworkVersion: string, projectData: IProjectData, pathToTemplate?: string): Promise; interpolateData(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise; - interpolateConfigurationFile(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise; + interpolateConfigurationFile(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): void; /** * Executes additional actions after native project is created. diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 0af64bff2d..384cd1a583 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -90,8 +90,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject this.validatePackageName(projectData.projectId); this.validateProjectName(projectData.projectName); - // this call will fail in case `android` is not set correctly. - await this.$androidToolsInfo.getPathToAndroidExecutable({ showWarningsAsErrors: true }); + this.$androidToolsInfo.validateAndroidHomeEnvVariable({ showWarningsAsErrors: true }); let javaCompilerVersion = await this.$sysInfo.getJavaCompilerVersion(); @@ -104,8 +103,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } this.$fs.ensureDirectoryExists(this.getPlatformData(projectData).projectRoot); - await this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); - let androidToolsInfo = await this.$androidToolsInfo.getToolsInfo(); + this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); + let androidToolsInfo = this.$androidToolsInfo.getToolsInfo(); let targetSdkVersion = androidToolsInfo.targetSdkVersion; this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`); this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "libs", "-R"); @@ -185,7 +184,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject public async interpolateData(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise { // Interpolate the apilevel and package - await this.interpolateConfigurationFile(projectData, platformSpecificData); + this.interpolateConfigurationFile(projectData, platformSpecificData); let stringsFilePath = path.join(this.getAppResourcesDestinationDirectoryPath(projectData), 'values', 'strings.xml'); shell.sed('-i', /__NAME__/, projectData.projectName, stringsFilePath); @@ -204,10 +203,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - public async interpolateConfigurationFile(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise { + public interpolateConfigurationFile(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): void { let manifestPath = this.getPlatformData(projectData).configurationFilePath; shell.sed('-i', /__PACKAGE__/, projectData.projectId, manifestPath); - const sdk = (platformSpecificData && platformSpecificData.sdk) || (await this.$androidToolsInfo.getToolsInfo()).compileSdkVersion.toString(); + const sdk = (platformSpecificData && platformSpecificData.sdk) || this.$androidToolsInfo.getToolsInfo().compileSdkVersion.toString(); shell.sed('-i', /__APILEVEL__/, sdk, manifestPath); } @@ -242,7 +241,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject public async buildProject(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise { if (this.canUseGradle(projectData)) { - let buildOptions = await this.getBuildOptions(buildConfig, projectData); + let buildOptions = this.getBuildOptions(buildConfig, projectData); if (this.$logger.getLevel() === "TRACE") { buildOptions.unshift("--stacktrace"); buildOptions.unshift("--debug"); @@ -266,10 +265,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - private async getBuildOptions(settings: IAndroidBuildOptionsSettings, projectData: IProjectData): Promise> { - await this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); + private getBuildOptions(settings: IAndroidBuildOptionsSettings, projectData: IProjectData): Array { + this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); - let androidToolsInfo = await this.$androidToolsInfo.getToolsInfo(); + let androidToolsInfo = this.$androidToolsInfo.getToolsInfo(); let compileSdk = androidToolsInfo.compileSdkVersion; let targetSdk = this.getTargetFromAndroidManifest(projectData) || compileSdk; let buildToolsVersion = androidToolsInfo.buildToolsVersion; @@ -401,7 +400,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } // We don't need release options here - let buildOptions = await this.getBuildOptions({ release: false }, projectData); + let buildOptions = this.getBuildOptions({ release: false }, projectData); let projectRoot = this.getPlatformData(projectData).projectRoot; diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index 9d7f36949f..794e4d6abc 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -46,7 +46,7 @@ class DoctorService implements IDoctorService { result = true; } - if (!sysInfo.androidInstalled) { + if (!sysInfo.emulatorInstalled) { this.$logger.warn("WARNING: The Android SDK is not installed or is not configured properly."); this.$logger.out("You will not be able to build your projects for Android and run them in the native emulator." + EOL + "To be able to build for Android and run apps in the native emulator, verify that you have" + EOL @@ -103,7 +103,7 @@ class DoctorService implements IDoctorService { this.$logger.out("To be able to work with iOS devices and projects, you need Mac OS X Mavericks or later." + EOL); } - let androidToolsIssues = await this.$androidToolsInfo.validateInfo(); + let androidToolsIssues = this.$androidToolsInfo.validateInfo(); let javaVersionIssue = await this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion); let doctorResult = result || androidToolsIssues || javaVersionIssue; diff --git a/lib/services/emulator-platform-service.ts b/lib/services/emulator-platform-service.ts index cda415a577..e414651d16 100644 --- a/lib/services/emulator-platform-service.ts +++ b/lib/services/emulator-platform-service.ts @@ -1,4 +1,3 @@ -import * as path from "path"; import { createTable, deferPromise } from "../common/helpers"; export class EmulatorPlatformService implements IEmulatorPlatformService { @@ -8,7 +7,8 @@ export class EmulatorPlatformService implements IEmulatorPlatformService { private $childProcess: IChildProcess, private $devicesService: Mobile.IDevicesService, private $options: IOptions, - private $logger: ILogger) { } + private $logger: ILogger, + private $androidEmulatorServices: Mobile.IAndroidEmulatorServices) { } public async startEmulator(info: IEmulatorInfo, projectData: IProjectData): Promise { if (!info.isRunning) { @@ -55,7 +55,7 @@ export class EmulatorPlatformService implements IEmulatorPlatformService { public async getEmulatorInfo(platform: string, idOrName: string): Promise { if (this.$mobileHelper.isAndroidPlatform(platform)) { - let androidEmulators = await this.getAndroidEmulators(); + let androidEmulators = this.getAndroidEmulators(); let found = androidEmulators.filter((info: IEmulatorInfo) => info.id === idOrName); if (found.length > 0) { return found[0]; @@ -108,7 +108,7 @@ export class EmulatorPlatformService implements IEmulatorPlatformService { } if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) { - let androidEmulators = await this.getAndroidEmulators(); + let androidEmulators = this.getAndroidEmulators(); if (androidEmulators) { emulators = emulators.concat(androidEmulators); } @@ -148,37 +148,12 @@ export class EmulatorPlatformService implements IEmulatorPlatformService { return emulators; } - public async getAndroidEmulators(): Promise { - let androidPath = path.join(process.env.ANDROID_HOME, "tools", "android"); - let text: string = await this.$childProcess.exec(`"${androidPath}" list avd`); - let notLoadedIndex = text.indexOf("The following"); - if (notLoadedIndex > 0) { - text = text.substring(0, notLoadedIndex); - } - - let textBlocks = text.split("---------"); - let emulators: IEmulatorInfo[] = []; - for (let block of textBlocks) { - let lines = block.split("\n"); - let info: IEmulatorInfo = { name: "", version: "", id: "", platform: "Android", type: "Emulator" }; - for (let line of lines) { - if (line.indexOf("Target") >= 0) { - info.version = line.substring(line.indexOf(":") + 1).replace("Android", "").trim(); - } - - if (line.indexOf("Name") >= 0) { - info.id = line.substring(line.indexOf(":") + 1).trim(); - } + public getAndroidEmulators(): IEmulatorInfo[] { + const androidVirtualDevices: Mobile.IAvdInfo[] = this.$androidEmulatorServices.getAvds().map(avd => this.$androidEmulatorServices.getInfoFromAvd(avd)); - if (line.indexOf("Device") >= 0) { - info.name = line.substring(line.indexOf(":") + 1).trim(); - } - - info.isRunning = false; - } - - emulators.push(info); - } + const emulators: IEmulatorInfo[] = _.map(androidVirtualDevices, avd => { + return { name: avd.device, version: avd.target, id: avd.name, platform: "Android", type: "Emulator", isRunning: false }; + }); return emulators; } diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 67f5953bf2..d0536a12cc 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -161,8 +161,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ this.replaceFileContent(pbxprojFilePath, projectData); } - public interpolateConfigurationFile(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise { - return Promise.resolve(); + public interpolateConfigurationFile(projectData: IProjectData, platformSpecificData: IPlatformSpecificData): void { + return undefined; } public afterCreateProject(projectRoot: string, projectData: IProjectData): void { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index eb5f71fd18..29c094d358 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -287,7 +287,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await platformData.platformProjectService.processConfigurationFilesFromAppResources(appFilesUpdaterOptions.release, projectData); } - await platformData.platformProjectService.interpolateConfigurationFile(projectData, platformSpecificData); + platformData.platformProjectService.interpolateConfigurationFile(projectData, platformSpecificData); this.$logger.out("Project successfully prepared (" + platform + ")"); } diff --git a/lib/sys-info.ts b/lib/sys-info.ts index d0c38cfc86..6f7fdd904a 100644 --- a/lib/sys-info.ts +++ b/lib/sys-info.ts @@ -7,14 +7,14 @@ export class SysInfo extends SysInfoBase { protected $iTunesValidator: Mobile.IiTunesValidator, protected $logger: ILogger, protected $winreg: IWinReg, + protected $androidEmulatorServices: Mobile.IAndroidEmulatorServices, private $androidToolsInfo: IAndroidToolsInfo) { - super($childProcess, $hostInfo, $iTunesValidator, $logger, $winreg); + super($childProcess, $hostInfo, $iTunesValidator, $logger, $winreg, $androidEmulatorServices); } - public async getSysInfo(pathToPackageJson: string, androidToolsInfo?: { pathToAdb: string, pathToAndroid: string }): Promise { + public async getSysInfo(pathToPackageJson: string, androidToolsInfo?: { pathToAdb: string }): Promise { let defaultAndroidToolsInfo = { - pathToAdb: await this.$androidToolsInfo.getPathToAdbFromAndroidHome(), - pathToAndroid: await this.$androidToolsInfo.getPathToAndroidExecutable() + pathToAdb: await this.$androidToolsInfo.getPathToAdbFromAndroidHome() }; return super.getSysInfo(pathToPackageJson || await path.join(__dirname, "..", "package.json"), androidToolsInfo || defaultAndroidToolsInfo); diff --git a/test/npm-support.ts b/test/npm-support.ts index f4c620367f..c6837f6306 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -157,7 +157,7 @@ async function setupProject(dependencies?: any): Promise { getAppResourcesDestinationDirectoryPath: () => path.join(androidFolderPath, "src", "main", "res"), processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, - interpolateConfigurationFile: () => Promise.resolve(), + interpolateConfigurationFile: (): void => undefined, isPlatformPrepared: (projectRoot: string) => false } }; diff --git a/test/platform-service.ts b/test/platform-service.ts index 94aca1a1e1..e2a3d08479 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -325,7 +325,7 @@ describe('Platform Service Tests', () => { getAppResourcesDestinationDirectoryPath: () => "", processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, - interpolateConfigurationFile: () => Promise.resolve(), + interpolateConfigurationFile: (): void => undefined, isPlatformPrepared: (projectRoot: string) => false } }; @@ -398,7 +398,7 @@ describe('Platform Service Tests', () => { getAppResourcesDestinationDirectoryPath: () => "", processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, - interpolateConfigurationFile: () => Promise.resolve(), + interpolateConfigurationFile: (): void => undefined, isPlatformPrepared: (projectRoot: string) => false } }; diff --git a/test/stubs.ts b/test/stubs.ts index f3aa5bce99..750c88f7f5 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -298,8 +298,8 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor async interpolateData(): Promise { return Promise.resolve(); } - async interpolateConfigurationFile(): Promise { - return Promise.resolve(); + interpolateConfigurationFile(): void { + return ; } afterCreateProject(projectRoot: string): void { return null; @@ -489,7 +489,7 @@ export class LiveSyncServiceStub implements ILiveSyncService { } export class AndroidToolsInfoStub implements IAndroidToolsInfo { - public async getToolsInfo(): Promise { + public getToolsInfo(): IAndroidToolsInfoData { let infoData: IAndroidToolsInfoData = Object.create(null); infoData.androidHomeEnvVar = ""; infoData.compileSdkVersion = 23; @@ -499,7 +499,7 @@ export class AndroidToolsInfoStub implements IAndroidToolsInfo { return infoData; } - public async validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): Promise { + public validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): boolean { return true; } @@ -511,9 +511,13 @@ export class AndroidToolsInfoStub implements IAndroidToolsInfo { return ""; } - async getPathToAdbFromAndroidHome(): Promise { + public async getPathToAdbFromAndroidHome(): Promise { return Promise.resolve(""); } + + public validateAndroidHomeEnvVariable(options?: { showWarningsAsErrors: boolean }): boolean { + return false; + } } export class ChildProcessStub { @@ -686,8 +690,8 @@ export class EmulatorPlatformService implements IEmulatorPlatformService { return Promise.resolve(null); } - public getAndroidEmulators(): Promise { - return Promise.resolve(null); + public getAndroidEmulators(): IEmulatorInfo[] { + return null; } public startEmulator(info: IEmulatorInfo): Promise {