From 245663ae228330d7bddb50f78f4ff4e2f39e2974 Mon Sep 17 00:00:00 2001 From: Fatme Date: Tue, 19 Aug 2014 20:02:31 +0300 Subject: [PATCH] Deploy on emulator --- lib/bootstrap.ts | 3 + lib/commands/deploy.ts | 8 +- lib/commands/emulate.ts | 8 ++ lib/common | 2 +- lib/declarations.ts | 5 ++ lib/definitions/platform.d.ts | 10 ++- lib/options.ts | 1 + lib/services/android-project-service.ts | 16 ++-- lib/services/emulator-settings-service.ts | 21 +++++ lib/services/ios-project-service.ts | 34 ++++--- lib/services/platform-service.ts | 105 +++++++++++++++------- package.json | 1 + resources/help.txt | 6 ++ test/platform-service.ts | 1 + 14 files changed, 161 insertions(+), 60 deletions(-) create mode 100644 lib/commands/emulate.ts create mode 100644 lib/services/emulator-settings-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 7472b9916f..2ff721bf3d 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -15,6 +15,8 @@ $injector.require("platformService", "./services/platform-service"); $injector.require("userSettingsService", "./services/user-settings-service"); $injector.require("analyticsSettingsService", "./services/analytics-settings-service"); +$injector.require("emulatorSettingsService", "./services/emulator-settings-service"); + $injector.requireCommand("create", "./commands/create-project"); $injector.requireCommand("platform|*list", "./commands/list-platforms"); $injector.requireCommand("platform|add", "./commands/add-platform"); @@ -24,6 +26,7 @@ $injector.requireCommand("prepare", "./commands/prepare"); $injector.requireCommand("build", "./commands/build"); $injector.requireCommand("deploy", "./commands/deploy"); $injector.requireCommand("dev-post-install", "./commands/post-install"); +$injector.requireCommand("emulate", "./commands/emulate"); $injector.require("npm", "./node-package-manager"); $injector.require("config", "./config"); diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 5a50638d9a..a2893c3a72 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -1,12 +1,10 @@ /// -export class DeployCommand implements ICommand { +export class DeployOnDeviceCommand implements ICommand { constructor(private $platformService: IPlatformService) { } execute(args: string[]): IFuture { - return (() => { - this.$platformService.deploy(args[0]).wait(); - }).future()(); + return this.$platformService.deployOnDevice(args[0]); } } -$injector.registerCommand("deploy", DeployCommand); \ No newline at end of file +$injector.registerCommand("deploy", DeployOnDeviceCommand); \ No newline at end of file diff --git a/lib/commands/emulate.ts b/lib/commands/emulate.ts new file mode 100644 index 0000000000..5dc69cc936 --- /dev/null +++ b/lib/commands/emulate.ts @@ -0,0 +1,8 @@ +/// + +export class EmulateCommand implements ICommand { + constructor(private $platformService: IPlatformService) { } + + execute(args: string[]): IFuture { return this.$platformService.deployOnEmulator(args[0]);} +} +$injector.registerCommand("emulate", EmulateCommand); \ No newline at end of file diff --git a/lib/common b/lib/common index 199ff9eb4e..afa2ced7e3 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 199ff9eb4eb09b989453f38dacb13e099fe97008 +Subproject commit afa2ced7e3b5cbd3e2c91d72356435fd82573ad9 diff --git a/lib/declarations.ts b/lib/declarations.ts index 0f810c9316..9b5504da72 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -6,3 +6,8 @@ interface INodePackageManager { interface IStaticConfig extends Config.IStaticConfig { } +interface IApplicationPackage { + packageName: string; + time: Date; +} + diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 77a45ebc6e..a3656b5aa7 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -6,16 +6,20 @@ interface IPlatformService { runPlatform(platform: string): IFuture; preparePlatform(platform: string): IFuture; buildPlatform(platform: string): IFuture; - deploy(platform: string): IFuture; + deployOnDevice(platform: string): IFuture; + deployOnEmulator(platform: string): IFuture; } interface IPlatformData { frameworkPackageName: string; platformProjectService: IPlatformProjectService; + emulatorServices: Mobile.IEmulatorPlatformServices; projectRoot: string; normalizedPlatformName: string; - buildOutputPath: string; - validPackageNames: string[]; + deviceBuildOutputPath: string; + emulatorBuildOutputPath?: string; + validPackageNamesForDevice: string[]; + validPackageNamesForEmulator?: string[]; targetedOS?: string[]; } diff --git a/lib/options.ts b/lib/options.ts index d533b4fcde..dcb9e8c015 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -13,6 +13,7 @@ var knownOpts:any = { "link-to": String, "release": String, "device": Boolean, + "emulator": Boolean, "version": Boolean, "help": Boolean }, diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 83f7646b0d..13e7815d82 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -10,20 +10,22 @@ class AndroidProjectService implements IPlatformProjectService { private targetApi: string; constructor(private $fs: IFileSystem, - private $errors: IErrors, - private $logger: ILogger, - private $childProcess: IChildProcess, - private $projectData: IProjectData, - private $propertiesParser: IPropertiesParser) { } + private $errors: IErrors, + private $logger: ILogger, + private $childProcess: IChildProcess, + private $projectData: IProjectData, + private $propertiesParser: IPropertiesParser, + private $androidEmulatorServices: Mobile.IEmulatorPlatformServices) { } public get platformData(): IPlatformData { return { frameworkPackageName: "tns-android", normalizedPlatformName: "Android", platformProjectService: this, + emulatorServices: this.$androidEmulatorServices, projectRoot: path.join(this.$projectData.platformsDir, "android"), - buildOutputPath: path.join(this.$projectData.platformsDir, "android", "bin"), - validPackageNames: [ + deviceBuildOutputPath: path.join(this.$projectData.platformsDir, "android", "bin"), + validPackageNamesForDevice: [ util.format("%s-%s.%s", this.$projectData.projectName, "debug", "apk"), util.format("%s-%s.%s", this.$projectData.projectName, "release", "apk") ] diff --git a/lib/services/emulator-settings-service.ts b/lib/services/emulator-settings-service.ts new file mode 100644 index 0000000000..087472b7d2 --- /dev/null +++ b/lib/services/emulator-settings-service.ts @@ -0,0 +1,21 @@ +/// + +export class EmulatorSettingsService implements Mobile.IEmulatorSettingsService { + private static REQURED_ANDROID_APILEVEL = 17; + + constructor(private $injector: IInjector) { } + + public canStart(platform: string): IFuture { + return (() => { + var platformService = this.$injector.resolve("platformService"); // this should be resolved here due to cyclic dependency + + var installedPlatforms = platformService.getInstalledPlatforms().wait(); + return _.contains(installedPlatforms, platform.toLowerCase()); + }).future()(); + } + + public get minVersion(): number { + return EmulatorSettingsService.REQURED_ANDROID_APILEVEL; + } +} +$injector.register("emulatorSettingsService", EmulatorSettingsService); \ No newline at end of file diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index d0961f8282..43a1698922 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -15,18 +15,24 @@ class IOSProjectService implements IPlatformProjectService { constructor(private $projectData: IProjectData, private $fs: IFileSystem, private $childProcess: IChildProcess, - private $errors: IErrors) { } + private $errors: IErrors, + private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices) { } public get platformData(): IPlatformData { return { frameworkPackageName: "tns-ios", normalizedPlatformName: "iOS", platformProjectService: this, + emulatorServices: this.$iOSEmulatorServices, projectRoot: path.join(this.$projectData.platformsDir, "ios"), - buildOutputPath: path.join(this.$projectData.platformsDir, "ios", "build", "device"), - validPackageNames: [ + deviceBuildOutputPath: path.join(this.$projectData.platformsDir, "ios", "build", "device"), + emulatorBuildOutputPath: path.join(this.$projectData.platformsDir, "ios", "build", "emulator"), + validPackageNamesForDevice: [ this.$projectData.projectName + ".ipa" ], + validPackageNamesForEmulator: [ + this.$projectData.projectName + ".app" + ], targetedOS: ['darwin'] }; } @@ -117,18 +123,20 @@ class IOSProjectService implements IPlatformProjectService { var childProcess = this.$childProcess.spawn("xcodebuild", args, {cwd: options, stdio: 'inherit'}); this.$fs.futureFromEvent(childProcess, "exit").wait(); - var buildOutputPath = path.join(projectRoot, "build", options.device ? "device" : "emulator"); + if(options.device) { + var buildOutputPath = path.join(projectRoot, "build", options.device ? "device" : "emulator"); - // Produce ipa file - var xcrunArgs = [ - "-sdk", "iphoneos", - "PackageApplication", - "-v", path.join(buildOutputPath, this.$projectData.projectName + ".app"), - "-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa") - ]; + // Produce ipa file + var xcrunArgs = [ + "-sdk", "iphoneos", + "PackageApplication", + "-v", path.join(buildOutputPath, this.$projectData.projectName + ".app"), + "-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa") + ]; - var childProcess = this.$childProcess.spawn("xcrun", xcrunArgs, {cwd: options, stdio: 'inherit'}); - this.$fs.futureFromEvent(childProcess, "exit").wait(); + var childProcess = this.$childProcess.spawn("xcrun", xcrunArgs, {cwd: options, stdio: 'inherit'}); + this.$fs.futureFromEvent(childProcess, "exit").wait(); + } }).future()(); } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index e2d66a13d7..614b32550b 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -164,14 +164,11 @@ export class PlatformService implements IPlatformService { platform = platform.toLowerCase(); this.preparePlatform(platform).wait(); - - // We need to set device option here - var cachedDeviceOption = options.device; - options.device = true; - this.buildPlatform(platform).wait(); - options.device = cachedDeviceOption; - - this.deploy(platform).wait(); + if(options.emulator) { + this.deployOnEmulator(platform).wait(); + } else { + this.deployOnDevice(platform).wait(); + } }).future()(); } @@ -191,35 +188,21 @@ export class PlatformService implements IPlatformService { }).future()(); } - public deploy(platform: string): IFuture { + public deployOnDevice(platform: string): IFuture { return (() => { - platform = platform.toLowerCase(); - this.validatePlatformInstalled(platform); + platform = platform.toLowerCase(); var platformData = this.$platformsData.getPlatformData(platform); - // Get latest package that is produced from build - var candidates = this.$fs.readDirectory(platformData.buildOutputPath).wait(); - var packages = _.filter(candidates, candidate => { - return _.contains(platformData.validPackageNames, candidate); - }).map(currentPackage => { - currentPackage = path.join(platformData.buildOutputPath, currentPackage); - - return { - pkg: currentPackage, - time: this.$fs.getFsStats(currentPackage).wait().mtime - }; - }); - - packages = _.sortBy(packages, pkg => pkg.time ).reverse(); // We need to reverse because sortBy always sorts in ascending order - - if(packages.length === 0) { - var packageExtName = path.extname(platformData.validPackageNames[0]); - this.$errors.fail("No %s found in %s directory", packageExtName, platformData.buildOutputPath) - } + // We need to build for device + var cachedDeviceOption = options.device; + options.device = true; + this.buildPlatform(platform).wait(); + options.device = cachedDeviceOption; - var packageFile = packages[0].pkg; + // Get latest package that is produced from build + var packageFile = this.getLatestApplicationPackageForDevice(platformData).wait().packageName; this.$logger.out("Using ", packageFile); this.$devicesServices.initialize(platform, options.device).wait(); @@ -229,6 +212,25 @@ export class PlatformService implements IPlatformService { }).future()(); } + public deployOnEmulator(platform: string): IFuture { + return (() => { + this.validatePlatformInstalled(platform); + platform = platform.toLowerCase(); + + var platformData = this.$platformsData.getPlatformData(platform); + var emulatorServices = platformData.emulatorServices; + + emulatorServices.checkAvailability().wait(); + + this.buildPlatform(platform).wait(); + + var packageFile = this.getLatestApplicationPackageForEmulator(platformData).wait().packageName; + this.$logger.out("Using ", packageFile); + + emulatorServices.startEmulator(packageFile, options.emulator).wait(); + }).future()(); + } + private validatePlatform(platform: string): void { if(!platform) { this.$errors.fail("No platform specified.") @@ -299,5 +301,46 @@ export class PlatformService implements IPlatformService { }); }).future()(); } + + private getApplicationPackages(buildOutputPath: string, validPackageNames: string[]): IFuture { + return (() => { + // Get latest package that is produced from build + var candidates = this.$fs.readDirectory(buildOutputPath).wait(); + var packages = _.filter(candidates, candidate => { + return _.contains(validPackageNames, candidate); + }).map(currentPackage => { + currentPackage = path.join(buildOutputPath, currentPackage); + + return { + packageName: currentPackage, + time: this.$fs.getFsStats(currentPackage).wait().mtime + }; + }); + + return packages; + }).future()(); + } + + private getLatestApplicationPackage(buildOutputPath: string, validPackageNames: string[]): IFuture { + return (() => { + var packages = this.getApplicationPackages(buildOutputPath, validPackageNames).wait(); + if (packages.length === 0) { + var packageExtName = path.extname(validPackageNames[0]); + this.$errors.fail("No %s found in %s directory", packageExtName, buildOutputPath); + } + + packages = _.sortBy(packages, pkg => pkg.time).reverse(); // We need to reverse because sortBy always sorts in ascending order + + return packages[0]; + }).future()(); + } + + private getLatestApplicationPackageForDevice(platformData: IPlatformData) { + return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.validPackageNamesForDevice); + } + + private getLatestApplicationPackageForEmulator(platformData: IPlatformData) { + return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.validPackageNamesForEmulator || platformData.validPackageNamesForDevice); + } } $injector.register("platformService", PlatformService); diff --git a/package.json b/package.json index 8a84a5c707..0f04fef5f8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "ffi": "https://github.com/icenium/node-ffi/tarball/master", "fibers": "https://github.com/icenium/node-fibers/tarball/master", "filesize": "2.0.3", + "iconv-lite": "0.4.4", "log4js": "0.6.9", "mkdirp": "0.3.5", "mute-stream": "0.0.4", diff --git a/resources/help.txt b/resources/help.txt index 8e6a40d10f..dc1d04877b 100644 --- a/resources/help.txt +++ b/resources/help.txt @@ -171,6 +171,12 @@ Runs your project on a connected device. This is shorthand for prepare, build, a --[/]-- +--[emulate]-- + +Usage: + $ tns emulate [] +--[/]-- + --[feature-usage-tracking]-- Usage: diff --git a/test/platform-service.ts b/test/platform-service.ts index c2542ea29b..7171d04362 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -18,6 +18,7 @@ testInjector.register('npm', stubs.NPMStub); testInjector.register('projectData', stubs.ProjectDataStub); testInjector.register('platformsData', stubs.PlatformsDataStub); testInjector.register('devicesServices', {}); +testInjector.register('androidEmulatorServices', {}); describe('PlatformService', function(){ describe('#updatePlatforms()', function(){