Skip to content

Commit 40c6e43

Browse files
FatmeFatme Havaluova
authored and
Fatme Havaluova
committed
* Implement lockfile to allow parallel execution
* Symlink option
1 parent 7844f2c commit 40c6e43

16 files changed

+203
-81
lines changed

lib/bootstrap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ $injector.requireCommand("emulate", "./commands/emulate");
3333
$injector.requireCommand("list-devices", "./commands/list-devices");
3434

3535
$injector.require("npm", "./node-package-manager");
36+
$injector.require("lockfile", "./lockfile");
3637
$injector.require("config", "./config");

lib/declarations.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
interface INodePackageManager {
2-
getCacheRootPath(): IFuture<string>;
2+
getCacheRootPath(): string;
33
addToCache(packageName: string, version: string): IFuture<void>;
44
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
55
load(config?: any): IFuture<void>;
@@ -21,3 +21,7 @@ interface IApplicationPackage {
2121
time: Date;
2222
}
2323

24+
interface ILockFile {
25+
lock(): IFuture<void>;
26+
unlock(): IFuture<void>;
27+
}

lib/definitions/lockfile.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
declare module "lockfile" {
2+
export function lock(lockFilename: string, lockParams: ILockParams, callback: (err: Error) => void): void;
3+
export function lockSync(lockFilename: string, lockParams: any): void;
4+
export function unlock(lockFilename: string, callback: (err: Error) => void): void;
5+
export function unlockSync(lockFilename: string): void;
6+
7+
interface ILockSyncParams {
8+
retries: number;
9+
stale: number;
10+
}
11+
12+
interface ILockParams extends ILockSyncParams {
13+
retryWait: number;
14+
}
15+
}

lib/lockfile.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
///<reference path=".d.ts"/>
2+
3+
import Future = require("fibers/future");
4+
import lockfile = require("lockfile");
5+
import path = require("path");
6+
import options = require("./options");
7+
8+
export class LockFile implements ILockFile {
9+
private static LOCK_FILENAME = path.join(options["profile-dir"], ".lock");
10+
private static LOCK_EXPIRY_PERIOD_SEC = 180;
11+
private static LOCK_PARAMS = {
12+
retryWait: 100,
13+
retries: LockFile.LOCK_EXPIRY_PERIOD_SEC*10,
14+
stale: LockFile.LOCK_EXPIRY_PERIOD_SEC*1000
15+
};
16+
17+
public lock(): IFuture<void> {
18+
var future = new Future<void>();
19+
lockfile.lock(LockFile.LOCK_FILENAME, LockFile.LOCK_PARAMS, (err: Error) => {
20+
if(err) {
21+
future.throw(err);
22+
} else {
23+
future.return();
24+
}
25+
});
26+
return future;
27+
}
28+
29+
public unlock(): IFuture<void> {
30+
var future = new Future<void>();
31+
lockfile.unlock(LockFile.LOCK_FILENAME, (err: Error) => {
32+
if(err) {
33+
future.throw(err);
34+
} else {
35+
future.return();
36+
}
37+
});
38+
return future;
39+
}
40+
}
41+
$injector.register("lockfile", LockFile);

lib/nativescript-cli.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
///<reference path=".d.ts"/>
22
"use strict";
3-
43
import path = require("path");
54

65
require("./bootstrap");

lib/node-package-manager.ts

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,15 @@ export class NodePackageManager implements INodePackageManager {
2222
private $staticConfig: IStaticConfig,
2323
private $fs: IFileSystem) {
2424
this.versionsCache = {};
25+
this.load().wait();
2526
}
2627

27-
public getCacheRootPath(): IFuture<string> {
28-
return (() => {
29-
this.load().wait();
30-
return npm.cache;
31-
}).future<string>()();
28+
public getCacheRootPath(): string {
29+
return npm.cache;
3230
}
3331

3432
public addToCache(packageName: string, version: string): IFuture<void> {
35-
return (() => {
36-
this.load().wait();
37-
this.addToCacheCore(packageName, version).wait();
38-
}).future<void>()();
33+
return this.addToCacheCore(packageName, version);
3934
}
4035

4136
public load(config?: any): IFuture<void> {
@@ -53,28 +48,21 @@ export class NodePackageManager implements INodePackageManager {
5348
public install(packageName: string, opts?: INpmInstallOptions): IFuture<string> {
5449
return (() => {
5550
try {
56-
this.load().wait(); // It's obligatory to execute load before whatever npm function
57-
5851
var packageToInstall = packageName;
5952
var pathToSave = (opts && opts.pathToSave) || npm.cache;
6053
var version = (opts && opts.version) || null;
61-
var isSemanticVersioningDisabled = options.frameworkPath ? true : false; // We need to disable sem versioning for local packages
6254

6355
if(version) {
6456
this.validateVersion(packageName, version).wait();
6557
packageToInstall = packageName + "@" + version;
6658
}
6759

68-
this.installCore(packageToInstall, pathToSave, isSemanticVersioningDisabled).wait();
60+
return this.installCore(packageToInstall, pathToSave, version).wait();
6961
} catch(error) {
7062
this.$logger.debug(error);
7163
this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED);
7264
}
7365

74-
var pathToNodeModules = path.join(pathToSave, "node_modules");
75-
var folders = this.$fs.readDirectory(pathToNodeModules).wait();
76-
return path.join(pathToNodeModules, folders[0]);
77-
7866
}).future<string>()();
7967
}
8068

@@ -86,21 +74,42 @@ export class NodePackageManager implements INodePackageManager {
8674
}).future<string>()();
8775
}
8876

89-
private installCore(packageName: string, pathToSave: string, isSemanticVersioningDisabled: boolean): IFuture<void> {
90-
var currentVersion = this.$staticConfig.version;
91-
if(!semver.valid(currentVersion)) {
92-
this.$errors.fail("Invalid version.");
93-
}
94-
95-
if(!isSemanticVersioningDisabled) {
96-
var incrementedVersion = semver.inc(currentVersion, constants.ReleaseType.MINOR);
97-
if(packageName.indexOf("@") < 0) {
98-
packageName = packageName + "@<" + incrementedVersion;
77+
private installCore(packageName: string, pathToSave: string, version: string): IFuture<string> {
78+
return (() => {
79+
if (options.frameworkPath) {
80+
if (this.$fs.getFsStats(options.frameworkPath).wait().isFile()) {
81+
this.npmInstall(packageName, pathToSave, version).wait();
82+
var pathToNodeModules = path.join(pathToSave, "node_modules");
83+
var folders = this.$fs.readDirectory(pathToNodeModules).wait();
84+
return path.join(pathToNodeModules, folders[0]);
85+
}
86+
return options.frameworkPath;
87+
} else {
88+
var version = version || this.getLatestVersion(packageName).wait();
89+
var cacheList = this.cacheList(packageName).wait();
90+
var packagePath = path.join(packageName, version, path.sep);
91+
if (!cacheList || !_.contains(cacheList, packagePath)) {
92+
this.npmInstall(packageName, pathToSave, version).wait();
93+
return path.join(pathToSave, "node_modules", packageName);
94+
} else {
95+
var unpackDirectoryPath = path.join(npm.cache, packageName, version, "package");
96+
if(!this.$fs.exists(unpackDirectoryPath).wait()) {
97+
this.cacheUnpack(packageName, version).wait();
98+
}
99+
return unpackDirectoryPath;
100+
}
99101
}
100-
}
102+
}).future<string>()();
103+
}
101104

105+
private npmInstall(packageName: string, pathToSave: string, version: string): IFuture<void> {
102106
this.$logger.out("Installing ", packageName);
103107

108+
var incrementedVersion = semver.inc(version, constants.ReleaseType.MINOR);
109+
if (!options.frameworkPath && packageName.indexOf("@") < 0) {
110+
packageName = packageName + "@<" + incrementedVersion;
111+
}
112+
104113
var future = new Future<void>();
105114
npm.commands["install"](pathToSave, packageName, (err: Error, data: any) => {
106115
if(err) {
@@ -113,6 +122,18 @@ export class NodePackageManager implements INodePackageManager {
113122
return future;
114123
}
115124

125+
private cacheList(packageName: string): IFuture<string[]> {
126+
var future = new Future<string[]>();
127+
npm.commands["cache"](["list", packageName], (err: Error, data: any) => {
128+
if(err) {
129+
future.throw(err);
130+
} else {
131+
future.return(data);
132+
}
133+
});
134+
return future;
135+
}
136+
116137
private addToCacheCore(packageName: string, version: string): IFuture<void> {
117138
var future = new Future<void>();
118139
npm.commands["cache"].add(packageName, version, undefined, (err: Error, data: any) => {

lib/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var knownOpts:any = {
1212
"release": Boolean,
1313
"device": Boolean,
1414
"emulator": Boolean,
15+
"symlink": Boolean,
1516
"keyStorePath": String,
1617
"keyStorePassword": String,
1718
"keyStoreAlias": String,

lib/services/android-project-service.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,8 @@ class AndroidProjectService implements IPlatformProjectService {
4949
return (() => {
5050
this.validateAndroidTarget(frameworkDir); // We need framework to be installed to validate android target so we can't call this method in validate()
5151

52-
var paths = "assets libs res".split(' ').map(p => path.join(frameworkDir, p));
53-
shell.cp("-R", paths, projectRoot);
54-
55-
paths = ".project AndroidManifest.xml project.properties".split(' ').map(p => path.join(frameworkDir, p));
56-
shell.cp("-f", paths, projectRoot);
52+
this.copyFiles(projectRoot, frameworkDir, "assets libs res").wait();
53+
this.copyFiles(projectRoot, frameworkDir, ".project AndroidManifest.xml project.properties").wait();
5754

5855
// Create src folder
5956
var packageName = this.$projectData.projectId;
@@ -124,6 +121,17 @@ class AndroidProjectService implements IPlatformProjectService {
124121
return [".jar", ".dat"];
125122
}
126123

124+
private copyFiles(projectRoot: string, frameworkDir: string, files: string): IFuture<void> {
125+
return (() => {
126+
var paths = files.split(' ').map(p => path.join(frameworkDir, p));
127+
if(options.symlink) {
128+
_.each(paths, p => this.$fs.symlink(p, projectRoot).wait());
129+
} else {
130+
shell.cp("-R", paths, projectRoot);
131+
}
132+
}).future<void>()();
133+
}
134+
127135
private spawn(command: string, args: string[]): IFuture<void> {
128136
if (hostInfo.isWindows()) {
129137
args.unshift('/s', '/c', command);

lib/services/ios-project-service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ class IOSProjectService implements IPlatformProjectService {
6262

6363
public createProject(projectRoot: string, frameworkDir: string): IFuture<void> {
6464
return (() => {
65-
shell.cp("-R", path.join(frameworkDir, "*"), projectRoot);
65+
if(options.symlink) {
66+
this.$fs.symlink(frameworkDir, projectRoot);
67+
} else {
68+
shell.cp("-R", path.join(frameworkDir, "*"), projectRoot);
69+
}
6670
}).future<void>()();
6771
}
6872

lib/services/platform-service.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,23 @@ export class PlatformService implements IPlatformService {
1717
private $platformsData: IPlatformsData,
1818
private $projectData: IProjectData,
1919
private $projectDataService: IProjectDataService,
20-
private $prompter: IPrompter) { }
20+
private $prompter: IPrompter,
21+
private $lockfile: ILockFile) { }
2122

2223
public addPlatforms(platforms: string[]): IFuture<void> {
2324
return (() => {
2425
var platformsDir = this.$projectData.platformsDir;
2526
this.$fs.ensureDirectoryExists(platformsDir).wait();
2627

27-
_.each(platforms, platform => {
28-
this.addPlatform(platform.toLowerCase()).wait();
29-
});
28+
this.$lockfile.lock().wait();
29+
30+
try {
31+
_.each(platforms, platform => {
32+
this.addPlatform(platform.toLowerCase()).wait();
33+
});
34+
} finally {
35+
this.$lockfile.unlock().wait();
36+
}
3037

3138
}).future<void>()();
3239
}
@@ -445,7 +452,7 @@ export class PlatformService implements IPlatformService {
445452

446453
private getNpmCacheDirectoryCore(packageName: string, version: string): IFuture<string> {
447454
return (() => {
448-
var npmCacheRoot = this.$npm.getCacheRootPath().wait();
455+
var npmCacheRoot = this.$npm.getCacheRootPath();
449456
return path.join(npmCacheRoot, packageName, version, "package");
450457
}).future<string>()();
451458
}

0 commit comments

Comments
 (0)