Skip to content

Deploy on emulator #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 21, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
8 changes: 3 additions & 5 deletions lib/commands/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
///<reference path="../.d.ts"/>

export class DeployCommand implements ICommand {
export class DeployOnDeviceCommand implements ICommand {
constructor(private $platformService: IPlatformService) { }

execute(args: string[]): IFuture<void> {
return (() => {
this.$platformService.deploy(args[0]).wait();
}).future<void>()();
return this.$platformService.deployOnDevice(args[0]);
}
}
$injector.registerCommand("deploy", DeployCommand);
$injector.registerCommand("deploy", DeployOnDeviceCommand);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a merge stopper, but consider adding a new line here.

8 changes: 8 additions & 0 deletions lib/commands/emulate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
///<reference path="../.d.ts"/>

export class EmulateCommand implements ICommand {
constructor(private $platformService: IPlatformService) { }

execute(args: string[]): IFuture<void> { return this.$platformService.deployOnEmulator(args[0]);}
}
$injector.registerCommand("emulate", EmulateCommand);
5 changes: 5 additions & 0 deletions lib/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ interface INodePackageManager {

interface IStaticConfig extends Config.IStaticConfig { }

interface IApplicationPackage {
packageName: string;
time: Date;
}

10 changes: 7 additions & 3 deletions lib/definitions/platform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ interface IPlatformService {
runPlatform(platform: string): IFuture<void>;
preparePlatform(platform: string): IFuture<void>;
buildPlatform(platform: string): IFuture<void>;
deploy(platform: string): IFuture<void>;
deployOnDevice(platform: string): IFuture<void>;
deployOnEmulator(platform: string): IFuture<void>;
}

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[];
}

Expand Down
1 change: 1 addition & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var knownOpts:any = {
"link-to": String,
"release": String,
"device": Boolean,
"emulator": Boolean,
"version": Boolean,
"help": Boolean
},
Expand Down
16 changes: 9 additions & 7 deletions lib/services/android-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
]
Expand Down
21 changes: 21 additions & 0 deletions lib/services/emulator-settings-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
///<reference path="../.d.ts"/>

export class EmulatorSettingsService implements Mobile.IEmulatorSettingsService {
private static REQURED_ANDROID_APILEVEL = 17;

constructor(private $injector: IInjector) { }

public canStart(platform: string): IFuture<boolean> {
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<boolean>()();
}

public get minVersion(): number {
return EmulatorSettingsService.REQURED_ANDROID_APILEVEL;
}
}
$injector.register("emulatorSettingsService", EmulatorSettingsService);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a merge stopper, but consider adding a new line here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

34 changes: 21 additions & 13 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']
};
}
Expand Down Expand Up @@ -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<void>()();
}

Expand Down
105 changes: 74 additions & 31 deletions lib/services/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>()();
}

Expand All @@ -191,35 +188,21 @@ export class PlatformService implements IPlatformService {
}).future<void>()();
}

public deploy(platform: string): IFuture<void> {
public deployOnDevice(platform: string): IFuture<void> {
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();
Expand All @@ -229,6 +212,25 @@ export class PlatformService implements IPlatformService {
}).future<void>()();
}

public deployOnEmulator(platform: string): IFuture<void> {
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<void>()();
}

private validatePlatform(platform: string): void {
if(!platform) {
this.$errors.fail("No platform specified.")
Expand Down Expand Up @@ -299,5 +301,46 @@ export class PlatformService implements IPlatformService {
});
}).future<void>()();
}

private getApplicationPackages(buildOutputPath: string, validPackageNames: string[]): IFuture<IApplicationPackage[]> {
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<IApplicationPackage[]>()();
}

private getLatestApplicationPackage(buildOutputPath: string, validPackageNames: string[]): IFuture<IApplicationPackage> {
return (() => {
var packages = this.getApplicationPackages(buildOutputPath, validPackageNames).wait();
if (packages.length === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check can be executed before the sortby+reverse operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good catch :) thanks

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<IApplicationPackage>()();
}

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);
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions resources/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ Runs your project on a connected device. This is shorthand for prepare, build, a

--[/]--

--[emulate]--

Usage:
$ tns emulate [<platform>]
--[/]--

--[feature-usage-tracking]--

Usage:
Expand Down
1 change: 1 addition & 0 deletions test/platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(){
Expand Down