Skip to content

Commit 81592bc

Browse files
Fatme HavaluovaFatme
Fatme Havaluova
authored andcommitted
Create, prepare and build commands for iOS
1 parent 6d0a921 commit 81592bc

12 files changed

+275
-150
lines changed

.idea/nativescript-cli.iml

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/declarations.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
interface INodePackageManager {
22
cache: string;
33
load(config?: any): IFuture<void>;
4-
install(packageName: string, pathToSave?: string): IFuture<string>;
4+
install(packageName: string, pathToSave?: string, version?: string): IFuture<string>;
55
}
66

7-
interface IStaticConfig extends Config.IStaticConfig { }
7+
interface IStaticConfig extends Config.IStaticConfig { }

lib/definitions/project.d.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ interface IProjectTemplatesService {
1515
}
1616

1717
interface IPlatformProjectService {
18+
platformData: IPlatformData;
1819
validate(): IFuture<void>;
1920
createProject(projectRoot: string, frameworkDir: string): IFuture<void>;
20-
interpolateData(projectRoot: string): void;
21-
afterCreateProject(projectRoot: string): void;
22-
prepareProject(normalizedPlatformName: string, platforms: string[]): IFuture<void>;
21+
interpolateData(projectRoot: string): IFuture<void>;
22+
afterCreateProject(projectRoot: string): IFuture<void>;
23+
prepareProject(platformData: IPlatformData): IFuture<string>;
2324
buildProject(projectRoot: string): IFuture<void>;
2425
}

lib/definitions/semver.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module "semver" {
2+
function gt(version1: string, version2: string): boolean;
3+
function lt(version1: string, version2: string): boolean;
4+
}

lib/nativescript-cli.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ errors.installUncaughtExceptionListener();
1212

1313
$injector.register("config", {
1414
CI_LOGGER: false,
15-
DEBUG: process.env.NATIVESCRIPT_DEBUG
15+
PROJECT_FILE_NAME: ".tnsproject",
16+
DEBUG: process.env.NATIVESCRIPT_DEBUG,
17+
version: require("../package.json").version,
18+
helpTextPath: path.join(__dirname, "../resources/help.txt"),
19+
client: "tns"
1620
});
1721

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

lib/node-package-manager.ts

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

3-
import npm = require("npm");
43
import Future = require("fibers/future");
5-
import shell = require("shelljs");
4+
import npm = require("npm");
65
import path = require("path");
6+
import shell = require("shelljs");
7+
import helpers = require("./common/helpers");
78

89
export class NodePackageManager implements INodePackageManager {
910
private static NPM_LOAD_FAILED = "Failed to retrieve data from npm. Please try again a little bit later.";
11+
private static NPM_REGISTRY_URL = "http://registry.npmjs.org/";
1012

1113
constructor(private $logger: ILogger,
12-
private $errors: IErrors) { }
14+
private $errors: IErrors,
15+
private $httpClient: Server.IHttpClient) { }
1316

