From 67a251601c55c5d6d902127614771aeee3a2eb2a Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Mon, 19 Jun 2017 23:09:16 +0300 Subject: [PATCH 1/3] Allow prepare for Android without ANDROID_HOME Allow preparing Android project without the requirement to have ANDROID_HOME set. The change is in the required services, which should work without ANDROID_HOME in some cases. However the commands should not allow this, so move the validation in the commands. --- lib/android-tools-info.ts | 9 ++-- lib/commands/add-platform.ts | 8 +++- lib/commands/build.ts | 5 +++ lib/commands/clean-app.ts | 60 ++++++++++++++----------- lib/commands/debug.ts | 25 +++++++---- lib/commands/deploy.ts | 7 ++- lib/commands/install.ts | 3 ++ lib/commands/platform-clean.ts | 11 ++++- lib/commands/prepare.ts | 10 ++++- lib/commands/remove-platform.ts | 14 ++++-- lib/commands/run.ts | 21 ++++++--- lib/commands/update-platform.ts | 11 ++++- lib/commands/update.ts | 8 ++++ lib/services/android-project-service.ts | 37 ++++++++------- lib/services/platform-service.ts | 4 -- test/platform-commands.ts | 6 ++- test/stubs.ts | 2 +- 17 files changed, 164 insertions(+), 77 deletions(-) diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index e519dab0ec..b864c91dc4 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -317,11 +317,12 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { @cache() private getInstalledTargets(): string[] { let installedTargets: string[] = []; - const pathToInstalledTargets = path.join(this.androidHome, "platforms"); - if (this.$fs.exists(pathToInstalledTargets)) { - installedTargets = this.$fs.readDirectory(pathToInstalledTargets); + if (this.androidHome) { + const pathToInstalledTargets = path.join(this.androidHome, "platforms"); + if (this.$fs.exists(pathToInstalledTargets)) { + installedTargets = this.$fs.readDirectory(pathToInstalledTargets); + } } - this.$logger.trace("Installed Android Targets are: ", installedTargets); return installedTargets; diff --git a/lib/commands/add-platform.ts b/lib/commands/add-platform.ts index 48ff06a732..ddb6156c9f 100644 --- a/lib/commands/add-platform.ts +++ b/lib/commands/add-platform.ts @@ -4,6 +4,7 @@ export class AddPlatformCommand implements ICommand { constructor(private $options: IOptions, private $platformService: IPlatformService, private $projectData: IProjectData, + private $platformsData: IPlatformsData, private $errors: IErrors) { this.$projectData.initializeProjectData(); } @@ -17,7 +18,12 @@ export class AddPlatformCommand implements ICommand { this.$errors.fail("No platform specified. Please specify a platform to add"); } - _.each(args, arg => this.$platformService.validatePlatform(arg, this.$projectData)); + for (let arg of args) { + this.$platformService.validatePlatform(arg, this.$projectData); + const platformData = this.$platformsData.getPlatformData(arg, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } return true; } diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 810baa4334..d0185286d2 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -84,6 +84,11 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); } + + const platformData = this.$platformsData.getPlatformData(this.$devicePlatformsConstants.Android, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + return args.length === 0 && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.Android); } } diff --git a/lib/commands/clean-app.ts b/lib/commands/clean-app.ts index 2d760fa04d..f948b4bfe8 100644 --- a/lib/commands/clean-app.ts +++ b/lib/commands/clean-app.ts @@ -1,56 +1,64 @@ -export class CleanAppCommandBase { +export class CleanAppCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + + protected platform: string; + constructor(protected $options: IOptions, protected $projectData: IProjectData, - protected $platformService: IPlatformService) { + protected $platformService: IPlatformService, + protected $errors: IErrors, + protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + protected $platformsData: IPlatformsData) { + this.$projectData.initializeProjectData(); } public async execute(args: string[]): Promise { - let platform = args[0].toLowerCase(); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; - return this.$platformService.cleanDestinationApp(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options); + return this.$platformService.cleanDestinationApp(this.platform.toLowerCase(), appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options); + } + + public async canExecute(args: string[]): Promise { + if (!this.$platformService.isPlatformSupportedForOS(this.platform, this.$projectData)) { + this.$errors.fail(`Applications for platform ${this.platform} can not be built on this OS`); + } + + let platformData = this.$platformsData.getPlatformData(this.platform, this.$projectData); + let platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + return true; } } export class CleanAppIosCommand extends CleanAppCommandBase implements ICommand { constructor(protected $options: IOptions, - private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $platformsData: IPlatformsData, - private $errors: IErrors, + protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + protected $platformsData: IPlatformsData, + protected $errors: IErrors, $platformService: IPlatformService, $projectData: IProjectData) { - super($options, $projectData, $platformService); + super($options, $projectData, $platformService, $errors, $devicePlatformsConstants, $platformsData); } - public allowedParameters: ICommandParameter[] = []; - - public async execute(args: string[]): Promise { - if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) { - this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`); - } - return super.execute([this.$platformsData.availablePlatforms.iOS]); + protected get platform(): string { + return this.$devicePlatformsConstants.iOS; } } $injector.registerCommand("clean-app|ios", CleanAppIosCommand); export class CleanAppAndroidCommand extends CleanAppCommandBase implements ICommand { - public allowedParameters: ICommandParameter[] = []; - constructor(protected $options: IOptions, - private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $platformsData: IPlatformsData, - private $errors: IErrors, + protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + protected $platformsData: IPlatformsData, + protected $errors: IErrors, $platformService: IPlatformService, $projectData: IProjectData) { - super($options, $projectData, $platformService); + super($options, $projectData, $platformService, $errors, $devicePlatformsConstants, $platformsData); } - public async execute(args: string[]): Promise { - if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) { - this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`); - } - return super.execute([this.$platformsData.availablePlatforms.Android]); + protected get platform(): string { + return this.$devicePlatformsConstants.Android; } } diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index d8ba7a9447..0a6e7804cc 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -10,6 +10,7 @@ protected $options: IOptions, protected $platformsData: IPlatformsData, protected $logger: ILogger, + protected $errors: IErrors, private $debugLiveSyncService: IDebugLiveSyncService, private $config: IConfiguration) { this.$projectData.initializeProjectData(); @@ -73,6 +74,14 @@ } public async canExecute(args: string[]): Promise { + if (!this.$platformService.isPlatformSupportedForOS(this.platform, this.$projectData)) { + this.$errors.fail(`Applications for platform ${this.platform} can not be built on this OS`); + } + + const platformData = this.$platformsData.getPlatformData(this.platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device, @@ -97,7 +106,7 @@ } export class DebugIOSCommand extends DebugPlatformCommand { - constructor(private $errors: IErrors, + constructor(protected $errors: IErrors, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $logger: ILogger, $iOSDebugService: IPlatformDebugService, @@ -109,8 +118,9 @@ export class DebugIOSCommand extends DebugPlatformCommand { $projectData: IProjectData, $platformsData: IPlatformsData, $iosDeviceOperations: IIOSDeviceOperations, - $debugLiveSyncService: IDebugLiveSyncService) { - super($iOSDebugService, $devicesService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger, $debugLiveSyncService, $config); + $debugLiveSyncService: IDebugLiveSyncService, ) { + super($iOSDebugService, $devicesService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger, + $errors, $debugLiveSyncService, $config); // Do not dispose ios-device-lib, so the process will remain alive and the debug application (NativeScript Inspector or Chrome DevTools) will be able to connect to the socket. // In case we dispose ios-device-lib, the socket will be closed and the code will fail when the debug application tries to read/send data to device socket. // That's why the `$ tns debug ios --justlaunch` command will not release the terminal. @@ -132,7 +142,7 @@ export class DebugIOSCommand extends DebugPlatformCommand { $injector.registerCommand("debug|ios", DebugIOSCommand); export class DebugAndroidCommand extends DebugPlatformCommand { - constructor(private $errors: IErrors, + constructor(protected $errors: IErrors, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $logger: ILogger, $androidDebugService: IPlatformDebugService, @@ -144,14 +154,11 @@ export class DebugAndroidCommand extends DebugPlatformCommand { $projectData: IProjectData, $platformsData: IPlatformsData, $debugLiveSyncService: IDebugLiveSyncService) { - super($androidDebugService, $devicesService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger, $debugLiveSyncService, $config); + super($androidDebugService, $devicesService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger, + $errors, $debugLiveSyncService, $config); } public async canExecute(args: string[]): Promise { - if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.Android, this.$projectData)) { - this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.Android} can not be built on this OS`); - } - return await super.canExecute(args) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.Android); } diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index ef6f904f4e..3625fb8ae2 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -6,7 +6,8 @@ export class DeployOnDeviceCommand implements ICommand { private $options: IOptions, private $projectData: IProjectData, private $errors: IErrors, - private $mobileHelper: Mobile.IMobileHelper) { + private $mobileHelper: Mobile.IMobileHelper, + private $platformsData: IPlatformsData) { this.$projectData.initializeProjectData(); } @@ -43,6 +44,10 @@ export class DeployOnDeviceCommand implements ICommand { this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); } + const platformData = this.$platformsData.getPlatformData(args[0], this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + return this.$platformService.validateOptions(this.$options.provision, this.$projectData, args[0]); } } diff --git a/lib/commands/install.ts b/lib/commands/install.ts index 56b1cd329b..798eed654b 100644 --- a/lib/commands/install.ts +++ b/lib/commands/install.ts @@ -31,6 +31,9 @@ export class InstallCommand implements ICommand { const frameworkPackageData = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); if (frameworkPackageData && frameworkPackageData.version) { try { + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + await this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`], this.$options.platformTemplate, this.$projectData, this.$options, this.$options.frameworkPath); } catch (err) { error = `${error}${EOL}${err}`; diff --git a/lib/commands/platform-clean.ts b/lib/commands/platform-clean.ts index e5c334b9d7..046ece328b 100644 --- a/lib/commands/platform-clean.ts +++ b/lib/commands/platform-clean.ts @@ -4,7 +4,8 @@ export class CleanCommand implements ICommand { constructor(private $options: IOptions, private $projectData: IProjectData, private $platformService: IPlatformService, - private $errors: IErrors) { + private $errors: IErrors, + private $platformsData: IPlatformsData) { this.$projectData.initializeProjectData(); } @@ -17,7 +18,13 @@ export class CleanCommand implements ICommand { this.$errors.fail("No platform specified. Please specify a platform to clean"); } - _.each(args, arg => this.$platformService.validatePlatformInstalled(arg, this.$projectData)); + for (let platform of args) { + this.$platformService.validatePlatformInstalled(platform, this.$projectData); + + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } return true; } diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 20b85624d3..502b44d57b 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -4,7 +4,8 @@ export class PrepareCommand implements ICommand { constructor(private $options: IOptions, private $platformService: IPlatformService, private $projectData: IProjectData, - private $platformCommandParameter: ICommandParameter) { + private $platformCommandParameter: ICommandParameter, + private $platformsData: IPlatformsData) { this.$projectData.initializeProjectData(); } @@ -14,7 +15,12 @@ export class PrepareCommand implements ICommand { } public async canExecute(args: string[]): Promise { - return await this.$platformCommandParameter.validate(args[0]) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, args[0]); + const platform = args[0]; + const result = await this.$platformCommandParameter.validate(platform) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, platform); + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + return result; } } diff --git a/lib/commands/remove-platform.ts b/lib/commands/remove-platform.ts index 2432525753..38b7ca016d 100644 --- a/lib/commands/remove-platform.ts +++ b/lib/commands/remove-platform.ts @@ -3,9 +3,10 @@ export class RemovePlatformCommand implements ICommand { constructor(private $platformService: IPlatformService, private $projectData: IProjectData, - private $errors: IErrors) { - this.$projectData.initializeProjectData(); - } + private $errors: IErrors, + private $platformsData: IPlatformsData) { + this.$projectData.initializeProjectData(); + } public execute(args: string[]): Promise { return this.$platformService.removePlatforms(args, this.$projectData); @@ -16,7 +17,12 @@ export class RemovePlatformCommand implements ICommand { this.$errors.fail("No platform specified. Please specify a platform to remove"); } - _.each(args, arg => this.$platformService.validatePlatformInstalled(arg, this.$projectData)); + for (let platform of args) { + this.$platformService.validatePlatformInstalled(platform, this.$projectData); + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } return true; } diff --git a/lib/commands/run.ts b/lib/commands/run.ts index 3540b92e53..d9ddb79c1a 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -13,7 +13,8 @@ export class RunCommandBase implements ICommand { private $devicesService: Mobile.IDevicesService, private $hostInfo: IHostInfo, private $iosDeviceOperations: IIOSDeviceOperations, - private $mobileHelper: Mobile.IMobileHelper) { + private $mobileHelper: Mobile.IMobileHelper, + protected $platformsData: IPlatformsData) { } public allowedParameters: ICommandParameter[] = []; @@ -31,6 +32,13 @@ export class RunCommandBase implements ICommand { this.platform = this.$devicePlatformsConstants.Android; } + const availablePlatforms = this.platform ? [this.platform] : this.$platformsData.availablePlatforms; + for (let platform of availablePlatforms) { + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } + return true; } @@ -46,6 +54,7 @@ export class RunCommandBase implements ICommand { skipDeviceDetectionInterval: true, skipInferPlatform: !this.platform }); + await this.$devicesService.detectCurrentlyAttachedDevices(); const devices = this.$devicesService.getDeviceInstances(); @@ -118,7 +127,7 @@ export class RunIosCommand extends RunCommandBase implements ICommand { } constructor($platformService: IPlatformService, - private $platformsData: IPlatformsData, + protected $platformsData: IPlatformsData, protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, protected $errors: IErrors, $liveSyncService: ILiveSyncService, @@ -129,7 +138,8 @@ export class RunIosCommand extends RunCommandBase implements ICommand { $hostInfo: IHostInfo, $iosDeviceOperations: IIOSDeviceOperations, $mobileHelper: Mobile.IMobileHelper) { - super($platformService, $liveSyncService, $projectData, $options, $emulatorPlatformService, $devicePlatformsConstants, $errors, $devicesService, $hostInfo, $iosDeviceOperations, $mobileHelper); + super($platformService, $liveSyncService, $projectData, $options, $emulatorPlatformService, $devicePlatformsConstants, $errors, + $devicesService, $hostInfo, $iosDeviceOperations, $mobileHelper, $platformsData); } public async execute(args: string[]): Promise { @@ -154,7 +164,7 @@ export class RunAndroidCommand extends RunCommandBase implements ICommand { } constructor($platformService: IPlatformService, - private $platformsData: IPlatformsData, + protected $platformsData: IPlatformsData, protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, protected $errors: IErrors, $liveSyncService: ILiveSyncService, @@ -165,7 +175,8 @@ export class RunAndroidCommand extends RunCommandBase implements ICommand { $hostInfo: IHostInfo, $iosDeviceOperations: IIOSDeviceOperations, $mobileHelper: Mobile.IMobileHelper) { - super($platformService, $liveSyncService, $projectData, $options, $emulatorPlatformService, $devicePlatformsConstants, $errors, $devicesService, $hostInfo, $iosDeviceOperations, $mobileHelper); + super($platformService, $liveSyncService, $projectData, $options, $emulatorPlatformService, $devicePlatformsConstants, $errors, + $devicesService, $hostInfo, $iosDeviceOperations, $mobileHelper, $platformsData); } public async execute(args: string[]): Promise { diff --git a/lib/commands/update-platform.ts b/lib/commands/update-platform.ts index d66257aa2e..e740d2bad9 100644 --- a/lib/commands/update-platform.ts +++ b/lib/commands/update-platform.ts @@ -4,7 +4,8 @@ export class UpdatePlatformCommand implements ICommand { constructor(private $options: IOptions, private $projectData: IProjectData, private $platformService: IPlatformService, - private $errors: IErrors) { + private $errors: IErrors, + private $platformsData: IPlatformsData) { this.$projectData.initializeProjectData(); } @@ -17,7 +18,13 @@ export class UpdatePlatformCommand implements ICommand { this.$errors.fail("No platform specified. Please specify platforms to update."); } - _.each(args, arg => this.$platformService.validatePlatform(arg.split("@")[0], this.$projectData)); + for (let arg of args) { + const platform = arg.split("@")[0]; + this.$platformService.validatePlatformInstalled(platform, this.$projectData); + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } return true; } diff --git a/lib/commands/update.ts b/lib/commands/update.ts index d7fff8024b..ca5ba7e888 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -55,6 +55,14 @@ export class UpdateCommand implements ICommand { } public async canExecute(args: string[]): Promise { + for (let arg of args) { + const platform = arg.split("@")[0]; + this.$platformService.validatePlatformInstalled(platform, this.$projectData); + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } + return args.length < 2 && this.$projectData.projectDir !== ""; } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index e04613141e..60c8708cda 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -101,6 +101,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject let javaCompilerVersion = await this.$sysInfo.getJavaCompilerVersion(); await this.$androidToolsInfo.validateJavacVersion(javaCompilerVersion, { showWarningsAsErrors: true }); + + await this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); } public async validatePlugins(): Promise { @@ -113,9 +115,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } this.$fs.ensureDirectoryExists(this.getPlatformData(projectData).projectRoot); - this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); let androidToolsInfo = this.$androidToolsInfo.getToolsInfo(); - let targetSdkVersion = androidToolsInfo.targetSdkVersion; + let targetSdkVersion = androidToolsInfo && androidToolsInfo.targetSdkVersion; this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`); this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "libs", "-R"); @@ -219,8 +220,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject 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) || this.$androidToolsInfo.getToolsInfo().compileSdkVersion.toString(); - shell.sed('-i', /__APILEVEL__/, sdk, manifestPath); + if (this.$androidToolsInfo.getToolsInfo().androidHomeEnvVar) { + const sdk = (platformSpecificData && platformSpecificData.sdk) || (this.$androidToolsInfo.getToolsInfo().compileSdkVersion).toString(); + shell.sed('-i', /__APILEVEL__/, sdk, manifestPath); + } } private getProjectNameFromId(projectData: IProjectData): string { @@ -431,9 +434,11 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public async cleanProject(projectRoot: string, projectData: IProjectData): Promise { - const buildOptions = this.getBuildOptions({ release: false }, projectData); - buildOptions.unshift("clean"); - await this.executeGradleCommand(projectRoot, buildOptions); + if (this.$androidToolsInfo.getToolsInfo().androidHomeEnvVar) { + const buildOptions = this.getBuildOptions({ release: false }, projectData); + buildOptions.unshift("clean"); + await this.executeGradleCommand(projectRoot, buildOptions); + } } public async cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise { @@ -509,16 +514,18 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } private async executeGradleCommand(projectRoot: string, gradleArgs: string[], childProcessOpts?: SpawnOptions, spawnFromEventOptions?: ISpawnFromEventOptions): Promise { - const gradlew = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew"; + if (this.$androidToolsInfo.getToolsInfo().androidHomeEnvVar) { + const gradlew = this.$hostInfo.isWindows ? "gradlew.bat" : "./gradlew"; - childProcessOpts = childProcessOpts || {}; - childProcessOpts.cwd = childProcessOpts.cwd || projectRoot; - childProcessOpts.stdio = childProcessOpts.stdio || "inherit"; + childProcessOpts = childProcessOpts || {}; + childProcessOpts.cwd = childProcessOpts.cwd || projectRoot; + childProcessOpts.stdio = childProcessOpts.stdio || "inherit"; - return await this.spawn(gradlew, - gradleArgs, - childProcessOpts, - spawnFromEventOptions); + return await this.spawn(gradlew, + gradleArgs, + childProcessOpts, + spawnFromEventOptions); + } } } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 6851c120fd..8b92bc5a19 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -96,10 +96,6 @@ export class PlatformService extends EventEmitter implements IPlatformService { version = this.getCurrentPlatformVersion(platform, projectData); } - // Copy platform specific files in platforms dir - let platformProjectService = platformData.platformProjectService; - await platformProjectService.validate(projectData); - // Log the values for project this.$logger.trace("Creating NativeScript project for the %s platform", platform); this.$logger.trace("Path: %s", platformData.projectRoot); diff --git a/test/platform-commands.ts b/test/platform-commands.ts index 31a2350f60..9c5b8ee2d9 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -27,7 +27,11 @@ let isCommandExecuted = true; class PlatformData implements IPlatformData { frameworkPackageName = "tns-android"; normalizedPlatformName = "Android"; - platformProjectService: IPlatformProjectService = null; + platformProjectService: IPlatformProjectService = { + validate: async (projectData: IProjectData): Promise => { + // intentionally left blank + } + }; emulatorServices: Mobile.IEmulatorPlatformServices = null; projectRoot = ""; deviceBuildOutputPath = ""; diff --git a/test/stubs.ts b/test/stubs.ts index d96520608e..f16b5b0016 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -509,7 +509,7 @@ export class LiveSyncServiceStub implements ILiveSyncService { export class AndroidToolsInfoStub implements IAndroidToolsInfo { public getToolsInfo(): IAndroidToolsInfoData { let infoData: IAndroidToolsInfoData = Object.create(null); - infoData.androidHomeEnvVar = ""; + infoData.androidHomeEnvVar = "ANDROID_HOME"; infoData.compileSdkVersion = 23; infoData.buildToolsVersion = "23"; infoData.targetSdkVersion = 23; From 7e8c9e22f87f1e921b4d79be20a9594fc3fe78a3 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 20 Jun 2017 12:54:52 +0300 Subject: [PATCH 2/3] Fix unable to uninstall application on macOS Update ios-device-lib where a fix for uninstalling application on macOS was not working. Update common lib where the following fixes are applied: * Fix `tns devices` not showing attached iOS devices. * Do not call uninstall of application when it is not installed - this produces some confusing warnings. --- lib/commands/debug.ts | 2 +- lib/commands/prepare.ts | 9 ++++++--- lib/common | 2 +- package.json | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 0a6e7804cc..1a9a79e12b 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -118,7 +118,7 @@ export class DebugIOSCommand extends DebugPlatformCommand { $projectData: IProjectData, $platformsData: IPlatformsData, $iosDeviceOperations: IIOSDeviceOperations, - $debugLiveSyncService: IDebugLiveSyncService, ) { + $debugLiveSyncService: IDebugLiveSyncService) { super($iOSDebugService, $devicesService, $debugDataService, $platformService, $projectData, $options, $platformsData, $logger, $errors, $debugLiveSyncService, $config); // Do not dispose ios-device-lib, so the process will remain alive and the debug application (NativeScript Inspector or Chrome DevTools) will be able to connect to the socket. diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 502b44d57b..4e01d647ea 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -17,9 +17,12 @@ export class PrepareCommand implements ICommand { public async canExecute(args: string[]): Promise { const platform = args[0]; const result = await this.$platformCommandParameter.validate(platform) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, platform); - const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); - const platformProjectService = platformData.platformProjectService; - await platformProjectService.validate(this.$projectData); + if (!result) { + const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); + const platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(this.$projectData); + } + return result; } } diff --git a/lib/common b/lib/common index cf3276e6bd..c01fd47f28 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit cf3276e6bd5fdd66070a88cd70bc40d29fc65a5a +Subproject commit c01fd47f2815ed528747427bcf72a4e6dc66da18 diff --git a/package.json b/package.json index 0eb8cc7d44..ed70bb2d90 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "glob": "^7.0.3", "iconv-lite": "0.4.11", "inquirer": "0.9.0", - "ios-device-lib": "0.4.5", + "ios-device-lib": "0.4.6", "ios-mobileprovision-finder": "1.0.9", "ios-sim-portable": "~3.0.0", "lockfile": "1.0.1", From 6aa867f882616028b79d91078b90da795f71b119 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 20 Jun 2017 13:55:08 +0300 Subject: [PATCH 3/3] Fix execution of livesync hooks when there are no node_modules In case `node_modules` are deleted from the project and `tns run ` or `tns debug ` is executed, the livesync hook fails as the package that's required in it is not installed. In order to fix this, extract the logic of the liveSync method in a separate method and decorate it with the hook. In the public method make sure all dependencies are installed and call the private method. This way the hook will be executed correctly. --- lib/commands/prepare.ts | 2 +- .../livesync/debug-livesync-service.ts | 2 + lib/services/livesync/livesync-service.ts | 37 +++++++++++-------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 4e01d647ea..bf1d775c9a 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -17,7 +17,7 @@ export class PrepareCommand implements ICommand { public async canExecute(args: string[]): Promise { const platform = args[0]; const result = await this.$platformCommandParameter.validate(platform) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, platform); - if (!result) { + if (result) { const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); const platformProjectService = platformData.platformProjectService; await platformProjectService.validate(this.$projectData); diff --git a/lib/services/livesync/debug-livesync-service.ts b/lib/services/livesync/debug-livesync-service.ts index be75692cf1..cf3dac408e 100644 --- a/lib/services/livesync/debug-livesync-service.ts +++ b/lib/services/livesync/debug-livesync-service.ts @@ -12,6 +12,7 @@ export class DebugLiveSyncService extends LiveSyncService implements IDebugLiveS protected $logger: ILogger, $processService: IProcessService, $hooksService: IHooksService, + $pluginsService: IPluginsService, protected $injector: IInjector, private $options: IOptions, private $debugDataService: IDebugDataService, @@ -28,6 +29,7 @@ export class DebugLiveSyncService extends LiveSyncService implements IDebugLiveS $logger, $processService, $hooksService, + $pluginsService, $injector); } diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index f5f84f0692..bf044bb82d 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -28,28 +28,16 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { protected $logger: ILogger, private $processService: IProcessService, private $hooksService: IHooksService, + private $pluginsService: IPluginsService, protected $injector: IInjector) { super(); } - @hook("liveSync") public async liveSync(deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo): Promise { const projectData = this.$projectDataService.getProjectData(liveSyncData.projectDir); - // In case liveSync is called for a second time for the same projectDir. - const isAlreadyLiveSyncing = this.liveSyncProcessesInfo[projectData.projectDir] && !this.liveSyncProcessesInfo[projectData.projectDir].isStopped; - this.setLiveSyncProcessInfo(liveSyncData.projectDir, deviceDescriptors); - - const deviceDescriptorsForInitialSync = isAlreadyLiveSyncing ? _.differenceBy(deviceDescriptors, this.liveSyncProcessesInfo[projectData.projectDir].deviceDescriptors, deviceDescriptorPrimaryKey) : deviceDescriptors; - - await this.initialSync(projectData, deviceDescriptorsForInitialSync, liveSyncData); - - if (!liveSyncData.skipWatcher && deviceDescriptors && deviceDescriptors.length) { - // Should be set after prepare - this.$injector.resolve("usbLiveSyncService").isInitialized = true; - - await this.startWatcher(projectData, liveSyncData); - } + await this.$pluginsService.ensureAllDependenciesAreInstalled(projectData); + await this.liveSyncOperation(deviceDescriptors, liveSyncData, projectData); } public async stopLiveSync(projectDir: string, deviceIdentifiers?: string[]): Promise { @@ -125,6 +113,25 @@ export class LiveSyncService extends EventEmitter implements ILiveSyncService { this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`); } + @hook("liveSync") + private async liveSyncOperation(deviceDescriptors: ILiveSyncDeviceInfo[], + liveSyncData: ILiveSyncInfo, projectData: IProjectData): Promise { + // In case liveSync is called for a second time for the same projectDir. + const isAlreadyLiveSyncing = this.liveSyncProcessesInfo[projectData.projectDir] && !this.liveSyncProcessesInfo[projectData.projectDir].isStopped; + this.setLiveSyncProcessInfo(liveSyncData.projectDir, deviceDescriptors); + + const deviceDescriptorsForInitialSync = isAlreadyLiveSyncing ? _.differenceBy(deviceDescriptors, this.liveSyncProcessesInfo[projectData.projectDir].deviceDescriptors, deviceDescriptorPrimaryKey) : deviceDescriptors; + + await this.initialSync(projectData, deviceDescriptorsForInitialSync, liveSyncData); + + if (!liveSyncData.skipWatcher && deviceDescriptors && deviceDescriptors.length) { + // Should be set after prepare + this.$injector.resolve("usbLiveSyncService").isInitialized = true; + + await this.startWatcher(projectData, liveSyncData); + } + } + private setLiveSyncProcessInfo(projectDir: string, deviceDescriptors: ILiveSyncDeviceInfo[]): void { this.liveSyncProcessesInfo[projectDir] = this.liveSyncProcessesInfo[projectDir] || Object.create(null); this.liveSyncProcessesInfo[projectDir].actionsChain = this.liveSyncProcessesInfo[projectDir].actionsChain || Promise.resolve();