Skip to content

Commit cbf2990

Browse files
FatmeFatme Havaluova
authored and
Fatme Havaluova
committed
* Implement lockfile to allow parallel execution
* Symlink option
1 parent e9520b3 commit cbf2990

16 files changed

+202
-88
lines changed

lib/bootstrap.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ $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");
3738
$injector.require("optionsService", "./services/options-service");

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

1616
private versionsCache: IDictionary<string[]>;
17-
private isLoaded: boolean;
1817

1918
constructor(private $logger: ILogger,
2019
private $errors: IErrors,
2120
private $httpClient: Server.IHttpClient,
22-
private $staticConfig: IStaticConfig,
2321
private $fs: IFileSystem) {
2422
this.versionsCache = {};
23+
this.load().wait();
2524
}
2625

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

3430
public addToCache(packageName: string, version: string): IFuture<void> {
35-
return (() => {
36-
this.load().wait();
37-
this.addToCacheCore(packageName, version).wait();
38-
}).future<void>()();
31+
return this.addToCacheCore(packageName, version);
3932
}
4033

4134
public load(config?: any): IFuture<void> {
@@ -53,28 +46,16 @@ export class NodePackageManager implements INodePackageManager {
5346
public install(packageName: string, opts?: INpmInstallOptions): IFuture<string> {
5447
return (() => {
5548
try {
56-
this.load().wait(); // It's obligatory to execute load before whatever npm function
57-
5849
var packageToInstall = packageName;
5950
var pathToSave = (opts && opts.pathToSave) || npm.cache;
6051
var version = (opts && opts.version) || null;
61-
var isSemanticVersioningDisabled = options.frameworkPath ? true : false; // We need to disable sem versioning for local packages
62-
63-
if(version) {
64-
this.validateVersion(packageName, version).wait();
65-
packageToInstall = packageName + "@" + version;
66-
}
6752

68-
this.installCore(packageToInstall, pathToSave, isSemanticVersioningDisabled).wait();
53+
return this.installCore(packageToInstall, pathToSave, version).wait();
6954
} catch(error) {
7055
this.$logger.debug(error);
71-
this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED);
56+
this.$errors.fail("%s. Error: %s", NodePackageManager.NPM_LOAD_FAILED, error);
7257
}
7358

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

@@ -86,21 +67,40 @@ export class NodePackageManager implements INodePackageManager {
8667
}).future<string>()();
8768
}
8869

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;
70+
private installCore(packageName: string, pathToSave: string, version: string): IFuture<string> {
71+
return (() => {
72+
if (options.frameworkPath) {
73+
if (this.$fs.getFsStats(options.frameworkPath).wait().isFile()) {
74+
this.npmInstall(packageName, pathToSave, version).wait();
75+
var pathToNodeModules = path.join(pathToSave, "node_modules");
76+
var folders = this.$fs.readDirectory(pathToNodeModules).wait();
77+
return path.join(pathToNodeModules, folders[0]);
78+
}
79+
return options.frameworkPath;
80+
} else {
81+
var version = version || this.getLatestVersion(packageName).wait();
82+
var packagePath = path.join(npm.cache, packageName, version, "package");
83+
if (this.isPackageCached(packagePath).wait()) {
84+
if(!this.isPackageUnpacked(packagePath).wait()) {
85+
this.cacheUnpack(packageName, version).wait();
86+
}
87+
return packagePath;
88+
} else {
89+
this.npmInstall(packageName, pathToSave, version).wait();
90+
return path.join(pathToSave, "node_modules", packageName);
91+
}
9992
}
100-
}
93+
}).future<string>()();
94+
}
10195

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

99+
var incrementedVersion = semver.inc(version, constants.ReleaseType.MINOR);
100+
if (!options.frameworkPath && packageName.indexOf("@") < 0) {
101+
packageName = packageName + "@<" + incrementedVersion;
102+
}
103+
104104
var future = new Future<void>();
105105
npm.commands["install"](pathToSave, packageName, (err: Error, data: any) => {
106106
if(err) {
@@ -113,6 +113,17 @@ export class NodePackageManager implements INodePackageManager {
113113
return future;
114114
}
115115

116+
private isPackageCached(packagePath: string): IFuture<boolean> {
117+
return this.$fs.exists(packagePath);
118+
}
119+
120+
private isPackageUnpacked(packagePath: string): IFuture<boolean> {
121+
return (() => {
122+
return this.$fs.getFsStats(packagePath).wait().isDirectory() &&
123+
helpers.enumerateFilesInDirectorySync(packagePath).length > 1;
124+
}).future<boolean>()();
125+
}
126+
116127
private addToCacheCore(packageName: string, version: string): IFuture<void> {
117128
var future = new Future<void>();
118129
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
@@ -13,6 +13,7 @@ var knownOpts:any = {
1313
"release": Boolean,
1414
"device": Boolean,
1515
"emulator": Boolean,
16+
"symlink": Boolean,
1617
"keyStorePath": String,
1718
"keyStorePassword": String,
1819
"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)