Skip to content

Commit 3bef0fb

Browse files
committed
Merge pull request #37 from NativeScript/fatme/mobile
Deploy to connected device
2 parents 42854fa + f67f722 commit 3bef0fb

22 files changed

+34606
-25
lines changed

lib/bootstrap.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ $injector.requireCommand("platform|remove", "./commands/remove-platform");
2222
$injector.requireCommand("run", "./commands/run");
2323
$injector.requireCommand("prepare", "./commands/prepare");
2424
$injector.requireCommand("build", "./commands/build");
25+
$injector.requireCommand("deploy", "./commands/deploy");
2526

2627
$injector.require("npm", "./node-package-manager");
2728
$injector.require("config", "./config");

lib/commands/deploy.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
///<reference path="../.d.ts"/>
2+
3+
export class DeployCommand implements ICommand {
4+
constructor(private $platformService: IPlatformService) { }
5+
6+
execute(args: string[]): IFuture<void> {
7+
return (() => {
8+
this.$platformService.deploy(args[0]).wait();
9+
}).future<void>()();
10+
}
11+
}
12+
$injector.registerCommand("deploy", DeployCommand);

lib/config.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
///<reference path=".d.ts"/>
22

33
import path = require("path");
4+
import util = require("util");
45

56
export class StaticConfig implements IStaticConfig {
67
public PROJECT_FILE_NAME = ".tnsproject";
78
public CLIENT_NAME = "tns";
89
public ANALYTICS_API_KEY = "5752dabccfc54c4ab82aea9626b7338e";
910
public TRACK_FEATURE_USAGE_SETTING_NAME = "TrackFeatureUsage";
1011
public ANALYTICS_INSTALLATION_ID_SETTING_NAME = "AnalyticsInstallationID";
12+
public START_PACKAGE_ACTIVITY_NAME = "com.tns.NativeScriptActivity";
1113

1214
public version = require("../package.json").version;
1315

14-
public get helpTextPath() {
16+
public get helpTextPath(): string {
1517
return path.join(__dirname, "../resources/help.txt");
1618
}
19+
20+
public get adbFilePath(): string {
21+
return path.join(__dirname, util.format("../resources/platform-tools/android/%s/adb", process.platform));
22+
}
1723
}
1824
$injector.register("staticConfig", StaticConfig);

lib/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
///<reference path=".d.ts"/>
22

33
export var APP_FOLDER_NAME = "app";
4+
export var TNS_MODULES_FOLDER_NAME = "tns_modules";
45
export var DEFAULT_PROJECT_ID = "com.telerik.tns.HelloWorld";
56
export var DEFAULT_PROJECT_NAME = "HelloNativescript";
67
export var APP_RESOURCES_FOLDER_NAME = "App_Resources";

lib/definitions/platform.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ interface IPlatformService {
22
addPlatforms(platforms: string[]): IFuture<void>;
33
getInstalledPlatforms(): IFuture<string[]>;
44
getAvailablePlatforms(): IFuture<string[]>;
5+
removePlatforms(platforms: string[]): IFuture<void>;
56
runPlatform(platform: string): IFuture<void>;
67
preparePlatform(platform: string): IFuture<void>;
78
buildPlatform(platform: string): IFuture<void>;
8-
removePlatforms(platforms: string[]): IFuture<void>;
9+
deploy(platform: string): IFuture<void>;
910
}
1011

1112
interface IPlatformData {
1213
frameworkPackageName: string;
1314
platformProjectService: IPlatformProjectService;
1415
projectRoot: string;
1516
normalizedPlatformName: string;
17+
buildOutputPath: string;
18+
validPackageNames: string[];
1619
targetedOS?: string[];
1720
}
1821

lib/nativescript-cli.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ $injector.register("config", {
1616
DEBUG: process.env.NATIVESCRIPT_DEBUG,
1717
version: require("../package.json").version,
1818
helpTextPath: path.join(__dirname, "../resources/help.txt"),
19-
client: "tns"
19+
client: "nativescript"
2020
});
2121

2222
var dispatcher = $injector.resolve("dispatcher");

lib/services/android-project-service.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import path = require("path");
33
import shell = require("shelljs");
44
import util = require("util");
55
import options = require("./../options");
6-
import helpers = require("./../common/helpers");
76
import constants = require("./../constants");
7+
import hostInfo = require("../common/host-info");
88

99
class AndroidProjectService implements IPlatformProjectService {
1010
private targetApi: string;
@@ -21,7 +21,12 @@ class AndroidProjectService implements IPlatformProjectService {
2121
frameworkPackageName: "tns-android",
2222
normalizedPlatformName: "Android",
2323
platformProjectService: this,
24-
projectRoot: path.join(this.$projectData.platformsDir, "android")
24+
projectRoot: path.join(this.$projectData.platformsDir, "android"),
25+
buildOutputPath: path.join(this.$projectData.platformsDir, "android", "bin"),
26+
validPackageNames: [
27+
util.format("%s-%s.%s", this.$projectData.projectName, "debug", "apk"),
28+
util.format("%s-%s.%s", this.$projectData.projectName, "release", "apk")
29+
]
2530
};
2631
}
2732

@@ -84,15 +89,15 @@ class AndroidProjectService implements IPlatformProjectService {
8489
var assetsDirectory = path.join(platformData.projectRoot, "assets");
8590
var resDirectory = path.join(platformData.projectRoot, "res");
8691

87-
shell.cp("-r", appSourceDirectory, assetsDirectory);
92+
shell.cp("-r", path.join(appSourceDirectory, "*"), assetsDirectory);
8893

89-
var appResourcesDirectoryPath = path.join(assetsDirectory, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
94+
var appResourcesDirectoryPath = path.join(assetsDirectory, constants.APP_RESOURCES_FOLDER_NAME);
9095
if (this.$fs.exists(appResourcesDirectoryPath).wait()) {
9196
shell.cp("-r", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), resDirectory);
9297
this.$fs.deleteDirectory(appResourcesDirectoryPath).wait();
9398
}
9499

95-
return path.join(assetsDirectory, constants.APP_FOLDER_NAME);
100+
return assetsDirectory;
96101

97102
}).future<string>()();
98103
}
@@ -106,7 +111,7 @@ class AndroidProjectService implements IPlatformProjectService {
106111
}
107112

