From fdde15c0ac780b510ef4d336191196d7a2a18d9b Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 21 Mar 2017 20:17:29 +0200 Subject: [PATCH 1/5] Fix isValidNativeScript project The isValidNativeScript project method is used in Fusion. However the implementation is not correct. Fix it to have correct behavior. In order to fix it, modify projectData.initialize method - there's obsolete code for migration from .tnsproject to package.json - we do not need it anymore, so remove it. Also fix a case where failing builds do not fail the build process (incorrect argument passed to childProcess.spawnFromEvent). Update documentation for PublicAPI. --- PublicAPI.md | 26 ++++ lib/constants.ts | 1 + lib/project-data.ts | 79 +++------- lib/services/android-project-service.ts | 2 +- lib/services/ios-project-service.ts | 2 +- lib/services/project-service.ts | 3 +- test/nativescript-cli-lib.ts | 2 +- test/project-data.ts | 2 - test/project-service.ts | 196 ++++++------------------ 9 files changed, 100 insertions(+), 213 deletions(-) diff --git a/PublicAPI.md b/PublicAPI.md index 3832969abb..e29610ab9a 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -113,6 +113,32 @@ tns.projectService.createProject(projectSettings) +* `isValidNativeScriptProject(projectDir: string): boolean` - Checks if the specified path is a valid NativeScript project. Returns `true` in case the directory is a valid project, `false` otherwise. + +Sample usage: + + + + + + + + + +
+ JavaScript + + TypeScript +
+
+const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myProject");
+
+
+
+const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myProject");
+
+
+ ## How to add a new method to Public API CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification. For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`. diff --git a/lib/constants.ts b/lib/constants.ts index 93b1aa85b1..c3fa4e4904 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -15,6 +15,7 @@ export const TESTING_FRAMEWORKS = ['jasmine', 'mocha', 'qunit']; export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; export const XML_FILE_EXTENSION = ".xml"; +export const PLATFORMS_DIR_NAME = "platforms"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/project-data.ts b/lib/project-data.ts index dc4ee0b10e..51cfefaabe 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -9,8 +9,6 @@ interface IProjectType { } export class ProjectData implements IProjectData { - private static OLD_PROJECT_FILE_NAME = ".tnsproject"; - /** * NOTE: Order of the elements is important as the TypeScript dependencies are commonly included in Angular project as well. */ @@ -42,45 +40,48 @@ export class ProjectData implements IProjectData { constructor(private $fs: IFileSystem, private $errors: IErrors, - private $logger: ILogger, private $projectHelper: IProjectHelper, private $staticConfig: IStaticConfig, private $options: IOptions) { } - public initializeProjectData(projectDir? :string): void { + public initializeProjectData(projectDir?: string): void { projectDir = projectDir || this.$projectHelper.projectDir; // If no project found, projectDir should be null if (projectDir) { - this.initializeProjectDataCore(projectDir); + const projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); let data: any = null; - if (this.$fs.exists(this.projectFilePath)) { + if (projectFilePath) { let fileContent: any = null; try { - fileContent = this.$fs.readJson(this.projectFilePath); + fileContent = this.$fs.readJson(projectFilePath); data = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; } catch (err) { - this.$errors.fail({ - formatStr: "The project file %s is corrupted." + EOL + + this.$errors.failWithoutHelp( + `The project file ${this.projectFilePath} is corrupted.` + EOL + "Consider restoring an earlier version from your source control or backup." + EOL + - "Additional technical info: %s", - suppressCommandHelp: true - }, - this.projectFilePath, err.toString()); + `Additional technical info: ${err.toString()}`); } if (data) { + this.projectDir = projectDir; + this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir)); + this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME); + this.projectFilePath = projectFilePath; + this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME); + this.appResourcesDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); this.projectId = data.id; this.dependencies = fileContent.dependencies; this.devDependencies = fileContent.devDependencies; this.projectType = this.getProjectType(); - } else { // This is the case when we have package.json file but nativescipt key is not presented in it - this.tryToUpgradeProject(); + + return; } } - } else { // This is the case when no project file found - this.tryToUpgradeProject(); } + + // This is the case when no project file found + this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || path.resolve(".")); } private getProjectType(): string { @@ -97,49 +98,5 @@ export class ProjectData implements IProjectData { return detectedProjectType; } - - private throwNoProjectFoundError(): void { - this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", this.$options.path || path.resolve(".")); - } - - private tryToUpgradeProject(): void { - let projectDir = this.projectDir || path.resolve(this.$options.path || "."); - let oldProjectFilePath = path.join(projectDir, ProjectData.OLD_PROJECT_FILE_NAME); - if (this.$fs.exists(oldProjectFilePath)) { - this.upgrade(projectDir, oldProjectFilePath); - } else { - this.throwNoProjectFoundError(); - } - } - - private upgrade(projectDir: string, oldProjectFilePath: string): void { - try { - let oldProjectData = this.$fs.readJson(oldProjectFilePath); - - let newProjectFilePath = this.projectFilePath || path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); - let newProjectData = this.$fs.exists(newProjectFilePath) ? this.$fs.readJson(newProjectFilePath) : {}; - newProjectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = oldProjectData; - this.$fs.writeJson(newProjectFilePath, newProjectData); - this.projectId = newProjectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE].id; - - this.$fs.deleteFile(oldProjectFilePath); - } catch (err) { - this.$logger.out("An error occurred while upgrading your project."); - throw err; - } - - this.initializeProjectDataCore(projectDir); - - this.$logger.out("Successfully upgraded your project file."); - } - - private initializeProjectDataCore(projectDir: string): void { - this.projectDir = projectDir; - this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir)); - this.platformsDir = path.join(projectDir, "platforms"); - this.projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); - this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME); - this.appResourcesDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); - } } $injector.register("projectData", ProjectData); diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 500d4831e6..452c3cdc38 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -262,7 +262,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject await this.spawn(gradleBin, buildOptions, { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, - { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true }); } else { this.$errors.failWithoutHelp("Cannot complete build because this project is ANT-based." + EOL + "Run `tns platform remove android && tns platform add android` to switch to Gradle and try again."); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index bbdb69932b..d7b57097df 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -339,7 +339,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ args, "exit", { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, - { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true }); // this.$logger.out("xcodebuild build succeded."); await this.createIpa(projectRoot, projectData, buildConfig.buildOutputStdio); diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 5054616043..150430e873 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -73,11 +73,10 @@ export class ProjectService implements IProjectService { public isValidNativeScriptProject(pathToProject?: string): boolean { try { this.$projectData.initializeProjectData(pathToProject); + return !!this.$projectData.projectDir && !!this.$projectData.projectId; } catch (e) { return false; } - - return true; } private getDataFromJson(templatePath: string): any { diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index fc2162d217..0afbc3dc80 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -14,7 +14,7 @@ describe("nativescript-cli-lib", () => { const publicApi: any = { deviceEmitter: null, - projectService: ["createProject"], + projectService: ["createProject", "isValidNativeScriptProject"], localBuildService: ["build"] }; diff --git a/test/project-data.ts b/test/project-data.ts index 44209529d7..2cee2f4b8a 100644 --- a/test/project-data.ts +++ b/test/project-data.ts @@ -25,8 +25,6 @@ describe("projectData", () => { testInjector.register("errors", stubs.ErrorsStub); - testInjector.register("logger", stubs.LoggerStub); - testInjector.register("options", {}); testInjector.register("projectData", ProjectData); diff --git a/test/project-service.ts b/test/project-service.ts index bb2421bfc6..a4bac957e1 100644 --- a/test/project-service.ts +++ b/test/project-service.ts @@ -5,7 +5,6 @@ import { ChildProcess } from "../lib/common/child-process"; import * as ProjectServiceLib from "../lib/services/project-service"; import { ProjectNameService } from "../lib/services/project-name-service"; import * as ProjectDataServiceLib from "../lib/services/project-data-service"; -import * as ProjectDataLib from "../lib/project-data"; import * as ProjectHelperLib from "../lib/common/project-helper"; import { StaticConfig } from "../lib/config"; import * as NpmLib from "../lib/node-package-manager"; @@ -72,7 +71,7 @@ class ProjectIntegrationTest { let sourceDir = projectSourceDirectory; // Hidden files (starting with dots ".") are not copied. - let expectedFiles = fs.enumerateFilesInDirectorySync(sourceDir, (file, stat) => stat.isDirectory() || !_.startsWith(path.basename(file), ".") ); + let expectedFiles = fs.enumerateFilesInDirectorySync(sourceDir, (file, stat) => stat.isDirectory() || !_.startsWith(path.basename(file), ".")); let actualFiles = fs.enumerateFilesInDirectorySync(appDirectoryPath); assert.isTrue(actualFiles.length >= expectedFiles.length, "Files in created project must be at least as files in app dir."); @@ -459,158 +458,65 @@ describe("Project Service Tests", () => { }); }); -}); - -function createTestInjector() { - let testInjector = new yok.Yok(); - - testInjector.register("errors", stubs.ErrorsStub); - testInjector.register('logger', stubs.LoggerStub); - testInjector.register("projectService", ProjectServiceLib.ProjectService); - testInjector.register("projectHelper", ProjectHelperLib.ProjectHelper); - testInjector.register("projectTemplatesService", stubs.ProjectTemplatesService); - testInjector.register("projectNameValidator", mockProjectNameValidator); - - testInjector.register("fs", FileSystem); - testInjector.register("projectDataService", ProjectDataServiceLib.ProjectDataService); - - testInjector.register("staticConfig", StaticConfig); - testInjector.register("analyticsService", { track: async () => undefined }); - - testInjector.register("npmInstallationManager", NpmInstallationManager); - testInjector.register("httpClient", HttpClientLib.HttpClient); - testInjector.register("lockfile", stubs.LockFile); - - testInjector.register("childProcess", ChildProcess); - - testInjector.register('projectData', ProjectDataLib.ProjectData); - testInjector.register("options", Options); - testInjector.register("hostInfo", HostInfo); - - return testInjector; -} - -describe("project upgrade procedure tests", () => { - it("should throw error when no nativescript project folder specified", () => { - let testInjector = createTestInjector(); - let tempFolder = temp.mkdirSync("project upgrade"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let isErrorThrown = false; - - try { - let projectData: IProjectData = testInjector.resolve("projectData"); - projectData.initializeProjectData(); // This should trigger upgrade procedure - } catch (err) { - isErrorThrown = true; - let expectedErrorMessage = "No project found at or above '%s' and neither was a --path specified.," + tempFolder; - assert.equal(expectedErrorMessage, err.toString()); - } - - assert.isTrue(isErrorThrown); - }); - it("should upgrade project when .tnsproject file exists but package.json file doesn't exist", () => { - let testInjector = createTestInjector(); - let fs: IFileSystem = testInjector.resolve("fs"); - - let tempFolder = temp.mkdirSync("projectUpgradeTest2"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let tnsProjectData = { - "id": "org.nativescript.Test", - "tns-ios": { - "version": "1.0.0" - }, - "description": "dummy", - "license": "MIT", - "readme": "dummy", - "repository": "dummy" - }; - let tnsProjectFilePath = path.join(tempFolder, ".tnsproject"); - fs.writeJson(tnsProjectFilePath, tnsProjectData); - let projectData: IProjectData = testInjector.resolve("projectData"); - projectData.initializeProjectData(); // This should trigger upgrade procedure - - let packageJsonFilePath = path.join(tempFolder, "package.json"); - let packageJsonFileContent = require(packageJsonFilePath); - assert.isTrue(fs.exists(packageJsonFilePath)); - assert.isFalse(fs.exists(tnsProjectFilePath)); - assert.deepEqual(tnsProjectData, packageJsonFileContent["nativescript"]); - }); - it("should upgrade project when .tnsproject and package.json exist but nativescript key is not presented in package.json file", () => { - let testInjector = createTestInjector(); - let fs: IFileSystem = testInjector.resolve("fs"); - - let tempFolder = temp.mkdirSync("projectUpgradeTest3"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let tnsProjectData = { - "id": "org.nativescript.Test", - "tns-ios": { - "version": "1.0.1" - } - }; - let packageJsonData = { - "name": "testModuleName", - "version": "0.0.0", - "dependencies": { - "myFirstDep": "0.0.1" - }, - "description": "dummy", - "license": "MIT", - "readme": "dummy", - "repository": "dummy" + describe("isValidNativeScriptProject", () => { + const getTestInjector = (): IInjector => { + const testInjector = new yok.Yok(); + testInjector.register("npm", {}); + testInjector.register("errors", {}); + testInjector.register("fs", {}); + testInjector.register("logger", {}); + testInjector.register("projectDataService", {}); + testInjector.register("projectNameService", {}); + testInjector.register("projectTemplatesService", {}); + testInjector.register("staticConfig", {}); + testInjector.register("projectHelper", {}); + + return testInjector; }; - let tnsProjectFilePath = path.join(tempFolder, ".tnsproject"); - fs.writeJson(tnsProjectFilePath, tnsProjectData); - let packageJsonFilePath = path.join(tempFolder, "package.json"); - fs.writeJson(packageJsonFilePath, packageJsonData); + it("returns true when initialize project data does not throw, projectDir and projectId are valid", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + projectDir: "projectDir", + projectId: "projectId", + initializeProjectData: (): void => undefined + }); - let projectData: IProjectData = testInjector.resolve("projectData"); - projectData.initializeProjectData(); // This should trigger upgrade procedure + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isTrue(projectService.isValidNativeScriptProject("some-dir")); + }); - let packageJsonFileContent = require(packageJsonFilePath); - let expectedPackageJsonContent: any = packageJsonData; - expectedPackageJsonContent["nativescript"] = tnsProjectData; - assert.deepEqual(expectedPackageJsonContent, packageJsonFileContent); - }); - it("shouldn't upgrade project when .tnsproject and package.json exist and nativescript key is presented in package.json file", () => { - let testInjector = createTestInjector(); - let fs: IFileSystem = testInjector.resolve("fs"); + it("returns false when initialize project data throws", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + initializeProjectData: (): void => { throw new Error("err"); } + }); - let tempFolder = temp.mkdirSync("projectUpgradeTest4"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let tnsProjectData = { + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isFalse(projectService.isValidNativeScriptProject("some-dir")); + }); - }; - let packageJsonData = { - "name": "testModuleName", - "version": "0.0.0", - "dependencies": { - "myFirstDep": "0.0.2" - }, - "nativescript": { - "id": "org.nativescript.Test", - "tns-ios": { - "version": "1.0.2" - } - }, - "description": "dummy", - "license": "MIT", - "readme": "dummy", - "repository": "dummy" - }; + it("returns false when initializeProjectData does not throw, but there's no projectDir set", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + projectId: "projectId", + initializeProjectData: (): void => undefined + }); - fs.writeJson(path.join(tempFolder, ".tnsproject"), tnsProjectData); - fs.writeJson(path.join(tempFolder, "package.json"), packageJsonData); - testInjector.resolve("projectData"); // This should trigger upgrade procedure + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isFalse(projectService.isValidNativeScriptProject("some-dir")); + }); - let packageJsonFilePath = path.join(tempFolder, "package.json"); - let packageJsonFileContent = require(packageJsonFilePath); + it("returns false when initializeProjectData does not throw, but there's no projectId set", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + projectDir: "projectDir", + initializeProjectData: (): void => undefined + }); - assert.deepEqual(packageJsonData, packageJsonFileContent); + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isFalse(projectService.isValidNativeScriptProject("some-dir")); + }); }); }); From 19ed1ffa235d4374df6341f54ced0e110d81f32d Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 21 Mar 2017 20:22:46 +0200 Subject: [PATCH 2/5] Throw correct error when build for iOS in non-interactive terminal Also in case CLI's executed in non-interactive terminal and there's no DEVELOPMENT_TEAM set, tns build ios --for-device fails with message "Console is not interactive and no default action specified" which does not give any info to the users. So in this case add more descriptive error message. --- lib/services/ios-project-service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index d7b57097df..aca97b78ff 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1170,6 +1170,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f teamId = teams[0].id; this.$logger.warn("Found and using the following development team installed on your system: " + teams[0].name + " (" + teams[0].id + ")"); } else if (teams.length > 0) { + if (!helpers.isInteractive()) { + this.$errors.failWithoutHelp(`Unable to determine default development team. Available development teams are: ${_.map(teams, team => team.id)}. Specify team in app/App_Resources/iOS/build.xcconfig file in the following way: DEVELOPMENT_TEAM = `); + } + let choices: string[] = []; for (let team of teams) { choices.push(team.name + " (" + team.id + ")"); From 824423111bc1153fbc93e32bdf918ff6ad6ed92d Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 21 Mar 2017 18:29:08 +0200 Subject: [PATCH 3/5] Get latest application package for current specified configuration When we try to get the latest application package for device/emulator, we check the build output directory and get latest available .apk/.ipa. However this is not always correct as sometimes we do not build the app. Consider the following case: * tns build android --release * tns build android * tns build android --release At the last point, the build will not be executed, as there are no changes. However the last built .apk is from the debug build (we have release .apk, but it's older). So in case we try to get the last build output from last operation, CLI will return the debug.apk Fix this by checking the current build configuration and get the latest result by using it. For iOS respect the expected output - is it for device or for simulator as the output is different. --- lib/commands/appstore-upload.ts | 2 +- lib/commands/build.ts | 2 +- lib/definitions/platform.d.ts | 15 ++++++++------- lib/providers/livesync-provider.ts | 4 ++-- lib/services/android-debug-service.ts | 2 +- lib/services/android-project-service.ts | 19 +++++++++++++------ lib/services/ios-debug-service.ts | 2 +- lib/services/ios-project-service.ts | 13 +++++++------ lib/services/local-build-service.ts | 2 +- lib/services/platform-service.ts | 22 +++++++++++----------- test/platform-commands.ts | 1 + test/stubs.ts | 4 ++-- 12 files changed, 49 insertions(+), 39 deletions(-) diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index 33736455a2..8fb3623acb 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -73,7 +73,7 @@ export class PublishIOS implements ICommand { // This is not very correct as if we build multiple targets we will try to sign all of them using the signing identity here. await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); await this.$platformService.buildPlatform(platform, iOSBuildConfig, this.$projectData); - ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice }, this.$projectData); + ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice, isReleaseBuild: iOSBuildConfig.release }, this.$projectData); } else { this.$logger.info("No .ipa, mobile provision or certificate set. Perfect! Now we'll build .xcarchive and let Xcode pick the distribution certificate and provisioning profile for you when exporting .ipa for AppStore submission."); await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 524656a0bc..2a7dbd8f2b 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -26,7 +26,7 @@ export class BuildCommandBase { }; await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); if (this.$options.copyTo) { - this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice }, this.$projectData); + this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice, isReleaseBuild: buildConfig.release }, this.$projectData); } } } diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 193bd93866..0c7897b1b9 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -143,28 +143,30 @@ interface IPlatformService extends NodeJS.EventEmitter { /** * Returns information about the latest built application for device in the current project. * @param {IPlatformData} platformData Data describing the current platform. + * @param {any} buildOptions Defines if the build is for release configuration. * @returns {IApplicationPackage} Information about latest built application. */ - getLatestApplicationPackageForDevice(platformData: IPlatformData): IApplicationPackage; + getLatestApplicationPackageForDevice(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage; /** * Returns information about the latest built application for simulator in the current project. * @param {IPlatformData} platformData Data describing the current platform. + * @param {any} buildOptions Defines if the build is for release configuration. * @returns {IApplicationPackage} Information about latest built application. */ - getLatestApplicationPackageForEmulator(platformData: IPlatformData): IApplicationPackage; + getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage; /** * Copies latest build output to a specified location. * @param {string} platform Mobile platform - Android, iOS. * @param {string} targetPath Destination where the build artifact should be copied. - * @param {{isForDevice: boolean}} settings Defines if the searched artifact should be for simulator. + * @param {{isForDevice: boolean, isReleaseBuild: boolean}} settings Defines if the searched artifact should be for simulator and is it built for release. * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - copyLastOutput(platform: string, targetPath: string, settings: {isForDevice: boolean}, projectData: IProjectData): void; + copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): void; - lastOutputPath(platform: string, settings: { isForDevice: boolean }, projectData: IProjectData): string; + lastOutputPath(platform: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): string; /** * Reads contents of a file on device. @@ -209,8 +211,7 @@ interface IPlatformData { appDestinationDirectoryPath: string; deviceBuildOutputPath: string; emulatorBuildOutputPath?: string; - validPackageNamesForDevice: string[]; - validPackageNamesForEmulator?: string[]; + getValidPackageNames(buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[]; frameworkFilesExtensions: string[]; frameworkDirectoriesExtensions?: string[]; frameworkDirectoriesNames?: string[]; diff --git a/lib/providers/livesync-provider.ts b/lib/providers/livesync-provider.ts index 747cf03a8b..24624367dc 100644 --- a/lib/providers/livesync-provider.ts +++ b/lib/providers/livesync-provider.ts @@ -45,10 +45,10 @@ export class LiveSyncProvider implements ILiveSyncProvider { await this.$platformService.buildPlatform(device.deviceInfo.platform, buildConfig, projectData); let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); if (device.isEmulator) { - return this.$platformService.getLatestApplicationPackageForEmulator(platformData).packageName; + return this.$platformService.getLatestApplicationPackageForEmulator(platformData, { isReleaseBuild: buildConfig.release }).packageName; } - return this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; + return this.$platformService.getLatestApplicationPackageForDevice(platformData, { isReleaseBuild: buildConfig.release }).packageName; } public async preparePlatformForSync(platform: string, provision: any, projectData: IProjectData): Promise { diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index 5f22534ea0..2f89a608db 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -117,7 +117,7 @@ class AndroidDebugService implements IDebugService { this.$options.forDevice = !!cachedDeviceOption; let platformData = this.$platformsData.getPlatformData(this.platform, projectData); - packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; + packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData, { isReleaseBuild: this.$options.release }).packageName; this.$logger.out("Using ", packageFile); } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 452c3cdc38..9daf727956 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -57,12 +57,19 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject emulatorServices: this.$androidEmulatorServices, projectRoot: projectRoot, deviceBuildOutputPath: path.join(projectRoot, "build", "outputs", "apk"), - validPackageNamesForDevice: [ - `${packageName}-debug.apk`, - `${packageName}-release.apk`, - `${projectData.projectName}-debug.apk`, - `${projectData.projectName}-release.apk` - ], + getValidPackageNames: (buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[] => { + if (buildOptions.isReleaseBuild) { + return [ + `${packageName}-release.apk`, + `${projectData.projectName}-release.apk` + ]; + } + + return [ + `${packageName}-debug.apk`, + `${projectData.projectName}-debug.apk`, + ]; + }, frameworkFilesExtensions: [".jar", ".dat", ".so"], configurationFileName: "AndroidManifest.xml", configurationFilePath: path.join(projectRoot, "src", "main", "AndroidManifest.xml"), diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 6f81637e98..e94d3590c5 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -96,7 +96,7 @@ class IOSDebugService implements IDebugService { private async emulatorDebugBrk(projectData: IProjectData, shouldBreak?: boolean): Promise { let platformData = this.$platformsData.getPlatformData(this.platform, projectData); - let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData); + let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData, { isReleaseBuild: this.$options.release }); let args = shouldBreak ? "--nativescript-debug-brk" : "--nativescript-debug-start"; let child_process = await this.$iOSEmulatorServices.runApplicationOnEmulator(emulatorPackage.packageName, { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index aca97b78ff..75438ad918 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -64,12 +64,13 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ projectRoot: projectRoot, deviceBuildOutputPath: path.join(projectRoot, "build", "device"), emulatorBuildOutputPath: path.join(projectRoot, "build", "emulator"), - validPackageNamesForDevice: [ - projectData.projectName + ".ipa" - ], - validPackageNamesForEmulator: [ - projectData.projectName + ".app" - ], + getValidPackageNames: (buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[] => { + if (buildOptions.isForDevice) { + return [projectData.projectName + ".ipa"]; + } + + return [projectData.projectName + ".app"]; + }, frameworkFilesExtensions: [".a", ".framework", ".bin"], frameworkDirectoriesExtensions: [".framework"], frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"], diff --git a/lib/services/local-build-service.ts b/lib/services/local-build-service.ts index cea0053505..1071bbd9fd 100644 --- a/lib/services/local-build-service.ts +++ b/lib/services/local-build-service.ts @@ -16,7 +16,7 @@ export class LocalBuildService extends EventEmitter { }); platformBuildOptions.buildOutputStdio = "pipe"; await this.$platformService.buildPlatform(platform, platformBuildOptions, this.$projectData); - return this.$platformService.lastOutputPath(platform, { isForDevice: platformBuildOptions.buildForDevice }, this.$projectData); + return this.$platformService.lastOutputPath(platform, { isForDevice: platformBuildOptions.buildForDevice, isReleaseBuild: platformBuildOptions.release }, this.$projectData); } } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 1880b754e7..5db966ea66 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -364,7 +364,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { if (!this.$fs.exists(outputPath)) { return true; } - let packageNames = forDevice ? platformData.validPackageNamesForDevice : platformData.validPackageNamesForEmulator; + let packageNames = platformData.getValidPackageNames({ isForDevice: forDevice }); let packages = this.getApplicationPackages(outputPath, packageNames); if (packages.length === 0) { return true; @@ -430,9 +430,9 @@ export class PlatformService extends EventEmitter implements IPlatformService { let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); let packageFile = ""; if (this.$devicesService.isiOSSimulator(device)) { - packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; + packageFile = this.getLatestApplicationPackageForEmulator(platformData, { isReleaseBuild: options.release }).packageName; } else { - packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; + packageFile = this.getLatestApplicationPackageForDevice(platformData, { isReleaseBuild: options.release }).packageName; } await platformData.platformProjectService.cleanDeviceTempFolder(device.deviceInfo.identifier, projectData); @@ -581,13 +581,13 @@ export class PlatformService extends EventEmitter implements IPlatformService { appUpdater.cleanDestinationApp(); } - public lastOutputPath(platform: string, settings: { isForDevice: boolean }, projectData: IProjectData): string { + public lastOutputPath(platform: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): string { let packageFile: string; let platformData = this.$platformsData.getPlatformData(platform, projectData); if (settings.isForDevice) { - packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; + packageFile = this.getLatestApplicationPackageForDevice(platformData, settings).packageName; } else { - packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; + packageFile = this.getLatestApplicationPackageForEmulator(platformData, settings).packageName; } if (!packageFile || !this.$fs.exists(packageFile)) { this.$errors.failWithoutHelp("Unable to find built application. Try 'tns build %s'.", platform); @@ -595,7 +595,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { return packageFile; } - public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }, projectData: IProjectData): void { + public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): void { platform = platform.toLowerCase(); targetPath = path.resolve(targetPath); @@ -742,12 +742,12 @@ export class PlatformService extends EventEmitter implements IPlatformService { return packages[0]; } - public getLatestApplicationPackageForDevice(platformData: IPlatformData): IApplicationPackage { - return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.validPackageNamesForDevice); + public getLatestApplicationPackageForDevice(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage { + return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: true, isReleaseBuild: buildOptions.isReleaseBuild })); } - public getLatestApplicationPackageForEmulator(platformData: IPlatformData): IApplicationPackage { - return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.validPackageNamesForEmulator || platformData.validPackageNamesForDevice); + public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage { + return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: false, isReleaseBuild: buildOptions.isReleaseBuild })); } private async updatePlatform(platform: string, version: string, platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise { diff --git a/test/platform-commands.ts b/test/platform-commands.ts index af5eec1d26..202f0d8c45 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -32,6 +32,7 @@ class PlatformData implements IPlatformData { emulatorServices: Mobile.IEmulatorPlatformServices = null; projectRoot = ""; deviceBuildOutputPath = ""; + getValidPackageNames = (buildOptions: {isForDevice?: boolean, isReleaseBuild?: boolean}) => [""]; validPackageNamesForDevice: string[] = []; frameworkFilesExtensions = [".jar", ".dat"]; appDestinationDirectoryPath = ""; diff --git a/test/stubs.ts b/test/stubs.ts index 010f78b230..b2ed72a0ba 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -255,7 +255,7 @@ export class PlatformsDataStub extends EventEmitter implements IPlatformsData { normalizedPlatformName: "", appDestinationDirectoryPath: "", deviceBuildOutputPath: "", - validPackageNamesForDevice: [], + getValidPackageNames: (buildOptions: {isForDevice?: boolean, isReleaseBuild?: boolean}) => [], frameworkFilesExtensions: [], relativeToFrameworkConfigurationFilePath: "", fastLivesyncFileExtensions: [] @@ -276,7 +276,7 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor emulatorServices: undefined, projectRoot: "", deviceBuildOutputPath: "", - validPackageNamesForDevice: [], + getValidPackageNames: (buildOptions: {isForDevice?: boolean, isReleaseBuild?: boolean}) => [], frameworkFilesExtensions: [], appDestinationDirectoryPath: "", relativeToFrameworkConfigurationFilePath: "", From 957f603316fd4049bd2661c8a2e68047248bc7bb Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Wed, 22 Mar 2017 08:55:29 +0200 Subject: [PATCH 4/5] Fix local builds when CLI is required as lib When CLI is required as library, the `$options` dependency is not populated. However the projectChangesService relies on it in order to determine if the project should be prepared/built. When CLI is required as library and you change only the build configuration (debug/release), the project is not rebuilt. However when you use the CLI and try the same, a new full build is triggered. Fix this by parsing required boolean flags to projectChangesService and exclude `$options` from its implementation. This way local builds will work in the same manner both from command line and when required as library. Steps to reproduce the problem: * use CLI as lib * create local build for android in debug configuration * create local build for android in release configuration - you'll notice gradle clean is not called at all and also in the `/platforms/android/build/outputs/apks/` there are both debug and release apks. This should never happen when changing configurations. --- lib/definitions/project-changes.d.ts | 4 +++- lib/services/android-project-service.ts | 4 ++++ lib/services/local-build-service.ts | 2 +- lib/services/platform-service.ts | 2 +- lib/services/project-changes-service.ts | 19 +++++++++---------- test/nativescript-cli-lib.ts | 4 ++-- test/npm-support.ts | 7 ++++--- test/platform-service.ts | 4 ++-- 8 files changed, 26 insertions(+), 20 deletions(-) diff --git a/lib/definitions/project-changes.d.ts b/lib/definitions/project-changes.d.ts index b394fb92e8..cd5bae3cfa 100644 --- a/lib/definitions/project-changes.d.ts +++ b/lib/definitions/project-changes.d.ts @@ -19,8 +19,10 @@ interface IProjectChangesInfo { changesRequireBuild: boolean; } +interface IProjectChangesOptions extends IAppFilesUpdaterOptions, IProvision {} + interface IProjectChangesService { - checkForChanges(platform: string, projectData: IProjectData): IProjectChangesInfo; + checkForChanges(platform: string, projectData: IProjectData, buildOptions: IProjectChangesOptions): IProjectChangesInfo; getPrepareInfo(platform: string, projectData: IProjectData): IPrepareInfo; savePrepareInfo(platform: string, projectData: IProjectData): void; getPrepareInfoFilePath(platform: string, projectData: IProjectData): string; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 9daf727956..4459821efa 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -433,6 +433,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public async cleanProject(projectRoot: string, options: string[], projectData: IProjectData): Promise { + // In case options are not passed, we'll use the default ones for cleaning the project. + // In case we do not do this, we'll required android-23 (default in build.gradle) for cleaning the project. + options = options.length === 0 ? this.getBuildOptions({ release: false }, projectData) : options; + options.unshift("clean"); let gradleBin = path.join(projectRoot, "gradlew"); diff --git a/lib/services/local-build-service.ts b/lib/services/local-build-service.ts index 1071bbd9fd..2a509e09b8 100644 --- a/lib/services/local-build-service.ts +++ b/lib/services/local-build-service.ts @@ -9,7 +9,7 @@ export class LocalBuildService extends EventEmitter { public async build(platform: string, platformBuildOptions: IPlatformBuildData, platformTemplate?: string): Promise { this.$projectData.initializeProjectData(platformBuildOptions.projectDir); - await this.$platformService.preparePlatform(platform, platformBuildOptions, platformTemplate, this.$projectData, undefined); + await this.$platformService.preparePlatform(platform, platformBuildOptions, platformTemplate, this.$projectData, { provision: platformBuildOptions.provision, sdk: null }); this.$platformService.on(BUILD_OUTPUT_EVENT_NAME, (data: any) => { data.projectDir = platformBuildOptions.projectDir; this.emit(BUILD_OUTPUT_EVENT_NAME, data); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 5db966ea66..70c8cf405e 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -233,7 +233,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await this.$pluginsService.validate(platformData, projectData); await this.ensurePlatformInstalled(platform, platformTemplate, projectData, platformSpecificData); - let changesInfo = this.$projectChangesService.checkForChanges(platform, projectData); + let changesInfo = this.$projectChangesService.checkForChanges(platform, projectData, { bundle: appFilesUpdaterOptions.bundle, release: appFilesUpdaterOptions.release, provision: platformSpecificData.provision }); this.$logger.trace("Changes info in prepare platform:", changesInfo); diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 1c454ffbf2..94e1d74aa4 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -38,7 +38,6 @@ export class ProjectChangesService implements IProjectChangesService { constructor( private $platformsData: IPlatformsData, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $options: IOptions, private $fs: IFileSystem) { } @@ -46,10 +45,10 @@ export class ProjectChangesService implements IProjectChangesService { return this._changesInfo; } - public checkForChanges(platform: string, projectData: IProjectData): IProjectChangesInfo { + public checkForChanges(platform: string, projectData: IProjectData, buildOptions: IProjectChangesOptions): IProjectChangesInfo { let platformData = this.$platformsData.getPlatformData(platform, projectData); this._changesInfo = new ProjectChangesInfo(); - if (!this.ensurePrepareInfo(platform, projectData)) { + if (!this.ensurePrepareInfo(platform, projectData, buildOptions)) { this._newFiles = 0; this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); this._changesInfo.packageChanged = this.filesChanged([path.join(projectData.projectDir, "package.json")]); @@ -77,7 +76,7 @@ export class ProjectChangesService implements IProjectChangesService { } } if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - const nextCommandProvisionUUID = this.$options.provision; + const nextCommandProvisionUUID = buildOptions.provision; // We should consider reading here the provisioning profile UUID from the xcodeproj and xcconfig. const prevProvisionUUID = this._prepareInfo.iOSProvisioningProfileUUID; if (nextCommandProvisionUUID !== prevProvisionUUID) { @@ -86,13 +85,13 @@ export class ProjectChangesService implements IProjectChangesService { this._prepareInfo.iOSProvisioningProfileUUID = nextCommandProvisionUUID; } } - if (this.$options.bundle !== this._prepareInfo.bundle || this.$options.release !== this._prepareInfo.release) { + if (buildOptions.bundle !== this._prepareInfo.bundle || buildOptions.release !== this._prepareInfo.release) { this._changesInfo.appFilesChanged = true; this._changesInfo.appResourcesChanged = true; this._changesInfo.modulesChanged = true; this._changesInfo.configChanged = true; - this._prepareInfo.release = this.$options.release; - this._prepareInfo.bundle = this.$options.bundle; + this._prepareInfo.release = buildOptions.release; + this._prepareInfo.bundle = buildOptions.bundle; } if (this._changesInfo.packageChanged) { this._changesInfo.modulesChanged = true; @@ -134,7 +133,7 @@ export class ProjectChangesService implements IProjectChangesService { this.$fs.writeJson(prepareInfoFilePath, this._prepareInfo); } - private ensurePrepareInfo(platform: string, projectData: IProjectData): boolean { + private ensurePrepareInfo(platform: string, projectData: IProjectData, buildOptions: { bundle: boolean, release: boolean, provision: string }): boolean { this._prepareInfo = this.getPrepareInfo(platform, projectData); if (this._prepareInfo) { let platformData = this.$platformsData.getPlatformData(platform, projectData); @@ -145,8 +144,8 @@ export class ProjectChangesService implements IProjectChangesService { } this._prepareInfo = { time: "", - bundle: this.$options.bundle, - release: this.$options.release, + bundle: buildOptions.bundle, + release: buildOptions.release, changesRequireBuild: true, changesRequireBuildTime: null }; diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index 0afbc3dc80..f4fe159e97 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -15,8 +15,8 @@ describe("nativescript-cli-lib", () => { const publicApi: any = { deviceEmitter: null, projectService: ["createProject", "isValidNativeScriptProject"], - localBuildService: ["build"] - + localBuildService: ["build"], + deviceLogProvider: null }; const pathToEntryPoint = path.join(__dirname, "..", "lib", "nativescript-cli-lib.js").replace(/\\/g, "\\\\"); diff --git a/test/npm-support.ts b/test/npm-support.ts index 3b5ab60f3d..261cfe9ad0 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -187,11 +187,12 @@ async function addDependencies(testInjector: IInjector, projectFolder: string, d } async function preparePlatform(testInjector: IInjector): Promise { - let platformService: IPlatformService = testInjector.resolve("platformService"); - let projectData: IProjectData = testInjector.resolve("projectData"); + const platformService: IPlatformService = testInjector.resolve("platformService"); + const projectData: IProjectData = testInjector.resolve("projectData"); projectData.initializeProjectData(); + const options: IOptions = testInjector.resolve("options"); - await platformService.preparePlatform("android", { bundle: false, release: false }, "", projectData, undefined); + await platformService.preparePlatform("android", { bundle: options.bundle, release: options.release }, "", projectData, { provision: options.provision, sdk: options.sdk }); } describe("Npm support tests", () => { diff --git a/test/platform-service.ts b/test/platform-service.ts index cd524e3a3c..7d7216d184 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -369,7 +369,7 @@ describe('Platform Service Tests', () => { platformService = testInjector.resolve("platformService"); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: release }; - await platformService.preparePlatform(platformToTest, appFilesUpdaterOptions, "", projectData, undefined); + await platformService.preparePlatform(platformToTest, appFilesUpdaterOptions, "", projectData, { provision: null, sdk: null }); let test1FileName = platformToTest.toLowerCase() === "ios" ? "test1.js" : "test2.js"; let test2FileName = platformToTest.toLowerCase() === "ios" ? "test2.js" : "test1.js"; @@ -446,7 +446,7 @@ describe('Platform Service Tests', () => { try { testInjector.resolve("$logger").warn = (text: string) => warnings += text; const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: false }; - await platformService.preparePlatform("android", appFilesUpdaterOptions, "", projectData, undefined); + await platformService.preparePlatform("android", appFilesUpdaterOptions, "", projectData, { provision: null, sdk: null }); } finally { testInjector.resolve("$logger").warn = oldLoggerWarner; } From c5b1129782ca8d061362bbf16bd57561babdb54c Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Thu, 23 Mar 2017 21:34:03 +0200 Subject: [PATCH 5/5] Pass buildConfig when getting path to last build output Pass the whole buildConfig object when getting the path to the last build output. This required changes in debug services and TestExecutionService. Also change the args of cleanProject method - the `options` passed to `gradle` are now determined in the method itself instead of passing them from the caller. This way we'll not require "android-23" (the default one set in build.gradle) when we miss to pass the required options. --- lib/commands/appstore-upload.ts | 2 +- lib/commands/build.ts | 2 +- lib/commands/debug.ts | 16 ++++++++----- lib/common | 2 +- lib/definitions/debug.d.ts | 4 ++-- lib/definitions/platform.d.ts | 23 +++++++++++------- lib/definitions/project.d.ts | 3 +-- lib/project-data.ts | 15 +++++++----- lib/providers/livesync-provider.ts | 4 ++-- lib/services/android-debug-service.ts | 16 ++++++------- lib/services/android-project-service.ts | 30 ++++++++--------------- lib/services/ios-debug-service.ts | 22 ++++++++--------- lib/services/ios-project-service.ts | 2 +- lib/services/local-build-service.ts | 2 +- lib/services/platform-service.ts | 32 ++++++++++++------------- lib/services/project-changes-service.ts | 18 +++++++------- lib/services/test-execution-service.ts | 29 ++++++++++++---------- test/project-data.ts | 2 ++ test/stubs.ts | 8 +++---- 19 files changed, 120 insertions(+), 112 deletions(-) diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index 8fb3623acb..89fa327030 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -73,7 +73,7 @@ export class PublishIOS implements ICommand { // This is not very correct as if we build multiple targets we will try to sign all of them using the signing identity here. await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); await this.$platformService.buildPlatform(platform, iOSBuildConfig, this.$projectData); - ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice, isReleaseBuild: iOSBuildConfig.release }, this.$projectData); + ipaFilePath = this.$platformService.lastOutputPath(platform, iOSBuildConfig, this.$projectData); } else { this.$logger.info("No .ipa, mobile provision or certificate set. Perfect! Now we'll build .xcarchive and let Xcode pick the distribution certificate and provisioning profile for you when exporting .ipa for AppStore submission."); await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 2a7dbd8f2b..d8107e9cb7 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -26,7 +26,7 @@ export class BuildCommandBase { }; await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); if (this.$options.copyTo) { - this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice, isReleaseBuild: buildConfig.release }, this.$projectData); + this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData); } } } diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index d9388f8b77..a51b3af5ab 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -16,11 +16,6 @@ } public async execute(args: string[]): Promise { - if (this.$options.start) { - return this.debugService.debug(this.$projectData); - } - - const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; const deployOptions: IDeployPlatformOptions = { clean: this.$options.clean, device: this.$options.device, @@ -31,6 +26,15 @@ provision: this.$options.provision, teamId: this.$options.teamId }; + + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); + + if (this.$options.start) { + return this.debugService.debug(this.$projectData, buildConfig); + } + + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + await this.$platformService.deployPlatform(this.$devicesService.platform, appFilesUpdaterOptions, deployOptions, this.$projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); this.$config.debugLivesync = true; let applicationReloadAction = async (deviceAppData: Mobile.IDeviceAppData): Promise => { @@ -45,7 +49,7 @@ await deviceAppData.device.applicationManager.stopApplication(applicationId); - await this.debugService.debug(this.$projectData); + await this.debugService.debug(this.$projectData, buildConfig); }; return this.$usbLiveSyncService.liveSync(this.$devicesService.platform, this.$projectData, applicationReloadAction); } diff --git a/lib/common b/lib/common index 137dc3cd5c..f0fec92382 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 137dc3cd5c9fcc8870ce484026ed90f3191cddd6 +Subproject commit f0fec923825f23c6ed85acfd8de3eff5c860560c diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 9d3026e655..e424594783 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -1,6 +1,6 @@ interface IDebugService { - debug(projectData: IProjectData): Promise; - debugStart(projectData: IProjectData): Promise; + debug(projectData: IProjectData, buildConfig: IBuildConfig): Promise; + debugStart(projectData: IProjectData, buildConfig: IBuildConfig): Promise; debugStop(): Promise platform: string; } \ No newline at end of file diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 0c7897b1b9..223fc2bfb1 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -1,6 +1,6 @@ interface IPlatformService extends NodeJS.EventEmitter { cleanPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, framework?: string): Promise; - + addPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, frameworkPath?: string): Promise; /** @@ -143,30 +143,37 @@ interface IPlatformService extends NodeJS.EventEmitter { /** * Returns information about the latest built application for device in the current project. * @param {IPlatformData} platformData Data describing the current platform. - * @param {any} buildOptions Defines if the build is for release configuration. + * @param {IBuildConfig} buildConfig Defines if the build is for release configuration. * @returns {IApplicationPackage} Information about latest built application. */ - getLatestApplicationPackageForDevice(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage; + getLatestApplicationPackageForDevice(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage; /** * Returns information about the latest built application for simulator in the current project. * @param {IPlatformData} platformData Data describing the current platform. - * @param {any} buildOptions Defines if the build is for release configuration. + * @param {IBuildConfig} buildConfig Defines if the build is for release configuration. * @returns {IApplicationPackage} Information about latest built application. */ - getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage; + getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage; /** * Copies latest build output to a specified location. * @param {string} platform Mobile platform - Android, iOS. * @param {string} targetPath Destination where the build artifact should be copied. - * @param {{isForDevice: boolean, isReleaseBuild: boolean}} settings Defines if the searched artifact should be for simulator and is it built for release. + * @param {IBuildConfig} buildConfig Defines if the searched artifact should be for simulator and is it built for release. * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): void; + copyLastOutput(platform: string, targetPath: string, buildConfig: IBuildConfig, projectData: IProjectData): void; - lastOutputPath(platform: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): string; + /** + * Gets the latest build output. + * @param {string} platform Mobile platform - Android, iOS. + * @param {IBuildConfig} buildConfig Defines if the searched artifact should be for simulator and is it built for release. + * @param {IProjectData} projectData DTO with information about the project. + * @returns {string} The path to latest built artifact. + */ + lastOutputPath(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): string; /** * Reads contents of a file on device. diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index bb180a357c..c08a61ab42 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -254,11 +254,10 @@ interface IPlatformProjectService extends NodeJS.EventEmitter { /** * Removes build artifacts specific to the platform * @param {string} projectRoot The root directory of the native project. - * @param {string[]} options Options that can be passed to clean command. * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - cleanProject(projectRoot: string, options: string[], projectData: IProjectData): Promise + cleanProject(projectRoot: string, projectData: IProjectData): Promise } interface IAndroidProjectPropertiesManager { diff --git a/lib/project-data.ts b/lib/project-data.ts index 51cfefaabe..da438d88cc 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -42,7 +42,8 @@ export class ProjectData implements IProjectData { private $errors: IErrors, private $projectHelper: IProjectHelper, private $staticConfig: IStaticConfig, - private $options: IOptions) { } + private $options: IOptions, + private $logger: ILogger) { } public initializeProjectData(projectDir?: string): void { projectDir = projectDir || this.$projectHelper.projectDir; @@ -51,15 +52,14 @@ export class ProjectData implements IProjectData { const projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); let data: any = null; - if (projectFilePath) { + if (this.$fs.exists(projectFilePath)) { let fileContent: any = null; try { fileContent = this.$fs.readJson(projectFilePath); data = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; } catch (err) { - this.$errors.failWithoutHelp( - `The project file ${this.projectFilePath} is corrupted.` + EOL + - "Consider restoring an earlier version from your source control or backup." + EOL + + this.$errors.failWithoutHelp(`The project file ${this.projectFilePath} is corrupted. ${EOL}` + + `Consider restoring an earlier version from your source control or backup.${EOL}` + `Additional technical info: ${err.toString()}`); } @@ -80,8 +80,11 @@ export class ProjectData implements IProjectData { } } + const currentDir = path.resolve("."); + this.$logger.trace(`Unable to find project. projectDir: ${projectDir}, options.path: ${this.$options.path}, ${currentDir}`); + // This is the case when no project file found - this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || path.resolve(".")); + this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || currentDir); } private getProjectType(): string { diff --git a/lib/providers/livesync-provider.ts b/lib/providers/livesync-provider.ts index 24624367dc..a175688275 100644 --- a/lib/providers/livesync-provider.ts +++ b/lib/providers/livesync-provider.ts @@ -45,10 +45,10 @@ export class LiveSyncProvider implements ILiveSyncProvider { await this.$platformService.buildPlatform(device.deviceInfo.platform, buildConfig, projectData); let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); if (device.isEmulator) { - return this.$platformService.getLatestApplicationPackageForEmulator(platformData, { isReleaseBuild: buildConfig.release }).packageName; + return this.$platformService.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; } - return this.$platformService.getLatestApplicationPackageForDevice(platformData, { isReleaseBuild: buildConfig.release }).packageName; + return this.$platformService.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; } public async preparePlatformForSync(platform: string, provision: any, projectData: IProjectData): Promise { diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index 2f89a608db..24ba6c0e1a 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -28,18 +28,18 @@ class AndroidDebugService implements IDebugService { this._device = newDevice; } - public async debug(projectData: IProjectData): Promise { + public async debug(projectData: IProjectData, buildConfig: IBuildConfig): Promise { return this.$options.emulator - ? this.debugOnEmulator(projectData) - : this.debugOnDevice(projectData); + ? this.debugOnEmulator(projectData, buildConfig) + : this.debugOnDevice(projectData, buildConfig); } - private async debugOnEmulator(projectData: IProjectData): Promise { + private async debugOnEmulator(projectData: IProjectData, buildConfig: IBuildConfig): Promise { // Assure we've detected the emulator as device // For example in case deployOnEmulator had stated new emulator instance // we need some time to detect it. Let's force detection. await this.$androidDeviceDiscovery.startLookingForDevices(); - await this.debugOnDevice(projectData); + await this.debugOnDevice(projectData, buildConfig); } private isPortAvailable(candidatePort: number): Promise { @@ -108,7 +108,7 @@ class AndroidDebugService implements IDebugService { return this.device.adb.executeCommand(["forward", `tcp:${local}`, `localabstract:${remote}`]); } - private async debugOnDevice(projectData: IProjectData): Promise { + private async debugOnDevice(projectData: IProjectData, buildConfig: IBuildConfig): Promise { let packageFile = ""; if (!this.$options.start && !this.$options.emulator) { @@ -117,7 +117,7 @@ class AndroidDebugService implements IDebugService { this.$options.forDevice = !!cachedDeviceOption; let platformData = this.$platformsData.getPlatformData(this.platform, projectData); - packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData, { isReleaseBuild: this.$options.release }).packageName; + packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; this.$logger.out("Using ", packageFile); } @@ -170,7 +170,7 @@ class AndroidDebugService implements IDebugService { await this.debugStartCore(packageName); } - public async debugStart(projectData: IProjectData): Promise { + public async debugStart(projectData: IProjectData, buildConfig: IBuildConfig): Promise { await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); let action = (device: Mobile.IAndroidDevice): Promise => { this.device = device; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 4459821efa..8baaa94d97 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -5,6 +5,7 @@ import * as semver from "semver"; import * as projectServiceBaseLib from "./platform-project-service-base"; import { DeviceAndroidDebugBridge } from "../common/mobile/android/device-android-debug-bridge"; import { EOL } from "os"; +import { Configurations } from "../common/constants"; export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { private static VALUES_DIRNAME = "values"; @@ -58,16 +59,11 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject projectRoot: projectRoot, deviceBuildOutputPath: path.join(projectRoot, "build", "outputs", "apk"), getValidPackageNames: (buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[] => { - if (buildOptions.isReleaseBuild) { - return [ - `${packageName}-release.apk`, - `${projectData.projectName}-release.apk` - ]; - } + const buildMode = buildOptions.isReleaseBuild ? Configurations.Release.toLowerCase() : Configurations.Debug.toLowerCase(); return [ - `${packageName}-debug.apk`, - `${projectData.projectName}-debug.apk`, + `${packageName}-${buildMode}.apk`, + `${projectData.projectName}-${buildMode}.apk` ]; }, frameworkFilesExtensions: [".jar", ".dat", ".so"], @@ -383,7 +379,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject // check whether the dependency that's being removed has native code let pluginConfigDir = path.join(this.getPlatformData(projectData).projectRoot, "configurations", pluginData.name); if (this.$fs.exists(pluginConfigDir)) { - await this.cleanProject(this.getPlatformData(projectData).projectRoot, [], projectData); + await this.cleanProject(this.getPlatformData(projectData).projectRoot, projectData); } } catch (e) { if (e.code === "ENOENT") { @@ -414,12 +410,9 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - // We don't need release options here - let buildOptions = this.getBuildOptions({ release: false }, projectData); - let projectRoot = this.getPlatformData(projectData).projectRoot; - await this.cleanProject(projectRoot, buildOptions, projectData); + await this.cleanProject(projectRoot, projectData); } } @@ -432,19 +425,16 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return this.$childProcess.spawnFromEvent(gradleBin, ["--stop", "--quiet"], "close", { stdio: "inherit", cwd: projectRoot }); } - public async cleanProject(projectRoot: string, options: string[], projectData: IProjectData): Promise { - // In case options are not passed, we'll use the default ones for cleaning the project. - // In case we do not do this, we'll required android-23 (default in build.gradle) for cleaning the project. - options = options.length === 0 ? this.getBuildOptions({ release: false }, projectData) : options; - - options.unshift("clean"); + public async cleanProject(projectRoot: string, projectData: IProjectData): Promise { + const buildOptions = this.getBuildOptions({ release: false }, projectData); + buildOptions.unshift("clean"); let gradleBin = path.join(projectRoot, "gradlew"); if (this.$hostInfo.isWindows) { gradleBin += ".bat"; } - await this.spawn(gradleBin, options, { stdio: "inherit", cwd: this.getPlatformData(projectData).projectRoot }); + await this.spawn(gradleBin, buildOptions, { stdio: "inherit", cwd: this.getPlatformData(projectData).projectRoot }); } public async cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise { diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index e94d3590c5..6ef796bec8 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -39,7 +39,7 @@ class IOSDebugService implements IDebugService { return "ios"; } - public async debug(projectData: IProjectData): Promise { + public async debug(projectData: IProjectData, buildConfig: IBuildConfig): Promise { if (this.$options.debugBrk && this.$options.start) { this.$errors.failWithoutHelp("Expected exactly one of the --debug-brk or --start options."); } @@ -50,26 +50,26 @@ class IOSDebugService implements IDebugService { if (this.$options.emulator) { if (this.$options.debugBrk) { - return this.emulatorDebugBrk(projectData, true); + return this.emulatorDebugBrk(projectData, buildConfig, true); } else if (this.$options.start) { return this.emulatorStart(projectData); } else { - return this.emulatorDebugBrk(projectData); + return this.emulatorDebugBrk(projectData, buildConfig); } } else { if (this.$options.debugBrk) { - return this.deviceDebugBrk(projectData, true); + return this.deviceDebugBrk(projectData, buildConfig, true); } else if (this.$options.start) { return this.deviceStart(projectData); } else { - return this.deviceDebugBrk(projectData, false); + return this.deviceDebugBrk(projectData, buildConfig, false); } } } - public async debugStart(projectData: IProjectData): Promise { + public async debugStart(projectData: IProjectData, buildConfig: IBuildConfig): Promise { await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); - this.$devicesService.execute(async (device: Mobile.IiOSDevice) => await device.isEmulator ? this.emulatorDebugBrk(projectData) : this.debugBrkCore(device, projectData)); + this.$devicesService.execute(async (device: Mobile.IiOSDevice) => await device.isEmulator ? this.emulatorDebugBrk(projectData, buildConfig) : this.debugBrkCore(device, projectData)); } public async debugStop(): Promise { @@ -93,10 +93,10 @@ class IOSDebugService implements IDebugService { } } - private async emulatorDebugBrk(projectData: IProjectData, shouldBreak?: boolean): Promise { + private async emulatorDebugBrk(projectData: IProjectData, buildConfig: IBuildConfig, shouldBreak?: boolean): Promise { let platformData = this.$platformsData.getPlatformData(this.platform, projectData); - let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData, { isReleaseBuild: this.$options.release }); + let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData, buildConfig); let args = shouldBreak ? "--nativescript-debug-brk" : "--nativescript-debug-start"; let child_process = await this.$iOSEmulatorServices.runApplicationOnEmulator(emulatorPackage.packageName, { @@ -135,11 +135,11 @@ class IOSDebugService implements IDebugService { await iOSEmulator.postDarwinNotification(attachRequestMessage); } - private async deviceDebugBrk(projectData: IProjectData, shouldBreak?: boolean): Promise { + private async deviceDebugBrk(projectData: IProjectData, buildConfig: IBuildConfig, shouldBreak?: boolean): Promise { await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); this.$devicesService.execute(async (device: iOSDevice.IOSDevice) => { if (device.isEmulator) { - return await this.emulatorDebugBrk(projectData, shouldBreak); + return await this.emulatorDebugBrk(projectData, buildConfig, shouldBreak); } const runOptions: IRunPlatformOptions = { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 75438ad918..48a16f9762 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -666,7 +666,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return { stderr: "", stdout: "", exitCode: 0 }; } - public async cleanProject(projectRoot: string, options: string[]): Promise { + public async cleanProject(projectRoot: string): Promise { return Promise.resolve(); } diff --git a/lib/services/local-build-service.ts b/lib/services/local-build-service.ts index 2a509e09b8..fedaeb1ce9 100644 --- a/lib/services/local-build-service.ts +++ b/lib/services/local-build-service.ts @@ -16,7 +16,7 @@ export class LocalBuildService extends EventEmitter { }); platformBuildOptions.buildOutputStdio = "pipe"; await this.$platformService.buildPlatform(platform, platformBuildOptions, this.$projectData); - return this.$platformService.lastOutputPath(platform, { isForDevice: platformBuildOptions.buildForDevice, isReleaseBuild: platformBuildOptions.release }, this.$projectData); + return this.$platformService.lastOutputPath(platform, platformBuildOptions, this.$projectData); } } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 70c8cf405e..841175a125 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -243,7 +243,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { let previousPrepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); // clean up prepared plugins when not building for release if (previousPrepareInfo && previousPrepareInfo.release !== appFilesUpdaterOptions.release) { - await platformData.platformProjectService.cleanProject(platformData.projectRoot, [], projectData); + await platformData.platformProjectService.cleanProject(platformData.projectRoot, projectData); } } @@ -425,21 +425,21 @@ export class PlatformService extends EventEmitter implements IPlatformService { return !localBuildInfo || !deviceBuildInfo || deviceBuildInfo.buildTime !== localBuildInfo.buildTime; } - public async installApplication(device: Mobile.IDevice, options: IRelease, projectData: IProjectData): Promise { + public async installApplication(device: Mobile.IDevice, buildConfig: IBuildConfig, projectData: IProjectData): Promise { this.$logger.out("Installing..."); let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); let packageFile = ""; if (this.$devicesService.isiOSSimulator(device)) { - packageFile = this.getLatestApplicationPackageForEmulator(platformData, { isReleaseBuild: options.release }).packageName; + packageFile = this.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; } else { - packageFile = this.getLatestApplicationPackageForDevice(platformData, { isReleaseBuild: options.release }).packageName; + packageFile = this.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; } await platformData.platformProjectService.cleanDeviceTempFolder(device.deviceInfo.identifier, projectData); await device.applicationManager.reinstallApplication(projectData.projectId, packageFile); - if (!options.release) { + if (!buildConfig.release) { let deviceFilePath = await this.getDeviceBuildInfoFilePath(device, projectData); let buildInfoFilePath = this.getBuildOutputPath(device.deviceInfo.platform, platformData, { buildForDevice: !device.isEmulator }); let appIdentifier = projectData.projectId; @@ -475,7 +475,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } if (deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, projectData))) { - await this.installApplication(device, deployOptions, projectData); + await this.installApplication(device, buildConfig, projectData); } else { this.$logger.out("Skipping install."); } @@ -581,13 +581,13 @@ export class PlatformService extends EventEmitter implements IPlatformService { appUpdater.cleanDestinationApp(); } - public lastOutputPath(platform: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): string { + public lastOutputPath(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): string { let packageFile: string; let platformData = this.$platformsData.getPlatformData(platform, projectData); - if (settings.isForDevice) { - packageFile = this.getLatestApplicationPackageForDevice(platformData, settings).packageName; + if (buildConfig.buildForDevice) { + packageFile = this.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; } else { - packageFile = this.getLatestApplicationPackageForEmulator(platformData, settings).packageName; + packageFile = this.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; } if (!packageFile || !this.$fs.exists(packageFile)) { this.$errors.failWithoutHelp("Unable to find built application. Try 'tns build %s'.", platform); @@ -595,11 +595,11 @@ export class PlatformService extends EventEmitter implements IPlatformService { return packageFile; } - public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean, isReleaseBuild: boolean }, projectData: IProjectData): void { + public copyLastOutput(platform: string, targetPath: string, buildConfig: IBuildConfig, projectData: IProjectData): void { platform = platform.toLowerCase(); targetPath = path.resolve(targetPath); - let packageFile = this.lastOutputPath(platform, settings, projectData); + let packageFile = this.lastOutputPath(platform, buildConfig, projectData); this.$fs.ensureDirectoryExists(path.dirname(targetPath)); @@ -742,12 +742,12 @@ export class PlatformService extends EventEmitter implements IPlatformService { return packages[0]; } - public getLatestApplicationPackageForDevice(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage { - return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: true, isReleaseBuild: buildOptions.isReleaseBuild })); + public getLatestApplicationPackageForDevice(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage { + return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: true, isReleaseBuild: buildConfig.release })); } - public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildOptions: { isReleaseBuild: boolean }): IApplicationPackage { - return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: false, isReleaseBuild: buildOptions.isReleaseBuild })); + public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage { + return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: false, isReleaseBuild: buildConfig.release })); } private async updatePlatform(platform: string, version: string, platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise { diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 94e1d74aa4..befe87b170 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -45,10 +45,10 @@ export class ProjectChangesService implements IProjectChangesService { return this._changesInfo; } - public checkForChanges(platform: string, projectData: IProjectData, buildOptions: IProjectChangesOptions): IProjectChangesInfo { + public checkForChanges(platform: string, projectData: IProjectData, projectChangesOptions: IProjectChangesOptions): IProjectChangesInfo { let platformData = this.$platformsData.getPlatformData(platform, projectData); this._changesInfo = new ProjectChangesInfo(); - if (!this.ensurePrepareInfo(platform, projectData, buildOptions)) { + if (!this.ensurePrepareInfo(platform, projectData, projectChangesOptions)) { this._newFiles = 0; this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); this._changesInfo.packageChanged = this.filesChanged([path.join(projectData.projectDir, "package.json")]); @@ -76,7 +76,7 @@ export class ProjectChangesService implements IProjectChangesService { } } if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - const nextCommandProvisionUUID = buildOptions.provision; + const nextCommandProvisionUUID = projectChangesOptions.provision; // We should consider reading here the provisioning profile UUID from the xcodeproj and xcconfig. const prevProvisionUUID = this._prepareInfo.iOSProvisioningProfileUUID; if (nextCommandProvisionUUID !== prevProvisionUUID) { @@ -85,13 +85,13 @@ export class ProjectChangesService implements IProjectChangesService { this._prepareInfo.iOSProvisioningProfileUUID = nextCommandProvisionUUID; } } - if (buildOptions.bundle !== this._prepareInfo.bundle || buildOptions.release !== this._prepareInfo.release) { + if (projectChangesOptions.bundle !== this._prepareInfo.bundle || projectChangesOptions.release !== this._prepareInfo.release) { this._changesInfo.appFilesChanged = true; this._changesInfo.appResourcesChanged = true; this._changesInfo.modulesChanged = true; this._changesInfo.configChanged = true; - this._prepareInfo.release = buildOptions.release; - this._prepareInfo.bundle = buildOptions.bundle; + this._prepareInfo.release = projectChangesOptions.release; + this._prepareInfo.bundle = projectChangesOptions.bundle; } if (this._changesInfo.packageChanged) { this._changesInfo.modulesChanged = true; @@ -133,7 +133,7 @@ export class ProjectChangesService implements IProjectChangesService { this.$fs.writeJson(prepareInfoFilePath, this._prepareInfo); } - private ensurePrepareInfo(platform: string, projectData: IProjectData, buildOptions: { bundle: boolean, release: boolean, provision: string }): boolean { + private ensurePrepareInfo(platform: string, projectData: IProjectData, projectChangesOptions: IProjectChangesOptions): boolean { this._prepareInfo = this.getPrepareInfo(platform, projectData); if (this._prepareInfo) { let platformData = this.$platformsData.getPlatformData(platform, projectData); @@ -144,8 +144,8 @@ export class ProjectChangesService implements IProjectChangesService { } this._prepareInfo = { time: "", - bundle: buildOptions.bundle, - release: buildOptions.release, + bundle: projectChangesOptions.bundle, + release: projectChangesOptions.release, changesRequireBuild: true, changesRequireBuildTime: null }; diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 40593cb418..099ec6343c 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -73,9 +73,10 @@ class TestExecutionService implements ITestExecutionService { await this.$usbLiveSyncService.liveSync(platform, projectData); if (this.$options.debugBrk) { + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); this.$logger.info('Starting debugger...'); let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); - await debugService.debugStart(projectData); + await debugService.debugStart(projectData, buildConfig); } resolve(); } catch (err) { @@ -127,20 +128,22 @@ class TestExecutionService implements ITestExecutionService { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + emulator: this.$options.emulator, + projectDir: this.$options.path, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); + if (this.$options.debugBrk) { - await this.getDebugService(platform).debug(projectData); + await this.getDebugService(platform).debug(projectData, buildConfig); } else { - const deployOptions: IDeployPlatformOptions = { - clean: this.$options.clean, - device: this.$options.device, - emulator: this.$options.emulator, - projectDir: this.$options.path, - platformTemplate: this.$options.platformTemplate, - release: this.$options.release, - provision: this.$options.provision, - teamId: this.$options.teamId - }; - await this.$platformService.deployPlatform(platform, appFilesUpdaterOptions, deployOptions, projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); await this.$usbLiveSyncService.liveSync(platform, projectData); } diff --git a/test/project-data.ts b/test/project-data.ts index 2cee2f4b8a..44209529d7 100644 --- a/test/project-data.ts +++ b/test/project-data.ts @@ -25,6 +25,8 @@ describe("projectData", () => { testInjector.register("errors", stubs.ErrorsStub); + testInjector.register("logger", stubs.LoggerStub); + testInjector.register("options", {}); testInjector.register("projectData", ProjectData); diff --git a/test/stubs.ts b/test/stubs.ts index b2ed72a0ba..46585c861c 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -352,7 +352,7 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor async stopServices(): Promise { return Promise.resolve({stderr: "", stdout: "", exitCode: 0}); } - async cleanProject(projectRoot: string, options: string[]): Promise { + async cleanProject(projectRoot: string, projectData: IProjectData): Promise { return Promise.resolve(); } } @@ -664,14 +664,14 @@ export class PlatformServiceStub extends EventEmitter implements IPlatformServic return null; } - public getLatestApplicationPackageForEmulator(platformData: IPlatformData): IApplicationPackage { + public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage { return null; } - public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }): void { + public copyLastOutput(platform: string, targetPath: string, buildConfig: IBuildConfig): void { } - public lastOutputPath(platform: string, settings: { isForDevice: boolean }): string { + public lastOutputPath(platform: string, buildConfig: IBuildConfig): string { return ""; }