1417
public get cache(): string {
1518
return npm.cache;
@@ -27,14 +30,24 @@ export class NodePackageManager implements INodePackageManager {
2730
return future;
2831
}
2932

30-
public install(packageName: string, pathToSave?: string): IFuture<string> {
33+
public install(packageName: string, pathToSave?: string, version?: string): IFuture<string> {
3134
return (() => {
32-
var action = (packageName: string) => {
35+
try {
36+
this.load().wait(); // It's obligatory to execute load before whatever npm function
3337
pathToSave = pathToSave || npm.cache;
34-
this.installCore(pathToSave, packageName).wait();
35-
};
38+
var packageToInstall = packageName;
39+
40+
if(version) {
41+
this.validateVersion(packageName, version).wait();
42+
packageToInstall = packageName + "@" + version;
43+
}
44+
45+
this.installCore(pathToSave, packageToInstall).wait();
3646

37-
this.tryExecuteAction(action, packageName).wait();
47+
} catch(error) {
48+
this.$logger.debug(error);
49+
this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED);
50+
}
3851

3952
return path.join(pathToSave, "node_modules", packageName);
4053

@@ -53,14 +66,20 @@ export class NodePackageManager implements INodePackageManager {
5366
return future;
5467
}
5568

56-
private tryExecuteAction(action: (...args: any[]) => void, ...args: any[]): IFuture<void> {
69+
private getAvailableVersions(packageName: string): IFuture<string[]> {
5770
return (() => {
58-
try {
59-
this.load().wait(); // It's obligatory to execute load before whatever npm function
60-
action.apply(null, args);
61-
} catch(error) {
62-
this.$logger.debug(error);
63-
this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED);
71+
var url = NodePackageManager.NPM_REGISTRY_URL + packageName;
72+
var response = this.$httpClient.httpRequest(url).wait().body;
73+
var json = JSON.parse(response);
74+
return _.keys(json.versions);
75+
}).future<string[]>()();
76+
}
77+
78+
private validateVersion(packageName: string, version: string): IFuture<void> {
79+
return (() => {
80+
var versions = this.getAvailableVersions(packageName).wait();
81+
if(!_.contains(versions, version)) {
82+
this.$errors.fail("Invalid version. Valid versions are: %s", helpers.formatListOfNames(versions, "and"));
6483
}
6584
}).future<void>()();
6685
}

lib/options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var knownOpts:any = {
1212
"copy-from": String,
1313
"link-to": String,
1414
"release": String,
15+
"device": Boolean,
1516
"version": Boolean,
1617
"help": Boolean
1718
},

lib/services/android-project-service.ts

+31-39
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ class AndroidProjectService implements IPlatformProjectService {
1616
private $projectData: IProjectData,
1717
private $propertiesParser: IPropertiesParser) { }
1818

19+
public get platformData(): IPlatformData {
20+
return {
21+
frameworkPackageName: "tns-android",
22+
normalizedPlatformName: "Android",
23+
platformProjectService: this,
24+
projectRoot: path.join(this.$projectData.platformsDir, "android")
25+
};
26+
}
27+
1928
public validate(): IFuture<void> {
2029
return (() => {
2130
this.validatePackageName(this.$projectData.projectId);
@@ -61,54 +70,49 @@ class AndroidProjectService implements IPlatformProjectService {
6170
}).future<void>()();
6271
}
6372

64-
public afterCreateProject(projectRoot: string) {
65-
var targetApi = this.getTarget(projectRoot).wait();
66-
this.$logger.trace("Android target: %s", targetApi);
67-
this.runAndroidUpdate(projectRoot, targetApi).wait();
73+
public afterCreateProject(projectRoot: string): IFuture<void> {
74+
return (() => {
75+
var targetApi = this.getTarget(projectRoot).wait();
76+
this.$logger.trace("Android target: %s", targetApi);
77+
this.runAndroidUpdate(projectRoot, targetApi).wait();
78+
}).future<void>()();
6879
}
6980

70-
public prepareProject(normalizedPlatformName: string, platforms: string[]): IFuture<void> {
81+
public prepareProject(platformData: IPlatformData): IFuture<string> {
7182
return (() => {
72-
var platform = normalizedPlatformName.toLowerCase();
73-
var assetsDirectoryPath = path.join(this.$projectData.platformsDir, platform, "assets");
74-
var appResourcesDirectoryPath = path.join(assetsDirectoryPath, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
75-
shell.cp("-r", path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), assetsDirectoryPath);
83+
var appSourceDirectory = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME);
84+
var assetsDirectory = path.join(platformData.projectRoot, "assets");
85+
var resDirectory = path.join(platformData.projectRoot, "res");
86+
87+
shell.cp("-r", appSourceDirectory, assetsDirectory);
7688

89+
var appResourcesDirectoryPath = path.join(assetsDirectory, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
7790
if (this.$fs.exists(appResourcesDirectoryPath).wait()) {
78-
shell.cp("-r", path.join(appResourcesDirectoryPath, normalizedPlatformName, "*"), path.join(this.$projectData.platformsDir, platform, "res"));
91+
shell.cp("-r", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), resDirectory);
7992
this.$fs.deleteDirectory(appResourcesDirectoryPath).wait();
8093
}
8194

82-
var files = helpers.enumerateFilesInDirectorySync(path.join(assetsDirectoryPath, constants.APP_FOLDER_NAME));
83-
var platformsAsString = platforms.join("|");
95+
return path.join(assetsDirectory, constants.APP_FOLDER_NAME);
8496

85-
_.each(files, fileName => {
86-
var platformInfo = AndroidProjectService.parsePlatformSpecificFileName(path.basename(fileName), platformsAsString);
87-
var shouldExcludeFile = platformInfo && platformInfo.platform !== platform;
88-
if (shouldExcludeFile) {
89-
this.$fs.deleteFile(fileName).wait();
90-
} else if (platformInfo && platformInfo.onDeviceName) {
91-
this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait();
92-
}
93-
});
94-
}).future<void>()();
97+
}).future<string>()();
9598
}
9699

97100
public buildProject(projectRoot: string): IFuture<void> {
98101
return (() => {
99102
var buildConfiguration = options.release ? "release" : "debug";
100103
var args = this.getAntArgs(buildConfiguration, projectRoot);
101-
this.spawn('ant', args);
104+
this.spawn('ant', args).wait();
102105
}).future<void>()();
103106
}
104107

105-
private spawn(command: string, args: string[], options?: any): void {
106-
if(helpers.isWindows()) {
108+
private spawn(command: string, args: string[]): IFuture<void> {
109+
if (helpers.isWindows()) {
107110
args.unshift('/s', '/c', command);
108111
command = 'cmd';
109112
}
110113

111-
this.$childProcess.spawn(command, args, {cwd: options, stdio: 'inherit'});
114+
var child = this.$childProcess.spawn(command, args, {stdio: "inherit"});
115+
return this.$fs.futureFromEvent(child, "close");
112116
}
113117

114118
private getAntArgs(configuration: string, projectRoot: string): string[] {
@@ -123,7 +127,7 @@ class AndroidProjectService implements IPlatformProjectService {
123127
"--target", targetApi
124128
];
125129

126-
this.spawn("android update project", args);
130+
this.spawn("android", ['update', 'project'].concat(args)).wait();
127131
}).future<void>()();
128132
}
129133

@@ -208,17 +212,5 @@ class AndroidProjectService implements IPlatformProjectService {
208212
}
209213
}).future<void>()();
210214
}
211-
212-
private static parsePlatformSpecificFileName(fileName: string, platforms: string): any {
213-
var regex = util.format("^(.+?)\.(%s)(\..+?)$", platforms);
214-
var parsed = fileName.toLowerCase().match(new RegExp(regex, "i"));
215-
if (parsed) {
216-
return {
217-
platform: parsed[2],
218-
onDeviceName: parsed[1] + parsed[3]
219-
};
220-
}
221-
return undefined;
222-
}
223215
}
224216
$injector.register("androidProjectService", AndroidProjectService);

lib/services/ios-project-service.ts

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

3+
import path = require("path");
4+
import shell = require("shelljs");
5+
import constants = require("./../constants");
6+
import helpers = require("./../common/helpers");
7+
import options = require("./../options");
8+
39
class IOSProjectService implements IPlatformProjectService {
10+
private static XCODE_PROJECT_EXT_NAME = ".xcodeproj";
11+
private static XCODEBUILD_MIN_VERSION = "5.0";
12+
private static IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__";
13+
14+
constructor(private $projectData: IProjectData,
15+
private $fs: IFileSystem,
16+
private $childProcess: IChildProcess,
17+
private $errors: IErrors) { }
18+
19+
public get platformData(): IPlatformData {
20+
return {
21+
frameworkPackageName: "tns-ios",
22+
normalizedPlatformName: "iOS",
23+
platformProjectService: this,
24+
projectRoot: path.join(this.$projectData.platformsDir, "ios"),
25+
targetedOS: ['darwin']
26+
};
27+
}
28+
429
public validate(): IFuture<void> {
530
return (() => {
31+
try {
32+
this.$childProcess.exec("which xcodebuild").wait();
33+
} catch(error) {
34+
this.$errors.fail("Xcode is not installed. Make sure you have Xcode installed and added to your PATH");
35+
}
36+
37+
var xcodeBuildVersion = this.$childProcess.exec("xcodebuild -version | head -n 1 | sed -e 's/Xcode //'").wait();
38+
if(helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) {
39+
this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION);
40+
}
41+
42+
}).future<void>()();
43+
}
44+
45+
public createProject(projectRoot: string, frameworkDir: string): IFuture<void> {
46+
return (() => {
47+
shell.cp("-r", path.join(frameworkDir, "*"), projectRoot);
648
}).future<void>()();
749
}
850

9-
public interpolateData(): void {
51+
public interpolateData(projectRoot: string): IFuture<void> {
52+
return (() => {
53+
this.replaceFileName("-Info.plist", path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait();
54+
this.replaceFileName("-Prefix.pch", path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait();
55+
this.replaceFileName(IOSProjectService.XCODE_PROJECT_EXT_NAME, projectRoot).wait();
56+
57+
var pbxprojFilePath = path.join(projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME, "project.pbxproj");
58+
this.replaceFileContent(pbxprojFilePath).wait();
59+
}).future<void>()();
60+
}
1061

62+
public afterCreateProject(projectRoot: string): IFuture<void> {
63+
return (() => {
64+
this.$fs.rename(path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER),
65+
path.join(projectRoot, this.$projectData.projectName)).wait();
66+
}).future<void>()();
1167
}
1268

13-
public afterCreateProject(): void {
69+
public prepareProject(platformData: IPlatformData): IFuture<string> {
70+
return (() => {
71+
var appSourceDirectory = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME);
72+
var appDestinationDirectory = path.join(platformData.projectRoot, this.$projectData.projectName);
73+
shell.cp("-r", appSourceDirectory, appDestinationDirectory);
1474

75+
return path.join(appDestinationDirectory, constants.APP_FOLDER_NAME);
76+
}).future<string>()();
1577
}
1678

17-
public createProject(): IFuture<void> {
79+
public buildProject(projectRoot: string): IFuture<void> {
1880
return (() => {
81+
var basicArgs = [
82+
"-project", path.join(projectRoot, this.$projectData.projectName + ".xcodeproj"),
83+
"-target", this.$projectData.projectName,
84+
"-configuration", options.release,
85+
"build"
86+
];
87+
var args = [];
1988

89+
if(options.device) {
90+
args = basicArgs.concat([
91+
"-xcconfig", path.join(projectRoot, "build.xcconfig"),
92+
"-sdk", "iphoneos",
93+
"ARCHS=\"armv7 armv7s arm64\"",
94+
"VALID_ARCHS=\"armv7 armv7s arm64\"",
95+
"CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "device")
96+
]);
97+
} else {
98+
args = basicArgs.concat([
99+
"-sdk", "iphonesimulator",
100+
"-arch", "i386",
101+
"VALID_ARCHS=\"i386\"",
102+
"CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "emulator")
103+
]);
104+
}
105+
106+
this.$childProcess.spawn("xcodebuild", args, {cwd: options, stdio: 'inherit'});
20107
}).future<void>()();
21108
}
22109

23-
public prepareProject(normalizedPlatformName: string, platforms: string[]): IFuture<void> {
110+
private replaceFileContent(file: string): IFuture<void> {
24111
return (() => {
25-
112+
var fileContent = this.$fs.readText(file).wait();
113+
var replacedContent = helpers.stringReplaceAll(fileContent, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, this.$projectData.projectName);
114+
this.$fs.writeFile(file, replacedContent).wait();
26115
}).future<void>()();
27116
}
28117

29-
public buildProject(): IFuture<void> {
118+
private replaceFileName(fileNamePart: string, fileRootLocation: string): IFuture<void> {
30119
return (() => {
120+
var oldFileName = IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + fileNamePart;
121+
var newFileName = this.$projectData.projectName + fileNamePart;
31122

123+
this.$fs.rename(path.join(fileRootLocation, oldFileName), path.join(fileRootLocation, newFileName)).wait();
32124
}).future<void>()();
33125
}
34126
}

0 commit comments

Comments
 (0)