108113
private spawn(command: string, args: string[]): IFuture<void> {
109-
if (helpers.isWindows()) {
114+
if (hostInfo.isWindows()) {
110115
args.unshift('/s', '/c', command);
111116
command = 'cmd';
112117
}
@@ -124,7 +129,8 @@ class AndroidProjectService implements IPlatformProjectService {
124129
return (() => {
125130
var args = [
126131
"--path", projectPath,
127-
"--target", targetApi
132+
"--target", targetApi,
133+
"--name", this.$projectData.projectName
128134
];
129135

130136
this.spawn("android", ['update', 'project'].concat(args)).wait();

lib/services/ios-project-service.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class IOSProjectService implements IPlatformProjectService {
2323
normalizedPlatformName: "iOS",
2424
platformProjectService: this,
2525
projectRoot: path.join(this.$projectData.platformsDir, "ios"),
26+
buildOutputPath: path.join(this.$projectData.platformsDir, "ios", "build", "device"),
27+
validPackageNames: [
28+
this.$projectData.projectName + ".ipa"
29+
],
2630
targetedOS: ['darwin']
2731
};
2832
}
@@ -76,9 +80,10 @@ class IOSProjectService implements IPlatformProjectService {
7680
return (() => {
7781
var appSourceDirectory = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME);
7882
var appDestinationDirectory = path.join(platformData.projectRoot, this.$projectData.projectName);
79-
shell.cp("-r", appSourceDirectory, appDestinationDirectory);
8083

81-
return path.join(appDestinationDirectory, constants.APP_FOLDER_NAME);
84+
shell.cp("-r", path.join(appSourceDirectory, "*"), appDestinationDirectory);
85+
86+
return appDestinationDirectory;
8287
}).future<string>()();
8388
}
8489

@@ -87,7 +92,7 @@ class IOSProjectService implements IPlatformProjectService {
8792
var basicArgs = [
8893
"-project", path.join(projectRoot, this.$projectData.projectName + ".xcodeproj"),
8994
"-target", this.$projectData.projectName,
90-
"-configuration", options.release ? "Release": "Debug",
95+
"-configuration", options.release ? "Release" : "Debug",
9196
"build"
9297
];
9398
var args: string[] = [];
@@ -112,6 +117,18 @@ class IOSProjectService implements IPlatformProjectService {
112117
var childProcess = this.$childProcess.spawn("xcodebuild", args, {cwd: options, stdio: 'inherit'});
113118
this.$fs.futureFromEvent(childProcess, "exit").wait();
114119

120+
var buildOutputPath = path.join(projectRoot, "build", options.device ? "device" : "emulator");
121+
122+
// Produce ipa file
123+
var xcrunArgs = [
124+
"-sdk", "iphoneos",
125+
"PackageApplication",
126+
"-v", path.join(buildOutputPath, this.$projectData.projectName + ".app"),
127+
"-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa")
128+
];
129+
130+
var childProcess = this.$childProcess.spawn("xcrun", xcrunArgs, {cwd: options, stdio: 'inherit'});
131+
this.$fs.futureFromEvent(childProcess, "exit").wait();
115132
}).future<void>()();
116133
}
117134

lib/services/platform-service.ts

+65-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import shell = require("shelljs");
55
import util = require("util");
66
import constants = require("./../constants");
77
import helpers = require("./../common/helpers");
8+
import options = require("./../options");
89

910
class PlatformsData implements IPlatformsData {
1011
private platformsData : { [index: string]: any } = {};
@@ -34,7 +35,8 @@ export class PlatformService implements IPlatformService {
3435
private $logger: ILogger,
3536
private $npm: INodePackageManager,
3637
private $projectData: IProjectData,
37-
private $platformsData: IPlatformsData) { }
38+
private $platformsData: IPlatformsData,
39+
private $devicesServices: Mobile.IDevicesServices) { }
3840

3941
public addPlatforms(platforms: string[]): IFuture<void> {
4042
return (() => {
@@ -138,17 +140,9 @@ export class PlatformService implements IPlatformService {
138140
var platformProjectService = platformData.platformProjectService;
139141

140142
var appFilesLocation = platformProjectService.prepareProject(platformData).wait();
141-
var files = helpers.enumerateFilesInDirectorySync(appFilesLocation);
142143

143-
_.each(files, fileName => {
144-
var platformInfo = PlatformService.parsePlatformSpecificFileName(path.basename(fileName), this.$platformsData.platformsNames);
145-
var shouldExcludeFile = platformInfo && platformInfo.platform !== platform;
146-
if (shouldExcludeFile) {
147-
this.$fs.deleteFile(fileName).wait();
148-
} else if (platformInfo && platformInfo.onDeviceName) {
149-
this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait();
150-
}
151-
});
144+
this.processPlatformSpecificFiles(platform, helpers.enumerateFilesInDirectorySync(path.join(appFilesLocation, constants.APP_FOLDER_NAME))).wait();
145+
this.processPlatformSpecificFiles(platform, helpers.enumerateFilesInDirectorySync(path.join(appFilesLocation, constants.TNS_MODULES_FOLDER_NAME))).wait();
152146

153147
}).future<void>()();
154148
}
@@ -170,7 +164,14 @@ export class PlatformService implements IPlatformService {
170164
platform = platform.toLowerCase();
171165

172166
this.preparePlatform(platform).wait();
167+
168+
// We need to set device option here
169+
var cachedDeviceOption = options.device;
170+
options.device = true;
173171
this.buildPlatform(platform).wait();
172+
options.device = cachedDeviceOption;
173+
174+
this.deploy(platform).wait();
174175
}).future<void>()();
175176
}
176177

@@ -190,6 +191,44 @@ export class PlatformService implements IPlatformService {
190191
}).future<void>()();
191192
}
192193

194+
public deploy(platform: string): IFuture<void> {
195+
return (() => {
196+
platform = platform.toLowerCase();
197+
198+
this.validatePlatformInstalled(platform);
199+
200+
var platformData = this.$platformsData.getPlatformData(platform);
201+
202+
// Get latest package that is produced from build
203+
var candidates = this.$fs.readDirectory(platformData.buildOutputPath).wait();
204+
var packages = _.filter(candidates, candidate => {
205+
return _.contains(platformData.validPackageNames, candidate);
206+
}).map(currentPackage => {
207+
currentPackage = path.join(platformData.buildOutputPath, currentPackage);
208+
209+
return {
210+
pkg: currentPackage,
211+
time: this.$fs.getFsStats(currentPackage).wait().mtime
212+
};
213+
});
214+
215+
packages = _.sortBy(packages, pkg => pkg.time ).reverse(); // We need to reverse because sortBy always sorts in ascending order
216+
217+
if(packages.length === 0) {
218+
var packageExtName = path.extname(platformData.validPackageNames[0]);
219+
this.$errors.fail("No %s found in %s directory", packageExtName, platformData.buildOutputPath)
220+
}
221+
222+
var packageFile = packages[0].pkg;
223+
this.$logger.out("Using ", packageFile);
224+
225+
this.$devicesServices.initialize(platform, options.device).wait();
226+
var action = (device: Mobile.IDevice): IFuture<void> => { return device.deploy(packageFile, this.$projectData.projectId); };
227+
this.$devicesServices.execute(action).wait();
228+
229+
}).future<void>()();
230+
}
231+
193232
private validatePlatform(platform: string): void {
194233
if(!platform) {
195234
this.$errors.fail("No platform specified.")
@@ -245,5 +284,20 @@ export class PlatformService implements IPlatformService {
245284
}
246285
return undefined;
247286
}
287+
288+
private processPlatformSpecificFiles( platform: string, files: string[]): IFuture<void> {
289+
// Renames the files that have `platform` as substring and removes the files from other platform
290+
return (() => {
291+
_.each(files, fileName => {
292+
var platformInfo = PlatformService.parsePlatformSpecificFileName(path.basename(fileName), this.$platformsData.platformsNames);
293+
var shouldExcludeFile = platformInfo && platformInfo.platform !== platform;
294+
if (shouldExcludeFile) {
295+
this.$fs.deleteFile(fileName).wait();
296+
} else if (platformInfo && platformInfo.onDeviceName) {
297+
this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait();
298+
}
299+
});
300+
}).future<void>()();
301+
}
248302
}
249303
$injector.register("platformService", PlatformService);

package.json

+7
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
"mobile"
2323
],
2424
"dependencies": {
25+
"bufferpack": "0.0.6",
26+
"byline": "4.1.1",
2527
"colors": "0.6.2",
28+
"ffi": "https://github.com/icenium/node-ffi/tarball/master",
2629
"fibers": "https://github.com/icenium/node-fibers/tarball/master",
2730
"filesize": "2.0.3",
2831
"log4js": "0.6.9",
@@ -31,13 +34,17 @@
3134
"node-uuid": "1.4.1",
3235
"npm": "1.4.10",
3336
"osenv": "0.1.0",
37+
"plistlib": "0.2.1",
3438
"progress-stream": "0.5.0",
3539
"prompt": "https://github.com/Icenium/prompt/tarball/master",
3640
"properties-parser": "0.2.3",
41+
"ref": "https://github.com/icenium/ref/tarball/master",
42+
"ref-struct": "0.0.5",
3743
"rimraf": "2.2.6",
3844
"semver": "3.0.1",
3945
"shelljs": "0.3.0",
4046
"tabtab": "https://github.com/tailsu/node-tabtab/tarball/master",
47+
"temp": "0.8.1",
4148
"underscore": "1.5.2",
4249
"unzip": "0.1.9",
4350
"watchr": "2.4.11",

resources/help.txt

+13
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ General commands:
1414
prepare Copies cross-platform content to the subdirectory for the selected target platform.
1515
This lets you build the project with the SDK for the selected platform and deploy it on device.
1616
build Builds the project for the selected target platform and produces an application package.
17+
deploy Deploys the project to a connected device.
1718
run Runs your project on a connected device. This is shorthand for prepare, build, and deploy.
1819
feature-usage-tracking Configures anonymous feature usage tracking.
1920

@@ -145,6 +146,18 @@ in the project.
145146

146147
--[/]--
147148

149+
--[deploy]--
150+
Usage:
151+
$ tns deploy <Platform> [--device <Device ID>]
152+
153+
Platform-specific usages:
154+
$ tns deploy android [--device <Device ID>]
155+
$ tns deploy ios [--device <Device ID>]
156+
157+
Deploys the project to a connected device.
158+
159+
--[/]--
160+
148161
--[run]--
149162

150163
Usage:

0 commit comments

Comments
 (0)